]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tuplet-bracket.cc
* input/regression/tie-chord.ly (testLong): add a chord in 4ths
[lilypond.git] / lily / tuplet-bracket.cc
index 5b80bb3c70778d2041c27e3fc4070f83bc723694..3442e2d522ec91ed67ba18cf0220fb0b38afaab3 100644 (file)
 #include "staff-symbol-referencer.hh"
 #include "lookup.hh"
 
-static Grob *
-get_x_bound_grob (Grob *g, Direction my_dir)
+static Item *
+get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
 {
-  if (Note_column::get_stem (g)
+  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)
-    {
-      g = Note_column::get_stem (g);
-    }
+    g = Note_column::get_stem (g);
+
   return g;
 }
 
 Grob *
-Tuplet_bracket::parallel_beam (Grob *me, Link_array<Grob> const &cols, bool *equally_long)
+Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array<Grob> const &cols, bool *equally_long)
 {
-  /*
-    ugh: code dup.
-  */
+  Spanner *me = dynamic_cast<Spanner *> (me_grob);
+
+  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 ());
 
+  if (s2 != me->get_bound (RIGHT))
+    return 0;
+
   Grob *b1 = s1 ? Stem::get_beam (s1) : 0;
   Grob *b2 = s2 ? Stem::get_beam (s2) : 0;
 
-  Spanner *sp = dynamic_cast<Spanner *> (me);
-
   *equally_long = false;
-  if (! (b1 && (b1 == b2) && !sp->is_broken ()))
+  if (! (b1 && (b1 == b2) && !me->is_broken ()))
     return 0;
 
   extract_grob_set (b1, "stems", beam_stems);
@@ -97,13 +103,10 @@ MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, 1);
 SCM
 Tuplet_bracket::print (SCM smob)
 {
-  Grob *me = unsmob_grob (smob);
+  Spanner *me = unsmob_spanner (smob);
   Stencil mol;
   extract_grob_set (me, "note-columns", columns);
 
-  if (!columns.size ())
-    return mol.smobbed_copy ();
-
   {
     SCM lp = me->get_property ("left-position");
     SCM rp = me->get_property ("right-position");
@@ -112,10 +115,10 @@ Tuplet_bracket::print (SCM smob)
       {
        /*
          UGH. dependency tracking!
-        */
+       */
        extract_grob_set (me, "tuplets", tuplets);
        for (int i = 0; i < tuplets.size (); i++)
-         Tuplet_bracket::print (tuplets[i]->self_scm());
+         Tuplet_bracket::print (tuplets[i]->self_scm ());
 
        after_line_breaking (smob);
       }
@@ -137,38 +140,63 @@ Tuplet_bracket::print (SCM smob)
   */
   SCM bracket = me->get_property ("bracket-visibility");
   if (scm_is_bool (bracket))
-    {
-      bracket_visibility = ly_scm2bool (bracket);
-    }
+    bracket_visibility = ly_scm2bool (bracket);
   else if (bracket == ly_symbol2scm ("if-no-beam"))
     bracket_visibility = !par_beam;
 
   SCM numb = me->get_property ("number-visibility");
   if (scm_is_bool (numb))
-    {
-      number_visibility = ly_scm2bool (numb);
-    }
+    number_visibility = ly_scm2bool (numb);
   else if (numb == ly_symbol2scm ("if-no-beam"))
     number_visibility = !par_beam;
 
-  Grob *commonx = columns[0]->common_refpoint (columns.top (), X_AXIS);
-
-  /*
-    Tuplet brackets are normally not broken, but we shouldn't crash if
-    they are.
-  */
+  Grob *commonx = common_refpoint_of_array (columns, me, X_AXIS);
   commonx = commonx->common_refpoint (sp->get_bound (LEFT), X_AXIS);
   commonx = commonx->common_refpoint (sp->get_bound (RIGHT), X_AXIS);
 
   Direction dir = get_grob_direction (me);
 
-  Grob *lgr = get_x_bound_grob (columns[0], dir);
-  Grob *rgr = get_x_bound_grob (columns.top (), dir);
+  Drul_array<Item *> bounds;
+  bounds[LEFT] = get_x_bound_item (me, LEFT, dir);
+  bounds[RIGHT] = 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];
-  Real w = x1 -x0;
+  Drul_array<bool> connect_to_other;
+  Interval x_span;
+  Direction d = LEFT;
+  do
+    {
+      x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
+      Direction break_dir = bounds[d]->break_status_dir ();
+      Spanner *orig_spanner = dynamic_cast<Spanner *> (me->original_);
+      connect_to_other[d]
+       = (break_dir
+          && (me->get_break_index () - break_dir < orig_spanner->broken_intos_.size ()));
+
+      if (connect_to_other[d])
+       {
+         Interval overshoot (robust_scm2drul (me->get_property ("break-overshoot"),
+                                              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.is_empty ()
+                  || (bounds[d]->get_column ()
+                      != dynamic_cast<Item *> (columns.top ())->get_column ())))
+       {
+         /*
+           TODO: make padding tunable?
+         */
+         x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT] - 1.0;
+       }
+    }
+  while (flip (&d) != LEFT);
 
+  Real w = x_span.length ();
   SCM number = me->get_property ("text");
 
   Output_def *pap = me->get_layout ();
@@ -176,7 +204,8 @@ Tuplet_bracket::print (SCM smob)
   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);
