2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include <libc-extension.hh>
14 #include "paper-column.hh"
15 #include "paper-score.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 ().superset (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 () || is_broken ())
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::get_break_index () const
133 Spanner::set_my_columns ()
135 Direction i = (Direction) LEFT;
138 if (!spanned_drul_[i]->get_system ())
139 set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
141 while (flip (&i) != LEFT);
145 Spanner::spanned_rank_iv ()
147 Interval_t<int> iv (0, 0);
149 if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
150 iv[LEFT] = Paper_column::get_rank (spanned_drul_[LEFT]->get_column ());
151 if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
152 iv[RIGHT] = Paper_column::get_rank (spanned_drul_[RIGHT]->get_column ());
158 Spanner::get_bound (Direction d) const
160 return spanned_drul_ [d];
164 Set the items that this spanner spans. If D == LEFT, we also set the
165 X-axis parent of THIS to S.
168 Spanner::set_bound (Direction d, Grob*s)
170 Item * i = dynamic_cast<Item*> (s);
173 programming_error ("Must have Item for spanner bound.");
180 We check for System to prevent the column -> line_of_score
181 -> column -> line_of_score -> etc situation */
182 if (d== LEFT && !dynamic_cast<System*> (this))
184 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))
197 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
201 Spanner::Spanner (SCM s)
205 spanned_drul_[LEFT]=0;
206 spanned_drul_[RIGHT]=0;
208 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
211 Spanner::Spanner (Spanner const &s)
214 spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
218 Spanner::spanner_length () const
220 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
221 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
224 programming_error ("spanner with negative length");
230 Spanner::get_system () const
232 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
234 if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
236 return spanned_drul_[LEFT]->get_system ();
241 Spanner::find_broken_piece (System*l) const
243 int idx = binsearch_links (broken_intos_, (Spanner*)l, Spanner::compare);
248 return broken_intos_ [idx];
253 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
255 return p1->get_system ()->rank_ - p2->get_system ()->rank_;
259 Spanner::is_broken () const
261 return broken_intos_.size ();
266 If this is a broken spanner, return the amount the left end is to be
267 shifted horizontally so that the spanner starts after the initial
268 clef and key on the staves. This is necessary for ties, slurs,
269 crescendo and decrescendo signs, for example.
272 Spanner::get_broken_left_end_align () const
274 Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->get_column ());
276 // Relevant only if left span point is first column in line
278 sc->break_status_dir () == RIGHT)
282 We used to do a full search for the Break_align_item.
283 But that doesn't make a difference, since the Paper_column
284 is likely to contain only a Break_align_item.
286 return sc->extent (sc, X_AXIS)[RIGHT];
293 Spanner::do_derived_mark () const
296 We'd be fucked if this is called before spanned_drul_[] is inited. */
297 if (status_ == ORPHAN)
302 if (spanned_drul_[d])
303 scm_gc_mark (spanned_drul_[d]->self_scm ());
304 while (flip (&d) != LEFT);
306 for (int i= broken_intos_.size () ; i--;)
307 scm_gc_mark (broken_intos_[i]->self_scm ());
314 Set left or right bound to IT.
316 Warning: caller should ensure that subsequent calls put in ITems
317 that are left-to-right ordered.
320 add_bound_item (Spanner* sp, Grob*it)
322 if (!sp->get_bound (LEFT))
323 sp->set_bound (LEFT, it);
325 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_l_drul_[LEFT] = sp->get_bound (LEFT);
338 r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
340 robust_scm2double (me->get_property ("minimum-length"), 0);
343 return SCM_UNSPECIFIED;
348 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
351 broken_spanner_index (Spanner * sp)
353 Spanner * parent = dynamic_cast<Spanner*> (sp->original_);
354 return parent->broken_intos_.find_index (sp);
359 unsmob_spanner (SCM s )
361 return dynamic_cast<Spanner*> (unsmob_grob (s));
364 ADD_INTERFACE (Spanner,
366 "Some objects are horizontally spanned between objects. For\n"
367 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
368 "@code{Spanner}. All spanners have two span-points (these must be\n"
369 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
370 "also the X-reference point of the spanner.\n"