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>
14 #include "pointer-group-interface.hh"
15 #include "libc-extension.hh"
16 #include "paper-column.hh"
17 #include "paper-column.hh"
18 #include "paper-score.hh"
24 Spanner::clone (int count) const
26 return new Spanner (*this, count);
30 Spanner::do_break_processing ()
33 Item *left = spanned_drul_[LEFT];
34 Item *right = spanned_drul_[RIGHT];
40 Check if our parent in X-direction spans equally wide
43 for (int a = X_AXIS; a < NO_AXES; a++)
45 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
47 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
49 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
51 parent->name ().to_str0 ()));
56 if (get_system () || is_broken ())
62 If we have a spanner spanning one column, we must break it
63 anyway because it might provide a parent for another item. */
67 Item *bound = left->find_prebroken_piece (d);
69 programming_error ("no broken bound");
70 else if (bound->get_system ())
72 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
73 span->set_bound (LEFT, bound);
74 span->set_bound (RIGHT, bound);
76 assert (span->get_system ());
77 span->get_system ()->typeset_grob (span);
78 broken_intos_.push (span);
81 while ((flip (&d)) != LEFT);
85 Link_array<Item> break_points = pscore_->root_system ()->broken_col_range (left, right);
87 break_points.insert (left, 0);
88 break_points.push (right);
90 for (int i = 1; i < break_points.size (); i++)
92 Drul_array<Item *> bounds;
93 bounds[LEFT] = break_points[i - 1];
94 bounds[RIGHT] = break_points[i];
98 if (!bounds[d]->get_system ())
99 bounds[d] = bounds[d]->find_prebroken_piece (- d);
101 while ((flip (&d)) != LEFT);
103 if (!bounds[LEFT] || ! bounds[RIGHT])
105 programming_error ("bounds of this piece aren't breakable. ");
109 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
110 span->set_bound (LEFT, bounds[LEFT]);
111 span->set_bound (RIGHT, bounds[RIGHT]);
113 if (!bounds[LEFT]->get_system ()
115 || !bounds[RIGHT]->get_system ()
116 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
118 programming_error ("bounds of spanner are invalid");
123 bounds[LEFT]->get_system ()->typeset_grob (span);
124 broken_intos_.push (span);
128 broken_intos_.sort (Spanner::compare);
129 for (int i = broken_intos_.size ();i--;)
130 broken_intos_[i]->break_index_ = i;
134 Spanner::get_break_index () const
140 Spanner::set_my_columns ()
142 Direction i = (Direction) LEFT;
145 if (!spanned_drul_[i]->get_system ())
146 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
148 while (flip (&i) != LEFT);
152 Spanner::spanned_rank_iv ()
154 Interval_t<int> iv (0, 0);
156 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
157 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
158 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
159 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
164 Spanner::get_bound (Direction d) const
166 return spanned_drul_ [d];
170 Set the items that this spanner spans. If D == LEFT, we also set the
171 X-axis parent of THIS to S.
174 Spanner::set_bound (Direction d, Grob *s)
176 Item *i = dynamic_cast<Item *> (s);
179 programming_error ("must have Item for spanner bound");
183 spanned_drul_[d] = i;
186 We check for System to prevent the column -> line_of_score
187 -> column -> line_of_score -> etc situation */
188 if (d == LEFT && !dynamic_cast<System *> (this))
189 set_parent (i, X_AXIS);
192 Signal that this column needs to be kept alive. They need to be
193 kept alive to have meaningful position and linebreaking.
195 [maybe we should try keeping all columns alive?, and perhaps
196 inherit position from their (non-)musical brother]
199 if (dynamic_cast<Paper_column *> (i))
200 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
203 Spanner::Spanner (SCM s, Object_key const *key)
207 spanned_drul_[LEFT] = 0;
208 spanned_drul_[RIGHT] = 0;
211 Spanner::Spanner (Spanner const &s, int count)
214 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
218 Spanner::spanner_length () const
220 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
221 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
224 programming_error ("spanner with negative length");
230 Spanner::get_system () const
232 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
234 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
236 return spanned_drul_[LEFT]->get_system ();
240 Spanner::find_broken_piece (System *l) const
242 int idx = binsearch_links (broken_intos_, (Spanner *)l, Spanner::compare);
247 return broken_intos_ [idx];
251 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
253 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
257 Spanner::is_broken () const
259 return broken_intos_.size ();
263 If this is a broken spanner, return the amount the left end is to be
264 shifted horizontally so that the spanner starts after the initial
265 clef and key on the staves. This is necessary for ties, slurs,
266 crescendo and decrescendo signs, for example.
269 Spanner::get_broken_left_end_align () const
271 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
273 // Relevant only if left span point is first column in line
275 && sc->break_status_dir () == RIGHT)
278 We used to do a full search for the Break_align_item.
279 But that doesn't make a difference, since the Paper_column
280 is likely to contain only a Break_align_item.
282 return sc->extent (sc, X_AXIS)[RIGHT];
289 Spanner::derived_mark () const
293 if (spanned_drul_[d])
294 scm_gc_mark (spanned_drul_[d]->self_scm ());
295 while (flip (&d) != LEFT)
298 for (int i = broken_intos_.size (); i--;)
299 scm_gc_mark (broken_intos_[i]->self_scm ());
303 Set left or right bound to IT.
305 Warning: caller should ensure that subsequent calls put in ITems
306 that are left-to-right ordered.
309 add_bound_item (Spanner *sp, Grob *it)
311 if (!sp->get_bound (LEFT))
312 sp->set_bound (LEFT, it);
314 sp->set_bound (RIGHT, it);
317 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
319 Spanner::set_spacing_rods (SCM smob)
321 Grob *me = unsmob_grob (smob);
324 Spanner *sp = dynamic_cast<Spanner *> (me);
325 r.item_drul_[LEFT] = sp->get_bound (LEFT);
326 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
328 = robust_scm2double (me->get_property ("minimum-length"), 0);
331 return SCM_UNSPECIFIED;
335 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
338 broken_spanner_index (Spanner *sp)
340 Spanner *parent = dynamic_cast<Spanner *> (sp->original_);
341 return parent->broken_intos_.find_index (sp);
345 unsmob_spanner (SCM s)
347 return dynamic_cast<Spanner *> (unsmob_grob (s));
350 ADD_INTERFACE (Spanner,
352 "Some objects are horizontally spanned between objects. For\n"
353 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
354 "@code{Spanner}. All spanners have two span-points (these must be\n"
355 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
356 "also the X-reference point of the spanner.\n",