]> git.donarmstrong.com Git - lilypond.git/blob - lily/spanner.cc
7272c2d91e76f3c2ae03250394f9ef6ebcfae55c
[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--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
9 #include <libc-extension.hh>
10
11
12 #include "debug.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 "line-of-score.hh"
20 #include "break-align-item.hh"
21 #include "group-interface.hh"
22
23 void
24 Spanner::do_break_processing ()
25 {
26   //break_into_pieces
27   Item * left = spanned_drul_[LEFT];
28   Item * right = spanned_drul_[RIGHT];
29
30   if (!left || !right)
31     return;
32   
33   /*
34     Check if our parent in X-direction spans equally wide
35     or wider than we do.
36    */
37   for (int a = X_AXIS; a < NO_AXES; a ++)
38     {
39       if (Spanner* parent = dynamic_cast<Spanner*> (parent_l ((Axis)a)))
40         {
41           if (!parent->spanned_rank_iv ().contains_b (this->spanned_rank_iv ()))
42             {
43               programming_error (to_str ("Spanner `%s' is not fully contained in parent spanner `%s'.",
44                                          name().ch_C(),
45                                          parent->name ().ch_C ()));
46             }
47         }
48     }
49   
50   if (line_l () || broken_b ())
51     return;
52
53   if  (left == right)
54     {
55       /*
56         If we have a spanner spanning one column, we must break it
57         anyway because it might provide a parent for another item.  */
58       Direction d = LEFT;
59       do
60         {
61           Item* bound = left->find_prebroken_piece (d);
62           if (!bound)
63             programming_error ("no broken bound");
64           else if (bound->line_l ())
65             {
66               Spanner * span_p = dynamic_cast<Spanner*>( clone ());
67               span_p->set_bound (LEFT, bound);
68               span_p->set_bound (RIGHT, bound);
69
70               assert (span_p->line_l ()); 
71               span_p->line_l ()->typeset_element (span_p);
72               broken_into_l_arr_.push (span_p);
73             }
74         }
75       while ((flip(&d))!= LEFT);
76     }
77   else
78     {
79       Link_array<Item> break_points = pscore_l_->line_l_->broken_col_range (left,right);
80
81       break_points.insert (left,0);
82       break_points.push (right);
83
84       for (int i=1; i < break_points.size(); i++) 
85         {
86           Drul_array<Item*> bounds;
87           bounds[LEFT] = break_points[i-1];
88           bounds[RIGHT] = break_points[i];
89           Direction d = LEFT;
90           do
91             {
92               if (!bounds[d]->line_l())
93                 bounds[d] = bounds[d]->find_prebroken_piece(- d);
94             }
95           while ((flip(&d))!= LEFT);
96
97           if (!bounds[LEFT] ||  ! bounds[RIGHT])
98             {
99               programming_error ("bounds of this piece aren't breakable. ");
100               continue; 
101             }
102
103           Spanner *span_p = dynamic_cast<Spanner*>(clone ());
104           span_p->set_bound(LEFT,bounds[LEFT]);
105           span_p->set_bound(RIGHT,bounds[RIGHT]);
106
107           if (bounds[LEFT]->line_l ()
108               && bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ())
109             {
110               programming_error ("bounds[LEFT]->line_l () != bounds[RIGHT]->line_l ()");
111 #if 0
112               /*
113                 lily crashes upon displaying this ...
114                 
115                */
116               
117               gh_display (ly_str02scm ("\nspanner:mutable_property_alist_\n"));
118               gh_display (mutable_property_alist_);
119               gh_display (ly_str02scm ("\nspanner:immutable_property_alist_\n"));
120               gh_display (immutable_property_alist_);
121               gh_newline ();
122 #endif
123               span_p->suicide ();
124             }
125           else if (!bounds[LEFT]->line_l ())
126             {
127               // bounds[LEFT]->line_l ()->typeset_element (span_p);
128               // broken_into_l_arr_.push (span_p);
129               programming_error ("bounds[LEFT]->line_l () == 0");
130               span_p->suicide ();
131             }
132           else if (!bounds[RIGHT]->line_l ())
133             {
134               // bounds[RIGHT]->line_l ()->typeset_element (span_p);
135               // broken_into_l_arr_.push (span_p);
136               programming_error ("bounds[RIGHT]->line_l () == 0");
137               span_p->suicide ();
138             }
139           else
140             {
141               bounds[LEFT]->line_l ()->typeset_element (span_p);
142               broken_into_l_arr_.push (span_p);
143             }
144         }
145     }
146   broken_into_l_arr_.sort (Spanner::compare);
147 }
148
149 void
150 Spanner::set_my_columns()
151 {
152   Direction i = (Direction) LEFT;
153   do 
154     {
155       if (!spanned_drul_[i]->line_l())
156         set_bound(i,spanned_drul_[i]->find_prebroken_piece((Direction) -i));
157     } 
158   while (flip(&i) != LEFT);
159 }       
160
161 Interval_t<int>
162 Spanner::spanned_rank_iv ()
163 {
164   Interval_t<int> iv (0, 0);
165
166   if (spanned_drul_[LEFT])
167     {
168       iv[LEFT] = Paper_column::rank_i (spanned_drul_[LEFT]->column_l ());
169     }
170   if (spanned_drul_[RIGHT])
171     {
172       iv[RIGHT] = Paper_column::rank_i (spanned_drul_[RIGHT]->column_l ());
173     }
174   return iv;
175 }
176
177 Item*
178 Spanner::get_bound (Direction d) const
179 {
180   return spanned_drul_ [d];
181 }
182
183 void
184 Spanner::set_bound(Direction d, Score_element*s)
185 {
186   Item * i = dynamic_cast<Item*> (s);
187   if (!i)
188     {
189       programming_error ("Must have Item for spanner bound.");
190       return;
191     }
192   
193   spanned_drul_[d] =i;
194
195   /**
196      We check for Line_of_score to prevent the column -> line_of_score
197      -> column -> line_of_score -> etc situation */
198   if (d== LEFT && !dynamic_cast<Line_of_score*> (this))
199     {
200       set_parent (i, X_AXIS);
201     }
202
203   /*
204     Signal that this column needs to be kept alive. They need to be
205     kept alive to have meaningful position and linebreaking.
206
207     [maybe we should try keeping all columns alive?, and perhaps
208     inherit position from their (non-)musical brother]
209     
210   */
211   if (dynamic_cast<Paper_column*> (i))
212     {
213       Pointer_group_interface (i, "bounded-by-me").add_element (this);  
214     }
215 }
216
217
218 Spanner::Spanner (SCM s)
219   : Score_element (s)
220 {
221   spanned_drul_[LEFT]=0;
222   spanned_drul_[RIGHT]=0;
223 }
224
225 Spanner::Spanner (Spanner const &s)
226   : Score_element (s)
227 {
228   spanned_drul_[LEFT] = spanned_drul_[RIGHT] =0;
229 }
230
231
232 Real
233 Spanner::spanner_length() const
234 {  
235   Real l = spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS);
236   Real r = spanned_drul_[RIGHT]->relative_coordinate (0, X_AXIS);
237
238   if (r< l)
239     programming_error ("spanner with negative length");
240
241   return r-l;
242 }
243
244 Line_of_score *
245 Spanner::line_l() const
246 {
247   if (!spanned_drul_[LEFT] || !spanned_drul_[RIGHT])
248     return 0;
249   if (spanned_drul_[LEFT]->line_l () != spanned_drul_[RIGHT]->line_l ())
250     return 0;
251   return spanned_drul_[LEFT]->line_l();
252 }
253
254
255 Score_element*
256 Spanner::find_broken_piece (Line_of_score*l) const
257 {
258   int idx = binsearch_link_array (broken_into_l_arr_,  (Spanner*)l, Spanner::compare);
259   
260   if (idx < 0)
261     return 0;
262   else
263     return broken_into_l_arr_ [idx];
264 }
265
266
267 int
268 Spanner::compare (Spanner * const &p1, Spanner * const &p2)
269 {
270   return  p1->line_l ()->rank_i_ - p2->line_l ()->rank_i_;
271 }
272
273 bool
274 Spanner::broken_b() const
275 {
276   return broken_into_l_arr_.size();
277 }
278
279
280 /*
281   If this is a broken spanner, return the amount the left end is to be
282   shifted horizontally so that the spanner starts after the initial
283   clef and key on the staves. This is necessary for ties, slurs,
284   crescendo and decrescendo signs, for example.
285 */
286 Real
287 Spanner::get_broken_left_end_align () const
288 {
289   Paper_column *sc = dynamic_cast<Paper_column*> (spanned_drul_[LEFT]->column_l());
290
291   // Relevant only if left span point is first column in line
292   if(sc != NULL &&
293      sc->break_status_dir () == RIGHT)
294     {
295       /*
296         
297         We used to do a full search for the Break_align_item.
298         But that doesn't make a difference, since the Paper_column
299         is likely to contain only a Break_align_item.
300       */
301       return sc->extent (X_AXIS)[RIGHT];
302     }
303
304   return 0.0;
305 }
306
307 SCM
308 Spanner::do_derived_mark ()
309 {
310   /*
311     We'd be fucked if this is called before spanned_drul_[] is inited.  */
312   if (status_i_ == ORPHAN)
313     return SCM_EOL;
314   
315   Direction d = LEFT;
316   do
317     if (spanned_drul_[d])
318       scm_gc_mark (spanned_drul_[d]->self_scm ());
319   while (flip (&d) != LEFT);
320
321   for (int i= broken_into_l_arr_.size () ; i--;)
322     scm_gc_mark (broken_into_l_arr_[i]->self_scm ());
323
324   return SCM_EOL;
325 }
326
327 void
328 add_bound_item (Spanner* sp, Item*it)
329 {
330   if (!sp->get_bound (LEFT))
331     sp->set_bound (LEFT, it);
332   else
333     sp->set_bound (RIGHT, it);
334 }
335
336 static void
337 extend_spanner_over_item (Item *it, SCM extremal_pair)
338 {
339   if (!it)
340     return;
341   Item * col = it->column_l ();
342   Item * i1 = dynamic_cast<Item*> (unsmob_element (gh_car (extremal_pair)));
343   Item * i2 = dynamic_cast<Item*> (unsmob_element (gh_cdr (extremal_pair)));
344   int r = Paper_column::rank_i (col);
345   if (!i1 || r < Paper_column::rank_i (i1->column_l ()))
346     {
347       gh_set_car_x (extremal_pair, it->self_scm ());
348     }
349   if (!i2 || r > Paper_column::rank_i (i2->column_l ()))
350     {
351       gh_set_cdr_x (extremal_pair, it->self_scm ());
352     }
353 }
354
355 static void
356 extend_spanner_over_elements (SCM value, SCM extremal_pair)
357 {
358   if (gh_pair_p (value))
359     {
360       extend_spanner_over_elements (gh_car (value), extremal_pair);
361       extend_spanner_over_elements (gh_cdr (value), extremal_pair);
362     }
363   else if (unsmob_element (value))
364     {
365       if (Spanner * sp = dynamic_cast<Spanner*> (unsmob_element(value)))
366         {
367           extend_spanner_over_item (sp->get_bound (LEFT), extremal_pair);
368           extend_spanner_over_item (sp->get_bound (RIGHT), extremal_pair);
369         }
370       else if (Item * it= dynamic_cast<Item*> (unsmob_element(value)))
371         extend_spanner_over_item (it, extremal_pair);
372     }
373 }
374
375
376 /*
377   Make sure that the left and right bounds encompasses all objects it
378   points to.
379
380   TODO: maybe be more specific. Most probably fucks up if someone sets
381   a pointer to the staffsymbol in S
382 */
383 void
384 extend_spanner_over_elements (Score_element*s)
385 {
386   Spanner*sp = dynamic_cast<Spanner*> (s);
387
388   SCM s1 = sp->get_bound (LEFT) ? sp->get_bound (LEFT)->self_scm () : SCM_EOL;
389   SCM s2 = sp->get_bound (RIGHT) ? sp->get_bound (RIGHT)->self_scm () : SCM_EOL;  
390   
391   SCM pair = gh_cons (s1,s2);
392   extend_spanner_over_elements (sp->mutable_property_alist_, pair);
393
394   Score_element *p1 =  unsmob_element (gh_car (pair));
395   Score_element* p2 = unsmob_element (gh_cdr (pair));
396   sp->set_bound (LEFT,p1);
397   sp->set_bound (RIGHT, p2);
398
399   //extra precaution.
400 }
401