+      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);
@@ -194,9 +223,7 @@ Tuplet_bracket::print (SCM smob)
   */
   if (bracket_visibility && number_visibility
       && mol.extent (X_AXIS).length () > w)
-    {
-      bracket_visibility = false;
-    }
+    bracket_visibility = false;
 
   if (bracket_visibility)
     {
@@ -205,7 +232,7 @@ Tuplet_bracket::print (SCM smob)
       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);
@@ -213,11 +240,38 @@ 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);
+      Drul_array<Stencil> edge_stencils;
 
       scale_drul (&height, -ss * dir);
       scale_drul (&flare, ss);
       scale_drul (&shorten, ss);
-      
+      do
+       {
+         if (connect_to_other[d])
+           {
+             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);
+
       Stencil brack = make_bracket (me, Y_AXIS,
                                    Offset (w, ry - ly),
                                    height,
@@ -227,11 +281,20 @@ Tuplet_bracket::print (SCM smob)
                                    */
                                    Interval (-0.5, 0.5) * gap + 0.1,
                                    flare, shorten);
+
+      do
+       {
+         if (!edge_stencils[d].is_empty ())
+           brack.add_stencil (edge_stencils[d]);
+       }
+      while (flip (&d) != LEFT);
+
       mol.add_stencil (brack);
     }
 
   mol.translate_axis (ly, Y_AXIS);
-  mol.translate_axis (x0 - sp->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS), X_AXIS);
+  mol.translate_axis (x_span[LEFT]
+                     - sp->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS), X_AXIS);
   return mol.smobbed_copy ();
 }
 
@@ -261,18 +324,16 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
 
   Direction d = LEFT;
   do
-    {
-      straight_corners[d] += -d * shorten[d] / length * dz;
-    }
-  while (flip (&d) != LEFT);
+    straight_corners[d] += -d * shorten[d] / length * dz;
+  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);
+    gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
+  while (flip (&d) != LEFT)
+    ;
 
   Drul_array<Offset> flare_corners = straight_corners;
   do
@@ -318,27 +379,35 @@ Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
     }
 }
 
