]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tuplet-bracket.cc
Set horizon_padding by default for tie formatting, vertical staff distance
[lilypond.git] / lily / tuplet-bracket.cc
index bc91d17aea1f9ce1f110b7d9d350d8434da38a23..d13cf1c613e50d7103533c0f9c779334e73008cb 100644 (file)
@@ -3,7 +3,7 @@
 
   source file of the GNU LilyPond music typesetter
 
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 1997--2005 Jan Nieuwenhuizen <janneke@gnu.org>
+  (c) 1997--2006 Jan Nieuwenhuizen <janneke@gnu.org>
   Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
 
   Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
 
   todo: handle breaking elegantly.
 */
 
   todo: handle breaking elegantly.
 */
 
-#include <math.h>
 
 #include "tuplet-bracket.hh"
 #include "line-interface.hh"
 #include "beam.hh"
 #include "warn.hh"
 
 #include "tuplet-bracket.hh"
 #include "line-interface.hh"
 #include "beam.hh"
 #include "warn.hh"
-#include "font-interface.hh"
 #include "output-def.hh"
 #include "output-def.hh"
+#include "font-interface.hh"
 #include "text-interface.hh"
 #include "stem.hh"
 #include "note-column.hh"
 #include "text-interface.hh"
 #include "stem.hh"
 #include "note-column.hh"
 #include "spanner.hh"
 #include "staff-symbol-referencer.hh"
 #include "lookup.hh"
 #include "spanner.hh"
 #include "staff-symbol-referencer.hh"
 #include "lookup.hh"
+#include "paper-column.hh"
+#include "moment.hh"
 
 static Item *
 get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
 {
 
 static Item *
 get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
 {
-  Spanner *me = dynamic_cast<Spanner*> (me_grob);
+  Spanner *me = dynamic_cast<Spanner *> (me_grob);
   Item *g = me->get_bound (hdir);
   if (Note_column::has_interface (g)
       && Note_column::get_stem (g)
       && Note_column::dir (g) == my_dir)
   Item *g = me->get_bound (hdir);
   if (Note_column::has_interface (g)
       && Note_column::get_stem (g)
       && Note_column::dir (g) == my_dir)
-    {
-      g = Note_column::get_stem (g);
-    }
-  
+    g = Note_column::get_stem (g);
+
   return g;
 }
 
   return g;
 }
 
+
+void
+flatten_number_pair_property (Grob *me,
+                             Direction xdir,  SCM sym)
+{
+  Drul_array<Real> zero (0, 0);
+  Drul_array<Real> pair
+    = robust_scm2drul (me->internal_get_property (sym), zero);
+  pair[xdir] = 0.0;
+  
+  me->set_property (sym, ly_interval2scm (pair));
+}
+
+
+/*
+  Return beam that encompasses the span of the tuplet bracket.
+*/
+
 Grob *
 Grob *
-Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array<Grob> const &cols, bool *equally_long)
+Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols,
+                              bool *equally_long)
 {
 {
-  /*
-    ugh: code dup.
-  */
   Spanner *me = dynamic_cast<Spanner *> (me_grob);
   Spanner *me = dynamic_cast<Spanner *> (me_grob);
-  
+
   if (me->get_bound (LEFT)->break_status_dir ()
       || me->get_bound (RIGHT)->break_status_dir ())
     return 0;
   if (me->get_bound (LEFT)->break_status_dir ()
       || me->get_bound (RIGHT)->break_status_dir ())
     return 0;
-  
-  Grob *s1 = Note_column::get_stem (cols[0]);
-  Grob *s2 = Note_column::get_stem (cols.top ());
 
 
-  Grob *b1 = s1 ? Stem::get_beam (s1) : 0;
-  Grob *b2 = s2 ? Stem::get_beam (s2) : 0;
+  Drul_array<Grob*> stems (Note_column::get_stem (cols[0]),
+                          Note_column::get_stem (cols.back ()));
 
 
+  if (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
+      != me->get_bound (RIGHT)->get_column())
+    return 0;
 
 
+  Drul_array<Grob*> beams;
+  Direction d = LEFT;
+  do {
+    beams[d] = stems[d] ? Stem::get_beam (stems[d]) : 0;
+  } while (flip (&d) != LEFT);
+  
   *equally_long = false;
   *equally_long = false;
-  if (! (b1 && (b1 == b2) && !me->is_broken ()))
+  if (! (beams[LEFT] && (beams[LEFT] == beams[RIGHT]) && !me->is_broken ()))
     return 0;
 
     return 0;
 
-  extract_grob_set (b1, "stems", beam_stems);
+  extract_grob_set (beams[LEFT], "stems", beam_stems);
   if (beam_stems.size () == 0)
     {
       programming_error ("beam under tuplet bracket has no stems");
   if (beam_stems.size () == 0)
     {
       programming_error ("beam under tuplet bracket has no stems");
@@ -92,140 +113,214 @@ Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array<Grob> const &cols, bool
       return 0;
     }
 
       return 0;
     }
 
-  *equally_long = (beam_stems[0] == s1 && beam_stems.top () == s2);
-  return b1;
+  *equally_long =
+    (beam_stems[0] == stems[LEFT]
+     && beam_stems.back () == stems[RIGHT]);
+  return beams[LEFT];
 }
 
 }
 
