]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-of-score.cc
release: 1.5.38
[lilypond.git] / lily / line-of-score.cc
1 /*
2   scoreline.cc -- implement Line_of_score
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "input-smob.hh"
10 #include "axis-group-interface.hh"
11 #include "debug.hh"
12 #include "line-of-score.hh"
13 #include "main.hh"
14 #include "paper-column.hh"
15 #include "paper-def.hh"
16 #include "paper-outputter.hh"
17 #include "paper-score.hh"
18 #include "string.hh"
19 #include "warn.hh"
20 #include "dimensions.hh"
21 #include "molecule.hh"
22 #include "all-font-metrics.hh"
23
24 // todo: use map.
25 void
26 fixup_refpoints (SCM s)
27 {
28   for (; gh_pair_p (s); s = ly_cdr (s))
29     {
30       Grob::fixup_refpoint (ly_car (s));
31     }
32 }
33
34
35 Line_of_score::Line_of_score (SCM s)
36   : Spanner (s)
37 {
38   rank_i_ = 0;
39
40   Axis_group_interface::set_interface (this);
41   Axis_group_interface::set_axes (this, Y_AXIS,X_AXIS);
42 }
43
44 int
45 Line_of_score::element_count () const
46 {
47   return scm_ilength (get_grob_property ("all-elements"));
48 }
49
50 void
51 Line_of_score::typeset_grob (Grob * elem_p)
52 {
53   elem_p->pscore_l_ = pscore_l_;
54   Pointer_group_interface::add_grob (this, ly_symbol2scm ("all-elements"),elem_p);
55   scm_gc_unprotect_object (elem_p->self_scm ());
56 }
57
58 void
59 Line_of_score::output_lines ()
60 {
61   for (SCM s = get_grob_property ("all-elements");
62        gh_pair_p (s); s = ly_cdr (s))
63     {
64       unsmob_grob (ly_car (s))->do_break_processing ();
65     }
66
67   /*
68     fixups must be done in broken line_of_scores, because new elements
69     are put over there.  */
70   int count = 0;
71   for (int i=0; i < broken_into_l_arr_.size (); i++)
72     {
73       Grob *se = broken_into_l_arr_[i];
74       SCM all = se->get_grob_property ("all-elements");
75       for (SCM s = all; gh_pair_p (s); s = ly_cdr (s))
76         {
77           fixup_refpoint (ly_car (s));
78         }
79       count += scm_ilength (all);
80     }
81   
82   /*
83     needed for doing items.
84    */
85   fixup_refpoints (get_grob_property ("all-elements"));
86
87   
88   for (SCM s = get_grob_property ("all-elements");
89        gh_pair_p (s); s = ly_cdr (s))
90     {
91       unsmob_grob (ly_car (s))->handle_broken_dependencies ();
92     }
93   handle_broken_dependencies ();
94
95   if (verbose_global_b)
96     progress_indication (_f ("Element count %d.",  count + element_count ()));
97
98   
99   for (int i=0; i < broken_into_l_arr_.size (); i++)
100     {
101       Line_of_score *line_l = dynamic_cast<Line_of_score*> (broken_into_l_arr_[i]);
102
103       if (verbose_global_b)
104         progress_indication ("[");
105       line_l->post_processing (i+1 == broken_into_l_arr_.size ());
106
107       if (verbose_global_b)
108         {
109           progress_indication (to_str (i));
110           progress_indication ("]");
111         }
112
113       if (i < broken_into_l_arr_.size () - 1)
114         {
115           SCM lastcol =  ly_car (line_l->get_grob_property ("columns"));
116           Grob*  e = unsmob_grob (lastcol);
117
118           SCM between = ly_symbol2scm ("between-system-string");
119           SCM inter = e->internal_get_grob_property (between);
120           if (gh_string_p (inter))
121             {
122               pscore_l_->outputter_l_
123                 ->output_scheme (scm_list_n (between, 
124                                              inter, SCM_UNDEFINED));          
125             }
126         }
127     }
128 }
129
130
131
132
133 /*
134   Find the loose columns in POSNS, and drape them around the columns
135   specified in BETWEEN-COLS.  */
136 void
137 set_loose_columns (Line_of_score* which, Column_x_positions const *posns)
138 {
139   for (int i = 0; i < posns->loose_cols_.size (); i++)
140     {
141       int divide_over = 1;
142       Item *loose = dynamic_cast<Item*> (posns->loose_cols_[i]);
143       Paper_column* col = dynamic_cast<Paper_column*> (loose);
144       
145       if (col->line_l_)
146         continue;
147
148       
149       Item * left = 0;
150       Item * right = 0;
151       do
152         {
153           SCM between = loose->get_grob_property ("between-cols");
154           if (!gh_pair_p (between))
155             break;
156
157
158           Item * l=dynamic_cast<Item*> (unsmob_grob (ly_car (between)));
159           Item * r=dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
160
161           if (!(l && r))
162             break ;
163           
164           if (!left && l)
165             {
166               left = l->column_l ();
167             }
168
169           divide_over ++;
170
171           loose = right = r->column_l ();
172         }
173       while (1);
174       
175     
176 #if 0
177       Real rx = right->relative_coordinate (right->get_parent (X_AXIS), X_AXIS);
178       Real lx = left->relative_coordinate (left->get_parent (X_AXIS), X_AXIS);
179
180       /*
181         divide space equally over loose columns.
182        */
183       int j = 1;
184       loose = col;
185       while (1)
186         {
187           SCM between = loose->get_grob_property ("between-cols");
188           if (!gh_pair_p (between))
189             break;
190
191           Paper_column *thiscol = dynamic_cast<Paper_column*> (loose);
192
193           thiscol->line_l_ = which;
194           thiscol->translate_axis (lx + j*(rx - lx)/divide_over, X_AXIS);
195
196           j ++; 
197           loose = dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
198         }
199 #else
200       /*
201         We divide the remaining space of the column over the left and
202         right side. At the moment, we  
203         
204       */
205       Grob * common = right->common_refpoint (left, X_AXIS);
206       
207       Real rx = right->extent(common, X_AXIS)[LEFT];
208       Real lx =  left->extent(common, X_AXIS)[RIGHT];
209       Real total_dx = rx - lx;
210       Interval cval =col->extent (col, X_AXIS);
211
212       /*
213         
214         We put it in the middle. This is not an ideal solution -- the
215         break alignment code inserts a fixed space before the clef
216         (about 1 SS), while the space following the clef is
217         flexible. In tight situations, the clef will almost be on top
218         of the following note. 
219         
220       */
221       Real dx = rx-lx - cval.length ();
222       if (total_dx < 2* cval.length ())
223         {
224           /*
225             todo: this is discontinuous. I'm too tired to
226             invent a sliding mechanism. Duh.
227
228             TODO.
229            */
230           dx *= 0.25;
231         }
232       else
233         dx *= 0.5;
234
235       col->line_l_ = which;
236       col->translate_axis (lx + dx - cval[LEFT], X_AXIS); 
237 #endif
238     }
239 }
240
241 // const?
242 void
243 Line_of_score::break_into_pieces (Array<Column_x_positions> const &breaking)
244 {
245   for (int i=0; i < breaking.size (); i++)
246     {
247       Line_of_score *line_l = dynamic_cast <Line_of_score*> (clone ());
248       line_l->rank_i_ = i;
249       //      line_l->set_immutable_grob_property ("rank", gh_int2scm (i));
250       Link_array<Grob> c (breaking[i].cols_);
251       pscore_l_->typeset_line (line_l);
252       
253       line_l->set_bound (LEFT,c[0]);
254       line_l->set_bound (RIGHT,c.top ());
255       for (int j=0; j < c.size (); j++)
256         {
257           c[j]->translate_axis (breaking[i].config_[j],X_AXIS);
258           dynamic_cast<Paper_column*> (c[j])->line_l_ = line_l;
259         }
260       set_loose_columns (line_l, &breaking[i]);
261       broken_into_l_arr_.push (line_l);
262     }
263 }
264
265
266 #define GLOBAL_SYMBOL(cname, name)  \
267 SCM cname ;                                     \
268 void \
269 cname ## _init_func ()                          \
270 {                                               \
271   cname = ly_symbol2scm (name);                 \
272   scm_permanent_object (cname);                 \
273 }                                               \
274 ADD_SCM_INIT_FUNC (cname,cname ## _init_func);\
275
276
277 GLOBAL_SYMBOL (offset_sym , "translate-molecule");
278 GLOBAL_SYMBOL (placebox_sym , "placebox");
279 GLOBAL_SYMBOL (combine_sym , "combine-molecule");
280
281
282
283 void
284 Line_of_score::output_molecule (SCM expr, Offset o)
285 {
286
287   while (1)
288     {
289       if (!gh_pair_p (expr))
290         return;
291   
292       SCM head =ly_car (expr);
293       if (unsmob_input (head))
294         {
295           Input * ip = unsmob_input (head);
296       
297
298           pscore_l_->outputter_l_->output_scheme (scm_list_n (ly_symbol2scm ("define-origin"),
299                                                            ly_str02scm (ip->file_str ().ch_C ()),
300                                                            gh_int2scm (ip->line_number ()),
301                                                            gh_int2scm (ip->column_number ()),
302                                                            SCM_UNDEFINED));
303           expr = ly_cadr (expr);
304         }
305       else  if (head ==  ly_symbol2scm ("no-origin"))
306         {
307           pscore_l_->outputter_l_->output_scheme (scm_list_n (head, SCM_UNDEFINED));
308           expr = ly_cadr (expr);
309         }
310       else if (head == ly_symbol2scm ("translate-molecule"))
311         {
312           o += ly_scm2offset (ly_cadr (expr));
313           expr = ly_caddr (expr);
314         }
315       else if (head == ly_symbol2scm ("combine-molecule"))
316         {
317           output_molecule (ly_cadr (expr), o);
318           expr = ly_caddr (expr);
319         }
320       else
321         {
322           pscore_l_->outputter_l_->
323             output_scheme (scm_list_n (placebox_sym,
324                                     gh_double2scm (o[X_AXIS]),
325                                     gh_double2scm (o[Y_AXIS]),
326                                     expr,
327                                     SCM_UNDEFINED));
328
329           return;
330         }
331     }
332 }
333
334 void
335 Line_of_score::output_scheme (SCM s)
336 {
337   pscore_l_->outputter_l_->output_scheme (s);
338 }
339
340 void
341 Line_of_score::add_column (Paper_column*p)
342 {
343   Grob *me = this;
344   SCM cs = me->get_grob_property ("columns");
345   Grob * prev =  gh_pair_p (cs) ? unsmob_grob (ly_car (cs)) : 0;
346
347   p->rank_i_ = prev ? Paper_column::rank_i (prev) + 1 : 0; 
348
349   me->set_grob_property ("columns",  gh_cons (p->self_scm (), cs));
350
351   Axis_group_interface::add_element (me, p);
352 }
353
354
355
356 /*
357   TODO: use scm_map iso. for loops.
358  */
359 void
360 Line_of_score::pre_processing ()
361 {
362   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
363     unsmob_grob (ly_car (s))->discretionary_processing ();
364
365   if (verbose_global_b)
366     progress_indication (_f ("Element count %d ",  element_count ()));
367
368   
369   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
370     unsmob_grob (ly_car (s))->handle_prebroken_dependencies ();
371   
372   fixup_refpoints (get_grob_property ("all-elements"));
373   
374   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
375     {
376       Grob* sc = unsmob_grob (ly_car (s));
377       sc->calculate_dependencies (PRECALCED, PRECALCING, ly_symbol2scm ("before-line-breaking-callback"));
378     }
379   
380   progress_indication ("\n" + _ ("Calculating column positions...") + " ");
381   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
382     {
383       Grob * e = unsmob_grob (ly_car (s));
384       SCM proc = e->get_grob_property ("spacing-procedure");
385       if (gh_procedure_p (proc))
386         gh_call1 (proc, e->self_scm ());
387     }
388 }
389
390 void
391 Line_of_score::post_processing (bool last_line)
392 {
393   for (SCM s = get_grob_property ("all-elements");
394        gh_pair_p (s); s = ly_cdr (s))
395     {
396       Grob* sc = unsmob_grob (ly_car (s));
397       sc->calculate_dependencies (POSTCALCED, POSTCALCING,
398                                   ly_symbol2scm ("after-line-breaking-callback"));
399     }
400
401   Interval i (extent (this, Y_AXIS));
402   if (i.empty_b ())
403     programming_error ("Huh?  Empty Line_of_score?");
404   else
405     translate_axis (- i[MAX], Y_AXIS);
406
407   Real height = i.length ();
408   if (height > 50 CM)
409     {
410       programming_error ("Improbable system height");
411       height = 50 CM;
412     }
413
414   /*
415     generate all molecules  to trigger all font loads.
416
417     (ugh. This is not very memory efficient.)  */
418   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
419     {
420       unsmob_grob (ly_car (s))->get_molecule ();
421     }
422   /*
423     font defs;
424    */
425   SCM font_names = ly_quote_scm (paper_l ()->font_descriptions ());  
426   output_scheme (scm_list_n (ly_symbol2scm ("define-fonts"),
427                              font_names,
428                              SCM_UNDEFINED));
429
430   /*
431     line preamble.
432    */
433   output_scheme (scm_list_n (ly_symbol2scm ("start-line"),
434                           gh_double2scm (height),
435                           SCM_UNDEFINED));
436   
437   /* Output elements in three layers, 0, 1, 2.
438      The default layer is 1. */
439   for (int i = 0; i < 3; i++)
440     for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s);
441          s = ly_cdr (s))
442       {
443         Grob *sc = unsmob_grob (ly_car (s));
444         Molecule *m = sc->get_molecule ();
445         if (!m)
446           continue;
447         
448         SCM s = sc->get_grob_property ("layer");
449         int layer = gh_number_p (s) ? gh_scm2int (s) : 1;
450         if (layer != i)
451           continue;
452         
453         Offset o (sc->relative_coordinate (this, X_AXIS),
454                   sc->relative_coordinate (this, Y_AXIS));
455         
456         SCM e = sc->get_grob_property ("extra-offset");
457         if (gh_pair_p (e))
458           {
459             o[X_AXIS] += gh_scm2double (ly_car (e));
460             o[Y_AXIS] += gh_scm2double (ly_cdr (e));      
461           }
462         
463         output_molecule (m->get_expr (), o);
464       }
465   
466   if (last_line)
467     {
468       output_scheme (scm_list_n (ly_symbol2scm ("stop-last-line"), SCM_UNDEFINED));
469     }
470   else
471     {
472       output_scheme (scm_list_n (ly_symbol2scm ("stop-line"), SCM_UNDEFINED));
473     }
474 }
475
476
477 Link_array<Item> 
478 Line_of_score::broken_col_range (Item const*l, Item const*r) const
479 {
480   Link_array<Item> ret;
481
482   l = l->column_l ();
483   r = r->column_l ();
484   SCM s = get_grob_property ("columns");
485
486   while (gh_pair_p (s) && ly_car (s) != r->self_scm ())
487     s = ly_cdr (s);
488     
489   if (gh_pair_p (s))
490     s = ly_cdr (s);
491   
492   while (gh_pair_p (s) && ly_car (s) != l->self_scm ())
493     {
494       Paper_column*c = dynamic_cast<Paper_column*> (unsmob_grob (ly_car (s)));
495       if (Item::breakable_b (c) && !c->line_l_)
496         ret.push (c);
497
498       s = ly_cdr (s);
499     }
500
501   ret.reverse ();
502   return ret;
503 }
504
505 /**
506    Return all columns, but filter out any unused columns , since they might
507    disrupt the spacing problem.
508  */
509 Link_array<Grob>
510 Line_of_score::column_l_arr ()const
511 {
512   Link_array<Grob> acs
513     = Pointer_group_interface__extract_grobs (this, (Grob*) 0, "columns");
514   bool bfound = false;
515   for (int i= acs.size (); i -- ;)
516     {
517       bool brb = Item::breakable_b (acs[i]);
518       bfound = bfound || brb;
519
520       /*
521         the last column should be breakable. Weed out any columns that
522         seem empty. We need to retain breakable columns, in case
523         someone forced a breakpoint.
524       */
525       if (!bfound || !Paper_column::used_b (acs[i]))
526         acs.del (i);
527     }
528   return acs;
529 }
530