2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "pointer-group-interface.hh"
10 #include "libc-extension.hh"
11 #include "paper-column.hh"
12 #include "paper-column.hh"
13 #include "paper-score.hh"
19 Spanner::clone (int count) const
21 return new Spanner (*this, count);
25 Spanner::do_break_processing ()
28 Item *left = spanned_drul_[LEFT];
29 Item *right = spanned_drul_[RIGHT];
35 Check if our parent in X-direction spans equally wide
38 for (int a = X_AXIS; a < NO_AXES; a++)
40 if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
42 if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
44 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
46 parent->name ().c_str ()));
51 if (get_system () || is_broken ())
57 If we have a spanner spanning one column, we must break it
58 anyway because it might provide a parent for another item. */
62 Item *bound = left->find_prebroken_piece (d);
64 programming_error ("no broken bound");
65 else if (bound->get_system ())
67 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
68 span->set_bound (LEFT, bound);
69 span->set_bound (RIGHT, bound);
71 assert (span->get_system ());
72 span->get_system ()->typeset_grob (span);
73 broken_intos_.push_back (span);
76 while ((flip (&d)) != LEFT);
80 System *root = get_root_system (this);
81 vector<Item*> break_points = root->broken_col_range (left, right);
83 break_points.insert (break_points.begin () + 0, left);
84 break_points.push_back (right);
86 for (vsize i = 1; i < break_points.size (); i++)
88 Drul_array<Item *> bounds;
89 bounds[LEFT] = break_points[i - 1];
90 bounds[RIGHT] = break_points[i];
94 if (!bounds[d]->get_system ())
95 bounds[d] = bounds[d]->find_prebroken_piece (- d);
97 while ((flip (&d)) != LEFT);
99 if (!bounds[LEFT] || ! bounds[RIGHT])
101 programming_error ("bounds of this piece aren't breakable. ");
105 Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
106 span->set_bound (LEFT, bounds[LEFT]);
107 span->set_bound (RIGHT, bounds[RIGHT]);
109 if (!bounds[LEFT]->get_system ()
111 || !bounds[RIGHT]->get_system ()
112 || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
114 programming_error ("bounds of spanner are invalid");
119 bounds[LEFT]->get_system ()->typeset_grob (span);
120 broken_intos_.push_back (span);
124 vector_sort (broken_intos_, Spanner::less);
125 for (vsize i = broken_intos_.size (); i--;)
126 broken_intos_[i]->break_index_ = i;
130 Spanner::get_break_index () const
136 Spanner::set_my_columns ()
138 Direction i = (Direction) LEFT;
141 if (!spanned_drul_[i]->get_system ())
142 set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
144 while (flip (&i) != LEFT);
148 Spanner::spanned_rank_iv ()
150 Interval_t<int> iv (0, 0);
152 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
153 iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
154 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
155 iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
160 Spanner::get_bound (Direction d) const
162 return spanned_drul_ [d];
166 Set the items that this spanner spans. If D == LEFT, we also set the
167 X-axis parent of THIS to S.
170 Spanner::set_bound (Direction d, Grob *s)
172 Item *i = dynamic_cast<Item *> (s);
175 programming_error ("must have Item for spanner bound");
179 spanned_drul_[d] = i;
182 We check for System to prevent the column -> line_of_score
183 -> column -> line_of_score -> etc situation */
184 if (d == LEFT && !dynamic_cast<System *> (this))
185 set_parent (i, X_AXIS);
188 Signal that this column needs to be kept alive. They need to be
189 kept alive to have meaningful position and linebreaking.
191 [maybe we should try keeping all columns alive?, and perhaps
192 inherit position from their (non-)musical brother]
195 if (dynamic_cast<Paper_column *> (i))
196 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
199 Spanner::Spanner (SCM s, Object_key const *key)
203 spanned_drul_[LEFT] = 0;
204 spanned_drul_[RIGHT] = 0;
207 Spanner::Spanner (Spanner const &s, int count)
210 spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
214 Spanner::spanner_length () const
216 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
217 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
220 programming_error ("spanner with negative length");
226 Spanner::get_system () const
228 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
230 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
232 return spanned_drul_[LEFT]->get_system ();
236 Spanner::find_broken_piece (System *l) const
238 vsize idx = binary_search (broken_intos_, (Spanner *)l, Spanner::less);
240 return broken_intos_ [idx];
245 Spanner::broken_neighbor (Direction d) const
250 vsize k = broken_spanner_index (this);
251 Spanner *orig = dynamic_cast<Spanner*> (original_);
253 if (j < 0 || vsize (j) >= orig->broken_intos_.size ())
256 return orig->broken_intos_[j];
260 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
262 return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
266 Spanner::less (Spanner *const &a, Spanner *const &b)
268 return a->get_system ()->get_rank () < b->get_system ()->get_rank ();
272 Spanner::is_broken () const
274 return broken_intos_.size ();
278 If this is a broken spanner, return the amount the left end is to be
279 shifted horizontally so that the spanner starts after the initial
280 clef and key on the staves. This is necessary for ties, slurs,
281 crescendo and decrescendo signs, for example.
284 Spanner::get_broken_left_end_align () const
286 Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
288 // Relevant only if left span point is first column in line
290 && sc->break_status_dir () == RIGHT)
293 We used to do a full search for the Break_align_item.
294 But that doesn't make a difference, since the Paper_column
295 is likely to contain only a Break_align_item.
297 return sc->extent (sc, X_AXIS)[RIGHT];
304 Spanner::derived_mark () const
308 if (spanned_drul_[d])
309 scm_gc_mark (spanned_drul_[d]->self_scm ());
310 while (flip (&d) != LEFT)
313 for (vsize i = broken_intos_.size (); i--;)
314 scm_gc_mark (broken_intos_[i]->self_scm ());
318 Set left or right bound to IT.
320 Warning: caller should ensure that subsequent calls put in ITems
321 that are left-to-right ordered.
324 add_bound_item (Spanner *sp, Grob *it)
326 if (!sp->get_bound (LEFT))
327 sp->set_bound (LEFT, it);
329 sp->set_bound (RIGHT, it);
332 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
334 Spanner::set_spacing_rods (SCM smob)
336 Grob *me = unsmob_grob (smob);
339 Spanner *sp = dynamic_cast<Spanner *> (me);
340 r.item_drul_[LEFT] = sp->get_bound (LEFT);
341 r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
343 SCM num_length = me->get_property ("minimum-length");
344 if (scm_is_number (num_length))
346 r.distance_ = robust_scm2double (num_length, 0);
350 return SCM_UNSPECIFIED;
354 Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
357 broken_spanner_index (Spanner const *sp)
359 Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
361 return find (parent->broken_intos_, (Spanner*) sp) - parent->broken_intos_.begin ();
365 unsmob_spanner (SCM s)
367 return dynamic_cast<Spanner *> (unsmob_grob (s));
370 ADD_INTERFACE (Spanner,
371 "Some objects are horizontally spanned between objects. For\n"
372 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
373 "@code{Spanner}. All spanners have two span-points (these must be\n"
374 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
375 "also the X-reference point of the spanner.\n",