-
 /*
   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);
-  
+
   Grob *commony = common_refpoint_of_array (columns, me, 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);
+
   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);
 
   Interval staff;
   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
-    staff = st->extent (commony, Y_AXIS);
-
+    {
+      staff = st->extent (commony, Y_AXIS);
+      Real pad = robust_scm2double (me->get_property ("staff-padding"), 0.5);
+      staff.widen (pad);
+    }
+  
   Direction dir = get_grob_direction (me);
 
   /*
@@ -373,37 +442,30 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
 
   *offset = -dir * infinity_f;
 
-  if (!columns.size ())
-    return;
-
-  Grob *lgr = get_x_bound_grob (columns[0], dir);
-  Grob *rgr = get_x_bound_grob (columns.top (), dir);
+  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;
-
   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++)
     {
       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;
       points.push (Offset (x, notey));
     }
-  
+
   /*
     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.
   */
-  
   Real ss = Staff_symbol_referencer::staff_space (me);
   for (int i = 0; i < tuplets.size (); i++)
     {
@@ -417,38 +479,38 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
 
       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.
            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];
-           }
+           = 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));
        }
       while (flip (&d) != LEFT);
     }
 
+  Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
   for (int i = 0; i < points.size (); i++)
     {
       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;
     }
-                 
+
   *offset += scm_to_double (me->get_property ("padding")) * dir;
 
   /*
@@ -458,8 +520,8 @@ 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.
   */
-  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;
@@ -500,18 +562,6 @@ Tuplet_bracket::after_line_breaking (SCM smob)
   Grob *me = unsmob_grob (smob);
   extract_grob_set (me, "note-columns", columns);
 
-  if (!columns.size ())
-    {
-      me->suicide ();
-      return SCM_UNSPECIFIED;
-    }
-  if (dynamic_cast<Spanner *> (me)->is_broken ())
-    {
-      me->warning (_ ("removing tuplet bracket across linebreak"));
-      me->suicide ();
-      return SCM_UNSPECIFIED;
-    }
-
   Direction dir = get_grob_direction (me);
   if (!dir)
     {
@@ -529,9 +579,7 @@ Tuplet_bracket::after_line_breaking (SCM smob)
   Real offset = 0.0;
   if (!par_beam
       || get_grob_direction (par_beam) != dir)
-    {
-      calc_position_and_height (me, &offset, &dy);
-    }
+    calc_position_and_height (me, &offset, &dy);
   else
     {
       SCM ps = par_beam->get_property ("positions");
@@ -543,20 +591,16 @@ Tuplet_bracket::after_line_breaking (SCM smob)
        duh. magic.
       */
       offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
-      dy = rp- lp;
+      dy = rp - lp;
     }
 
   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);
-    }
+    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);
-    }
+    lp = scm_from_double (scm_to_double (rp) - dy);
   else if (!scm_is_number (rp) && !scm_is_number (lp))
     {
       lp = scm_from_double (offset);
@@ -577,7 +621,7 @@ Tuplet_bracket::get_default_dir (Grob *me)
 {
   Drul_array<int> dirs (0, 0);
   extract_grob_set (me, "note-columns", columns);
-  for (int i = 0 ; i < columns.size (); i++)
+  for (int i = 0; i < columns.size (); i++)
     {
       Grob *nc = columns[i];
       Direction d = Note_column::dir (nc);
@@ -604,14 +648,29 @@ Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
   me->add_dependency (bracket);
 }
 
-
-
 ADD_INTERFACE (Tuplet_bracket,
               "tuplet-bracket-interface",
-              "A bracket with a number in the middle, used for tuplets.",
-
-              "note-columns bracket-flare edge-height shorten-pair "
-              "tuplets "
-              "padding left-position right-position bracket-visibility "
-              "number-visibility thickness direction");
+              "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 "
+              "beyond the staff. "
+              "At a line break, the markups in the @code{edge-text} are printed "
+              "at the edges. ",
+
+              
+              "bracket-flare "
+              "bracket-visibility "
+              "break-overshoot "
+              "direction "
+              "edge-height "
+              "edge-text "
+              "left-position "
+              "note-columns "
+              "number-visibility "
+              "padding "
+              "right-position "
+              "shorten-pair "
+              "staff-padding "
+              "thickness "
+              "tuplets ");