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>
13 #include <libc-extension.hh>
16 #include "paper-column.hh"
17 #include "paper-score.hh"
19 #include "paper-outputter.hh"
20 #include "paper-column.hh"
22 #include "group-interface.hh"
27 Spanner::clone (int count) const
29 return new Spanner (*this, count);
33 Spanner::do_break_processing ()
36 Item * left = spanned_drul_[LEFT];
37 Item * right = spanned_drul_[RIGHT];
43 Check if our parent in X-direction spans equally wide
46 for (int a = X_AXIS; a < NO_AXES; a ++)
48 if (Spanner* parent = dynamic_cast<Spanner*> (get_parent ((Axis)a)))
50 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
52 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
54 parent->name ().to_str0 ()));
59 if (get_system () || is_broken ())
65 If we have a spanner spanning one column, we must break it
66 anyway because it might provide a parent for another item. */
70 Item* bound = left->find_prebroken_piece (d);
72 programming_error ("no broken bound");
73 else if (bound->get_system ())
75 Spanner * span = dynamic_cast<Spanner*> (clone (broken_intos_.size ()));
76 span->set_bound (LEFT, bound);
77 span->set_bound (RIGHT, bound);
79 assert (span->get_system ());
80 span->get_system ()->typeset_grob (span);
81 broken_intos_.push (span);
84 while ((flip (&d))!= LEFT);
88 Link_array<Item> break_points = pscore_->system_->broken_col_range (left,right);
90 break_points.insert (left,0);
91 break_points.push (right);
93 for (int i =1; i < break_points.size (); i++)
95 Drul_array<Item*> bounds;
96 bounds[LEFT] = break_points[i-1];
97 bounds[RIGHT] = break_points[i];
101 if (!bounds[d]->get_system ())
102 bounds[d] = bounds[d]->find_prebroken_piece (- d);
104 while ((flip (&d))!= LEFT);
106 if (!bounds[LEFT] || ! bounds[RIGHT])
108 programming_error ("bounds of this piece aren't breakable. ");
112 Spanner *span = dynamic_cast<Spanner*> (clone (broken_intos_.size ()));
113 span->set_bound (LEFT,bounds[LEFT]);
114 span->set_bound (RIGHT,bounds[RIGHT]);
116 if (!bounds[LEFT]->get_system ()
118 || !bounds[RIGHT]->get_system ()
119 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
121 programming_error ("bounds of spanner are invalid");
126 bounds[LEFT]->get_system ()->typeset_grob (span);
127 broken_intos_.push (span);
131 broken_intos_.sort (Spanner::compare);
132 for (int i = broken_intos_.size ();i--;)
133 broken_intos_[i]->break_index_ = i;
137 Spanner::get_break_index () const
143 Spanner::set_my_columns ()
145 Direction i = (Direction) LEFT;
148 if (!spanned_drul_[i]->get_system ())
149 set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
151 while (flip (&i) != LEFT);
155 Spanner::spanned_rank_iv ()
157 Interval_t<int> iv (0, 0);
159 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
160 iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
161 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
162 iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
168 Spanner::get_bound (Direction d) const
170 return spanned_drul_ [d];
174 Set the items that this spanner spans. If D == LEFT, we also set the
175 X-axis parent of THIS to S.
178 Spanner::set_bound (Direction d, Grob*s)
180 Item * i = dynamic_cast<Item*> (s);
183 programming_error ("Must have Item for spanner bound.");
187 spanned_drul_[d] = i;
190 We check for System to prevent the column -> line_of_score
191 -> column -> line_of_score -> etc situation */
192 if (d == LEFT && !dynamic_cast<System*> (this))
194 set_parent (i, X_AXIS);
198 Signal that this column needs to be kept alive. They need to be
199 kept alive to have meaningful position and linebreaking.
201 [maybe we should try keeping all columns alive?, and perhaps
202 inherit position from their (non-)musical brother]
205 if (dynamic_cast<Paper_column*> (i))
207 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
211 Spanner::Spanner (SCM s, Object_key const*key)
215 spanned_drul_[LEFT]= 0;
216 spanned_drul_[RIGHT]= 0;
218 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
221 Spanner::Spanner (Spanner const &s, int count)
224 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
228 Spanner::spanner_length () const
230 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
231 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
234 programming_error ("spanner with negative length");
240 Spanner::get_system () const
242 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
244 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
246 return spanned_drul_[LEFT]->get_system ();
251 Spanner::find_broken_piece (System*l) const
253 int idx = binsearch_links (broken_intos_, (Spanner*)l, Spanner::compare);
258 return broken_intos_ [idx];
263 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
265 return p1->get_system ()->rank_ - p2->get_system ()->rank_;
269 Spanner::is_broken () const
271 return broken_intos_.size ();
276 If this is a broken spanner, return the amount the left end is to be
277 shifted horizontally so that the spanner starts after the initial
278 clef and key on the staves. This is necessary for ties, slurs,
279 crescendo and decrescendo signs, for example.
282 Spanner::get_broken_left_end_align () const
284 Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->get_column ());
286 // Relevant only if left span point is first column in line
288 sc->break_status_dir () == RIGHT)
292 We used to do a full search for the Break_align_item.
293 But that doesn't make a difference, since the Paper_column
294 is likely to contain only a Break_align_item.
296 return sc->extent (sc, X_AXIS)[RIGHT];
303 Spanner::do_derived_mark () const
306 We'd be fucked if this is called before spanned_drul_[] is inited. */
307 if (status_ == ORPHAN)
312 if (spanned_drul_[d])
313 scm_gc_mark (spanned_drul_[d]->self_scm ());
314 while (flip (&d) != LEFT);
316 for (int i = broken_intos_.size () ; i--;)
317 scm_gc_mark (broken_intos_[i]->self_scm ());
324 Set left or right bound to IT.
326 Warning: caller should ensure that subsequent calls put in ITems
327 that are left-to-right ordered.
330 add_bound_item (Spanner* sp, Grob*it)
332 if (!sp->get_bound (LEFT))
333 sp->set_bound (LEFT, it);
335 sp->set_bound (RIGHT, it);
339 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
341 Spanner::set_spacing_rods (SCM smob)
343 Grob*me = unsmob_grob (smob);
346 Spanner*sp = dynamic_cast<Spanner*> (me);
347 r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
348 r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
350 robust_scm2double (me->get_property ("minimum-length"), 0);
353 return SCM_UNSPECIFIED;
358 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
361 broken_spanner_index (Spanner * sp)
363 Spanner * parent = dynamic_cast<Spanner*> (sp->original_);
364 return parent->broken_intos_.find_index (sp);
369 unsmob_spanner (SCM s )
371 return dynamic_cast<Spanner*> (unsmob_grob (s));
374 ADD_INTERFACE (Spanner,
376 "Some objects are horizontally spanned between objects. For\n"
377 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
378 "@code{Spanner}. All spanners have two span-points (these must be\n"
379 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
380 "also the X-reference point of the spanner.\n"