2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "pointer-group-interface.hh"
10 #include "libc-extension.hh"
11 #include "paper-column.hh"
12 #include "paper-column.hh"
13 #include "paper-score.hh"
20 Spanner::clone () const
22 return new Spanner (*this);
26 Spanner::do_break_processing ()
29 Item *left = spanned_drul_[LEFT];
30 Item *right = spanned_drul_[RIGHT];
36 Check if our parent in X-direction spans equally wide
39 for (int a = X_AXIS; a < NO_AXES; a++)
41 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
43 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
45 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
47 parent->name ().c_str ()));
52 if (get_system () || is_broken ())
58 If we have a spanner spanning one column, we must break it
59 anyway because it might provide a parent for another item. */
63 Item *bound = left->find_prebroken_piece (d);
65 programming_error ("no broken bound");
66 else if (bound->get_system ())
68 Spanner *span = dynamic_cast<Spanner *> (clone ());
69 span->set_bound (LEFT, bound);
70 span->set_bound (RIGHT, bound);
72 assert (span->get_system ());
73 span->get_system ()->typeset_grob (span);
74 broken_intos_.push_back (span);
77 while ((flip (&d)) != LEFT);
81 System *root = get_root_system (this);
82 vector<Item*> break_points = root->broken_col_range (left, right);
84 break_points.insert (break_points.begin () + 0, left);
85 break_points.push_back (right);
87 for (vsize i = 1; i < break_points.size (); i++)
89 Drul_array<Item *> bounds;
90 bounds[LEFT] = break_points[i - 1];
91 bounds[RIGHT] = break_points[i];
95 if (!bounds[d]->get_system ())
96 bounds[d] = bounds[d]->find_prebroken_piece (- d);
98 while ((flip (&d)) != LEFT);
100 if (!bounds[LEFT] || ! bounds[RIGHT])
102 programming_error ("bounds of this piece aren't breakable. ");
106 Spanner *span = dynamic_cast<Spanner *> (clone ());
107 span->set_bound (LEFT, bounds[LEFT]);
108 span->set_bound (RIGHT, bounds[RIGHT]);
110 if (!bounds[LEFT]->get_system ()
112 || !bounds[RIGHT]->get_system ()
113 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
115 programming_error ("bounds of spanner are invalid");
120 bounds[LEFT]->get_system ()->typeset_grob (span);
121 broken_intos_.push_back (span);
125 vector_sort (broken_intos_, Spanner::less);
126 for (vsize i = broken_intos_.size (); i--;)
127 broken_intos_[i]->break_index_ = i;
131 Spanner::get_break_index () const
137 Spanner::set_my_columns ()
139 Direction i = (Direction) LEFT;
142 if (!spanned_drul_[i]->get_system ())
143 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
145 while (flip (&i) != LEFT);
149 Spanner::spanned_rank_iv () const
151 Interval_t<int> iv (0, 0);
153 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
154 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
155 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
156 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
161 Spanner::spanned_time () const
163 Interval_t<Moment> iv;
168 if (spanned_drul_[d] && spanned_drul_[d]->get_column ())
169 iv[d] = robust_scm2moment (spanned_drul_[d]->get_column ()->get_property ("when"),
172 while (flip (&d) != LEFT);
176 if (!spanned_drul_[d] || !spanned_drul_[d]->get_column ())
179 while (flip (&d) != LEFT);
186 Spanner::get_bound (Direction d) const
188 return spanned_drul_ [d];
192 Set the items that this spanner spans. If D == LEFT, we also set the
193 X-axis parent of THIS to S.
196 Spanner::set_bound (Direction d, Grob *s)
198 Item *i = dynamic_cast<Item *> (s);
201 programming_error ("must have Item for spanner bound");
205 spanned_drul_[d] = i;
208 We check for System to prevent the column -> line_of_score
209 -> column -> line_of_score -> etc situation */
210 if (d == LEFT && !dynamic_cast<System *> (this))
211 set_parent (i, X_AXIS);
214 Signal that this column needs to be kept alive. They need to be
215 kept alive to have meaningful position and linebreaking.
217 [maybe we should try keeping all columns alive?, and perhaps
218 inherit position from their (non-)musical brother]
221 if (dynamic_cast<Paper_column *> (i))
222 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
225 Spanner::Spanner (SCM s)
229 spanned_drul_[LEFT] = 0;
230 spanned_drul_[RIGHT] = 0;
233 Spanner::Spanner (Spanner const &s)
236 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
240 Spanner::spanner_length () const
242 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
243 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
246 programming_error ("spanner with negative length");
252 Spanner::get_system () const
254 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
256 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
258 return spanned_drul_[LEFT]->get_system ();
262 Spanner::find_broken_piece (System *l) const
264 vsize idx = binary_search (broken_intos_, (Spanner *)l, Spanner::less);
266 return broken_intos_ [idx];
271 Spanner::broken_neighbor (Direction d) const
276 vsize k = broken_spanner_index (this);
277 Spanner *orig = dynamic_cast<Spanner*> (original_);
279 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
282 return orig->broken_intos_[j];
286 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
288 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
292 Spanner::less (Spanner *const &a, Spanner *const &b)
294 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
298 Spanner::is_broken () const
300 return broken_intos_.size ();
304 If this is a broken spanner, return the amount the left end is to be
305 shifted horizontally so that the spanner starts after the initial
306 clef and key on the staves. This is necessary for ties, slurs,
307 crescendo and decrescendo signs, for example.
310 Spanner::get_broken_left_end_align () const
312 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
314 // Relevant only if left span point is first column in line
316 && sc->break_status_dir () == RIGHT)
319 We used to do a full search for the Break_align_item.
320 But that doesn't make a difference, since the Paper_column
321 is likely to contain only a Break_align_item.
323 return sc->extent (sc, X_AXIS)[RIGHT];
330 Spanner::derived_mark () const
334 if (spanned_drul_[d])
335 scm_gc_mark (spanned_drul_[d]->self_scm ());
336 while (flip (&d) != LEFT)
339 for (vsize i = broken_intos_.size (); i--;)
340 scm_gc_mark (broken_intos_[i]->self_scm ());
344 Set left or right bound to IT.
346 Warning: caller should ensure that subsequent calls put in ITems
347 that are left-to-right ordered.
350 add_bound_item (Spanner *sp, Grob *it)
352 if (!sp->get_bound (LEFT))
353 sp->set_bound (LEFT, it);
355 sp->set_bound (RIGHT, it);
358 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
360 Spanner::set_spacing_rods (SCM smob)
362 Grob *me = unsmob_grob (smob);
363 SCM num_length = me->get_property ("minimum-length");
364 if (scm_is_number (num_length))
367 Spanner *sp = dynamic_cast<Spanner *> (me);
370 System *root = get_root_system (me);
371 vector<Item*> cols (root->broken_col_range (sp->get_bound (LEFT)->get_column (),
372 sp->get_bound (RIGHT)->get_column ()));
377 r.item_drul_[LEFT] = sp->get_bound (LEFT);
378 r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
379 r.distance_ = robust_scm2double (num_length, 0);
382 r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
383 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
387 r.distance_ = robust_scm2double (num_length, 0);
388 r.item_drul_[LEFT] = sp->get_bound (LEFT);
389 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
393 return SCM_UNSPECIFIED;
397 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
400 broken_spanner_index (Spanner const *sp)
402 Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
404 return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
408 unsmob_spanner (SCM s)
410 return dynamic_cast<Spanner *> (unsmob_grob (s));
413 MAKE_SCHEME_CALLBACK(Spanner, bounds_width, 1);
415 Spanner::bounds_width (SCM grob)
417 Spanner *me = unsmob_spanner (grob);
420 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
422 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
423 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
425 w -= me->relative_coordinate (common, X_AXIS);
427 return ly_interval2scm (w);
430 ADD_INTERFACE (Spanner,
431 "Some objects are horizontally spanned between objects. For\n"
432 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
433 "@code{Spanner}. All spanners have two span-points (these must be\n"
434 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
435 "also the X-reference point of the spanner.\n",