]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tuplet-bracket.cc
Merge branch 'master' of ssh+git://hanwen@git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / lily / tuplet-bracket.cc
index 90edb7c94a53ce1a392b35ce792f7bdb6ed90401..cfae0b98650d2317976c2ff9fae9338fe434a149 100644 (file)
@@ -3,7 +3,7 @@
 
   source file of the GNU LilyPond music typesetter
 
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 1997--2006 Jan Nieuwenhuizen <janneke@gnu.org>
+  (c) 1997--2007 Jan Nieuwenhuizen <janneke@gnu.org>
   Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
 
   Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
 
@@ -44,6 +44,8 @@
 #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)
@@ -64,15 +66,20 @@ flatten_number_pair_property (Grob *me,
                              Direction xdir,  SCM sym)
 {
   Drul_array<Real> zero (0, 0);
                              Direction xdir,  SCM sym)
 {
   Drul_array<Real> zero (0, 0);
-  Drul_array<Real> pair = robust_scm2drul (me->internal_get_property (sym), zero);
+  Drul_array<Real> pair
+    = robust_scm2drul (me->internal_get_property (sym), zero);
   pair[xdir] = 0.0;
   
   pair[xdir] = 0.0;
   
-  me->internal_set_property (sym, ly_interval2scm (pair));
+  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)
 {
   Spanner *me = dynamic_cast<Spanner *> (me_grob);
 
 {
   Spanner *me = dynamic_cast<Spanner *> (me_grob);
 
@@ -81,10 +88,12 @@ Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array<Grob> const &cols, bool
     return 0;
 
   Drul_array<Grob*> stems (Note_column::get_stem (cols[0]),
     return 0;
 
   Drul_array<Grob*> stems (Note_column::get_stem (cols[0]),
-                          Note_column::get_stem (cols.top ()));
+                          Note_column::get_stem (cols.back ()));
 
 
-  if (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
-      != me->get_bound (RIGHT)->get_column())
+  if (!stems[RIGHT]
+      || !stems[LEFT]
+      || (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
+         != me->get_bound (RIGHT)->get_column ()))
     return 0;
 
   Drul_array<Grob*> beams;
     return 0;
 
   Drul_array<Grob*> beams;
@@ -105,12 +114,14 @@ Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array<Grob> const &cols, bool
       return 0;
     }
 
       return 0;
     }
 
-  *equally_long = (beam_stems[0] == stems[LEFT] && beam_stems.top () == stems[RIGHT]);
+  *equally_long =
+    (beam_stems[0] == stems[LEFT]
+     && beam_stems.back () == stems[RIGHT]);
   return beams[LEFT];
 }
 
 
   return beams[LEFT];
 }
 
 
-MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_connect_to_neighbors,1);
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_connect_to_neighbors,1);
 SCM
 Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
 {
 SCM
 Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
 {
@@ -125,10 +136,8 @@ Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
   do
     {
       Direction break_dir = bounds[d]->break_status_dir ();
   do
     {
       Direction break_dir = bounds[d]->break_status_dir ();
-      
       Spanner *orig_spanner = dynamic_cast<Spanner*> (me->original ());
       Spanner *orig_spanner = dynamic_cast<Spanner*> (me->original ());
-
-      int neighbor_idx = me->get_break_index () - break_dir;
+      vsize neighbor_idx = me->get_break_index () - break_dir;
       if (break_dir
          && d == RIGHT
          && neighbor_idx < orig_spanner->broken_intos_.size ())
       if (break_dir
          && d == RIGHT
          && neighbor_idx < orig_spanner->broken_intos_.size ())
@@ -141,8 +150,7 @@ Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
 
       connect_to_other[d]
        = (break_dir
 
       connect_to_other[d]
        = (break_dir
-          && (neighbor_idx < orig_spanner->broken_intos_.size ()
-              && neighbor_idx >= 0)
+          && neighbor_idx < orig_spanner->broken_intos_.size ()
           && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
     }
   while (flip (&d) != LEFT);
           && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
     }
   while (flip (&d) != LEFT);
@@ -167,7 +175,7 @@ Tuplet_bracket::get_common_x (Spanner *me)
   return commonx;
 }
   
   return commonx;
 }
   
-MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_control_points,1)
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_control_points,1)
 SCM
 Tuplet_bracket::calc_control_points (SCM smob)
 {
 SCM
 Tuplet_bracket::calc_control_points (SCM smob)
 {
@@ -175,8 +183,15 @@ Tuplet_bracket::calc_control_points (SCM smob)
 
   extract_grob_set (me, "note-columns", columns);
 
 
   extract_grob_set (me, "note-columns", columns);
 
+  SCM scm_positions = me->get_property ("positions");
+  if (!me->is_live ())
+    return SCM_EOL;
+  
+  if (!scm_is_pair (scm_positions))
+    programming_error ("Positions should be number pair");
+    
   Drul_array<Real> positions
   Drul_array<Real> positions
-    = ly_scm2realdrul (me->get_property ("positions"));
+    = robust_scm2drul (scm_positions, Drul_array<Real> (0,0));
 
   Grob *commonx = get_common_x (me);
   Direction dir = get_grob_direction (me);
 
   Grob *commonx = get_common_x (me);
   Direction dir = get_grob_direction (me);
@@ -207,12 +222,16 @@ Tuplet_bracket::calc_control_points (SCM smob)
            x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
              - overshoot[LEFT];
        }
            x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
              - overshoot[LEFT];
        }
