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