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