2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include <libc-extension.hh>
14 #include "paper-column.hh"
15 #include "paper-score.hh"
16 #include "molecule.hh"
17 #include "paper-outputter.hh"
18 #include "paper-column.hh"
19 #include "line-of-score.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 ().contains_b (this->spanned_rank_iv ()))
42 programming_error (to_str ("Spanner `%s' is not fully contained in parent spanner `%s'.",
44 parent->name ().ch_C ()));
49 if (line_l () || broken_b ())
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->line_l ())
65 Spanner * span_p = dynamic_cast<Spanner*> (clone ());
66 span_p->set_bound (LEFT, bound);
67 span_p->set_bound (RIGHT, bound);
69 assert (span_p->line_l ());
70 span_p->line_l ()->typeset_grob (span_p);
71 broken_into_l_arr_.push (span_p);
74 while ((flip (&d))!= LEFT);
78 Link_array<Item> break_points = pscore_l_->line_l_->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]->line_l ())
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_p = dynamic_cast<Spanner*> (clone ());
103 span_p->set_bound (LEFT,bounds[LEFT]);
104 span_p->set_bound (RIGHT,bounds[RIGHT]);
106 if (!bounds[LEFT]->line_l ()
108 || !bounds[RIGHT]->line_l ()
109 || bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ())
111 programming_error ("bounds of spanner are invalid");
116 bounds[LEFT]->line_l ()->typeset_grob (span_p);
117 broken_into_l_arr_.push (span_p);
121 broken_into_l_arr_.sort (Spanner::compare);
125 Spanner::set_my_columns ()
127 Direction i = (Direction) LEFT;
130 if (!spanned_drul_[i]->line_l ())
131 set_bound (i,spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
133 while (flip (&i) != LEFT);
137 Spanner::spanned_rank_iv ()
139 Interval_t<int> iv (0, 0);
141 if (spanned_drul_[LEFT])
143 iv[LEFT] = Paper_column::rank_i (spanned_drul_[LEFT]->column_l ());
145 if (spanned_drul_[RIGHT])
147 iv[RIGHT] = Paper_column::rank_i (spanned_drul_[RIGHT]->column_l ());
153 Spanner::get_bound (Direction d) const
155 return spanned_drul_ [d];
159 Set the items that this spanner spans. If D == LEFT, we also set the
160 X-axis parent of THIS to S.
163 Spanner::set_bound (Direction d, Grob*s)
165 Item * i = dynamic_cast<Item*> (s);
168 programming_error ("Must have Item for spanner bound.");
175 We check for System to prevent the column -> line_of_score
176 -> column -> line_of_score -> etc situation */
177 if (d== LEFT && !dynamic_cast<System*> (this))
179 set_parent (i, X_AXIS);
183 Signal that this column needs to be kept alive. They need to be
184 kept alive to have meaningful position and linebreaking.
186 [maybe we should try keeping all columns alive?, and perhaps
187 inherit position from their (non-)musical brother]
190 if (dynamic_cast<Paper_column*> (i))
192 Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
196 Spanner::Spanner (SCM s)
199 spanned_drul_[LEFT]=0;
200 spanned_drul_[RIGHT]=0;
201 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
206 Spanner::Spanner (Spanner const &s)
209 spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
213 Spanner::spanner_length () const
215 Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
216 Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
219 programming_error ("spanner with negative length");
225 Spanner::line_l () const
227 if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
229 if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
231 return spanned_drul_[LEFT]->line_l ();
236 Spanner::find_broken_piece (System*l) const
238 int idx = binsearch_link_array (broken_into_l_arr_, (Spanner*)l, Spanner::compare);
243 return broken_into_l_arr_ [idx];
248 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
250 return p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
254 Spanner::broken_b () const
256 return broken_into_l_arr_.size ();
261 If this is a broken spanner, return the amount the left end is to be
262 shifted horizontally so that the spanner starts after the initial
263 clef and key on the staves. This is necessary for ties, slurs,
264 crescendo and decrescendo signs, for example.
267 Spanner::get_broken_left_end_align () const
269 Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l ());
271 // Relevant only if left span point is first column in line
273 sc->break_status_dir () == RIGHT)
277 We used to do a full search for the Break_align_item.
278 But that doesn't make a difference, since the Paper_column
279 is likely to contain only a Break_align_item.
281 return sc->extent (sc, X_AXIS)[RIGHT];
288 Spanner::do_derived_mark ()
291 We'd be fucked if this is called before spanned_drul_[] is inited. */
292 if (status_c_ == ORPHAN)
297 if (spanned_drul_[d])
298 scm_gc_mark (spanned_drul_[d]->self_scm ());
299 while (flip (&d) != LEFT);
301 for (int i= broken_into_l_arr_.size () ; i--;)
302 scm_gc_mark (broken_into_l_arr_[i]->self_scm ());
308 add_bound_item (Spanner* sp, Item*it)
310 if (!sp->get_bound (LEFT))
311 sp->set_bound (LEFT, it);
313 sp->set_bound (RIGHT, it);
317 Extends EXTREMAL_PAIR to include IT
320 extend_spanner_over_item (Item *it, SCM extremal_pair)
324 Item * col = it->column_l ();
325 Item * i1 = dynamic_cast<Item*> (unsmob_grob (ly_car (extremal_pair)));
326 Item * i2 = dynamic_cast<Item*> (unsmob_grob (ly_cdr (extremal_pair)));
327 int r = Paper_column::rank_i (col);
328 if (!i1 || r < Paper_column::rank_i (i1->column_l ()))
330 gh_set_car_x (extremal_pair, it->self_scm ());
332 if (!i2 || r > Paper_column::rank_i (i2->column_l ()))
334 gh_set_cdr_x (extremal_pair, it->self_scm ());
339 Extends EXTREMAL_PAIR to include every grob in VALUE
342 extend_spanner_over_elements (SCM value, SCM extremal_pair)
344 if (gh_pair_p (value))
346 extend_spanner_over_elements (ly_car (value), extremal_pair);
347 extend_spanner_over_elements (ly_cdr (value), extremal_pair);
349 else if (unsmob_grob (value))
351 if (Spanner * sp = dynamic_cast<Spanner*> (unsmob_grob (value)))
353 extend_spanner_over_item (sp->get_bound (LEFT), extremal_pair);
354 extend_spanner_over_item (sp->get_bound (RIGHT), extremal_pair);
356 else if (Item * it= dynamic_cast<Item*> (unsmob_grob (value)))
357 extend_spanner_over_item (it, extremal_pair);
363 Make sure that the left and right bounds encompasses all objects it
366 TODO: maybe be more specific. Most probably fucks up if someone sets
367 a pointer to the staff symbol in S
370 extend_spanner_over_elements (Grob*s)
372 Spanner*sp = dynamic_cast<Spanner*> (s);
374 SCM s1 = sp->get_bound (LEFT) ? sp->get_bound (LEFT)->self_scm () : SCM_EOL;
375 SCM s2 = sp->get_bound (RIGHT) ? sp->get_bound (RIGHT)->self_scm () : SCM_EOL;
377 SCM pair = gh_cons (s1,s2);
378 extend_spanner_over_elements (sp->mutable_property_alist_, pair);
380 Grob *p1 = unsmob_grob (ly_car (pair));
381 Grob* p2 = unsmob_grob (ly_cdr (pair));
382 sp->set_bound (LEFT,p1);
383 sp->set_bound (RIGHT, p2);
387 MAKE_SCHEME_CALLBACK (Spanner,set_spacing_rods,1);
389 Spanner::set_spacing_rods (SCM smob)
391 Grob*me = unsmob_grob (smob);
394 Spanner*sp = dynamic_cast<Spanner*> (me);
395 r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
396 r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
398 gh_scm2double (me->get_grob_property ("minimum-length"))
402 return SCM_UNSPECIFIED;
407 unsmob_spanner (SCM s )
409 return dynamic_cast<Spanner*> (unsmob_grob (s));
412 ADD_INTERFACE(Spanner,