2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include <libc-extension.hh>
14 #include "paper-column.hh"
15 #include "paper-score.hh"
16 #include "molecule.hh"
17 #include "paper-outputter.hh"
18 #include "paper-column.hh"
20 #include "group-interface.hh"
23 Spanner::do_break_processing ()
26 Item * left = spanned_drul_[LEFT];
27 Item * right = spanned_drul_[RIGHT];
33 Check if our parent in X-direction spans equally wide
36 for (int a = X_AXIS; a < NO_AXES; a ++)
38 if (Spanner* parent = dynamic_cast<Spanner*> (get_parent ((Axis)a)))
40 if (!parent->spanned_rank_iv ().contains_b (this->spanned_rank_iv ()))
42 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
44 parent->name ().to_str0 ()));
49 if (get_system () || broken_b ())
55 If we have a spanner spanning one column, we must break it
56 anyway because it might provide a parent for another item. */
60 Item* bound = left->find_prebroken_piece (d);
62 programming_error ("no broken bound");
63 else if (bound->get_system ())
65 Spanner * span = dynamic_cast<Spanner*> (clone ());
66 span->set_bound (LEFT, bound);
67 span->set_bound (RIGHT, bound);
69 assert (span->get_system ());
70 span->get_system ()->typeset_grob (span);
71 broken_intos_.push (span);
74 while ((flip (&d))!= LEFT);
78 Link_array<Item> break_points = pscore_->system_->broken_col_range (left,right);
80 break_points.insert (left,0);
81 break_points.push (right);
83 for (int i=1; i < break_points.size (); i++)
85 Drul_array<Item*> bounds;
86 bounds[LEFT] = break_points[i-1];
87 bounds[RIGHT] = break_points[i];
91 if (!bounds[d]->get_system ())
92 bounds[d] = bounds[d]->find_prebroken_piece (- d);
94 while ((flip (&d))!= LEFT);
96 if (!bounds[LEFT] || ! bounds[RIGHT])
98 programming_error ("bounds of this piece aren't breakable. ");
102 Spanner *span = dynamic_cast<Spanner*> (clone ());
103 span->set_bound (LEFT,bounds[LEFT]);
104 span->set_bound (RIGHT,bounds[RIGHT]);
106 if (!bounds[LEFT]->get_system ()
108 || !bounds[RIGHT]->get_system ()
109 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
111 programming_error ("bounds of spanner are invalid");
116 bounds[LEFT]->get_system ()->typeset_grob (span);
117 broken_intos_.push (span);
121 broken_intos_.sort (Spanner::compare);
122 for (int i= broken_intos_.size();i--;)
123 broken_intos_[i]->break_index_ = i;
127 Spanner::set_my_columns ()
129 Direction i = (Direction) LEFT;
132 if (!spanned_drul_[i]->get_system ())
133 set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
135 while (flip (&i) != LEFT);
139 Spanner::spanned_rank_iv ()
141 Interval_t<int> iv (0, 0);
143 if (spanned_drul_[LEFT])
145 iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
147 if (spanned_drul_[RIGHT])
149 iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
156 Spanner::get_bound (Direction d) const
158 return spanned_drul_ [d];
162 Set the items that this spanner spans. If D == LEFT, we also set the
163 X-axis parent of THIS to S.
166 Spanner::set_bound (Direction d, Grob*s)
168 Item * i = dynamic_cast<Item*> (s);
171 programming_error ("Must have Item for spanner bound.");
178 We check for System to prevent the column -> line_of_score
179 -> column -> line_of_score -> etc situation */
180 if (d== LEFT && !dynamic_cast<System*> (this))
182 set_parent (i, X_AXIS);
186 Signal that this column needs to be kept alive. They need to be
187 kept alive to have meaningful position and linebreaking.
189 [maybe we should try keeping all columns alive?, and perhaps
190 inherit position from their (non-)musical brother]
193 if (dynamic_cast<Paper_column*> (i))
195 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
199 Spanner::Spanner (SCM s)
203 spanned_drul_[LEFT]=0;
204 spanned_drul_[RIGHT]=0;
205 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
210 Spanner::Spanner (Spanner const &s)
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 ();
240 Spanner::find_broken_piece (System*l) const
242 int idx = binsearch_links (broken_intos_, (Spanner*)l, Spanner::compare);
247 return broken_intos_ [idx];
252 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
254 return p1->get_system ()->rank_ - p2->get_system ()->rank_;
258 Spanner::broken_b () const
260 return broken_intos_.size ();
265 If this is a broken spanner, return the amount the left end is to be
266 shifted horizontally so that the spanner starts after the initial
267 clef and key on the staves. This is necessary for ties, slurs,
268 crescendo and decrescendo signs, for example.
271 Spanner::get_broken_left_end_align () const
273 Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->get_column ());
275 // Relevant only if left span point is first column in line
277 sc->break_status_dir () == RIGHT)
281 We used to do a full search for the Break_align_item.
282 But that doesn't make a difference, since the Paper_column
283 is likely to contain only a Break_align_item.
285 return sc->extent (sc, X_AXIS)[RIGHT];
292 Spanner::do_derived_mark () const
295 We'd be fucked if this is called before spanned_drul_[] is inited. */
296 if (status_ == ORPHAN)
301 if (spanned_drul_[d])
302 scm_gc_mark (spanned_drul_[d]->self_scm ());
303 while (flip (&d) != LEFT);
305 for (int i= broken_intos_.size () ; i--;)
306 scm_gc_mark (broken_intos_[i]->self_scm ());
313 Set left or right bound to IT.
315 Warning: caller should ensure that subsequent calls put in ITems
316 that are left-to-right ordered.
319 add_bound_item (Spanner* sp, Grob*it)
321 if (!sp->get_bound (LEFT))
322 sp->set_bound (LEFT, it);
324 sp->set_bound (RIGHT, it);
328 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
330 Spanner::set_spacing_rods (SCM smob)
332 Grob*me = unsmob_grob (smob);
335 Spanner*sp = dynamic_cast<Spanner*> (me);
336 r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
337 r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
339 robust_scm2double (me->get_grob_property ("minimum-length"), 0);
342 return SCM_UNSPECIFIED;
347 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
350 broken_spanner_index (Spanner * sp)
352 Spanner * parent = dynamic_cast<Spanner*> (sp->original_);
353 return parent->broken_intos_.find_index (sp);
358 unsmob_spanner (SCM s )
360 return dynamic_cast<Spanner*> (unsmob_grob (s));
363 ADD_INTERFACE(Spanner,
365 "Other grobs have a shape that depends on the horizontal spacing. For\n"
366 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
367 "@code{Spanner}. All spanners have two span-points (these must be\n"
368 "@code{Item}s), one on the left and one on the right. The left bound is\n"
369 "also the X-reference point of the spanner.\n"