]> git.donarmstrong.com Git - lilypond.git/blob - lily/line-of-score.cc
release: 1.5.21
[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--2001 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_element (this, "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     fixups must be done in broken line_of_scores, because new elements
68     are put over there.  */
69   int count = 0;
70   for (int i=0; i < broken_into_l_arr_.size (); i++)
71     {
72       Grob *se = broken_into_l_arr_[i];
73       SCM all = se->get_grob_property ("all-elements");
74       for (SCM s = all; gh_pair_p (s); s = ly_cdr (s))
75         {
76           fixup_refpoint (ly_car (s));
77         }
78       count += scm_ilength (all);
79     }
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   Find the loose columns in POSNS, and drape them around the columns
132   specified in BETWEEN-COLS.  */
133 void
134 set_loose_columns (Line_of_score* which, Column_x_positions const *posns)
135 {
136   for (int i = 0; i<posns->loose_cols_.size (); i++)
137     {
138       int divide_over = 1;
139       Item *loose = dynamic_cast<Item*> (posns->loose_cols_[i]);
140       Paper_column* col = dynamic_cast<Paper_column*> (loose);
141       
142       if (col->line_l_)
143         continue;
144
145       
146       Item * left = 0;
147       Item * right = 0;
148       while (1)
149         {
150           
151           SCM between = loose->get_grob_property ("between-cols");
152           if (!gh_pair_p (between))
153             break;
154
155           if (!left)
156             {
157               left = dynamic_cast<Item*> (unsmob_grob (ly_car (between)));
158               left = left->column_l ();
159             }
160           divide_over ++;       
161           loose = dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
162           loose = loose->column_l ();
163         }
164
165       right = loose;
166
167       Real rx = right->relative_coordinate (right->parent_l (X_AXIS), X_AXIS);
168       Real lx = left->relative_coordinate (left->parent_l (X_AXIS), X_AXIS);
169
170       int j = 1;
171       loose = col;
172       while (1)
173         {
174           SCM between = loose->get_grob_property ("between-cols");
175           if (!gh_pair_p (between))
176             break;
177
178           Paper_column *thiscol = dynamic_cast<Paper_column*> (loose);
179
180           thiscol->line_l_ = which;
181           thiscol->translate_axis (lx + j*(rx - lx)/divide_over, X_AXIS);
182
183           j ++; 
184           loose = dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
185         }
186       
187     }
188 }
189
190 // const?
191 void
192 Line_of_score::break_into_pieces (Array<Column_x_positions> const &breaking)
193 {
194   for (int i=0; i < breaking.size (); i++)
195     {
196       Line_of_score *line_l = dynamic_cast <Line_of_score*> (clone ());
197       line_l->rank_i_ = i;
198       //      line_l->set_immutable_grob_property ("rank", gh_int2scm (i));
199       Link_array<Grob> c (breaking[i].cols_);
200       pscore_l_->typeset_line (line_l);
201       
202       line_l->set_bound (LEFT,c[0]);
203       line_l->set_bound (RIGHT,c.top ());
204       for (int j=0; j < c.size (); j++)
205         {
206           c[j]->translate_axis (breaking[i].config_[j],X_AXIS);
207           dynamic_cast<Paper_column*> (c[j])->line_l_ = line_l;
208         }
209       set_loose_columns (line_l, &breaking[i]);
210       broken_into_l_arr_.push (line_l);
211     }
212 }
213
214
215 #define GLOBAL_SYMBOL(cname, name)  \
216 SCM cname ;                                     \
217 void \
218 cname ## _init_func ()                          \
219 {                                               \
220   cname = ly_symbol2scm (name);                 \
221   scm_permanent_object (cname);                 \
222 }                                               \
223 ADD_SCM_INIT_FUNC (cname,cname ## _init_func);\
224
225
226 GLOBAL_SYMBOL (offset_sym , "translate-molecule");
227 GLOBAL_SYMBOL (placebox_sym , "placebox");
228 GLOBAL_SYMBOL (combine_sym , "combine-molecule");
229 GLOBAL_SYMBOL (no_origin_sym , "no-origin");
230 GLOBAL_SYMBOL (define_origin_sym , "define-origin");
231
232
233
234 void
235 Line_of_score::output_molecule (SCM expr, Offset o)
236 {
237
238   while (1)
239     {
240       if (!gh_pair_p (expr))
241         return;
242   
243       SCM head =ly_car (expr);
244       if (unsmob_input (head))
245         {
246           Input * ip = unsmob_input (head);
247       
248
249           pscore_l_->outputter_l_->output_scheme (scm_list_n (define_origin_sym,
250                                                            ly_str02scm (ip->file_str ().ch_C ()),
251                                                            gh_int2scm (ip->line_number ()),
252                                                            gh_int2scm (ip->column_number ()),
253                                                            SCM_UNDEFINED));
254           expr = ly_cadr (expr);
255         }
256       else  if (head ==  no_origin_sym)
257         {
258           pscore_l_->outputter_l_->output_scheme (scm_list_n (no_origin_sym, SCM_UNDEFINED));
259           expr = ly_cadr (expr);
260         }
261       else if (head == offset_sym)
262         {
263           o += ly_scm2offset (ly_cadr (expr));
264           expr = ly_caddr (expr);
265         }
266       else if (head == combine_sym)
267         {
268           output_molecule (ly_cadr (expr), o);
269           expr = ly_caddr (expr);
270         }
271       else
272         {
273           pscore_l_->outputter_l_->
274             output_scheme (scm_list_n (placebox_sym,
275                                     gh_double2scm (o[X_AXIS]),
276                                     gh_double2scm (o[Y_AXIS]),
277                                     expr,
278                                     SCM_UNDEFINED));
279
280           return;
281         }
282     }
283 }
284
285 void
286 Line_of_score::output_scheme (SCM s)
287 {
288   pscore_l_->outputter_l_->output_scheme (s);
289 }
290
291 void
292 Line_of_score::add_column (Paper_column*p)
293 {
294   Grob *me = this;
295   SCM cs = me->get_grob_property ("columns");
296   Grob * prev =  gh_pair_p (cs) ? unsmob_grob (ly_car (cs)) : 0;
297
298   p->rank_i_ = prev ? Paper_column::rank_i (prev) + 1 : 0; 
299
300   me->set_grob_property ("columns",  gh_cons (p->self_scm (), cs));
301
302   Axis_group_interface::add_element (me, p);
303 }
304
305
306
307 /*
308   TODO: use scm_map iso. for loops.
309  */
310 void
311 Line_of_score::pre_processing ()
312 {
313   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
314     unsmob_grob (ly_car (s))->discretionary_processing ();
315
316   if (verbose_global_b)
317     progress_indication (_f ("Element count %d ",  element_count ()));
318
319   
320   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
321     unsmob_grob (ly_car (s))->handle_prebroken_dependencies ();
322   
323   fixup_refpoints (get_grob_property ("all-elements"));
324   
325   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
326     {
327       Grob* sc = unsmob_grob (ly_car (s));
328       sc->calculate_dependencies (PRECALCED, PRECALCING, ly_symbol2scm ("before-line-breaking-callback"));
329     }
330   
331   progress_indication ("\n" + _ ("Calculating column positions...") + " ");
332   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
333     {
334       Grob * e = unsmob_grob (ly_car (s));
335       SCM proc = e->get_grob_property ("spacing-procedure");
336       if (gh_procedure_p (proc))
337         gh_call1 (proc, e->self_scm ());
338     }
339 }
340
341 void
342 Line_of_score::post_processing (bool last_line)
343 {
344   for (SCM s = get_grob_property ("all-elements");
345        gh_pair_p (s); s = ly_cdr (s))
346     {
347       Grob* sc = unsmob_grob (ly_car (s));
348       sc->calculate_dependencies (POSTCALCED, POSTCALCING,
349                                   ly_symbol2scm ("after-line-breaking-callback"));
350     }
351
352   Interval i (extent (this, Y_AXIS));
353   if (i.empty_b ())
354     programming_error ("Huh?  Empty Line_of_score?");
355   else
356     translate_axis (- i[MAX], Y_AXIS);
357
358   Real height = i.length ();
359   if (height > 50 CM)
360     {
361       programming_error ("Improbable system height");
362       height = 50 CM;
363     }
364
365   /*
366     generate all molecules  to trigger all font loads.
367
368  (ugh. This is not very memory efficient.)  */
369   for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s); s = ly_cdr (s))
370     {
371       unsmob_grob (ly_car (s))->get_molecule ();
372     }
373   /*
374     font defs;
375    */
376   SCM font_names = ly_quote_scm (paper_l ()->font_descriptions ());  
377   output_scheme (scm_list_n (ly_symbol2scm ("define-fonts"),
378                              font_names,
379                              SCM_UNDEFINED));
380
381   /*
382     line preamble.
383    */
384   output_scheme (scm_list_n (ly_symbol2scm ("start-line"),
385                           gh_double2scm (height),
386                           SCM_UNDEFINED));
387   
388   /* Output elements in three layers, 0, 1, 2.
389      The default layer is 1. */
390   for (int i = 0; i < 3; i++)
391     for (SCM s = get_grob_property ("all-elements"); gh_pair_p (s);
392          s = ly_cdr (s))
393       {
394         Grob *sc = unsmob_grob (ly_car (s));
395         Molecule *m = sc->get_molecule ();
396         if (!m)
397           continue;
398         
399         SCM s = sc->get_grob_property ("layer");
400         int layer = gh_number_p (s) ? gh_scm2int (s) : 1;
401         if (layer != i)
402           continue;
403         
404         Offset o (sc->relative_coordinate (this, X_AXIS),
405                   sc->relative_coordinate (this, Y_AXIS));
406         
407         SCM e = sc->get_grob_property ("extra-offset");
408         if (gh_pair_p (e))
409           {
410             o[X_AXIS] += gh_scm2double (ly_car (e));
411             o[Y_AXIS] += gh_scm2double (ly_cdr (e));      
412           }
413         
414         output_molecule (m->get_expr (), o);
415       }
416   
417   if (last_line)
418     {
419       output_scheme (scm_list_n (ly_symbol2scm ("stop-last-line"), SCM_UNDEFINED));
420     }
421   else
422     {
423       output_scheme (scm_list_n (ly_symbol2scm ("stop-line"), SCM_UNDEFINED));
424     }
425 }
426
427
428 Link_array<Item> 
429 Line_of_score::broken_col_range (Item const*l, Item const*r) const
430 {
431   Link_array<Item> ret;
432
433   l = l->column_l ();
434   r = r->column_l ();
435   SCM s = get_grob_property ("columns");
436
437   while (gh_pair_p (s) && ly_car (s) != r->self_scm ())
438     s = ly_cdr (s);
439     
440   if (gh_pair_p (s))
441     s = ly_cdr (s);
442   
443   while (gh_pair_p (s) && ly_car (s) != l->self_scm ())
444     {
445       Paper_column*c = dynamic_cast<Paper_column*> (unsmob_grob (ly_car (s)));
446       if (Item::breakable_b (c) && !c->line_l_)
447         ret.push (c);
448
449       s = ly_cdr (s);
450     }
451
452   ret.reverse ();
453   return ret;
454 }
455
456 /**
457    Return all columns, but filter out any unused columns , since they might
458    disrupt the spacing problem.
459  */
460 Link_array<Grob>
461 Line_of_score::column_l_arr ()const
462 {
463   Link_array<Grob> acs
464     = Pointer_group_interface__extract_elements (this, (Grob*) 0, "columns");
465   bool bfound = false;
466   for (int i= acs.size (); i -- ;)
467     {
468       bool brb = Item::breakable_b (acs[i]);
469       bfound = bfound || brb;
470
471       /*
472         the last column should be breakable. Weed out any columns that
473         seem empty. We need to retain breakable columns, in case
474         someone forced a breakpoint.
475       */
476       if (!bfound || !Paper_column::used_b (acs[i]))
477         acs.del (i);
478     }
479   return acs;
480 }
481