]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
Make non-title markup blocks page-breakable by default.
[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--2007 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
21 #include "ly-smobs.icc"
22
23 Paper_book::Paper_book ()
24 {
25   header_ = SCM_EOL;
26   header_0_ = SCM_EOL;
27   pages_ = SCM_BOOL_F;
28   scores_ = SCM_EOL;
29   performances_ = SCM_EOL;
30   systems_ = SCM_BOOL_F;
31
32   paper_ = 0;
33   smobify_self ();
34 }
35
36 Paper_book::~Paper_book ()
37 {
38 }
39
40 IMPLEMENT_DEFAULT_EQUAL_P (Paper_book);
41 IMPLEMENT_SMOBS (Paper_book);
42 IMPLEMENT_TYPE_P (Paper_book, "ly:paper-book?");
43
44 SCM
45 Paper_book::mark_smob (SCM smob)
46 {
47   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
48   if (b->paper_)
49     scm_gc_mark (b->paper_->self_scm ());
50   scm_gc_mark (b->header_);
51   scm_gc_mark (b->header_0_);
52   scm_gc_mark (b->pages_);
53   scm_gc_mark (b->performances_);
54   scm_gc_mark (b->scores_);
55   return b->systems_;
56 }
57
58 int
59 Paper_book::print_smob (SCM smob, SCM port, scm_print_state*)
60 {
61   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
62   (void)b;
63   scm_puts ("#<Paper_book>", port);
64   return 1;
65 }
66
67 SCM
68 dump_fields ()
69 {
70   SCM fields = SCM_EOL;
71   for (vsize i = dump_header_fieldnames_global.size (); i--;)
72     fields
73       = scm_cons (ly_symbol2scm (dump_header_fieldnames_global[i].c_str ()),
74                   fields);
75   return fields;
76 }
77
78 void
79 Paper_book::add_score (SCM s)
80 {
81   scores_ = scm_cons (s, scores_);
82 }
83
84 void
85 Paper_book::add_performance (SCM s)
86 {
87   performances_ = scm_cons (s, performances_);
88 }
89
90 void
91 Paper_book::output (SCM output_channel)
92 {
93   if (scm_is_pair (performances_))
94     {
95       SCM proc = ly_lily_module_constant ("write-performances-midis");
96  
97       scm_call_2 (proc, performances (), output_channel);
98     }
99
100   if (scores_ == SCM_EOL)
101     return;
102
103   /* Generate all stencils to trigger font loads.  */
104   pages ();
105
106   SCM scopes = SCM_EOL;
107   if (ly_is_module (header_))
108     scopes = scm_cons (header_, scopes);
109
110   string mod_nm = "scm framework-" + get_output_backend_name ();
111
112   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
113
114   if (get_program_option ("print-pages"))
115     {
116       SCM func = scm_c_module_lookup (mod, "output-framework");
117
118       func = scm_variable_ref (func);
119       scm_apply_0 (func, scm_list_n (output_channel,
120                                      self_scm (),
121                                      scopes,
122                                      dump_fields (),
123                                      SCM_UNDEFINED));
124     }
125
126   if (get_program_option ("preview"))
127     {
128       SCM func = scm_c_module_lookup (mod, "output-preview-framework");
129       func = scm_variable_ref (func);
130       scm_apply_0 (func, scm_list_n (output_channel,
131                                      self_scm (),
132                                      scopes,
133                                      dump_fields (),
134                                      SCM_UNDEFINED));
135     }
136 }
137
138 void
139 Paper_book::classic_output (SCM output)
140 {
141   if (scm_is_pair (performances_))
142     {
143       SCM proc = ly_lily_module_constant ("write-performances-midis");
144  
145       scm_call_2 (proc, performances (), output);
146     }
147   
148   /* Generate all stencils to trigger font loads.  */
149   systems ();
150
151   SCM scopes = SCM_EOL;
152   if (ly_is_module (header_))
153     scopes = scm_cons (header_, scopes);
154
155   if (ly_is_module (header_0_))
156     scopes = scm_cons (header_0_, scopes);
157
158   string format = get_output_backend_name ();
159   string mod_nm = "scm framework-" + format;
160
161   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
162   SCM func = scm_c_module_lookup (mod, "output-classic-framework");
163
164   func = scm_variable_ref (func);
165   scm_apply_0 (func, scm_list_n (output,
166                                  self_scm (),
167                                  scopes,
168                                  dump_fields (),
169                                  SCM_UNDEFINED));
170
171   progress_indication ("\n");
172 }
173
174 /* TODO: resurrect more complex user-tweaks for titling?  */
175 Stencil
176 Paper_book::book_title ()
177 {
178   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("book-title"));
179   Stencil title;
180
181   SCM scopes = SCM_EOL;
182   if (ly_is_module (header_))
183     scopes = scm_cons (header_, scopes);
184
185   SCM tit = SCM_EOL;
186   if (ly_is_procedure (title_func))
187     tit = scm_call_2 (title_func,
188                       paper_->self_scm (),
189                       scopes);
190
191   if (unsmob_stencil (tit))
192     title = *unsmob_stencil (tit);
193
194   if (!title.is_empty ())
195     title.align_to (Y_AXIS, UP);
196
197   return title;
198 }
199
200 Stencil
201 Paper_book::score_title (SCM header)
202 {
203   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("score-title"));
204
205   Stencil title;
206
207   SCM scopes = SCM_EOL;
208   if (ly_is_module (header_))
209     scopes = scm_cons (header_, scopes);
210
211   if (ly_is_module (header))
212     scopes = scm_cons (header, scopes);
213
214   SCM tit = SCM_EOL;
215   if (ly_is_procedure (title_func))
216     tit = scm_call_2 (title_func,
217                       paper_->self_scm (),
218                       scopes);
219
220   if (unsmob_stencil (tit))
221     title = *unsmob_stencil (tit);
222
223   if (!title.is_empty ())
224     title.align_to (Y_AXIS, UP);
225
226   return title;
227 }
228
229 /* read the breakbefore property of a score block and set up the preceding
230    system-spec to honour it. That is, SYM should be the system spec that
231    immediately precedes the score (from which HEADER is taken)
232    in the get_system_specs () list */
233 void
234 set_system_penalty (SCM sys, SCM header)
235 {
236   if (ly_is_module (header))
237     {
238       SCM force = ly_module_lookup (header, ly_symbol2scm ("breakbefore"));
239       if (SCM_VARIABLEP (force)
240           && scm_is_bool (SCM_VARIABLE_REF (force)))
241         {
242           bool b = to_boolean (SCM_VARIABLE_REF (force));
243           SCM sym = b ? ly_symbol2scm ("force") : SCM_EOL;
244
245           if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (sys)))
246             {
247               vector<Grob*> cols = ps->get_columns ();
248               if (cols.size ())
249                 {
250                   Paper_column *col = dynamic_cast<Paper_column*> (cols.back ());
251                   col->set_property ("page-break-permission", sym);
252                   col->find_prebroken_piece (LEFT)->set_property ("page-break-permission", sym);
253                 }
254             }
255           else if (Prob *pb = unsmob_prob (sys))
256             pb->set_property ("page-break-permission", sym);
257         }
258     }
259 }
260
261
262 SCM
263 Paper_book::get_score_title (SCM header)
264 {
265   Stencil title = score_title (header);
266   if (title.is_empty ())
267     title = score_title (header_);
268   if (!title.is_empty ())
269     {
270       /*
271         TODO: this should come from the \layout {} block, which should
272         override settings from \paper {}
273       */
274       SCM props = paper_->lookup_variable (ly_symbol2scm ("score-title-properties"));
275       Prob *ps = make_paper_system (props);
276       paper_system_set_stencil (ps, title);
277
278       return ps->self_scm ();
279     }
280
281   return SCM_BOOL_F;
282 }
283
284
285 SCM
286 Paper_book::get_system_specs ()
287 {
288   SCM system_specs = SCM_EOL;
289   
290   Stencil title = book_title ();
291   if (!title.is_empty ())
292     {
293       SCM props = paper_->lookup_variable (ly_symbol2scm ("book-title-properties"));
294       Prob *ps = make_paper_system (props);
295       paper_system_set_stencil (ps, title);
296
297       system_specs = scm_cons (ps->self_scm (), system_specs);
298       ps->unprotect ();
299     }
300
301   SCM page_properties
302     = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
303                   paper_->self_scm ());
304
305   SCM header = SCM_EOL;
306   for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
307     {
308       if (ly_is_module (scm_car (s)))
309         {
310           header = scm_car (s);
311           if (header_0_ == SCM_EOL)
312             header_0_ = header;
313         }
314       else if (Music_output *mop = unsmob_music_output (scm_car (s)))
315         {
316           if (Paper_score *pscore = dynamic_cast<Paper_score *> (mop))
317             {
318               SCM title = get_score_title (header);
319
320               if (scm_is_pair (system_specs))
321                 set_system_penalty (scm_car (system_specs), header);
322
323               if (unsmob_prob (title))
324                 {
325                   system_specs = scm_cons (title, system_specs);
326                   unsmob_prob (title)->unprotect ();
327                 }
328
329               header = SCM_EOL;
330               system_specs = scm_cons (pscore->self_scm (), system_specs);
331             }
332           else
333             {
334               /*
335                 Ignore MIDI
336               */
337             }
338         }
339       else if (Text_interface::is_markup (scm_car (s)))
340         {
341           SCM t = Text_interface::interpret_markup (paper_->self_scm (),
342                                                     page_properties,
343                                                     scm_car (s));
344           
345           // TODO: init props
346           Prob *ps = make_paper_system (SCM_EOL);
347           /* we don't have a way of specifying page-{break,turn} on
348              markup blocks, so for now we just set everything turnable
349             and breakable by default */
350           ps->set_property ("page-break-permission", ly_symbol2scm ("allow"));
351           ps->set_property ("page-turn-permission", ly_symbol2scm ("allow"));
352           
353           paper_system_set_stencil (ps, *unsmob_stencil (t));
354           ps->set_property ("is-title", SCM_BOOL_T); 
355           system_specs = scm_cons (ps->self_scm (), system_specs);
356           ps->unprotect ();
357           
358           // FIXME: figure out penalty.
359           //set_system_penalty (ps, scores_[i].header_);
360         }
361       else
362         assert (0);
363     }
364
365   system_specs = scm_reverse_x (system_specs, SCM_EOL);
366   return system_specs;
367 }
368
369 SCM
370 Paper_book::systems ()
371 {
372   if (systems_ != SCM_BOOL_F)
373     return systems_;
374
375   systems_ = SCM_EOL;
376   SCM specs = get_system_specs ();
377   for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
378     {
379       if (Paper_score *pscore = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
380         {
381           SCM system_list = scm_vector_to_list (pscore->get_paper_systems ());
382           system_list = scm_reverse (system_list);
383           systems_ = scm_append (scm_list_2 (system_list, systems_));
384         }
385       else
386         {
387           systems_ = scm_cons (scm_car (s), systems_);
388         }
389     }
390   
391   systems_ = scm_reverse (systems_);
392
393   /* backwards compatibility for the old page breaker */
394   int i = 0;
395   Prob *last = 0;
396   for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s))
397     {
398       Prob *ps = unsmob_prob (scm_car (s));
399       ps->set_property ("number", scm_from_int (++i));
400
401       if (last
402           && to_boolean (last->get_property ("is-title"))
403           && !scm_is_number (ps->get_property ("penalty")))
404         ps->set_property ("penalty", scm_from_int (10000));
405       last = ps;
406
407       if (scm_is_pair (scm_cdr (s)))
408         {
409           SCM perm = ps->get_property ("page-break-permission");
410           Prob *next = unsmob_prob (scm_cadr (s));
411           if (perm == SCM_EOL)
412             next->set_property ("penalty", scm_from_int (10001));
413           else if (perm == ly_symbol2scm ("force"))
414             next->set_property ("penalty", scm_from_int (-10001));
415         }
416     }
417
418   return systems_;
419 }
420
421 SCM
422 Paper_book::pages ()
423 {
424   if (SCM_BOOL_F != pages_)
425     return pages_;
426
427   pages_ = SCM_EOL;
428   SCM proc = paper_->c_variable ("page-breaking-wrapper");
429   pages_ = scm_apply_0 (proc, scm_list_1 (self_scm ()));
430
431   /* set systems_ from the pages */
432   if (systems_ == SCM_BOOL_F)
433     {
434       systems_ = SCM_EOL;
435       for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p))
436         {
437           Prob *page = unsmob_prob (scm_car (p));
438           SCM systems = page->get_property ("lines");
439
440           systems_ = scm_append (scm_list_2 (systems_, systems));
441         }
442     }
443
444   return pages_;
445 }
446
447 SCM
448 Paper_book::performances () const
449 {
450   return scm_reverse (performances_);
451 }