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