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