Fix flag snippet in NEWS.tely.
[lilypond.git] / lily / spanner.cc
blob0ae372ded3d4d485541efaeb14c4a351eaa25b1c
1 /*
2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
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"
14 #include "stencil.hh"
15 #include "system.hh"
16 #include "warn.hh"
17 #include "moment.hh"
19 Grob *
20 Spanner::clone () const
22 return new Spanner (*this);
25 void
26 Spanner::do_break_processing ()
28 //break_into_pieces
29 Item *left = spanned_drul_[LEFT];
30 Item *right = spanned_drul_[RIGHT];
32 if (!left || !right)
33 return;
36 if (get_system () || is_broken ())
37 return;
39 if (left == right)
42 If we have a spanner spanning one column, we must break it
43 anyway because it might provide a parent for another item. */
44 Direction d = LEFT;
47 Item *bound = left->find_prebroken_piece (d);
48 if (!bound)
49 programming_error ("no broken bound");
50 else if (bound->get_system ())
52 Spanner *span = dynamic_cast<Spanner *> (clone ());
53 span->set_bound (LEFT, bound);
54 span->set_bound (RIGHT, bound);
56 assert (span->get_system ());
57 span->get_system ()->typeset_grob (span);
58 broken_intos_.push_back (span);
61 while ((flip (&d)) != LEFT);
63 else
65 System *root = get_root_system (this);
66 vector<Item*> break_points = root->broken_col_range (left, right);
68 break_points.insert (break_points.begin () + 0, left);
69 break_points.push_back (right);
71 Slice parent_rank_slice;
72 parent_rank_slice.set_full ();
75 Check if our parent in X-direction spans equally wide
76 or wider than we do.
78 for (int a = X_AXIS; a < NO_AXES; a++)
80 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
82 parent_rank_slice.intersect (parent->spanned_rank_interval ());
86 for (vsize i = 1; i < break_points.size (); i++)
88 Drul_array<Item *> bounds;
89 bounds[LEFT] = break_points[i - 1];
90 bounds[RIGHT] = break_points[i];
91 Direction d = LEFT;
94 if (!bounds[d]->get_system ())
95 bounds[d] = bounds[d]->find_prebroken_piece (- d);
97 while ((flip (&d)) != LEFT);
99 if (!bounds[LEFT] || ! bounds[RIGHT])
101 programming_error ("bounds of this piece aren't breakable. ");
102 continue;
105 bool ok = parent_rank_slice.contains (bounds[LEFT]->get_column ()->get_rank ());
106 ok = ok && parent_rank_slice.contains (bounds[RIGHT]->get_column ()->get_rank ());
108 if (!ok)
110 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner. Ignoring orphaned part",
111 name ().c_str ()));
112 continue;
116 Spanner *span = dynamic_cast<Spanner *> (clone ());
117 span->set_bound (LEFT, bounds[LEFT]);
118 span->set_bound (RIGHT, bounds[RIGHT]);
120 if (!bounds[LEFT]->get_system ()
122 || !bounds[RIGHT]->get_system ()
123 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
125 programming_error ("bounds of spanner are invalid");
126 span->suicide ();
128 else
130 bounds[LEFT]->get_system ()->typeset_grob (span);
131 broken_intos_.push_back (span);
135 vector_sort (broken_intos_, Spanner::less);
136 for (vsize i = broken_intos_.size (); i--;)
137 broken_intos_[i]->break_index_ = i;
140 vsize
141 Spanner::get_break_index () const
143 return break_index_;
146 void
147 Spanner::set_my_columns ()
149 Direction i = (Direction) LEFT;
152 if (!spanned_drul_[i]->get_system ())
153 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
155 while (flip (&i) != LEFT);
158 Interval_t<int>
159 Spanner::spanned_rank_interval () const
161 Interval_t<int> iv (0, 0);
163 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
164 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
165 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
166 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
167 return iv;
170 Interval_t<Moment>
171 Spanner::spanned_time () const
173 return spanned_time_interval (spanned_drul_[LEFT],
174 spanned_drul_[RIGHT]);
178 Item *
179 Spanner::get_bound (Direction d) const
181 return spanned_drul_ [d];
185 Set the items that this spanner spans. If D == LEFT, we also set the
186 X-axis parent of THIS to S.
188 void
189 Spanner::set_bound (Direction d, Grob *s)
191 Item *i = dynamic_cast<Item *> (s);
192 if (!i)
194 programming_error ("must have Item for spanner bound of " + name());
195 return;
198 spanned_drul_[d] = i;
201 We check for System to prevent the column -> line_of_score
202 -> column -> line_of_score -> etc situation */
203 if (d == LEFT && !dynamic_cast<System *> (this))
204 set_parent (i, X_AXIS);
207 Signal that this column needs to be kept alive. They need to be
208 kept alive to have meaningful position and linebreaking.
210 [maybe we should try keeping all columns alive?, and perhaps
211 inherit position from their (non-)musical brother]
214 if (dynamic_cast<Paper_column *> (i))
215 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
218 Spanner::Spanner (SCM s)
219 : Grob (s)
221 break_index_ = 0;
222 spanned_drul_[LEFT] = 0;
223 spanned_drul_[RIGHT] = 0;
226 Spanner::Spanner (Spanner const &s)
227 : Grob (s)
229 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
232 Real
233 Spanner::spanner_length () const
235 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
236 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
238 if (r < l)
239 programming_error ("spanner with negative length");
241 return r - l;
244 System *
245 Spanner::get_system () const
247 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
248 return 0;
249 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
250 return 0;
251 return spanned_drul_[LEFT]->get_system ();
254 Grob *
255 Spanner::find_broken_piece (System *l) const
257 vsize idx = binary_search (broken_intos_, (Spanner *)l, Spanner::less);
258 if (idx != VPOS)
259 return broken_intos_ [idx];
260 return 0;
263 Spanner *
264 Spanner::broken_neighbor (Direction d) const
266 if (!original_)
267 return 0;
269 vsize k = broken_spanner_index (this);
270 Spanner *orig = dynamic_cast<Spanner*> (original_);
271 int j = int (k) + d;
272 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
273 return 0;
275 return orig->broken_intos_[j];
279 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
281 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
284 bool
285 Spanner::less (Spanner *const &a, Spanner *const &b)
287 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
290 bool
291 Spanner::is_broken () const
293 return broken_intos_.size ();
297 If this is a broken spanner, return the amount the left end is to be
298 shifted horizontally so that the spanner starts after the initial
299 clef and key on the staves. This is necessary for ties, slurs,
300 crescendo and decrescendo signs, for example.
302 Real
303 Spanner::get_broken_left_end_align () const
305 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
307 // Relevant only if left span point is first column in line
308 if (sc != NULL
309 && sc->break_status_dir () == RIGHT)
312 We used to do a full search for the Break_align_item.
313 But that doesn't make a difference, since the Paper_column
314 is likely to contain only a Break_align_item.
316 return sc->extent (sc, X_AXIS)[RIGHT];
319 return 0.0;
322 void
323 Spanner::derived_mark () const
325 Direction d = LEFT;
327 if (spanned_drul_[d])
328 scm_gc_mark (spanned_drul_[d]->self_scm ());
329 while (flip (&d) != LEFT)
332 for (vsize i = broken_intos_.size (); i--;)
333 scm_gc_mark (broken_intos_[i]->self_scm ());
337 Set left or right bound to IT.
339 Warning: caller should ensure that subsequent calls put in ITems
340 that are left-to-right ordered.
342 void
343 add_bound_item (Spanner *sp, Grob *it)
345 if (!sp->get_bound (LEFT))
346 sp->set_bound (LEFT, it);
347 else
348 sp->set_bound (RIGHT, it);
351 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
353 Spanner::set_spacing_rods (SCM smob)
355 Grob *me = unsmob_grob (smob);
356 SCM num_length = me->get_property ("minimum-length");
357 if (scm_is_number (num_length))
359 Rod r;
360 Spanner *sp = dynamic_cast<Spanner *> (me);
361 System *root = get_root_system (me);
362 Drul_array<Item*> bounds (sp->get_bound (LEFT),
363 sp->get_bound (RIGHT));
364 if (!bounds[LEFT] || !bounds[RIGHT])
365 return SCM_UNSPECIFIED;
367 vector<Item*> cols (root->broken_col_range (bounds[LEFT]->get_column (),
368 bounds[RIGHT]->get_column ()));
370 if (cols.size ())
372 Rod r ;
373 r.item_drul_[LEFT] = sp->get_bound (LEFT);
374 r.item_drul_[RIGHT] = cols[0]->find_prebroken_piece (LEFT);
375 r.distance_ = robust_scm2double (num_length, 0);
376 r.add_to_cols ();
378 r.item_drul_[LEFT] = cols.back ()->find_prebroken_piece (RIGHT);
379 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
380 r.add_to_cols ();
383 r.distance_ = robust_scm2double (num_length, 0);
384 r.item_drul_[LEFT] = sp->get_bound (LEFT);
385 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
386 r.add_to_cols ();
389 return SCM_UNSPECIFIED;
393 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
396 broken_spanner_index (Spanner const *sp)
398 Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
399 /* ugh: casting */
400 return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
403 Spanner *
404 unsmob_spanner (SCM s)
406 return dynamic_cast<Spanner *> (unsmob_grob (s));
409 MAKE_SCHEME_CALLBACK (Spanner, bounds_width, 1);
411 Spanner::bounds_width (SCM grob)
413 Spanner *me = unsmob_spanner (grob);
416 Grob *common = me->get_bound (LEFT)->common_refpoint (me->get_bound (RIGHT), X_AXIS);
418 Interval w (me->get_bound (LEFT)->relative_coordinate (common, X_AXIS),
419 me->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
421 w -= me->relative_coordinate (common, X_AXIS);
423 return ly_interval2scm (w);
426 ADD_INTERFACE (Spanner,
427 "Some objects are horizontally spanned between objects. For"
428 " example, slurs, beams, ties, etc. These grobs form a subtype"
429 " called @code{Spanner}. All spanners have two span points"
430 " (these must be @code{Item} objects), one on the left and one"
431 " on the right. The left bound is also the X@tie{}reference"
432 " point of the spanner.",
434 /* properties */
435 "minimum-length "
436 "to-barline "