]> git.donarmstrong.com Git - lilypond.git/blob - lily/system.cc
* scripts/lilypond.py: Remove LaTeX titling kludge. Remove page
[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--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "axis-group-interface.hh"
10 #include "warn.hh"
11 #include "system.hh"
12 #include "main.hh"
13 #include "paper-column.hh"
14 #include "paper-def.hh"
15 #include "paper-outputter.hh"
16 #include "paper-score.hh"
17 #include "string.hh"
18 #include "warn.hh"
19 #include "stencil.hh"
20 #include "all-font-metrics.hh"
21 #include "spacing-interface.hh"
22 #include "staff-symbol-referencer.hh"
23 #include "paper-book.hh"
24 #include "paper-line.hh"
25
26 System::System (SCM s)
27   : Spanner (s)
28 {
29   rank_ = 0;
30 }
31
32 int
33 System::element_count () const
34 {
35   return scm_ilength (get_property ("all-elements"));
36 }
37
38 int
39 System::spanner_count () const
40 {
41   int k = 0;
42   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
43     if (dynamic_cast<Spanner*> (unsmob_grob (ly_car (s))))
44       k++;
45   return k;
46 }
47
48 void
49 System::typeset_grob (Grob * elem)
50 {
51   if (elem->pscore_)
52     programming_error ("Adding element twice.");
53   
54   elem->pscore_ = pscore_;
55   Pointer_group_interface::add_grob (this, ly_symbol2scm ("all-elements"), elem);
56   scm_gc_unprotect_object (elem->self_scm ());
57 }
58
59 // todo: use map.
60 static void
61 fixup_refpoints (SCM s)
62 {
63   for (; is_pair (s); s = ly_cdr (s))
64     {
65       Grob::fixup_refpoint (ly_car (s));
66     }
67 }
68
69 SCM
70 System::get_lines ()
71 {
72   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
73     {
74       Grob *g = unsmob_grob (ly_car (s));
75       if (g->internal_has_interface (ly_symbol2scm ("only-prebreak-interface")))
76         {
77           /*
78             Kill no longer needed grobs. 
79            */
80           Item * it = dynamic_cast<Item*> (g);
81           if (it && Item::is_breakable (it))
82             {
83               it->find_prebroken_piece (LEFT)->suicide ();
84               it->find_prebroken_piece (RIGHT)->suicide ();
85             }
86           g->suicide ();
87         }
88       else if (g->live ())
89         g->do_break_processing ();
90     }
91
92   /*
93     fixups must be done in broken line_of_scores, because new elements
94     are put over there.  */
95   int count = 0;
96   for (int i=0; i < broken_intos_.size (); i++)
97     {
98       Grob *se = broken_intos_[i];
99       SCM all = se->get_property ("all-elements");
100       for (SCM s = all; is_pair (s); s = ly_cdr (s))
101         fixup_refpoint (ly_car (s));
102       count += scm_ilength (all);
103     }
104   
105   /*
106     needed for doing items.
107    */
108   fixup_refpoints (get_property ("all-elements"));
109
110   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
111     unsmob_grob (ly_car (s))->handle_broken_dependencies ();
112   handle_broken_dependencies ();
113
114 #if 0  /* don't do this: strange side effects.  */
115   
116   /* Because the this->get_property (all-elements) contains items in 3
117      versions, handle_broken_dependencies () will leave duplicated
118      items in all-elements.  Strictly speaking this is harmless, but
119      it leads to duplicated symbols in the output.  ly_list_qsort_uniq_x ()
120      makes sure that no duplicates are in the list.  */
121   for (int i = 0; i < line_count; i++)
122     {
123       SCM all = broken_intos_[i]->get_property ("all-elements");
124       all = ly_list_qsort_uniq_x(all); 
125     }
126 #endif
127   
128   if (verbose_global_b)
129     progress_indication (_f ("Element count %d.",  count + element_count ()));
130
131   int line_count = broken_intos_.size ();
132   SCM lines = scm_c_make_vector (line_count, SCM_UNDEFINED);
133   
134    for (int i = 0; i < line_count; i++)
135     {
136       if (verbose_global_b)
137         progress_indication ("[");
138
139       System *system = dynamic_cast<System*> (broken_intos_[i]);
140       system->post_processing ();
141       scm_vector_set_x (lines, scm_int2num (i), system->get_line ());
142
143       if (verbose_global_b)
144         progress_indication (to_string (i) + "]");
145     }
146    return lines;
147 }
148
149
150
151
152 /* Find the loose columns in POSNS, and drape them around the columns
153    specified in BETWEEN-COLS.  */
154 static void
155 set_loose_columns (System* which, Column_x_positions const *posns)
156 {
157   for (int i = 0; i < posns->loose_cols_.size (); i++)
158     {
159       int divide_over = 1;
160       Item *loose = dynamic_cast<Item*> (posns->loose_cols_[i]);
161       Paper_column* col = dynamic_cast<Paper_column*> (loose);
162       
163       if (col->system_)
164         continue;
165       
166       Item * left = 0;
167       Item * right = 0;
168       do
169         {
170           SCM between = loose->get_property ("between-cols");
171           if (!is_pair (between))
172             break;
173
174
175           Item *le = dynamic_cast<Item*> (unsmob_grob (ly_car (between)));
176           Item *re = dynamic_cast<Item*> (unsmob_grob (ly_cdr (between)));
177
178           if (!(le && re))
179             break ;
180           
181           if (!left && le)
182             {
183               left = le->get_column ();
184               if (!left->get_system ())
185                 left = left->find_prebroken_piece (RIGHT);
186             }
187
188           divide_over ++;
189           loose = right = re->get_column ();
190         }
191       while (1);
192
193       if (!right->get_system ())
194         right = right->find_prebroken_piece (LEFT);
195       
196       /* Divide the remaining space of the column over the left and
197         right side.  At the moment,  FIXME  */
198       Grob *common = right->common_refpoint (left, X_AXIS);
199       
200       Real rx = right->extent (common, X_AXIS)[LEFT];
201       Real lx = left->extent (common, X_AXIS)[RIGHT];
202       Real total_dx = rx - lx;
203       Interval cval =col->extent (col, X_AXIS);
204
205       /* Put it in the middle.  This is not an ideal solution -- the
206          break alignment code inserts a fixed space before the clef
207          (about 1 SS), while the space following the clef is flexible.
208          In tight situations, the clef will almost be on top of the
209          following note.  */
210       Real dx = rx - lx - cval.length ();
211       if (total_dx < 2* cval.length ())
212         {
213           /* TODO: this is discontinuous. I'm too tired to
214             invent a sliding mechanism.  Duh. */
215           dx *= 0.25;
216         }
217       else
218         dx *= 0.5;
219
220       col->system_ = which;
221       col->translate_axis (-col->relative_coordinate (common, X_AXIS), X_AXIS);
222       col->translate_axis (lx + dx - cval[LEFT], X_AXIS); 
223     }
224 }
225
226 // const?
227 void
228 System::break_into_pieces (Array<Column_x_positions> const &breaking)
229 {
230   for (int i=0; i < breaking.size (); i++)
231     {
232       System *system = dynamic_cast <System*> (clone ());
233       system->rank_ = i;
234
235       Link_array<Grob> c (breaking[i].cols_);
236       pscore_->typeset_line (system);
237       
238       system->set_bound (LEFT,c[0]);
239       system->set_bound (RIGHT,c.top ());
240       for (int j=0; j < c.size (); j++)
241         {
242           c[j]->translate_axis (breaking[i].config_[j],X_AXIS);
243           dynamic_cast<Paper_column*> (c[j])->system_ = system;
244         }
245       set_loose_columns (system, &breaking[i]);
246       broken_intos_.push (system);
247     }
248 }
249
250 void
251 System::add_column (Paper_column*p)
252 {
253   Grob *me = this;
254   SCM cs = me->get_property ("columns");
255   Grob * prev =  is_pair (cs) ? unsmob_grob (ly_car (cs)) : 0;
256
257   p->rank_ = prev ? Paper_column::get_rank (prev) + 1 : 0; 
258
259   me->set_property ("columns",  scm_cons (p->self_scm (), cs));
260
261   Axis_group_interface::add_element (me, p);
262 }
263
264 void
265 System::pre_processing ()
266 {
267   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
268     unsmob_grob (ly_car (s))->discretionary_processing ();
269
270   if (verbose_global_b)
271     progress_indication (_f ("Grob count %d",  element_count ()));
272
273   
274   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
275     unsmob_grob (ly_car (s))->handle_prebroken_dependencies ();
276   
277   fixup_refpoints (get_property ("all-elements"));
278   
279   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
280     {
281       Grob* sc = unsmob_grob (ly_car (s));
282       sc->calculate_dependencies (PRECALCED, PRECALCING, ly_symbol2scm ("before-line-breaking-callback"));
283     }
284   
285   progress_indication ("\n" + _ ("Calculating line breaks...") + " ");
286   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
287     {
288       Grob * e = unsmob_grob (ly_car (s));
289       SCM proc = e->get_property ("spacing-procedure");
290       if (is_procedure (proc))
291         scm_call_1 (proc, e->self_scm ());
292     }
293 }
294
295 void
296 System::post_processing ()
297 {
298   for (SCM s = get_property ("all-elements"); is_pair (s); s = ly_cdr (s))
299     {
300       Grob *g = unsmob_grob (ly_car (s));
301       g->calculate_dependencies (POSTCALCED, POSTCALCING,
302           ly_symbol2scm ("after-line-breaking-callback"));
303     }
304
305   Interval iv (extent (this, Y_AXIS));
306   if (iv.is_empty ())
307     programming_error ("System with zero extent.");
308   else
309     translate_axis (-iv[MAX], Y_AXIS);
310
311   /* Generate all stencils to trigger font loads.
312      This might seem inefficient, but Stencils are cached per grob
313      anyway. */
314   SCM all = get_property ("all-elements");
315   all = ly_list_qsort_uniq_x (all);
316
317   this->get_stencil ();
318   for (SCM s = all; is_pair (s); s = ly_cdr (s))
319     {
320       Grob *g = unsmob_grob (ly_car (s));
321       g->get_stencil ();
322     }
323 }
324
325 SCM
326 System::get_line ()
327 {  
328   static int const LAYER_COUNT = 3;
329
330   SCM stencils = SCM_EOL;
331   if (Stencil *me = get_stencil ())
332     stencils = scm_cons (me->smobbed_copy (), stencils);
333
334   /* Output stencils in three layers: 0, 1, 2.  The default layer is
335      1.
336
337      Start with layer 3, since  scm_cons prepends to list.
338      
339   */
340   SCM all = get_property ("all-elements");
341   
342   for (int i = LAYER_COUNT; i--;)
343     for (SCM s = all; is_pair (s); s = ly_cdr (s))
344       {
345         Grob *g = unsmob_grob (ly_car (s));
346         Stencil *stil = g->get_stencil ();
347
348         /* Skip empty stencils and grobs that are not in this layer.  */
349         if (!stil
350             || robust_scm2int (g->get_property ("layer"), 1) != i)
351           continue;
352
353         Offset o (g->relative_coordinate (this, X_AXIS),
354                   g->relative_coordinate (this, Y_AXIS));
355
356         Offset extra = robust_scm2offset (g->get_property ("extra-offset"),
357                                           Offset (0, 0))
358           * Staff_symbol_referencer::staff_space (g);
359
360         /*
361           must copy the stencil, for we cannot change the stencil
362           cached in G.
363         */
364         SCM my_stencil = stil->smobbed_copy ();
365         unsmob_stencil (my_stencil)->translate (o + extra);
366         stencils = scm_cons (my_stencil, stencils);
367       }
368
369   Interval x (extent (this, X_AXIS));
370   Interval y (extent (this, Y_AXIS));
371   Paper_line *pl = new Paper_line (Offset (x.length (), y.length ()),
372                                    stencils);
373
374   scm_gc_unprotect_object (pl->self_scm ());
375   return pl->self_scm ();
376 }
377
378 Link_array<Item> 
379 System::broken_col_range (Item const*l, Item const*r) const
380 {
381   Link_array<Item> ret;
382
383   l = l->get_column ();
384   r = r->get_column ();
385   SCM s = get_property ("columns");
386
387   while (is_pair (s) && ly_car (s) != r->self_scm ())
388     s = ly_cdr (s);
389
390   if (is_pair (s))
391     s = ly_cdr (s);
392
393   while (is_pair (s) && ly_car (s) != l->self_scm ())
394     {
395       Paper_column*c = dynamic_cast<Paper_column*> (unsmob_grob (ly_car (s)));
396       if (Item::is_breakable (c) && !c->system_)
397         ret.push (c);
398
399       s = ly_cdr (s);
400     }
401
402   ret.reverse ();
403   return ret;
404 }
405
406 /**
407    Return all columns, but filter out any unused columns , since they might
408    disrupt the spacing problem.
409  */
410 Link_array<Grob>
411 System::columns ()const
412 {
413   Link_array<Grob> acs
414     = Pointer_group_interface__extract_grobs (this, (Grob*) 0, "columns");
415   bool bfound = false;
416   for (int i= acs.size (); i -- ;)
417     {
418       bool brb = Item::is_breakable (acs[i]);
419       bfound = bfound || brb;
420
421       /*
422         the last column should be breakable. Weed out any columns that
423         seem empty. We need to retain breakable columns, in case
424         someone forced a breakpoint.
425       */
426       if (!bfound || !Paper_column::is_used (acs[i]))
427         acs.del (i);
428     }
429   return acs;
430 }
431
432 ADD_INTERFACE (System,"system-interface",
433                "This is the toplevel object: each object in a score "
434                "ultimately has a System object as its X and Y parent. ",
435                "all-elements columns")