]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
Merge branch 'master' of carldsorensen@git.sv.gnu.org:/srv/git/lilypond into fret...
[lilypond.git] / lily / paper-book.cc
1 /*
2   paper-book.cc -- implement Paper_book
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2004--2009 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "paper-book.hh"
10
11 #include "grob.hh"
12 #include "main.hh"
13 #include "output-def.hh"
14 #include "paper-column.hh"
15 #include "paper-score.hh"
16 #include "paper-system.hh"
17 #include "text-interface.hh"
18 #include "warn.hh"
19 #include "program-option.hh"
20 #include "page-marker.hh"
21
22 #include "ly-smobs.icc"
23
24 Paper_book::Paper_book ()
25 {
26   header_ = SCM_EOL;
27   header_0_ = SCM_EOL;
28   pages_ = SCM_BOOL_F;
29   scores_ = SCM_EOL;
30   bookparts_ = SCM_EOL;
31   performances_ = SCM_EOL;
32   systems_ = SCM_BOOL_F;
33
34   paper_ = 0;
35   parent_ = 0;
36   smobify_self ();
37 }
38
39 Paper_book::~Paper_book ()
40 {
41 }
42
43 IMPLEMENT_DEFAULT_EQUAL_P (Paper_book);
44 IMPLEMENT_SMOBS (Paper_book);
45 IMPLEMENT_TYPE_P (Paper_book, "ly:paper-book?");
46
47 SCM
48 Paper_book::mark_smob (SCM smob)
49 {
50   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
51   if (b->paper_)
52     scm_gc_mark (b->paper_->self_scm ());
53   if (b->parent_)
54     scm_gc_mark (b->parent_->self_scm ());
55   scm_gc_mark (b->header_);
56   scm_gc_mark (b->header_0_);
57   scm_gc_mark (b->pages_);
58   scm_gc_mark (b->performances_);
59   scm_gc_mark (b->scores_);
60   scm_gc_mark (b->bookparts_);
61   return b->systems_;
62 }
63
64 int
65 Paper_book::print_smob (SCM smob, SCM port, scm_print_state*)
66 {
67   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
68   (void)b;
69   scm_puts ("#<Paper_book>", port);
70   return 1;
71 }
72
73 Output_def *
74 Paper_book::top_paper ()
75 {
76   Output_def *paper = paper_;
77   while (paper->parent_)
78     paper = paper->parent_;
79   return paper;
80 }
81
82 SCM
83 dump_fields ()
84 {
85   SCM fields = SCM_EOL;
86   for (vsize i = dump_header_fieldnames_global.size (); i--;)
87     fields
88       = scm_cons (ly_symbol2scm (dump_header_fieldnames_global[i].c_str ()),
89                   fields);
90   return fields;
91 }
92
93 void
94 Paper_book::add_score (SCM s)
95 {
96   scores_ = scm_cons (s, scores_);
97 }
98
99 void
100 Paper_book::add_bookpart (SCM p)
101 {
102   bookparts_ = scm_cons (p, bookparts_);
103 }
104
105 void
106 Paper_book::add_performance (SCM s)
107 {
108   performances_ = scm_cons (s, performances_);
109 }
110
111 int
112 Paper_book::output_aux (SCM output_channel,
113                         bool is_last,
114                         int *first_page_number,
115                         int *first_performance_number)
116 {
117   int page_nb = 0;
118   if (scm_is_pair (performances_))
119     {
120       SCM proc = ly_lily_module_constant ("write-performances-midis");
121  
122       scm_call_3 (proc,
123                   performances (),
124                   output_channel,
125                   scm_long2num (*first_performance_number));
126       *first_performance_number += scm_ilength (performances_);
127     }
128
129   if (scm_is_pair (bookparts_))
130     {
131       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
132         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
133           {
134             bool is_last_part = (is_last && !scm_is_pair (scm_cdr (p)));
135             page_nb += pbookpart->output_aux (output_channel,
136                                               is_last_part,
137                                               first_page_number,
138                                               first_performance_number);
139           }
140     }
141   else
142     {
143       if (scores_ == SCM_EOL)
144         return 0;
145       paper_->set_variable (ly_symbol2scm ("first-page-number"),
146                             scm_long2num (*first_page_number));
147       paper_->set_variable (ly_symbol2scm ("is-last-bookpart"),
148                             ly_bool2scm (is_last));
149       /* Generate all stencils to trigger font loads.  */
150       page_nb = scm_ilength (pages ());
151       *first_page_number += page_nb;
152     }
153   return page_nb;
154 }
155
156 void
157 Paper_book::output (SCM output_channel)
158 {
159   int first_page_number = robust_scm2int (paper_->c_variable ("first-page-number"), 1);
160   int first_performance_number = 0;
161   if (!output_aux (output_channel,
162                    true,
163                    &first_page_number,
164                    &first_performance_number))
165     return;
166       
167   SCM scopes = SCM_EOL;
168   if (ly_is_module (header_))
169     scopes = scm_cons (header_, scopes);
170
171   string mod_nm = "scm framework-" + get_output_backend_name ();
172
173   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
174
175   if (get_program_option ("print-pages"))
176     {
177       SCM func = scm_c_module_lookup (mod, "output-framework");
178
179       func = scm_variable_ref (func);
180       scm_apply_0 (func, scm_list_n (output_channel,
181                                      self_scm (),
182                                      scopes,
183                                      dump_fields (),
184                                      SCM_UNDEFINED));
185     }
186
187   if (get_program_option ("preview"))
188     {
189       SCM func = scm_c_module_lookup (mod, "output-preview-framework");
190       func = scm_variable_ref (func);
191       scm_apply_0 (func, scm_list_n (output_channel,
192                                      self_scm (),
193                                      scopes,
194                                      dump_fields (),
195                                      SCM_UNDEFINED));
196     }
197 }
198
199 void
200 Paper_book::classic_output_aux (SCM output,
201                                 int *first_performance_number)
202 {
203   if (scm_is_pair (performances_))
204     {
205       SCM proc = ly_lily_module_constant ("write-performances-midis");
206       scm_call_3 (proc,
207                   performances (),
208                   output,
209                   scm_long2num (*first_performance_number));
210       *first_performance_number += scm_ilength (performances_);
211     }
212   
213   /* Generate all stencils to trigger font loads.  */
214   systems ();
215 }
216
217 void
218 Paper_book::classic_output (SCM output)
219 {
220   int first_performance_number = 0;
221   classic_output_aux (output, &first_performance_number);
222
223   SCM scopes = SCM_EOL;
224   if (ly_is_module (header_))
225     scopes = scm_cons (header_, scopes);
226
227   if (ly_is_module (header_0_))
228     scopes = scm_cons (header_0_, scopes);
229
230   string format = get_output_backend_name ();
231   string mod_nm = "scm framework-" + format;
232
233   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
234   SCM func = scm_c_module_lookup (mod, "output-classic-framework");
235
236   func = scm_variable_ref (func);
237   scm_apply_0 (func, scm_list_n (output,
238                                  self_scm (),
239                                  scopes,
240                                  dump_fields (),
241                                  SCM_UNDEFINED));
242
243   progress_indication ("\n");
244 }
245
246 /* TODO: resurrect more complex user-tweaks for titling?  */
247 Stencil
248 Paper_book::book_title ()
249 {
250   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("book-title"));
251   Stencil title;
252
253   SCM scopes = SCM_EOL;
254   if (ly_is_module (header_))
255     scopes = scm_cons (header_, scopes);
256
257   SCM tit = SCM_EOL;
258   if (ly_is_procedure (title_func))
259     tit = scm_call_2 (title_func,
260                       paper_->self_scm (),
261                       scopes);
262
263   if (unsmob_stencil (tit))
264     title = *unsmob_stencil (tit);
265
266   if (!title.is_empty ())
267     title.align_to (Y_AXIS, UP);
268
269   return title;
270 }
271
272 Stencil
273 Paper_book::score_title (SCM header)
274 {
275   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("score-title"));
276
277   Stencil title;
278
279   SCM scopes = SCM_EOL;
280   if (ly_is_module (header_))
281     scopes = scm_cons (header_, scopes);
282
283   if (ly_is_module (header))
284     scopes = scm_cons (header, scopes);
285
286   SCM tit = SCM_EOL;
287   if (ly_is_procedure (title_func))
288     tit = scm_call_2 (title_func,
289                       paper_->self_scm (),
290                       scopes);
291
292   if (unsmob_stencil (tit))
293     title = *unsmob_stencil (tit);
294
295   if (!title.is_empty ())
296     title.align_to (Y_AXIS, UP);
297
298   return title;
299 }
300
301 void
302 set_page_permission (SCM sys, SCM symbol, SCM permission)
303 {
304   if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (sys)))
305     {
306       vector<Grob*> cols = ps->get_columns ();
307       if (cols.size ())
308         {
309           Paper_column *col = dynamic_cast<Paper_column*> (cols.back ());
310           col->set_property (symbol, permission);
311           col->find_prebroken_piece (LEFT)->set_property (symbol, permission);
312         }
313     }
314   else if (Prob *pb = unsmob_prob (sys))
315     pb->set_property (symbol, permission);
316 }
317
318 /* read the breakbefore property of a score block and set up the preceding
319    system-spec to honour it. That is, SYS should be the system spec that
320    immediately precedes the score (from which HEADER is taken)
321    in the get_system_specs () list */
322 void
323 set_system_penalty (SCM sys, SCM header)
324 {
325   if (ly_is_module (header))
326     {
327       SCM force = ly_module_lookup (header, ly_symbol2scm ("breakbefore"));
328       if (SCM_VARIABLEP (force)
329           && scm_is_bool (SCM_VARIABLE_REF (force)))
330         {
331           if (to_boolean (SCM_VARIABLE_REF (force)))
332             {
333               set_page_permission (sys, ly_symbol2scm ("page-break-permission"),
334                                    ly_symbol2scm ("force"));
335               set_page_permission (sys, ly_symbol2scm ("line-break-permission"),
336                                    ly_symbol2scm ("force"));
337             }
338           else
339             set_page_permission (sys, ly_symbol2scm ("page-break-permission"), SCM_EOL);
340         }
341     }
342 }
343
344 void
345 set_labels (SCM sys, SCM labels)
346 {
347   if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (sys)))
348     {
349       vector<Grob*> cols = ps->get_columns ();
350       if (cols.size ())
351         {
352           Paper_column *col = dynamic_cast<Paper_column*> (cols[0]);
353           col->set_property ("labels", 
354                              scm_append_x (scm_list_2 (col->get_property ("labels"),
355                                                        labels)));
356           Paper_column *col_right = dynamic_cast<Paper_column*> (col->find_prebroken_piece (RIGHT));
357           col_right->set_property ("labels", 
358                                    scm_append_x (scm_list_2 (col_right->get_property ("labels"),
359                                                              labels)));
360         }
361     }
362   else if (Prob *pb = unsmob_prob (sys))
363     pb->set_property ("labels", 
364                       scm_append_x (scm_list_2 (pb->get_property ("labels"),
365                                                 labels)));
366 }
367
368 SCM
369 Paper_book::get_score_title (SCM header)
370 {
371   Stencil title = score_title (header);
372   if (title.is_empty ())
373     title = score_title (header_);
374   if (!title.is_empty ())
375     {
376       /*
377         TODO: this should come from the \layout {} block, which should
378         override settings from \paper {}
379       */
380       SCM props = paper_->lookup_variable (ly_symbol2scm ("score-title-properties"));
381       Prob *ps = make_paper_system (props);
382       paper_system_set_stencil (ps, title);
383
384       return ps->self_scm ();
385     }
386
387   return SCM_BOOL_F;
388 }
389
390
391 SCM
392 Paper_book::get_system_specs ()
393 {
394   SCM system_specs = SCM_EOL;
395   
396   Stencil title = book_title ();
397   if (!title.is_empty ())
398     {
399       SCM props = paper_->lookup_variable (ly_symbol2scm ("book-title-properties"));
400       Prob *ps = make_paper_system (props);
401       paper_system_set_stencil (ps, title);
402       
403       system_specs = scm_cons (ps->self_scm (), system_specs);
404       ps->unprotect ();
405     }
406
407   SCM page_properties
408     = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
409                   paper_->self_scm ());
410
411   SCM interpret_markup_list = ly_lily_module_constant ("interpret-markup-list");
412   SCM header = SCM_EOL;
413   SCM labels = SCM_EOL;
414   for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
415     {
416       if (ly_is_module (scm_car (s)))
417         {
418           header = scm_car (s);
419           if (header_0_ == SCM_EOL)
420             header_0_ = header;
421         }
422       else if (Page_marker *page_marker = unsmob_page_marker (scm_car (s)))
423         {
424           /* page markers are used to set page breaking/turning permission,
425              or to place bookmarking labels */ 
426           if (scm_is_symbol (page_marker->permission_symbol ()))
427             {
428               /* set previous element page break or turn permission */
429               if (scm_is_pair (system_specs))
430                 set_page_permission (scm_car (system_specs),
431                                      page_marker->permission_symbol (),
432                                      page_marker->permission_value ());
433             }
434           if (scm_is_symbol (page_marker->label ()))
435             {
436               /* The next element label is to be set */
437               labels = scm_cons (page_marker->label (), labels);
438             }
439         }
440       else if (Music_output *mop = unsmob_music_output (scm_car (s)))
441         {
442           if (Paper_score *pscore = dynamic_cast<Paper_score *> (mop))
443             {
444               SCM title = get_score_title (header);
445
446               if (scm_is_pair (system_specs))
447                 set_system_penalty (scm_car (system_specs), header);
448
449               if (unsmob_prob (title))
450                 {
451                   system_specs = scm_cons (title, system_specs);
452                   unsmob_prob (title)->unprotect ();
453                 }
454
455               header = SCM_EOL;
456               system_specs = scm_cons (pscore->self_scm (), system_specs);
457               if (scm_is_pair (labels))
458                 {
459                   set_labels (scm_car (system_specs), labels);
460                   labels = SCM_EOL;
461                 }
462             }
463           else
464             {
465               /*
466                 Ignore MIDI
467               */
468             }
469         }
470       else if (Text_interface::is_markup_list (scm_car (s)))
471         {
472           SCM texts = scm_call_3 (interpret_markup_list,
473                                   paper_->self_scm (),
474                                   page_properties,
475                                   scm_car (s));
476           for (SCM list = texts ; scm_is_pair (list) ; list = scm_cdr (list))
477             {
478               SCM t = scm_car (list);
479               // TODO: init props
480               Prob *ps = make_paper_system (SCM_EOL);
481               ps->set_property ("page-break-permission", ly_symbol2scm ("allow"));
482               ps->set_property ("page-turn-permission", ly_symbol2scm ("allow"));
483               
484               paper_system_set_stencil (ps, *unsmob_stencil (t));
485               ps->set_property ("is-title", SCM_BOOL_T); 
486               if (scm_is_pair (scm_cdr (list)))
487                 {
488                   /* If an other markup is following, set this markup 
489                    * next padding and next space to 0, so that baseline-skip 
490                    * only should be taken into account for lines vertical
491                    * spacing. */
492                   ps->set_property ("next-padding", scm_double2num (0.0));
493                   ps->set_property ("next-space", scm_double2num (0.0));
494                 }
495               system_specs = scm_cons (ps->self_scm (), system_specs);
496               ps->unprotect ();
497               
498               if (scm_is_pair (labels))
499                 {
500                   set_labels (scm_car (system_specs), labels);
501                   labels = SCM_EOL;
502                 }
503               // FIXME: figure out penalty.
504               //set_system_penalty (ps, scores_[i].header_);
505             }
506         }
507       else
508         assert (0);
509     }
510
511   system_specs = scm_reverse_x (system_specs, SCM_EOL);
512   return system_specs;
513 }
514
515 SCM
516 Paper_book::systems ()
517 {
518   if (systems_ != SCM_BOOL_F)
519     return systems_;
520
521   systems_ = SCM_EOL;
522   if (scm_is_pair (bookparts_))
523     {
524       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
525         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
526           systems_ = scm_append_x (scm_list_2 (systems_, pbookpart->systems ()));
527     }
528   else
529     {
530       SCM specs = get_system_specs ();
531       for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
532         {
533           if (Paper_score *pscore = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
534             {
535               SCM system_list = scm_vector_to_list (pscore->get_paper_systems ());
536               system_list = scm_reverse (system_list);
537               systems_ = scm_append (scm_list_2 (system_list, systems_));
538             }
539           else
540             {
541               systems_ = scm_cons (scm_car (s), systems_);
542             }
543         }
544       systems_ = scm_reverse (systems_);
545
546       /* backwards compatibility for the old page breaker */
547       int i = 0;
548       Prob *last = 0;
549       for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s))
550         {
551           Prob *ps = unsmob_prob (scm_car (s));
552           ps->set_property ("number", scm_from_int (++i));
553           
554           if (last
555               && to_boolean (last->get_property ("is-title"))
556               && !scm_is_number (ps->get_property ("penalty")))
557             ps->set_property ("penalty", scm_from_int (10000));
558           last = ps;
559           
560           if (scm_is_pair (scm_cdr (s)))
561             {
562               SCM perm = ps->get_property ("page-break-permission");
563               Prob *next = unsmob_prob (scm_cadr (s));
564               if (perm == SCM_EOL)
565                 next->set_property ("penalty", scm_from_int (10001));
566               else if (perm == ly_symbol2scm ("force"))
567                 next->set_property ("penalty", scm_from_int (-10001));
568             }
569         }
570     }
571
572   return systems_;
573 }
574
575 SCM
576 Paper_book::pages ()
577 {
578   if (SCM_BOOL_F != pages_)
579     return pages_;
580
581   pages_ = SCM_EOL;
582   if (scm_is_pair (bookparts_))
583     {
584       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
585         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
586           pages_ = scm_append_x (scm_list_2 (pages_, pbookpart->pages ()));
587     }
588   else if (scm_is_pair (scores_))
589     {
590       SCM proc = paper_->c_variable ("page-breaking-wrapper");
591       pages_ = scm_apply_0 (proc, scm_list_1 (self_scm ()));
592
593       /* set systems_ from the pages */
594       if (systems_ == SCM_BOOL_F)
595         {
596           systems_ = SCM_EOL;
597           for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p))
598             {
599               Prob *page = unsmob_prob (scm_car (p));
600               SCM systems = page->get_property ("lines");
601               systems_ = scm_append (scm_list_2 (systems_, systems));
602             }
603         }
604     }
605   return pages_;
606 }
607
608 SCM
609 Paper_book::performances () const
610 {
611   return scm_reverse (performances_);
612 }