2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
12 #include "pointer-group-interface.hh"
13 #include "libc-extension.hh"
14 #include "paper-column.hh"
15 #include "paper-column.hh"
16 #include "paper-score.hh"
22 Spanner::clone (int count) const
24 return new Spanner (*this, count);
28 Spanner::do_break_processing ()
31 Item *left = spanned_drul_[LEFT];
32 Item *right = spanned_drul_[RIGHT];
38 Check if our parent in X-direction spans equally wide
41 for (int a = X_AXIS; a < NO_AXES; a++)
43 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
45 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
47 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
49 parent->name ().c_str ()));
54 if (get_system () || is_broken ())
60 If we have a spanner spanning one column, we must break it
61 anyway because it might provide a parent for another item. */
65 Item *bound = left->find_prebroken_piece (d);
67 programming_error ("no broken bound");
68 else if (bound->get_system ())
70 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
71 span->set_bound (LEFT, bound);
72 span->set_bound (RIGHT, bound);
74 assert (span->get_system ());
75 span->get_system ()->typeset_grob (span);
76 broken_intos_.push_back (span);
79 while ((flip (&d)) != LEFT);
83 System *root = get_root_system (this);
84 vector<Item*> break_points = root->broken_col_range (left, right);
86 break_points.insert (break_points.begin () + 0, left);
87 break_points.push_back (right);
89 for (vsize i = 1; i < break_points.size (); i++)
91 Drul_array<Item *> bounds;
92 bounds[LEFT] = break_points[i - 1];
93 bounds[RIGHT] = break_points[i];
97 if (!bounds[d]->get_system ())
98 bounds[d] = bounds[d]->find_prebroken_piece (- d);
100 while ((flip (&d)) != LEFT);
102 if (!bounds[LEFT] || ! bounds[RIGHT])
104 programming_error ("bounds of this piece aren't breakable. ");
108 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
109 span->set_bound (LEFT, bounds[LEFT]);
110 span->set_bound (RIGHT, bounds[RIGHT]);
112 if (!bounds[LEFT]->get_system ()
114 || !bounds[RIGHT]->get_system ()
115 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
117 programming_error ("bounds of spanner are invalid");
122 bounds[LEFT]->get_system ()->typeset_grob (span);
123 broken_intos_.push_back (span);
127 vector_sort (broken_intos_, Spanner::less);
128 for (vsize i = broken_intos_.size (); i--;)
129 broken_intos_[i]->break_index_ = i;
133 Spanner::get_break_index () const
139 Spanner::set_my_columns ()
141 Direction i = (Direction) LEFT;
144 if (!spanned_drul_[i]->get_system ())
145 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
147 while (flip (&i) != LEFT);
151 Spanner::spanned_rank_iv ()
153 Interval_t<int> iv (0, 0);
155 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
156 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
157 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
158 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
163 Spanner::get_bound (Direction d) const
165 return spanned_drul_ [d];
169 Set the items that this spanner spans. If D == LEFT, we also set the
170 X-axis parent of THIS to S.
173 Spanner::set_bound (Direction d, Grob *s)
175 Item *i = dynamic_cast<Item *> (s);
178 programming_error ("must have Item for spanner bound");
182 spanned_drul_[d] = i;
185 We check for System to prevent the column -> line_of_score
186 -> column -> line_of_score -> etc situation */
187 if (d == LEFT && !dynamic_cast<System *> (this))
188 set_parent (i, X_AXIS);
191 Signal that this column needs to be kept alive. They need to be
192 kept alive to have meaningful position and linebreaking.
194 [maybe we should try keeping all columns alive?, and perhaps
195 inherit position from their (non-)musical brother]
198 if (dynamic_cast<Paper_column *> (i))
199 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
202 Spanner::Spanner (SCM s, Object_key const *key)
206 spanned_drul_[LEFT] = 0;
207 spanned_drul_[RIGHT] = 0;
210 Spanner::Spanner (Spanner const &s, int count)
213 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
217 Spanner::spanner_length () const
219 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
220 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
223 programming_error ("spanner with negative length");
229 Spanner::get_system () const
231 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
233 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
235 return spanned_drul_[LEFT]->get_system ();
239 Spanner::find_broken_piece (System *l) const
241 vsize idx = binary_search (broken_intos_, (Spanner *)l, Spanner::less);
243 return broken_intos_ [idx];
248 Spanner::broken_neighbor (Direction d) const
253 vsize k = broken_spanner_index (this);
254 Spanner *orig = dynamic_cast<Spanner*> (original_);
256 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
259 return orig->broken_intos_[j];
263 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
265 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
269 Spanner::less (Spanner *const &a, Spanner *const &b)
271 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
275 Spanner::is_broken () const
277 return broken_intos_.size ();
281 If this is a broken spanner, return the amount the left end is to be
282 shifted horizontally so that the spanner starts after the initial
283 clef and key on the staves. This is necessary for ties, slurs,
284 crescendo and decrescendo signs, for example.
287 Spanner::get_broken_left_end_align () const
289 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
291 // Relevant only if left span point is first column in line
293 && sc->break_status_dir () == RIGHT)
296 We used to do a full search for the Break_align_item.
297 But that doesn't make a difference, since the Paper_column
298 is likely to contain only a Break_align_item.
300 return sc->extent (sc, X_AXIS)[RIGHT];
307 Spanner::derived_mark () const
311 if (spanned_drul_[d])
312 scm_gc_mark (spanned_drul_[d]->self_scm ());
313 while (flip (&d) != LEFT)
316 for (vsize i = broken_intos_.size (); i--;)
317 scm_gc_mark (broken_intos_[i]->self_scm ());
321 Set left or right bound to IT.
323 Warning: caller should ensure that subsequent calls put in ITems
324 that are left-to-right ordered.
327 add_bound_item (Spanner *sp, Grob *it)
329 if (!sp->get_bound (LEFT))
330 sp->set_bound (LEFT, it);
332 sp->set_bound (RIGHT, it);
335 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
337 Spanner::set_spacing_rods (SCM smob)
339 Grob *me = unsmob_grob (smob);
342 Spanner *sp = dynamic_cast<Spanner *> (me);
343 r.item_drul_[LEFT] = sp->get_bound (LEFT);
344 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
346 SCM num_length = me->get_property ("minimum-length");
347 if (scm_is_number (num_length))
349 r.distance_ = robust_scm2double (num_length, 0);
353 return SCM_UNSPECIFIED;
357 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
360 broken_spanner_index (Spanner const *sp)
362 Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
364 return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
368 unsmob_spanner (SCM s)
370 return dynamic_cast<Spanner *> (unsmob_grob (s));
373 ADD_INTERFACE (Spanner,
374 "Some objects are horizontally spanned between objects. For\n"
375 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
376 "@code{Spanner}. All spanners have two span-points (these must be\n"
377 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
378 "also the X-reference point of the spanner.\n",