+      
       else if (d == RIGHT
       else if (d == RIGHT
-              && (columns.is_empty ()
+              && (columns.empty ()
                   || (bounds[d]->get_column ()
                   || (bounds[d]->get_column ()
-                      != dynamic_cast<Item *> (columns.top ())->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;
            TODO: make padding tunable?
          */
          Real padding = 1.0;
@@ -220,7 +239,9 @@ Tuplet_bracket::calc_control_points (SCM smob)
          if (bounds[d]->break_status_dir ())
            padding = 0.0;
          
          if (bounds[d]->break_status_dir ())
            padding = 0.0;
          
-         x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT] - padding;
+         x_span[d]
+           = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT]
+           - padding;
        }
     }
   while (flip (&d) != LEFT);
        }
     }
   while (flip (&d) != LEFT);
@@ -258,7 +279,6 @@ Tuplet_bracket::print (SCM smob)
     bracket_visibility = ly_scm2bool (bracket);
   else if (bracket == ly_symbol2scm ("if-no-beam"))
     bracket_visibility = !par_beam;
     bracket_visibility = ly_scm2bool (bracket);
   else if (bracket == ly_symbol2scm ("if-no-beam"))
     bracket_visibility = !par_beam;
-
   
   SCM cpoints =  me->get_property ("control-points");
   if (scm_ilength (cpoints) < 2)
   
   SCM cpoints =  me->get_property ("control-points");
   if (scm_ilength (cpoints) < 2)
@@ -274,8 +294,6 @@ Tuplet_bracket::print (SCM smob)
   Interval x_span (points[LEFT][X_AXIS], points[RIGHT][X_AXIS]);
   Drul_array<Real> positions (points[LEFT][Y_AXIS], points[RIGHT][Y_AXIS]);
 
   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"));
   Output_def *pap = me->layout ();
 
   Grob *number_grob = unsmob_grob (me->get_object ("tuplet-number"));
@@ -398,15 +416,14 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
   Direction d = LEFT;
   do
     straight_corners[d] += -d * shorten[d] / length * dz;
   Direction d = LEFT;
   do
     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
-    gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
-  while (flip (&d) != LEFT)
-    ;
+  if (!gap.is_empty ())
+    {
+      do
+       gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
+      while (flip (&d) != LEFT);
+    }
 
   Drul_array<Offset> flare_corners = straight_corners;
   do
 
   Drul_array<Offset> flare_corners = straight_corners;
   do
@@ -420,14 +437,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;
 }
 
@@ -435,20 +458,20 @@ 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];
     }
 }
 
     }
 }
 
@@ -467,73 +490,112 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
     commony = st->common_refpoint (commony, Y_AXIS);
   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
     commony = st->common_refpoint (commony, Y_AXIS);
+  Real my_offset = me->relative_coordinate (commony, Y_AXIS);
 
   Grob *commonx = get_common_x (me);
   commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
 
   Interval staff;
 
   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))
+  Grob *st = Staff_symbol_referencer::get_staff_symbol (me);
+
+  /* staff-padding doesn't work correctly on cross-staff tuplets
+     because it only considers one staff symbol. Until this works,
+     disable it. */
+  if (st && !to_boolean (me->get_property ("cross-staff")))
     {
       Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
       if  (pad >= 0.0)
        {
     {
       Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
       if  (pad >= 0.0)
        {
-         staff = st->extent (commony, Y_AXIS);
+         staff = st->extent (commony, Y_AXIS) - my_offset;
          staff.widen (pad);
        }
     }
   
   Direction dir = get_grob_direction (me);
 
          staff.widen (pad);
        }
     }
   
   Direction dir = get_grob_direction (me);
 
