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>
13 #include <libc-extension.hh>
16 #include "paper-column.hh"
17 #include "paper-score.hh"
19 #include "paper-column.hh"
21 #include "group-interface.hh"
26 Spanner::clone (int count) const
28 return new Spanner (*this, count);
32 Spanner::do_break_processing ()
35 Item * left = spanned_drul_[LEFT];
36 Item * right = spanned_drul_[RIGHT];
42 Check if our parent in X-direction spans equally wide
45 for (int a = X_AXIS; a < NO_AXES; a ++)
47 if (Spanner* parent = dynamic_cast<Spanner*> (get_parent ((Axis)a)))
49 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
51 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
53 parent->name ().to_str0 ()));
58 if (get_system () || is_broken ())
64 If we have a spanner spanning one column, we must break it
65 anyway because it might provide a parent for another item. */
69 Item* bound = left->find_prebroken_piece (d);
71 programming_error ("no broken bound");
72 else if (bound->get_system ())
74 Spanner * span = dynamic_cast<Spanner*> (clone (broken_intos_.size ()));
75 span->set_bound (LEFT, bound);
76 span->set_bound (RIGHT, bound);
78 assert (span->get_system ());
79 span->get_system ()->typeset_grob (span);
80 broken_intos_.push (span);
83 while ((flip (&d))!= LEFT);
87 Link_array<Item> break_points = pscore_->system_->broken_col_range (left, right);
89 break_points.insert (left, 0);
90 break_points.push (right);
92 for (int i =1; i < break_points.size (); i++)
94 Drul_array<Item*> bounds;
95 bounds[LEFT] = break_points[i-1];
96 bounds[RIGHT] = break_points[i];
100 if (!bounds[d]->get_system ())
101 bounds[d] = bounds[d]->find_prebroken_piece (- d);
103 while ((flip (&d))!= LEFT);
105 if (!bounds[LEFT] || ! bounds[RIGHT])
107 programming_error ("bounds of this piece aren't breakable. ");
111 Spanner *span = dynamic_cast<Spanner*> (clone (broken_intos_.size ()));
112 span->set_bound (LEFT, bounds[LEFT]);
113 span->set_bound (RIGHT, bounds[RIGHT]);
115 if (!bounds[LEFT]->get_system ()
117 || !bounds[RIGHT]->get_system ()
118 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
120 programming_error ("bounds of spanner are invalid");
125 bounds[LEFT]->get_system ()->typeset_grob (span);
126 broken_intos_.push (span);
130 broken_intos_.sort (Spanner::compare);
131 for (int i = broken_intos_.size ();i--;)
132 broken_intos_[i]->break_index_ = i;
136 Spanner::get_break_index () const
142 Spanner::set_my_columns ()
144 Direction i = (Direction) LEFT;
147 if (!spanned_drul_[i]->get_system ())
148 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
150 while (flip (&i) != LEFT);
154 Spanner::spanned_rank_iv ()
156 Interval_t<int> iv (0, 0);
158 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
159 iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
160 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
161 iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
167 Spanner::get_bound (Direction d) const
169 return spanned_drul_ [d];
173 Set the items that this spanner spans. If D == LEFT, we also set the
174 X-axis parent of THIS to S.
177 Spanner::set_bound (Direction d, Grob*s)
179 Item * i = dynamic_cast<Item*> (s);
182 programming_error ("Must have Item for spanner bound.");
186 spanned_drul_[d] = i;
189 We check for System to prevent the column -> line_of_score
190 -> column -> line_of_score -> etc situation */
191 if (d == LEFT && !dynamic_cast<System*> (this))
193 set_parent (i, X_AXIS);
197 Signal that this column needs to be kept alive. They need to be
198 kept alive to have meaningful position and linebreaking.
200 [maybe we should try keeping all columns alive?, and perhaps
201 inherit position from their (non-)musical brother]
204 if (dynamic_cast<Paper_column*> (i))
206 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
210 Spanner::Spanner (SCM s, Object_key const*key)
214 spanned_drul_[LEFT]= 0;
215 spanned_drul_[RIGHT]= 0;
217 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
220 Spanner::Spanner (Spanner const &s, int count)
223 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
227 Spanner::spanner_length () const
229 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
230 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
233 programming_error ("spanner with negative length");
239 Spanner::get_system () const
241 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
243 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
245 return spanned_drul_[LEFT]->get_system ();
250 Spanner::find_broken_piece (System*l) const
252 int idx = binsearch_links (broken_intos_, (Spanner*)l, Spanner::compare);
257 return broken_intos_ [idx];
262 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
264 return p1->get_system ()->rank_ - p2->get_system ()->rank_;
268 Spanner::is_broken () const
270 return broken_intos_.size ();
275 If this is a broken spanner, return the amount the left end is to be
276 shifted horizontally so that the spanner starts after the initial
277 clef and key on the staves. This is necessary for ties, slurs,
278 crescendo and decrescendo signs, for example.
281 Spanner::get_broken_left_end_align () const
283 Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->get_column ());
285 // Relevant only if left span point is first column in line
287 sc->break_status_dir () == RIGHT)
291 We used to do a full search for the Break_align_item.
292 But that doesn't make a difference, since the Paper_column
293 is likely to contain only a Break_align_item.
295 return sc->extent (sc, X_AXIS)[RIGHT];
302 Spanner::do_derived_mark () const
305 We'd be fucked if this is called before spanned_drul_[] is inited. */
306 if (status_ == ORPHAN)
311 if (spanned_drul_[d])
312 scm_gc_mark (spanned_drul_[d]->self_scm ());
313 while (flip (&d) != LEFT);
315 for (int i = broken_intos_.size () ; i--;)
316 scm_gc_mark (broken_intos_[i]->self_scm ());
323 Set left or right bound to IT.
325 Warning: caller should ensure that subsequent calls put in ITems
326 that are left-to-right ordered.
329 add_bound_item (Spanner* sp, Grob*it)
331 if (!sp->get_bound (LEFT))
332 sp->set_bound (LEFT, it);
334 sp->set_bound (RIGHT, it);
338 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
340 Spanner::set_spacing_rods (SCM smob)
342 Grob*me = unsmob_grob (smob);
345 Spanner*sp = dynamic_cast<Spanner*> (me);
346 r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
347 r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
349 robust_scm2double (me->get_property ("minimum-length"), 0);
352 return SCM_UNSPECIFIED;
357 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
360 broken_spanner_index (Spanner * sp)
362 Spanner * parent = dynamic_cast<Spanner*> (sp->original_);
363 return parent->broken_intos_.find_index (sp);
368 unsmob_spanner (SCM s )
370 return dynamic_cast<Spanner*> (unsmob_grob (s));
373 ADD_INTERFACE (Spanner,
375 "Some objects are horizontally spanned between objects. For\n"
376 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
377 "@code{Spanner}. All spanners have two span-points (these must be\n"
378 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
379 "also the X-reference point of the spanner.\n"