2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include <libc-extension.hh>
14 #include "paper-column.hh"
15 #include "paper-score.hh"
17 #include "paper-outputter.hh"
18 #include "paper-column.hh"
20 #include "group-interface.hh"
25 Spanner::clone (int count) const
27 return new Spanner (*this, count);
31 Spanner::do_break_processing ()
34 Item * left = spanned_drul_[LEFT];
35 Item * right = spanned_drul_[RIGHT];
41 Check if our parent in X-direction spans equally wide
44 for (int a = X_AXIS; a < NO_AXES; a ++)
46 if (Spanner* parent = dynamic_cast<Spanner*> (get_parent ((Axis)a)))
48 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
50 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
52 parent->name ().to_str0 ()));
57 if (get_system () || is_broken ())
63 If we have a spanner spanning one column, we must break it
64 anyway because it might provide a parent for another item. */
68 Item* bound = left->find_prebroken_piece (d);
70 programming_error ("no broken bound");
71 else if (bound->get_system ())
73 Spanner * span = dynamic_cast<Spanner*> (clone (broken_intos_.size ()));
74 span->set_bound (LEFT, bound);
75 span->set_bound (RIGHT, bound);
77 assert (span->get_system ());
78 span->get_system ()->typeset_grob (span);
79 broken_intos_.push (span);
82 while ((flip (&d))!= LEFT);
86 Link_array<Item> break_points = pscore_->system_->broken_col_range (left,right);
88 break_points.insert (left,0);
89 break_points.push (right);
91 for (int i=1; i < break_points.size (); i++)
93 Drul_array<Item*> bounds;
94 bounds[LEFT] = break_points[i-1];
95 bounds[RIGHT] = break_points[i];
99 if (!bounds[d]->get_system ())
100 bounds[d] = bounds[d]->find_prebroken_piece (- d);
102 while ((flip (&d))!= LEFT);
104 if (!bounds[LEFT] || ! bounds[RIGHT])
106 programming_error ("bounds of this piece aren't breakable. ");
110 Spanner *span = dynamic_cast<Spanner*> (clone (broken_intos_.size ()));
111 span->set_bound (LEFT,bounds[LEFT]);
112 span->set_bound (RIGHT,bounds[RIGHT]);
114 if (!bounds[LEFT]->get_system ()
116 || !bounds[RIGHT]->get_system ()
117 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
119 programming_error ("bounds of spanner are invalid");
124 bounds[LEFT]->get_system ()->typeset_grob (span);
125 broken_intos_.push (span);
129 broken_intos_.sort (Spanner::compare);
130 for (int i= broken_intos_.size ();i--;)
131 broken_intos_[i]->break_index_ = i;
135 Spanner::get_break_index () const
141 Spanner::set_my_columns ()
143 Direction i = (Direction) LEFT;
146 if (!spanned_drul_[i]->get_system ())
147 set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
149 while (flip (&i) != LEFT);
153 Spanner::spanned_rank_iv ()
155 Interval_t<int> iv (0, 0);
157 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
158 iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
159 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
160 iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
166 Spanner::get_bound (Direction d) const
168 return spanned_drul_ [d];
172 Set the items that this spanner spans. If D == LEFT, we also set the
173 X-axis parent of THIS to S.
176 Spanner::set_bound (Direction d, Grob*s)
178 Item * i = dynamic_cast<Item*> (s);
181 programming_error ("Must have Item for spanner bound.");
188 We check for System to prevent the column -> line_of_score
189 -> column -> line_of_score -> etc situation */
190 if (d== LEFT && !dynamic_cast<System*> (this))
192 set_parent (i, X_AXIS);
196 Signal that this column needs to be kept alive. They need to be
197 kept alive to have meaningful position and linebreaking.
199 [maybe we should try keeping all columns alive?, and perhaps
200 inherit position from their (non-)musical brother]
203 if (dynamic_cast<Paper_column*> (i))
205 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
209 Spanner::Spanner (SCM s, Object_key const*key)
213 spanned_drul_[LEFT]=0;
214 spanned_drul_[RIGHT]=0;
216 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
219 Spanner::Spanner (Spanner const &s, int count)
222 spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
226 Spanner::spanner_length () const
228 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
229 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
232 programming_error ("spanner with negative length");
238 Spanner::get_system () const
240 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
242 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
244 return spanned_drul_[LEFT]->get_system ();
249 Spanner::find_broken_piece (System*l) const
251 int idx = binsearch_links (broken_intos_, (Spanner*)l, Spanner::compare);
256 return broken_intos_ [idx];
261 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
263 return p1->get_system ()->rank_ - p2->get_system ()->rank_;
267 Spanner::is_broken () const
269 return broken_intos_.size ();
274 If this is a broken spanner, return the amount the left end is to be
275 shifted horizontally so that the spanner starts after the initial
276 clef and key on the staves. This is necessary for ties, slurs,
277 crescendo and decrescendo signs, for example.
280 Spanner::get_broken_left_end_align () const
282 Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->get_column ());
284 // Relevant only if left span point is first column in line
286 sc->break_status_dir () == RIGHT)
290 We used to do a full search for the Break_align_item.
291 But that doesn't make a difference, since the Paper_column
292 is likely to contain only a Break_align_item.
294 return sc->extent (sc, X_AXIS)[RIGHT];
301 Spanner::do_derived_mark () const
304 We'd be fucked if this is called before spanned_drul_[] is inited. */
305 if (status_ == ORPHAN)
310 if (spanned_drul_[d])
311 scm_gc_mark (spanned_drul_[d]->self_scm ());
312 while (flip (&d) != LEFT);
314 for (int i= broken_intos_.size () ; i--;)
315 scm_gc_mark (broken_intos_[i]->self_scm ());
322 Set left or right bound to IT.
324 Warning: caller should ensure that subsequent calls put in ITems
325 that are left-to-right ordered.
328 add_bound_item (Spanner* sp, Grob*it)
330 if (!sp->get_bound (LEFT))
331 sp->set_bound (LEFT, it);
333 sp->set_bound (RIGHT, it);
337 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
339 Spanner::set_spacing_rods (SCM smob)
341 Grob*me = unsmob_grob (smob);
344 Spanner*sp = dynamic_cast<Spanner*> (me);
345 r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
346 r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
348 robust_scm2double (me->get_property ("minimum-length"), 0);
351 return SCM_UNSPECIFIED;
356 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
359 broken_spanner_index (Spanner * sp)
361 Spanner * parent = dynamic_cast<Spanner*> (sp->original_);
362 return parent->broken_intos_.find_index (sp);
367 unsmob_spanner (SCM s )
369 return dynamic_cast<Spanner*> (unsmob_grob (s));
372 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"