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"
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_->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] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
158 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
159 iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
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))
190 set_parent (i, X_AXIS);
194 Signal that this column needs to be kept alive. They need to be
195 kept alive to have meaningful position and linebreaking.
197 [maybe we should try keeping all columns alive?, and perhaps
198 inherit position from their (non-)musical brother]
201 if (dynamic_cast<Paper_column *> (i))
203 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
207 Spanner::Spanner (SCM s, Object_key const *key)
211 spanned_drul_[LEFT]= 0;
212 spanned_drul_[RIGHT]= 0;
214 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
217 Spanner::Spanner (Spanner const &s, int count)
220 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
224 Spanner::spanner_length () const
226 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
227 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
230 programming_error ("spanner with negative length");
236 Spanner::get_system () const
238 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
240 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
242 return spanned_drul_[LEFT]->get_system ();
246 Spanner::find_broken_piece (System *l) const
248 int idx = binsearch_links (broken_intos_, (Spanner *)l, Spanner::compare);
253 return broken_intos_ [idx];
257 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
259 return p1->get_system ()->rank_ - p2->get_system ()->rank_;
263 Spanner::is_broken () const
265 return broken_intos_.size ();
269 If this is a broken spanner, return the amount the left end is to be
270 shifted horizontally so that the spanner starts after the initial
271 clef and key on the staves. This is necessary for ties, slurs,
272 crescendo and decrescendo signs, for example.
275 Spanner::get_broken_left_end_align () const
277 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
279 // Relevant only if left span point is first column in line
281 && sc->break_status_dir () == RIGHT)
284 We used to do a full search for the Break_align_item.
285 But that doesn't make a difference, since the Paper_column
286 is likely to contain only a Break_align_item.
288 return sc->extent (sc, X_AXIS)[RIGHT];
295 Spanner::do_derived_mark () const
298 We'd be fucked if this is called before spanned_drul_[] is inited. */
299 if (status_ == ORPHAN)
304 if (spanned_drul_[d])
305 scm_gc_mark (spanned_drul_[d]->self_scm ());
306 while (flip (&d) != LEFT);
308 for (int i = broken_intos_.size (); i--;)
309 scm_gc_mark (broken_intos_[i]->self_scm ());
315 Set left or right bound to IT.
317 Warning: caller should ensure that subsequent calls put in ITems
318 that are left-to-right ordered.
321 add_bound_item (Spanner *sp, Grob *it)
323 if (!sp->get_bound (LEFT))
324 sp->set_bound (LEFT, it);
326 sp->set_bound (RIGHT, it);
329 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
331 Spanner::set_spacing_rods (SCM smob)
333 Grob *me = unsmob_grob (smob);
336 Spanner *sp = dynamic_cast<Spanner *> (me);
337 r.item_drul_[LEFT] = sp->get_bound (LEFT);
338 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
340 = robust_scm2double (me->get_property ("minimum-length"), 0);
343 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);
357 unsmob_spanner (SCM s)
359 return dynamic_cast<Spanner *> (unsmob_grob (s));
362 ADD_INTERFACE (Spanner,
364 "Some objects are horizontally spanned between objects. For\n"
365 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
366 "@code{Spanner}. All spanners have two span-points (these must be\n"
367 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
368 "also the X-reference point of the spanner.\n",