-  /*
-    Use outer non-rest columns to determine slope
-  */
-  Grob *left_col = 0;
-  Grob *right_col = 0;
-  get_bounds (me, &left_col, &right_col);
-  if (left_col && right_col)
-    {
-      Interval rv = right_col->extent (commony, Y_AXIS);
-      Interval lv = left_col->extent (commony, Y_AXIS);
-      rv.unite (staff);
-      lv.unite (staff);
-      Real graphical_dy = rv[dir] - lv[dir];
-
-      Slice ls = Note_column::head_positions_interval (left_col);
-      Slice rs = Note_column::head_positions_interval (right_col);
-
-      Interval musical_dy;
-      musical_dy[UP] = rs[UP] - ls[UP];
-      musical_dy[DOWN] = rs[DOWN] - ls[DOWN];
-      if (sign (musical_dy[UP]) != sign (musical_dy[DOWN]))
-       *dy = 0.0;
-      else if (sign (graphical_dy) != sign (musical_dy[DOWN]))
-       *dy = 0.0;
-      else
-       *dy = graphical_dy;
-    }
-  else
-    *dy = 0;
-
-  *offset = -dir * infinity_f;
-
+  bool equally_long = false;
+  Grob *par_beam = parallel_beam (me, columns, &equally_long);
+  
   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];
-
-  Array<Offset> points;
-  points.push (Offset (x0 - x0, staff[dir]));
-  points.push (Offset (x1 - x0, staff[dir]));
-
-  for (int i = 0; i < columns.size (); i++)
+  bool follow_beam = par_beam
+    && get_grob_direction (par_beam) == dir
+    && ! to_boolean (par_beam->get_property ("knee"));
+
+  vector<Offset> points;
+  if (columns.size ()
+      && follow_beam
+      && Note_column::get_stem (columns[0])
+      && Note_column::get_stem (columns.back ()))
     {
     {
-      Interval note_ext = columns[i]->extent (commony, Y_AXIS);
-      Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
+      /*
+       trigger set_stem_ends
+       */
+      (void) par_beam->get_property ("quantized-positions");
+
+      Drul_array<Grob *> stems (Note_column::get_stem (columns[0]),
+                               Note_column::get_stem (columns.back ()));
+
+      Real ss = 0.5 * Staff_symbol_referencer::staff_space (me);
+      Real lp = ss * robust_scm2double (stems[LEFT]->get_property ("stem-end-position"), 0.0)
+        + stems[LEFT]->get_parent (Y_AXIS)->relative_coordinate (commony, Y_AXIS);
+      Real rp = ss * robust_scm2double (stems[RIGHT]->get_property ("stem-end-position"), 0.0)
+        + stems[RIGHT]->get_parent (Y_AXIS)->relative_coordinate (commony, Y_AXIS);
+
+      *dy = rp - lp;
+      points.push_back (Offset (stems[LEFT]->relative_coordinate (commonx, X_AXIS) - x0, lp));
+      points.push_back (Offset (stems[RIGHT]->relative_coordinate (commonx, X_AXIS) - x0, rp));
+    }
+  else
+    {
+      /*
+       Use outer non-rest columns to determine slope
+      */
+      Grob *left_col = 0;
+      Grob *right_col = 0;
+      get_bounds (me, &left_col, &right_col);
+      if (left_col && right_col)
+       {
+         Interval rv = Note_column::cross_staff_extent (right_col, commony);
+         Interval lv = Note_column::cross_staff_extent (left_col, commony);
+         rv.unite (staff);
+         lv.unite (staff);
+
+         Real graphical_dy = rv[dir] - lv[dir];
+
+         Slice ls = Note_column::head_positions_interval (left_col);
+         Slice rs = Note_column::head_positions_interval (right_col);
+
+         Interval musical_dy;
+         musical_dy[UP] = rs[UP] - ls[UP];
+         musical_dy[DOWN] = rs[DOWN] - ls[DOWN];
+         if (sign (musical_dy[UP]) != sign (musical_dy[DOWN]))
+           *dy = 0.0;
+         else if (sign (graphical_dy) != sign (musical_dy[DOWN]))
+           *dy = 0.0;
+         else
+           *dy = graphical_dy;
+       }
+      else
+       *dy = 0;
 
 
-      Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
-      points.push (Offset (x, notey));
+      for (vsize i = 0; i < columns.size (); i++)
+       {
+         Interval note_ext = Note_column::cross_staff_extent (columns[i], commony);
+         Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
+
+         points.push_back (Offset (x, note_ext[dir]));
+       }
     }
 
     }
 
