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