2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2005 Han-Wen Nienhuys <hanwen@cs.uu.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 ().to_str0 ()));
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 (span);
79 while ((flip (&d)) != LEFT);
83 Link_array<Item> break_points = pscore_->root_system ()->broken_col_range (left, right);
85 break_points.insert (left, 0);
86 break_points.push (right);
88 for (int i = 1; i < break_points.size (); i++)
90 Drul_array<Item *> bounds;
91 bounds[LEFT] = break_points[i - 1];
92 bounds[RIGHT] = break_points[i];
96 if (!bounds[d]->get_system ())
97 bounds[d] = bounds[d]->find_prebroken_piece (- d);
99 while ((flip (&d)) != LEFT);
101 if (!bounds[LEFT] || ! bounds[RIGHT])
103 programming_error ("bounds of this piece aren't breakable. ");
107 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
108 span->set_bound (LEFT, bounds[LEFT]);
109 span->set_bound (RIGHT, bounds[RIGHT]);
111 if (!bounds[LEFT]->get_system ()
113 || !bounds[RIGHT]->get_system ()
114 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
116 programming_error ("bounds of spanner are invalid");
121 bounds[LEFT]->get_system ()->typeset_grob (span);
122 broken_intos_.push (span);
126 broken_intos_.sort (Spanner::compare);
127 for (int i = broken_intos_.size ();i--;)
128 broken_intos_[i]->break_index_ = i;
132 Spanner::get_break_index () const
138 Spanner::set_my_columns ()
140 Direction i = (Direction) LEFT;
143 if (!spanned_drul_[i]->get_system ())
144 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
146 while (flip (&i) != LEFT);
150 Spanner::spanned_rank_iv ()
152 Interval_t<int> iv (0, 0);
154 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
155 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
156 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
157 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
162 Spanner::get_bound (Direction d) const
164 return spanned_drul_ [d];
168 Set the items that this spanner spans. If D == LEFT, we also set the
169 X-axis parent of THIS to S.
172 Spanner::set_bound (Direction d, Grob *s)
174 Item *i = dynamic_cast<Item *> (s);
177 programming_error ("must have Item for spanner bound");
181 spanned_drul_[d] = i;
184 We check for System to prevent the column -> line_of_score
185 -> column -> line_of_score -> etc situation */
186 if (d == LEFT && !dynamic_cast<System *> (this))
187 set_parent (i, X_AXIS);
190 Signal that this column needs to be kept alive. They need to be
191 kept alive to have meaningful position and linebreaking.
193 [maybe we should try keeping all columns alive?, and perhaps
194 inherit position from their (non-)musical brother]
197 if (dynamic_cast<Paper_column *> (i))
198 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
201 Spanner::Spanner (SCM s, Object_key const *key)
205 spanned_drul_[LEFT] = 0;
206 spanned_drul_[RIGHT] = 0;
209 Spanner::Spanner (Spanner const &s, int count)
212 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
216 Spanner::spanner_length () const
218 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
219 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
222 programming_error ("spanner with negative length");
228 Spanner::get_system () const
230 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
232 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
234 return spanned_drul_[LEFT]->get_system ();
238 Spanner::find_broken_piece (System *l) const
240 int idx = binsearch_links (broken_intos_, (Spanner *)l, Spanner::compare);
245 return broken_intos_ [idx];
249 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
251 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
255 Spanner::is_broken () const
257 return broken_intos_.size ();
261 If this is a broken spanner, return the amount the left end is to be
262 shifted horizontally so that the spanner starts after the initial
263 clef and key on the staves. This is necessary for ties, slurs,
264 crescendo and decrescendo signs, for example.
267 Spanner::get_broken_left_end_align () const
269 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
271 // Relevant only if left span point is first column in line
273 && sc->break_status_dir () == RIGHT)
276 We used to do a full search for the Break_align_item.
277 But that doesn't make a difference, since the Paper_column
278 is likely to contain only a Break_align_item.
280 return sc->extent (sc, X_AXIS)[RIGHT];
287 Spanner::derived_mark () const
291 if (spanned_drul_[d])
292 scm_gc_mark (spanned_drul_[d]->self_scm ());
293 while (flip (&d) != LEFT)
296 for (int i = broken_intos_.size (); i--;)
297 scm_gc_mark (broken_intos_[i]->self_scm ());
301 Set left or right bound to IT.
303 Warning: caller should ensure that subsequent calls put in ITems
304 that are left-to-right ordered.
307 add_bound_item (Spanner *sp, Grob *it)
309 if (!sp->get_bound (LEFT))
310 sp->set_bound (LEFT, it);
312 sp->set_bound (RIGHT, it);
315 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
317 Spanner::set_spacing_rods (SCM smob)
319 Grob *me = unsmob_grob (smob);
322 Spanner *sp = dynamic_cast<Spanner *> (me);
323 r.item_drul_[LEFT] = sp->get_bound (LEFT);
324 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
326 = robust_scm2double (me->get_property ("minimum-length"), 0);
329 return SCM_UNSPECIFIED;
333 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
336 broken_spanner_index (Spanner *sp)
338 Spanner *parent = dynamic_cast<Spanner *> (sp->original_);
339 return parent->broken_intos_.find_index (sp);
343 unsmob_spanner (SCM s)
345 return dynamic_cast<Spanner *> (unsmob_grob (s));
348 ADD_INTERFACE (Spanner,
350 "Some objects are horizontally spanned between objects. For\n"
351 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
352 "@code{Spanner}. All spanners have two span-points (these must be\n"
353 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
354 "also the X-reference point of the spanner.\n",