+  if (!follow_beam)
+    {
+      points.push_back (Offset (x0 - x0, staff[dir]));
+      points.push_back (Offset (x1 - x0, staff[dir]));
+    }
+  
   /*
     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.
@@ -541,13 +603,17 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
     We assume that the smaller bracket is 1.0 space high.
   */
   Real ss = Staff_symbol_referencer::staff_space (me);
     We assume that the smaller bracket is 1.0 space high.
   */
   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;
-      Drul_array<Real> positions = ly_scm2realdrul (tuplets[i]->get_property ("positions"));
+      Drul_array<Real> positions = robust_scm2interval (tuplets[i]->get_property ("positions"),
+                                                       Interval (0,0));
 
       
       Real other_dy = positions[RIGHT] - positions[LEFT];
 
       
       Real other_dy = positions[RIGHT] - positions[LEFT];
@@ -557,30 +623,23 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
          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);
     }
 
+  *offset = -dir * infinity_f;
   Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
   Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
-  for (int i = 0; i < points.size (); i++)
+  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 + my_offset;
 
       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;
@@ -594,6 +653,8 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
     Kind of pointless since we put them outside the staff anyway, but
     let's leave code for the future when possibly allow them to move
     into the staff once again.
     Kind of pointless since we put them outside the staff anyway, but
     let's leave code for the future when possibly allow them to move
     into the staff once again.
+  
+    This doesn't seem to support cross-staff tuplets atm.
   */
   if (*dy == 0
       && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
   */
   if (*dy == 0
       && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
@@ -602,7 +663,7 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
       *offset *= 2 / ss;
 
       *offset = rint (*offset);
       *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;
@@ -623,35 +684,21 @@ MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_positions, 1);
 SCM
 Tuplet_bracket::calc_positions (SCM smob)
 {
 SCM
 Tuplet_bracket::calc_positions (SCM smob)
 {
-  Grob *me = unsmob_grob (smob);
-  extract_grob_set (me, "note-columns", columns);
-
-  Direction dir = get_grob_direction (me);
-  bool equally_long = false;
-  Grob *par_beam = parallel_beam (me, columns, &equally_long);
+  Spanner *me = unsmob_spanner (smob);
 
   /*
 
   /*
-    We follow the beam only if there is one, and we are next to it.
-  */
-  Real dy = 0.0;
-  Real offset = 0.0;
-  if (!par_beam
-      || get_grob_direction (par_beam) != dir)
-    calc_position_and_height (me, &offset, &dy);
-  else
+    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)))
     {
     {
-      SCM ps = par_beam->get_property ("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;
+      me->suicide ();
+      return SCM_EOL;
     }
 
     }
 
+  Real dy = 0.0;
+  Real offset = 0.0;
+  calc_position_and_height (me, &offset, &dy);
   
   SCM x = scm_cons (scm_from_double (offset),
                    scm_from_double (offset + dy));
   
   SCM x = scm_cons (scm_from_double (offset),
                    scm_from_double (offset + dy));
@@ -667,7 +714,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);
@@ -691,8 +738,37 @@ Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
 }
 
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
 }
 
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_cross_staff, 1);
+SCM
+Tuplet_bracket::calc_cross_staff (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  Grob *staff_symbol = 0;
+  extract_grob_set (me, "note-columns", cols);
+  bool equally_long = false;
+  Grob *par_beam = parallel_beam (me, cols, &equally_long);
+
+  if (par_beam)
+    return par_beam->get_property ("cross-staff");
+
+  for (vsize i = 0; i < cols.size (); i++)
+    {
+      Grob *stem = unsmob_grob (cols[i]->get_object ("stem"));
+      if (!stem)
+       continue;
+      
+      if (to_boolean (stem->get_property ("cross-staff")))
+       return SCM_BOOL_T;
+
+      Grob *stem_staff = Staff_symbol_referencer::get_staff_symbol (stem);
+      if (staff_symbol && (stem_staff != staff_symbol))
+        return SCM_BOOL_T;
+      staff_symbol = stem_staff;
+    }
+  return SCM_BOOL_F;
+}
+
 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 "