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 "group-interface.hh"
14 #include "libc-extension.hh"
15 #include "paper-column.hh"
16 #include "paper-column.hh"
17 #include "paper-score.hh"
23 Spanner::clone (int count) const
25 return new Spanner (*this, count);
29 Spanner::do_break_processing ()
32 Item *left = spanned_drul_[LEFT];
33 Item *right = spanned_drul_[RIGHT];
39 Check if our parent in X-direction spans equally wide
42 for (int a = X_AXIS; a < NO_AXES; a++)
44 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
46 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
48 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
50 parent->name ().to_str0 ()));
55 if (get_system () || is_broken ())
61 If we have a spanner spanning one column, we must break it
62 anyway because it might provide a parent for another item. */
66 Item *bound = left->find_prebroken_piece (d);
68 programming_error ("no broken bound");
69 else if (bound->get_system ())
71 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
72 span->set_bound (LEFT, bound);
73 span->set_bound (RIGHT, bound);
75 assert (span->get_system ());
76 span->get_system ()->typeset_grob (span);
77 broken_intos_.push (span);
80 while ((flip (&d)) != LEFT);
84 Link_array<Item> break_points = pscore_->root_system ()->broken_col_range (left, right);
86 break_points.insert (left, 0);
87 break_points.push (right);
89 for (int i = 1; i < break_points.size (); i++)
91 Drul_array<Item *> bounds;
92 bounds[LEFT] = break_points[i - 1];
93 bounds[RIGHT] = break_points[i];
97 if (!bounds[d]->get_system ())
98 bounds[d] = bounds[d]->find_prebroken_piece (- d);
100 while ((flip (&d)) != LEFT);
102 if (!bounds[LEFT] || ! bounds[RIGHT])
104 programming_error ("bounds of this piece aren't breakable. ");
108 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
109 span->set_bound (LEFT, bounds[LEFT]);
110 span->set_bound (RIGHT, bounds[RIGHT]);
112 if (!bounds[LEFT]->get_system ()
114 || !bounds[RIGHT]->get_system ()
115 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
117 programming_error ("bounds of spanner are invalid");
122 bounds[LEFT]->get_system ()->typeset_grob (span);
123 broken_intos_.push (span);
127 broken_intos_.sort (Spanner::compare);
128 for (int i = broken_intos_.size ();i--;)
129 broken_intos_[i]->break_index_ = i;
133 Spanner::get_break_index () const
139 Spanner::set_my_columns ()
141 Direction i = (Direction) LEFT;
144 if (!spanned_drul_[i]->get_system ())
145 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
147 while (flip (&i) != LEFT);
151 Spanner::spanned_rank_iv ()
153 Interval_t<int> iv (0, 0);
155 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
156 iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
157 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
158 iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
163 Spanner::get_bound (Direction d) const
165 return spanned_drul_ [d];
169 Set the items that this spanner spans. If D == LEFT, we also set the
170 X-axis parent of THIS to S.
173 Spanner::set_bound (Direction d, Grob *s)
175 Item *i = dynamic_cast<Item *> (s);
178 programming_error ("must have Item for spanner bound");
182 spanned_drul_[d] = i;
185 We check for System to prevent the column -> line_of_score
186 -> column -> line_of_score -> etc situation */
187 if (d == LEFT && !dynamic_cast<System *> (this))
189 set_parent (i, X_AXIS);
193 Signal that this column needs to be kept alive. They need to be
194 kept alive to have meaningful position and linebreaking.
196 [maybe we should try keeping all columns alive?, and perhaps
197 inherit position from their (non-)musical brother]
200 if (dynamic_cast<Paper_column *> (i))
202 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
206 Spanner::Spanner (SCM s, Object_key const *key)
210 spanned_drul_[LEFT] = 0;
211 spanned_drul_[RIGHT] = 0;
213 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
216 Spanner::Spanner (Spanner const &s, int count)
219 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
223 Spanner::spanner_length () const
225 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
226 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
229 programming_error ("spanner with negative length");
235 Spanner::get_system () const
237 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
239 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
241 return spanned_drul_[LEFT]->get_system ();
245 Spanner::find_broken_piece (System *l) const
247 int idx = binsearch_links (broken_intos_, (Spanner *)l, Spanner::compare);
252 return broken_intos_ [idx];
256 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
258 return p1->get_system ()->rank_ - p2->get_system ()->rank_;
262 Spanner::is_broken () const
264 return broken_intos_.size ();
268 If this is a broken spanner, return the amount the left end is to be
269 shifted horizontally so that the spanner starts after the initial
270 clef and key on the staves. This is necessary for ties, slurs,
271 crescendo and decrescendo signs, for example.
274 Spanner::get_broken_left_end_align () const
276 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
278 // Relevant only if left span point is first column in line
280 && sc->break_status_dir () == RIGHT)
283 We used to do a full search for the Break_align_item.
284 But that doesn't make a difference, since the Paper_column
285 is likely to contain only a Break_align_item.
287 return sc->extent (sc, X_AXIS)[RIGHT];
294 Spanner::do_derived_mark () const
297 We'd be fucked if this is called before spanned_drul_[] is inited. */
298 if (status_ == ORPHAN)
303 if (spanned_drul_[d])
304 scm_gc_mark (spanned_drul_[d]->self_scm ());
305 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",