]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-engraver.cc
* lily/system.cc (do_derived_mark): don't mark from object_alist_
[lilypond.git] / lily / score-engraver.cc
1 /*
2   score-engraver.cc -- implement Score_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "all-font-metrics.hh"
10 #include "afm.hh"
11 #include "warn.hh"
12 #include "main.hh"
13 #include "system.hh"
14 #include "score-engraver.hh"
15 #include "paper-score.hh"
16 #include "paper-column.hh"
17 #include "output-def.hh"
18 #include "axis-group-interface.hh"
19 #include "context-def.hh"
20 #include "staff-spacing.hh"
21 #include "note-spacing.hh"
22 #include "global-context.hh"
23 #include "open-type-font.hh"
24
25 /*
26   TODO: the column creation logic is rather hairy. Revise it.
27 */
28 Score_engraver::Score_engraver ()
29 {
30   system_ = 0;
31   command_column_ = 0;
32   musical_column_ = 0;
33   breaks_ = 0;
34   pscore_ = 0;
35 }
36
37 void
38 Score_engraver::derived_mark () const
39 {
40   if (pscore_)
41     scm_gc_mark (pscore_->self_scm ());
42   Score_translator::derived_mark ();
43   Engraver_group_engraver::derived_mark ();
44 }
45
46 void
47 Score_engraver::make_columns ()
48 {
49   /*
50     ugh.
51   */
52   if (!command_column_)
53     {
54       SCM nmp
55         = updated_grob_properties (context (),
56                                    ly_symbol2scm ("NonMusicalPaperColumn"));
57
58       Object_key const *key1 = context ()->get_grob_key ("NonMusicalPaperColumn");
59
60       SCM pc = updated_grob_properties (context (),
61                                         ly_symbol2scm ("PaperColumn"));
62       Object_key const *key2 = context ()->get_grob_key ("PaperColumn");
63       set_columns (new Paper_column (nmp, key1), new Paper_column (pc, key2));
64
65       Grob_info i1 (this, command_column_);
66       announce_grob (i1);
67
68       Grob_info i2 (this, musical_column_);
69       announce_grob (i2);
70     }
71 }
72
73 void
74 Score_engraver::prepare (Moment m)
75 {
76   /*
77     TODO: don't make columns when skipTypesetting is true.
78   */
79   make_columns ();
80
81   SCM w = m.smobbed_copy ();
82   command_column_->set_property ("when", w);
83   musical_column_->set_property ("when", w);
84
85   recurse_over_translators (context (), &Translator::start_translation_timestep, DOWN);
86 }
87
88 void
89 Score_engraver::finish ()
90 {
91   if ((breaks_ % 8))
92     progress_indication ("[" + to_string (breaks_) + "]");
93
94   recurse_over_translators (context (), &Translator::finalize, UP);
95 }
96
97 #define MUSIC_FONT "emmentaler-20"
98
99 /*
100   use start/finish?
101 */
102 void
103 Score_engraver::initialize ()
104 {
105   Font_metric *fm = all_fonts_global->find_otf (MUSIC_FONT);
106   if (!fm)
107     {
108       error (_f ("cannot find `%s'", MUSIC_FONT ".otf")
109              + "\n"
110              + _ ("Music font has not been installed properly.")
111              + "\n"
112              + _f ("Search path `%s'", global_path.to_string ().to_str0 ())
113              + "\n"
114              + _ ("Aborting"));
115     }
116
117   pscore_ = new Paper_score (dynamic_cast<Output_def *> (get_output_def ()));
118   scm_gc_unprotect_object (pscore_->self_scm ()); 
119
120   SCM props = updated_grob_properties (context (), ly_symbol2scm ("System"));
121
122   Object_key const *sys_key = context ()->get_grob_key ("System");
123   pscore_->typeset_system (new System (props, sys_key));
124
125   system_ = pscore_->root_system ();
126   make_columns ();
127   system_->set_bound (LEFT, command_column_);
128   command_column_->set_property ("breakable", SCM_BOOL_T);
129
130   Engraver_group_engraver::initialize ();
131 }
132
133 void
134 Score_engraver::finalize ()
135 {
136   Score_translator::finalize ();
137
138   Grob *cc
139     = unsmob_grob (get_property ("currentCommandColumn"));
140   system_->set_bound (RIGHT, cc);
141   cc->set_property ("breakable", SCM_BOOL_T);
142
143   typeset_all ();
144 }
145
146 void
147 Score_engraver::one_time_step ()
148 {
149   if (!to_boolean (get_property ("skipTypesetting")))
150     {
151       recurse_over_translators (context (), &Engraver::process_music, UP);
152       Engraver_group_engraver::do_announces ();
153     }
154
155   recurse_over_translators (context (), &Translator::stop_translation_timestep, UP);
156 }
157
158 void
159 Score_engraver::announce_grob (Grob_info info)
160 {
161   announce_infos_.push (info);
162   pscore_->root_system ()->typeset_grob (info.grob ());
163   elems_.push (info.grob ());
164 }
165
166 void
167 Score_engraver::typeset_all ()
168 {
169   for (int i = 0; i < elems_.size (); i++)
170     {
171       Grob *elem = elems_[i];
172
173       if (dynamic_cast<Item *> (elem))
174         {
175           if ((!elem->get_parent (X_AXIS)
176                || !unsmob_grob (elem->get_object ("axis-group-parent-X")))
177               && elem != command_column_
178               && elem != musical_column_)
179             {
180               bool br = to_boolean (elem->get_property ("breakable"));
181               Axis_group_interface::add_element (br ? command_column_ : musical_column_, elem);
182             }
183         }
184       if (!elem->get_parent (Y_AXIS))
185         Axis_group_interface::add_element (system_, elem);
186     }
187   elems_.clear ();
188 }
189
190 void
191 Score_engraver::stop_translation_timestep ()
192 {
193   // this generates all items.
194   Engraver_group_engraver::stop_translation_timestep ();
195
196   typeset_all ();
197   if (to_boolean (command_column_->get_property ("breakable")))
198     {
199       breaks_++;
200       if (! (breaks_%8))
201         progress_indication ("[" + to_string (breaks_) + "]");
202     }
203
204   command_column_ = 0;
205   musical_column_ = 0;
206 }
207
208 void
209 Score_engraver::set_columns (Paper_column *new_command,
210                              Paper_column *new_musical)
211 {
212   assert (!command_column_ && !musical_column_);
213
214   command_column_ = new_command;
215   musical_column_ = new_musical;
216   if (new_command)
217     {
218       context ()->set_property ("currentCommandColumn", new_command->self_scm ());
219     }
220
221   if (new_musical)
222     {
223       context ()->set_property ("currentMusicalColumn", new_musical->self_scm ());
224     }
225
226   system_->add_column (command_column_);
227   system_->add_column (musical_column_);
228 }
229
230 SCM
231 Score_engraver::get_output ()
232 {
233   Music_output *o = pscore_;
234   return o->self_scm ();
235 }
236
237 bool
238 Score_engraver::try_music (Music *m)
239 {
240   if (Engraver_group_engraver::try_music (m))
241     return true;
242
243   if (m->is_mus_type ("break-event"))
244     {
245       SCM pen = command_column_->get_property ("penalty");
246       Real total_penalty = scm_is_number (pen) ? scm_to_double (pen) : 0.0;
247
248       SCM mpen = m->get_property ("penalty");
249       if (scm_is_number (mpen))
250         total_penalty += scm_to_double (mpen);
251
252       command_column_->set_property ("penalty", scm_make_real (total_penalty));
253
254       /* ugh.  arbitrary, hardcoded */
255       if (total_penalty > 10000.0)
256         forbid_breaks ();
257
258       SCM page_pen = command_column_->get_property ("page-penalty");
259       Real total_pp = scm_is_number (page_pen) ? scm_to_double (page_pen) : 0.0;
260       SCM mpage_pen = m->get_property ("page-penalty");
261       if (scm_is_number (mpage_pen))
262         total_pp += scm_to_double (mpage_pen);
263
264       command_column_->set_property ("page-penalty", scm_make_real (total_pp));
265       return true;
266     }
267   return false;
268 }
269
270 void
271 Score_engraver::forbid_breaks ()
272 {
273   if (command_column_)
274     command_column_->set_property ("breakable", SCM_EOL);
275 }
276
277 void
278 Score_engraver::acknowledge_grob (Grob_info gi)
279 {
280   if (Staff_spacing::has_interface (gi.grob ()))
281     {
282       Pointer_group_interface::add_grob (command_column_,
283                                          ly_symbol2scm ("spacing-wishes"),
284                                          gi.grob ());
285     }
286   if (Note_spacing::has_interface (gi.grob ()))
287     {
288       Pointer_group_interface::add_grob (musical_column_,
289                                          ly_symbol2scm ("spacing-wishes"),
290                                          gi.grob ());
291     }
292
293   if (Axis_group_interface::has_interface (gi.grob ())
294       && gi.grob ()->internal_has_interface (ly_symbol2scm ("vertically-spaceable-interface")))
295     {
296       SCM spaceable = get_property ("verticallySpacedContexts");
297       Context *orig = gi.origin_contexts (this)[0];
298
299       if (scm_memq (ly_symbol2scm (orig->context_name ().to_str0 ()),
300                     spaceable) != SCM_BOOL_F)
301         {
302           Pointer_group_interface::add_grob (system_,
303                                              ly_symbol2scm ("spaceable-staves"),
304                                              gi.grob ());
305         }
306     }
307 }
308
309 ADD_TRANSLATOR (Score_engraver,
310                 /* descr */ "Top level engraver. Takes care of generating columns and the complete  system (ie. System) "
311                 "\n\n "
312                 "This engraver decides whether a column is breakable. The default is "
313                 "that a column is always breakable. However, when every Bar_engraver "
314                 "that does not have a barline at a certain point will call "
315                 "Score_engraver::forbid_breaks to stop linebreaks.  In practice, this "
316                 "means that you can make a breakpoint by creating a barline (assuming "
317                 "that there are no beams or notes that prevent a breakpoint.) ",
318                 /* creats*/ "System PaperColumn NonMusicalPaperColumn",
319                 /* accepts */ "break-event",
320                 /* acks  */ "note-spacing-interface staff-spacing-interface axis-group-interface",
321                 /* reads */ "currentMusicalColumn currentCommandColumn verticallySpacedContexts",
322                 /* write */ "");