-/*
-  TODO:
 
 
-  in the case that there is no bracket, but there is a (single) beam,
-  follow beam precisely for determining tuplet number location.
-*/
-MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, 1);
+MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_connect_to_neighbors,1);
 SCM
 SCM
-Tuplet_bracket::print (SCM smob)
+Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
 {
 {
-  Grob *me = unsmob_grob (smob);
-  Stencil mol;
-  extract_grob_set (me, "note-columns", columns);
+  Spanner *me = unsmob_spanner (smob);
 
 
-  if (!columns.size ())
-    return mol.smobbed_copy ();
-
-  {
-    SCM lp = me->get_property ("left-position");
-    SCM rp = me->get_property ("right-position");
+  Direction dir = get_grob_direction (me); 
+  Drul_array<Item *> bounds (get_x_bound_item (me, LEFT, dir),
+                            get_x_bound_item (me, RIGHT, dir));
+  
+  Drul_array<bool> connect_to_other (false, false);
+  Direction d = LEFT;
+  do
+    {
+      Direction break_dir = bounds[d]->break_status_dir ();
+      
+      Spanner *orig_spanner = dynamic_cast<Spanner*> (me->original ());
 
 
-    if (!scm_is_number (rp) || !scm_is_number (lp))
-      {
-       /*
-         UGH. dependency tracking!
-        */
-       extract_grob_set (me, "tuplets", tuplets);
-       for (int i = 0; i < tuplets.size (); i++)
-         Tuplet_bracket::print (tuplets[i]->self_scm());
+      vsize neighbor_idx = me->get_break_index () - break_dir;
+      if (break_dir
+         && d == RIGHT
+         && neighbor_idx < orig_spanner->broken_intos_.size ())
+       {
+         Grob *neighbor = orig_spanner->broken_intos_[neighbor_idx];
 
 
-       after_line_breaking (smob);
-      }
-  }
+         /* trigger possible suicide*/
+         (void) neighbor->get_property ("positions");
+       }
 
 
-  Real ly = robust_scm2double (me->get_property ("left-position"), 0);
-  Real ry = robust_scm2double (me->get_property ("right-position"), 0);
+      connect_to_other[d]
+       = (break_dir
+          && neighbor_idx < orig_spanner->broken_intos_.size ()
+          && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
+    }
+  while (flip (&d) != LEFT);
 
 
-  bool equally_long = false;
-  Grob *par_beam = parallel_beam (me, columns, &equally_long);
 
 
-  Spanner *sp = dynamic_cast<Spanner *> (me);
+  if (connect_to_other[LEFT] || connect_to_other[RIGHT])
+    return scm_cons (scm_from_bool (connect_to_other[LEFT]),
+                    scm_from_bool (connect_to_other[RIGHT]));
+                    
+  return SCM_EOL;
+}
 
 
-  bool bracket_visibility = !(par_beam && equally_long);
-  bool number_visibility = true;
+Grob* 
+Tuplet_bracket::get_common_x (Spanner *me)
+{
+  extract_grob_set (me, "note-columns", columns);
 
 
-  /*
-    Fixme: the type of this prop is sucky.
-  */
-  SCM bracket = me->get_property ("bracket-visibility");
-  if (scm_is_bool (bracket))
-    {
-      bracket_visibility = ly_scm2bool (bracket);
-    }
-  else if (bracket == ly_symbol2scm ("if-no-beam"))
-    bracket_visibility = !par_beam;
+  Grob * commonx = common_refpoint_of_array (columns, me, X_AXIS);
+  commonx = commonx->common_refpoint (me->get_bound (LEFT), X_AXIS);
+  commonx = commonx->common_refpoint (me->get_bound (RIGHT), X_AXIS);
 
 
-  SCM numb = me->get_property ("number-visibility");
-  if (scm_is_bool (numb))
-    {
-      number_visibility = ly_scm2bool (numb);
-    }
-  else if (numb == ly_symbol2scm ("if-no-beam"))
-    number_visibility = !par_beam;
+  return commonx;
+}
+  
+MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_control_points,1)
+SCM
+Tuplet_bracket::calc_control_points (SCM smob)
+{
+  Spanner *me = unsmob_spanner (smob);
 
 
-  Grob *commonx = columns[0]->common_refpoint (columns.top (), X_AXIS);
+  extract_grob_set (me, "note-columns", columns);
 
 
+  SCM scm_positions = me->get_property ("positions");
+  if (!me->is_live ())
+    return SCM_EOL;
   
   
-  commonx = commonx->common_refpoint (sp->get_bound (LEFT), X_AXIS);
-  commonx = commonx->common_refpoint (sp->get_bound (RIGHT), X_AXIS);
+  if (!scm_is_pair (scm_positions))
+    programming_error ("Positions should be number pair");
+    
+  Drul_array<Real> positions
+    = robust_scm2drul (scm_positions, Drul_array<Real> (0,0));
 
 
+  Grob *commonx = get_common_x (me);
   Direction dir = get_grob_direction (me);
 
   Drul_array<Item *> bounds;
   bounds[LEFT] = get_x_bound_item (me, LEFT, dir);
   bounds[RIGHT] = get_x_bound_item (me, RIGHT, dir);
 
   Direction dir = get_grob_direction (me);
 
   Drul_array<Item *> bounds;
   bounds[LEFT] = get_x_bound_item (me, LEFT, dir);
   bounds[RIGHT] = get_x_bound_item (me, RIGHT, dir);
 
+  Drul_array<bool> connect_to_other =
+    robust_scm2booldrul (me->get_property ("connect-to-neighbor"),
+                        Drul_array<bool> (false, false));
+  
+    
   Interval x_span;
   Direction d = LEFT;
   do
     {
       x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
 
   Interval x_span;
   Direction d = LEFT;
   do
     {
       x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
 
-      if (bounds[d]->break_status_dir())
+      if (connect_to_other[d])
        {
          Interval overshoot (robust_scm2drul (me->get_property ("break-overshoot"),
        {
          Interval overshoot (robust_scm2drul (me->get_property ("break-overshoot"),
-                                              Interval (0,0)));
+                                              Interval (-0.5, 0.0)));
+
+         if (d == RIGHT)
+           x_span[d] += d * overshoot[d];
+         else
+           x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
+             - overshoot[LEFT];
+       }
+      
+      else if (d == RIGHT
+              && (columns.empty ()
+                  || (bounds[d]->get_column ()
+                      != dynamic_cast<Item *> (columns.back ())->get_column ())))
+       {
+         /*
+           We're connecting to a column, for the last bit of a broken
+           fullLength bracket.
+           
+           TODO: make padding tunable?
+         */
+         Real padding = 1.0;
 
 
-         x_span[d] += d * overshoot[d];
+         if (bounds[d]->break_status_dir ())
+           padding = 0.0;
+         
+         x_span[d]
+           = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT]
+           - padding;
        }
     }
   while (flip (&d) != LEFT);
 
        }
     }
   while (flip (&d) != LEFT);
 
-  Real w = x_span.length();
-  SCM number = me->get_property ("text");
+  
+  
+  x_span -= me->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS);
+  return scm_list_2 (ly_offset2scm (Offset (x_span[LEFT], positions[LEFT])),
+                    ly_offset2scm (Offset (x_span[RIGHT], positions[RIGHT])));
+}
 
 
-  Output_def *pap = me->get_layout ();
-  Stencil num;
-  if (scm_is_string (number) && number_visibility)
-    {
-      SCM properties = Font_interface::text_font_alist_chain (me);
-      SCM snum = Text_interface::interpret_markup (pap->self_scm (), properties, number);
-      num = *unsmob_stencil (snum);
-      num.align_to (X_AXIS, CENTER);
-      num.translate_axis (w / 2, X_AXIS);
-      num.align_to (Y_AXIS, CENTER);
+/*
+  TODO:
+
+  in the case that there is no bracket, but there is a (single) beam,
+  follow beam precisely for determining tuplet number location.
+*/
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, 1);
+SCM
+Tuplet_bracket::print (SCM smob)
+{
+  Spanner *me = unsmob_spanner (smob);
+  Stencil mol;
 
 
-      num.translate_axis ((ry - ly) / 2, Y_AXIS);
+  extract_grob_set (me, "note-columns", columns);
+  bool equally_long = false;
+  Grob *par_beam = parallel_beam (me, columns, &equally_long);
+  
+  bool bracket_visibility = !(par_beam && equally_long);
+  /*
+    Fixme: the type of this prop is sucky.
+  */
+  SCM bracket = me->get_property ("bracket-visibility");
+  if (scm_is_bool (bracket))
+    bracket_visibility = ly_scm2bool (bracket);
+  else if (bracket == ly_symbol2scm ("if-no-beam"))
+    bracket_visibility = !par_beam;
 
 
-      mol.add_stencil (num);
+  
+  SCM cpoints =  me->get_property ("control-points");
+  if (scm_ilength (cpoints) < 2)
+    {
+      me->suicide ();
+      return SCM_EOL;
     }
     }
