]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
(class Grob): move pscore, dim_cache_,
[lilypond.git] / lily / spanner.cc
1 /*
2   spanner.cc -- implement Spanner
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "spanner.hh"
10
11
12 #include "pointer-group-interface.hh"
13 #include "libc-extension.hh"
14 #include "paper-column.hh"
15 #include "paper-column.hh"
16 #include "paper-score.hh"
17 #include "stencil.hh"
18 #include "system.hh"
19 #include "warn.hh"
20
21 Grob *
22 Spanner::clone (int count) const
23 {
24   return new Spanner (*this, count);
25 }
26
27 void
28 Spanner::do_break_processing ()
29 {
30   //break_into_pieces
31   Item *left = spanned_drul_[LEFT];
32   Item *right = spanned_drul_[RIGHT];
33
34   if (!left || !right)
35     return;
36
37   /*
38     Check if our parent in X-direction spans equally wide
39     or wider than we do.
40   */
41   for (int a = X_AXIS; a < NO_AXES; a++)
42     {
43       if (Spanner *parent = dynamic_cast<Spanner *> (get_parent ((Axis)a)))
44         {
45           if (!parent->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
46             {
47               programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
48                                             name ().to_str0 (),
49                                             parent->name ().to_str0 ()));
50             }
51         }
52     }
53
54   if (get_system () || is_broken ())
55     return;
56
57   if (left == right)
58     {
59       /*
60         If we have a spanner spanning one column, we must break it
61         anyway because it might provide a parent for another item.  */
62       Direction d = LEFT;
63       do
64         {
65           Item *bound = left->find_prebroken_piece (d);
66           if (!bound)
67             programming_error ("no broken bound");
68           else if (bound->get_system ())
69             {
70               Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
71               span->set_bound (LEFT, bound);
72               span->set_bound (RIGHT, bound);
73
74               assert (span->get_system ());
75               span->get_system ()->typeset_grob (span);
76               broken_intos_.push (span);
77             }
78         }
79       while ((flip (&d)) != LEFT);
80     }
81   else
82     {
83       Link_array<Item> break_points = pscore_->root_system ()->broken_col_range (left, right);
84
85       break_points.insert (left, 0);
86       break_points.push (right);
87
88       for (int i = 1; i < break_points.size (); i++)
89         {
90           Drul_array<Item *> bounds;
91           bounds[LEFT] = break_points[i - 1];
92           bounds[RIGHT] = break_points[i];
93           Direction d = LEFT;
94           do
95             {
96               if (!bounds[d]->get_system ())
97                 bounds[d] = bounds[d]->find_prebroken_piece (- d);
98             }
99           while ((flip (&d)) != LEFT);
100
101           if (!bounds[LEFT] || ! bounds[RIGHT])
102             {
103               programming_error ("bounds of this piece aren't breakable. ");
104               continue;
105             }
106
107           Spanner *span = dynamic_cast<Spanner *> (clone (broken_intos_.size ()));
108           span->set_bound (LEFT, bounds[LEFT]);
109           span->set_bound (RIGHT, bounds[RIGHT]);
110
111           if (!bounds[LEFT]->get_system ()
112
113               || !bounds[RIGHT]->get_system ()
114               || bounds[LEFT]->get_system () != bounds[RIGHT]->get_system ())
115             {
116               programming_error ("bounds of spanner are invalid");
117               span->suicide ();
118             }
119           else
120             {
121               bounds[LEFT]->get_system ()->typeset_grob (span);
122               broken_intos_.push (span);
123             }
124         }
125     }
126   broken_intos_.sort (Spanner::compare);
127   for (int i = broken_intos_.size ();i--;)
128     broken_intos_[i]->break_index_ = i;
129 }
130
131 int
132 Spanner::get_break_index () const
133 {
134   return break_index_;
135 }
136
137 void
138 Spanner::set_my_columns ()
139 {
140   Direction i = (Direction) LEFT;
141   do
142     {
143       if (!spanned_drul_[i]->get_system ())
144         set_bound (i, spanned_drul_[i]->find_prebroken_piece ((Direction) -i));
145     }
146   while (flip (&i) != LEFT);
147 }
148
149 Interval_t<int>
150 Spanner::spanned_rank_iv ()
151 {
152   Interval_t<int> iv (0, 0);
153
154   if (spanned_drul_[LEFT] && spanned_drul_[LEFT]->get_column ())
155     iv[LEFT] = spanned_drul_[LEFT]->get_column ()->get_rank ();
156   if (spanned_drul_[RIGHT] && spanned_drul_[RIGHT]->get_column ())
157     iv[RIGHT] = spanned_drul_[RIGHT]->get_column ()->get_rank ();
158   return iv;
159 }
160
161 Item *
162 Spanner::get_bound (Direction d) const
163 {
164   return spanned_drul_ [d];
165 }
166
167 /*
168   Set the items that this spanner spans. If D == LEFT, we also set the
169   X-axis parent of THIS to S.
170 */
171 void
172 Spanner::set_bound (Direction d, Grob *s)
173 {
174   Item *i = dynamic_cast<Item *> (s);
175   if (!i)
176     {
177       programming_error ("must have Item for spanner bound");
178       return;
179     }
180
181   spanned_drul_[d] = i;
182
183   /**
184      We check for System to prevent the column -> line_of_score
185      -> column -> line_of_score -> etc situation */
186   if (d == LEFT && !dynamic_cast<System *> (this))
187     set_parent (i, X_AXIS);
188
189   /*
190     Signal that this column needs to be kept alive. They need to be
191     kept alive to have meaningful position and linebreaking.
192
193     [maybe we should try keeping all columns alive?, and perhaps
194     inherit position from their (non-)musical brother]
195
196   */
197   if (dynamic_cast<Paper_column *> (i))
198     Pointer_group_interface::add_grob (i, ly_symbol2scm ("bounded-by-me"), this);
199 }
200
201 Spanner::Spanner (SCM s, Object_key const *key)
202   : Grob (s, key)
203 {
204   break_index_ = 0;
205   spanned_drul_[LEFT] = 0;
206   spanned_drul_[RIGHT] = 0;
207 }
208
209 Spanner::Spanner (Spanner const &s, int count)
210   : Grob (s, count)
211 {
212   spanned_drul_[LEFT] = spanned_drul_[RIGHT] = 0;
213 }
214
215 Real
216 Spanner::spanner_length () const
217 {
218   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
219   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
220
221   if (r < l)
222     programming_error ("spanner with negative length");
223
224   return r - l;
225 }
226
227 System *
228 Spanner::get_system () const
229 {
230   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
231     return 0;
232   if (spanned_drul_[LEFT]->get_system () != spanned_drul_[RIGHT]->get_system ())
233     return 0;
234   return spanned_drul_[LEFT]->get_system ();
235 }
236
237 Grob *
238 Spanner::find_broken_piece (System *l) const
239 {
240   int idx = binsearch_links (broken_intos_, (Spanner *)l, Spanner::compare);
241
242   if (idx < 0)
243     return 0;
244   else
245     return broken_intos_ [idx];
246 }
247
248 int
249 Spanner::compare (Spanner *const &p1, Spanner *const &p2)
250 {
251   return p1->get_system ()->get_rank () - p2->get_system ()->get_rank ();
252 }
253
254 bool
255 Spanner::is_broken () const
256 {
257   return broken_intos_.size ();
258 }
259
260 /*
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.
265 */
266 Real
267 Spanner::get_broken_left_end_align () const
268 {
269   Paper_column *sc = dynamic_cast<Paper_column *> (spanned_drul_[LEFT]->get_column ());
270
271   // Relevant only if left span point is first column in line
272   if (sc != NULL
273       && sc->break_status_dir () == RIGHT)
274     {
275       /*
276         We used to do a full search for the Break_align_item.
277         But that doesn't make a difference, since the Paper_column
278         is likely to contain only a Break_align_item.
279       */
280       return sc->extent (sc, X_AXIS)[RIGHT];
281     }
282
283   return 0.0;
284 }
285
286 void
287 Spanner::derived_mark () const
288 {
289   Direction d = LEFT;
290   do
291     if (spanned_drul_[d])
292       scm_gc_mark (spanned_drul_[d]->self_scm ());
293   while (flip (&d) != LEFT)
294     ;
295
296   for (int i = broken_intos_.size (); i--;)
297     scm_gc_mark (broken_intos_[i]->self_scm ());
298 }
299
300 /*
301   Set left or right bound to IT.
302
303   Warning: caller should ensure that subsequent calls put in ITems
304   that are left-to-right ordered.
305 */
306 void
307 add_bound_item (Spanner *sp, Grob *it)
308 {
309   if (!sp->get_bound (LEFT))
310     sp->set_bound (LEFT, it);
311   else
312     sp->set_bound (RIGHT, it);
313 }
314
315 MAKE_SCHEME_CALLBACK (Spanner, set_spacing_rods, 1);
316 SCM
317 Spanner::set_spacing_rods (SCM smob)
318 {
319   Grob *me = unsmob_grob (smob);
320
321   Rod r;
322   Spanner *sp = dynamic_cast<Spanner *> (me);
323   r.item_drul_[LEFT] = sp->get_bound (LEFT);
324   r.item_drul_[RIGHT] = sp->get_bound (RIGHT);
325   r.distance_
326     = robust_scm2double (me->get_property ("minimum-length"), 0);
327
328   r.add_to_cols ();
329   return SCM_UNSPECIFIED;
330 }
331
332 /*
333   Return I such that SP == SP->ORIGINAL ()->BROKEN_INTOS_[I].
334 */
335 int
336 broken_spanner_index (Spanner *sp)
337 {
338   Spanner *parent = dynamic_cast<Spanner *> (sp->original ());
339   return parent->broken_intos_.find_index (sp);
340 }
341
342 Spanner *
343 unsmob_spanner (SCM s)
344 {
345   return dynamic_cast<Spanner *> (unsmob_grob (s));
346 }
347
348 ADD_INTERFACE (Spanner,
349                "spanner-interface",
350                "Some objects are horizontally spanned between objects. For\n"
351                "example, slur, beam, tie, etc. These grobs form a subtype called\n"
352                "@code{Spanner}. All spanners have two span-points (these must be\n"
353                "@code{Item} objects), one on the left and one on the right. The left bound is\n"
354                "also the X-reference point of the spanner.\n",
355                "minimum-length");