+  
+  Drul_array<Offset> points;
+  points[LEFT] = ly_scm2offset (scm_car (cpoints));
+  points[RIGHT] = ly_scm2offset (scm_cadr (cpoints));
+  
+  Interval x_span (points[LEFT][X_AXIS], points[RIGHT][X_AXIS]);
+  Drul_array<Real> positions (points[LEFT][Y_AXIS], points[RIGHT][Y_AXIS]);
+
 
 
+
+  Output_def *pap = me->layout ();
+
+  Grob *number_grob = unsmob_grob (me->get_object ("tuplet-number"));
+  
   /*
     No bracket when it would be smaller than the number.
   /*
     No bracket when it would be smaller than the number.
-
-    TODO: should use GAP in calculation too.
   */
   */
-  if (bracket_visibility && number_visibility
-      && mol.extent (X_AXIS).length () > w)
+  Real gap = 0.;
+  if (bracket_visibility && number_grob)
     {
     {
-      bracket_visibility = false;
+      Interval ext = number_grob->extent (number_grob, X_AXIS);
+      if (!ext.is_empty ())
+       {
+         gap = ext.length () + 1.0;
+      
+         if (0.75 * x_span.length () < gap)
+           bracket_visibility = false;
+       }
     }
 
   if (bracket_visibility)
     {
     }
 
   if (bracket_visibility)
     {
-      Real gap = 0.;
-
-      if (!num.extent (X_AXIS).is_empty ())
-       gap = num.extent (X_AXIS).length () + 1.0;
-
-      Drul_array<Real> zero (0,0);
+      Drul_array<Real> zero (0, 0);
       Real ss = Staff_symbol_referencer::staff_space (me);
       Drul_array<Real> height
        = robust_scm2drul (me->get_property ("edge-height"), zero);
       Real ss = Staff_symbol_referencer::staff_space (me);
       Drul_array<Real> height
        = robust_scm2drul (me->get_property ("edge-height"), zero);
@@ -233,26 +328,49 @@ Tuplet_bracket::print (SCM smob)
        = robust_scm2drul (me->get_property ("bracket-flare"), zero);
       Drul_array<Real> shorten
        = robust_scm2drul (me->get_property ("shorten-pair"), zero);
        = robust_scm2drul (me->get_property ("bracket-flare"), zero);
       Drul_array<Real> shorten
        = robust_scm2drul (me->get_property ("shorten-pair"), zero);
+      Drul_array<Stencil> edge_stencils;
 
 
+      Direction dir = get_grob_direction (me);
+      
       scale_drul (&height, -ss * dir);
       scale_drul (&flare, ss);
       scale_drul (&shorten, ss);
 
       scale_drul (&height, -ss * dir);
       scale_drul (&flare, ss);
       scale_drul (&shorten, ss);
 
-      
+      Drul_array<bool> connect_to_other =
+       robust_scm2booldrul (me->get_property ("connect-to-neighbor"),
+                            Drul_array<bool> (false, false));
+
+      Direction d = LEFT;
       do
        {
       do
        {
-         if (bounds[d]->break_status_dir ())
+         if (connect_to_other[d])
            {
              height[d] = 0.0;
              flare[d] = 0.0;
              shorten[d] = 0.0;
            {
              height[d] = 0.0;
              flare[d] = 0.0;
              shorten[d] = 0.0;
+
+             SCM edge_text = me->get_property ("edge-text");
+
+             if (scm_is_pair (edge_text))
+               {
+                 SCM properties = Font_interface::text_font_alist_chain (me);
+                 SCM text = index_get_cell (edge_text, d);
+                 if (Text_interface::is_markup (text))
+                   {
+                     SCM t = Text_interface::interpret_markup (pap->self_scm (),
+                                                               properties, text);
+
+                     Stencil *edge_text = unsmob_stencil (t);
+                     edge_text->translate_axis (x_span[d] - x_span[LEFT], X_AXIS);
+                     edge_stencils[d] = *edge_text;
+                   }
+               }
            }
        }
       while (flip (&d) != LEFT);
 
            }
        }
       while (flip (&d) != LEFT);
 
       Stencil brack = make_bracket (me, Y_AXIS,
       Stencil brack = make_bracket (me, Y_AXIS,
-                                   Offset (w, ry - ly),
+                                   points[RIGHT] - points[LEFT],
                                    height,
                                    /*
                                      0.1 = more space at right due to italics
                                    height,
                                    /*
                                      0.1 = more space at right due to italics
@@ -263,32 +381,15 @@ Tuplet_bracket::print (SCM smob)
 
       do
        {
 
       do
        {
-         if (bounds[d]->break_status_dir ())
-           {
-             SCM properties = Font_interface::text_font_alist_chain (me);
-             SCM edge_text = me->get_property ("edge-text");
-             
-             SCM text = index_get_cell (edge_text, d);
-             if (Text_interface::is_markup (text))
-               {
-                 SCM t = Text_interface::interpret_markup (pap->self_scm (), properties,
-                                                           text);
-                 
-                 Stencil *edge_text = unsmob_stencil (t);
-                 edge_text->translate_axis (x_span[d] - x_span[LEFT], X_AXIS);
-                 mol.add_stencil (*edge_text);
-               }
-           }
+         if (!edge_stencils[d].is_empty ())
+           brack.add_stencil (edge_stencils[d]);
        }
       while (flip (&d) != LEFT);
 
        }
       while (flip (&d) != LEFT);
 
-      
       mol.add_stencil (brack);
     }
 
       mol.add_stencil (brack);
     }
 
-  mol.translate_axis (ly, Y_AXIS);
-  mol.translate_axis (x_span[LEFT]
-                     - sp->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS), X_AXIS);
+  mol.translate (points[LEFT]);
   return mol.smobbed_copy ();
 }
 
   return mol.smobbed_copy ();
 }
 
@@ -318,18 +419,15 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
 
   Direction d = LEFT;
   do
 
   Direction d = LEFT;
   do
-    {
-      straight_corners[d] += -d * shorten[d] / length * dz;
-    }
+    straight_corners[d] += -d * shorten[d] / length * dz;
   while (flip (&d) != LEFT);
 
   while (flip (&d) != LEFT);
 
-  if (gap.is_empty ())
-    gap = Interval (0, 0);
-  do
+  if (!gap.is_empty ())
     {
     {
-      gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
+      do
+       gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
+      while (flip (&d) != LEFT);
     }
     }
-  while (flip (&d) != LEFT);
 
   Drul_array<Offset> flare_corners = straight_corners;
   do
 
   Drul_array<Offset> flare_corners = straight_corners;
   do
@@ -343,14 +441,20 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
   Stencil m;
   do
     {
   Stencil m;
   do
     {
-      m.add_stencil (Line_interface::line (me, straight_corners[d],
-                                          gap_corners[d]));
+      if (!gap.is_empty ())
+       m.add_stencil (Line_interface::line (me, straight_corners[d],
+                                            gap_corners[d]));
 
       m.add_stencil (Line_interface::line (me, straight_corners[d],
                                           flare_corners[d]));
     }
 
       m.add_stencil (Line_interface::line (me, straight_corners[d],
                                           flare_corners[d]));
     }
+
   while (flip (&d) != LEFT);
 
   while (flip (&d) != LEFT);
 
+  if (gap.is_empty ())
+    m.add_stencil (Line_interface::line (me, straight_corners[LEFT],
+                                        straight_corners[RIGHT]));
+  
   return m;
 }
 
   return m;
 }
 
@@ -358,47 +462,53 @@ void
 Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
 {
   extract_grob_set (me, "note-columns", columns);
 Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
 {
   extract_grob_set (me, "note-columns", columns);
-  int l = 0;
+  vsize l = 0;
   while (l < columns.size () && Note_column::has_rests (columns[l]))
     l++;
 
   while (l < columns.size () && Note_column::has_rests (columns[l]))
     l++;
 
-  int r = columns.size ()- 1;
-  while (r >= l && Note_column::has_rests (columns[r]))
+  vsize r = columns.size ();
+  while (r > l && Note_column::has_rests (columns[r-1]))
     r--;
 
   *left = *right = 0;
 
     r--;
 
   *left = *right = 0;
 
-  if (l <= r)
+  if (l < r)
     {
       *left = columns[l];
     {
       *left = columns[l];
-      *right = columns[r];
+      *right = columns[r-1];
     }
 }
 
     }
 }
 
-
 /*
   use first -> last note for slope, and then correct for disturbing
   notes in between.  */
 void
 Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
 {
 /*
   use first -> last note for slope, and then correct for disturbing
   notes in between.  */
 void
 Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
 {
-  Spanner *me = dynamic_cast<Spanner*> (me_grob);
-  
+  Spanner *me = dynamic_cast<Spanner *> (me_grob);
+
   extract_grob_set (me, "note-columns", columns);
   extract_grob_set (me, "tuplets", tuplets);
   extract_grob_set (me, "note-columns", columns);
   extract_grob_set (me, "tuplets", tuplets);
-  
+
   Grob *commony = common_refpoint_of_array (columns, me, Y_AXIS);
   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
   Grob *commony = common_refpoint_of_array (columns, me, Y_AXIS);
   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
-  Grob *commonx = common_refpoint_of_array (columns, me, X_AXIS);
-  commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
-  commonx = commonx->common_refpoint (me->get_bound (LEFT), X_AXIS);
-  commonx = commonx->common_refpoint (me->get_bound (RIGHT), X_AXIS);
+  if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
+    commony = st->common_refpoint (commony, Y_AXIS);
 
 
+  Grob *commonx = get_common_x (me);
+  commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
 
   Interval staff;
   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
 
   Interval staff;
   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
-    staff = st->extent (commony, Y_AXIS);
-
+    {
+      Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
+      if  (pad >= 0.0)
+       {
+         staff = st->extent (commony, Y_AXIS);
+         staff.widen (pad);
+       }
+    }
+  
   Direction dir = get_grob_direction (me);
 
   /*
   Direction dir = get_grob_direction (me);
 
   /*
@@ -433,82 +543,72 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
 
   *offset = -dir * infinity_f;
 
 
   *offset = -dir * infinity_f;
 
-  if (!columns.size ())
-    return;
-
   Item *lgr = get_x_bound_item (me, LEFT, dir);
   Item *rgr = get_x_bound_item (me, RIGHT, dir);
   Real x0 = robust_relative_extent (lgr, commonx, X_AXIS)[LEFT];
   Real x1 = robust_relative_extent (rgr, commonx, X_AXIS)[RIGHT];
 
   Item *lgr = get_x_bound_item (me, LEFT, dir);
   Item *rgr = get_x_bound_item (me, RIGHT, dir);
   Real x0 = robust_relative_extent (lgr, commonx, X_AXIS)[LEFT];
   Real x1 = robust_relative_extent (rgr, commonx, X_AXIS)[RIGHT];
 
-  /*
-    offset
-  */
-  Real factor = columns.size () > 1 ? 1 / (x1 - x0) : 1.0;
+  vector<Offset> points;
+  points.push_back (Offset (x0 - x0, staff[dir]));
+  points.push_back (Offset (x1 - x0, staff[dir]));
 
 
-  Array<Offset> points;
-  for (int i = 0; i < columns.size (); i++)
+  for (vsize i = 0; i < columns.size (); i++)
     {
       Interval note_ext = columns[i]->extent (commony, Y_AXIS);
     {
       Interval note_ext = columns[i]->extent (commony, Y_AXIS);
-      note_ext.unite (staff);
       Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
 
       Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
       Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
 
       Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
-      points.push (Offset (x, notey));
+      points.push_back (Offset (x, notey));
     }
     }
-  
+
   /*
     This is a slight hack. We compute two encompass points from the
     bbox of the smaller tuplets.
   /*
     This is a slight hack. We compute two encompass points from the
     bbox of the smaller tuplets.
-    
+
     We assume that the smaller bracket is 1.0 space high.
   */
     We assume that the smaller bracket is 1.0 space high.
   */
-  
   Real ss = Staff_symbol_referencer::staff_space (me);
   Real ss = Staff_symbol_referencer::staff_space (me);
-  for (int i = 0; i < tuplets.size (); i++)
+  for (vsize i = 0; i < tuplets.size (); i++)
     {
       Interval tuplet_x (tuplets[i]->extent (commonx, X_AXIS));
       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
 
     {
       Interval tuplet_x (tuplets[i]->extent (commonx, X_AXIS));
       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
 
+      if (!tuplets[i]->is_live ())
+       continue;
+      
       Direction d = LEFT;
       Direction d = LEFT;
-      Real lp = scm_to_double (tuplets[i]->get_property ("left-position"));
-      Real rp = scm_to_double (tuplets[i]->get_property ("right-position"));
-      Real other_dy = rp - lp;
+      Drul_array<Real> positions = robust_scm2interval (tuplets[i]->get_property ("positions"),
+                                                       Interval (0,0));
+
+      
+      Real other_dy = positions[RIGHT] - positions[LEFT];
 
       do
        {
 
       do
        {
-         Real y =
-           tuplet_y.linear_combination (d * sign (other_dy));
+         Real y
+           tuplet_y.linear_combination (d * sign (other_dy));
 
 
-#if 0
          /*
          /*
-           Let's not take padding into account for nested tuplets.
+           We don't take padding into account for nested tuplets.
            the edges can come very close to the stems, likewise for
            nested tuplets?
            the edges can come very close to the stems, likewise for
            nested tuplets?
-          */
-         Drul_array<Real> my_height
-           = robust_scm2drul (me->get_property ("edge-height"), Interval (0,0));
-         if (dynamic_cast<Spanner*> (tuplets[i])->get_bound (d)
-             ==  me->get_bound (d))
-           {
-             y += dir * my_height[d];
-           }
-#endif
-         
-         points.push (Offset (tuplet_x[d] - x0, y));
+         */
+
+         points.push_back (Offset (tuplet_x[d] - x0, y));
        }
       while (flip (&d) != LEFT);
     }
 
        }
       while (flip (&d) != LEFT);
     }
 
-  for (int i = 0; i < points.size (); i++)
+  Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
+  for (vsize i = 0; i < points.size (); i++)
     {
       Real x = points[i][X_AXIS];
     {
       Real x = points[i][X_AXIS];
-      Real tuplety = *dy * x * factor;
+      Real tuplety = (*dy) * x * factor;
 
       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
        *offset = points[i][Y_AXIS] - tuplety;
     }
 
       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
        *offset = points[i][Y_AXIS] - tuplety;
     }
-                 
+
   *offset += scm_to_double (me->get_property ("padding")) * dir;
 
   /*
   *offset += scm_to_double (me->get_property ("padding")) * dir;
 
   /*
@@ -518,67 +618,49 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
     let's leave code for the future when possibly allow them to move
     into the staff once again.
   */
     let's leave code for the future when possibly allow them to move
     into the staff once again.
   */
-  if (*dy == 0 &&
-      fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
+  if (*dy == 0
+      && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
     {
       // quantize, then do collision check.
       *offset *= 2 / ss;
 
       *offset = rint (*offset);
     {
       // quantize, then do collision check.
       *offset *= 2 / ss;
 
       *offset = rint (*offset);
-      if (Staff_symbol_referencer::on_staffline (me, (int) rint (*offset)))
+      if (Staff_symbol_referencer::on_line (me, (int) rint (*offset)))
        *offset += dir;
 
       *offset *= 0.5 * ss;
     }
 }
 
        *offset += dir;
 
       *offset *= 0.5 * ss;
     }
 }
 
-/*
-  We depend on the beams if there are any.
-*/
-MAKE_SCHEME_CALLBACK (Tuplet_bracket, before_line_breaking, 1);
+
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_direction, 1);
 SCM
 SCM
-Tuplet_bracket::before_line_breaking (SCM smob)
+Tuplet_bracket::calc_direction (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
 {
   Grob *me = unsmob_grob (smob);
-  extract_grob_set (me, "note-columns", columns);
-
-  for (int i = columns.size (); i--;)
-    {
-      Grob *s = Note_column::get_stem (columns[i]);
-      Grob *b = s ? Stem::get_beam (s) : 0;
-      if (b)
-       me->add_dependency (b);
-    }
-  return SCM_UNSPECIFIED;
+  Direction dir = Tuplet_bracket::get_default_dir (me);
+  return scm_from_int (dir);
 }
 
 }
 
-MAKE_SCHEME_CALLBACK (Tuplet_bracket, after_line_breaking, 1);
-
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_positions, 1);
 SCM
 SCM
-Tuplet_bracket::after_line_breaking (SCM smob)
+Tuplet_bracket::calc_positions (SCM smob)
 {
 {
-  Grob *me = unsmob_grob (smob);
+  Spanner *me = unsmob_spanner (smob);
   extract_grob_set (me, "note-columns", columns);
 
   extract_grob_set (me, "note-columns", columns);
 
-  if (!columns.size ())
-    {
-      me->suicide ();
-      return SCM_UNSPECIFIED;
-    }
-  if (dynamic_cast<Spanner *> (me)->is_broken ())
+  /*
+    Don't print if it doesn't span time.
+   */
+  if (robust_scm2moment (me->get_bound (LEFT)->get_column ()->get_property ("when"), Moment (0))
+      == robust_scm2moment (me->get_bound (RIGHT)->get_column ()->get_property ("when"), Moment (0)))
     {
     {
-      me->warning (_ ("removing tuplet bracket across linebreak"));
       me->suicide ();
       me->suicide ();
-      return SCM_UNSPECIFIED;
+      return SCM_EOL;
     }
 
     }
 
+  
   Direction dir = get_grob_direction (me);
   Direction dir = get_grob_direction (me);
-  if (!dir)
-    {
-      dir = Tuplet_bracket::get_default_dir (me);
-      set_grob_direction (me, dir);
-    }
-
   bool equally_long = false;
   Grob *par_beam = parallel_beam (me, columns, &equally_long);
 
   bool equally_long = false;
   Grob *par_beam = parallel_beam (me, columns, &equally_long);
 
@@ -589,44 +671,37 @@ Tuplet_bracket::after_line_breaking (SCM smob)
   Real offset = 0.0;
   if (!par_beam
       || get_grob_direction (par_beam) != dir)
   Real offset = 0.0;
   if (!par_beam
       || get_grob_direction (par_beam) != dir)
+    calc_position_and_height (me, &offset, &dy);
+  else if  (columns.size ()
+           && Note_column::get_stem (columns[0])
+           && Note_column::get_stem (columns.back ()))
     {
     {
-      calc_position_and_height (me, &offset, &dy);
-    }
-  else
-    {
-      SCM ps = par_beam->get_property ("positions");
+      /*
+       trigger set_stem_ends
+       */
+      (void) par_beam->get_property ("quantized-positions");
 
 
-      Real lp = scm_to_double (scm_car (ps));
-      Real rp = scm_to_double (scm_cdr (ps));
 
 
-      /*
-       duh. magic.
-      */
-      offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
-      dy = rp- lp;
-    }
+      Drul_array<Grob *> stems (Note_column::get_stem (columns[0]),
+                               Note_column::get_stem (columns.back ()));
 
 
-  SCM lp = me->get_property ("left-position");
-  SCM rp = me->get_property ("right-position");
+      
+      
 
 
-  if (scm_is_number (lp) && !scm_is_number (rp))
-    {
-      rp = scm_from_double (scm_to_double (lp) + dy);
-    }
-  else if (scm_is_number (rp) && !scm_is_number (lp))
-    {
-      lp = scm_from_double (scm_to_double (rp) - dy);
-    }
-  else if (!scm_is_number (rp) && !scm_is_number (lp))
-    {
-      lp = scm_from_double (offset);
-      rp = scm_from_double (offset + dy);
-    }
+      Real ss = 0.5 * Staff_symbol_referencer::staff_space (me);
+      Real lp = ss * robust_scm2double (stems[LEFT]->get_property ("stem-end-position"), 0.0);
+      Real rp = ss * robust_scm2double (stems[RIGHT]->get_property ("stem-end-position"), 0.0);
 
 
-  me->set_property ("left-position", lp);
-  me->set_property ("right-position", rp);
+      
+      offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
+      dy = (rp - lp);
+    }
 
 
-  return SCM_UNSPECIFIED;
+  
+  SCM x = scm_cons (scm_from_double (offset),
+                   scm_from_double (offset + dy));
+  
+  return x;
 }
 
 /*
 }
 
 /*
@@ -637,7 +712,7 @@ Tuplet_bracket::get_default_dir (Grob *me)
 {
   Drul_array<int> dirs (0, 0);
   extract_grob_set (me, "note-columns", columns);
 {
   Drul_array<int> dirs (0, 0);
   extract_grob_set (me, "note-columns", columns);
-  for (int i = 0 ; i < columns.size (); i++)
+  for (vsize i = 0; i < columns.size (); i++)
     {
       Grob *nc = columns[i];
       Direction d = Note_column::dir (nc);
     {
       Grob *nc = columns[i];
       Direction d = Note_column::dir (nc);
@@ -652,8 +727,6 @@ void
 Tuplet_bracket::add_column (Grob *me, Item *n)
 {
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
 Tuplet_bracket::add_column (Grob *me, Item *n)
 {
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
-  me->add_dependency (n);
-
   add_bound_item (dynamic_cast<Spanner *> (me), n);
 }
 
   add_bound_item (dynamic_cast<Spanner *> (me), n);
 }
 
@@ -661,13 +734,9 @@ void
 Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
 {
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
 Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
 {
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
-  me->add_dependency (bracket);
 }
 
 }
 
-
-
 ADD_INTERFACE (Tuplet_bracket,
 ADD_INTERFACE (Tuplet_bracket,
-              "tuplet-bracket-interface",
               "A bracket with a number in the middle, used for tuplets. "
               "When the bracket spans  a line break, the value of "
               "@code{break-overshoot} determines how far it extends "
               "A bracket with a number in the middle, used for tuplets. "
               "When the bracket spans  a line break, the value of "
               "@code{break-overshoot} determines how far it extends "
@@ -675,8 +744,24 @@ ADD_INTERFACE (Tuplet_bracket,
               "At a line break, the markups in the @code{edge-text} are printed "
               "at the edges. ",
 
               "At a line break, the markups in the @code{edge-text} are printed "
               "at the edges. ",
 
-              "note-columns bracket-flare edge-height shorten-pair "
-              "tuplets edge-text break-overshoot "
-              "padding left-position right-position bracket-visibility "
-              "number-visibility thickness direction");
+
+              /* properties */
+              "bracket-flare "
+              "bracket-visibility "
+              "break-overshoot "
+              "connect-to-neighbor "
+              "control-points "
+              "direction "
+              "edge-height "
+              "edge-text "
+              "gap "
+              "positions "
+              "note-columns "
+              "padding "
+              "tuplet-number "
+              "shorten-pair "
+              "staff-padding "
+              "thickness "
+              "tuplets ");
+