]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-engraver.cc
693158620ba71fbc41889ffb947651fdf95b8a3c
[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--2002 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 "item.hh"
15 #include "score-engraver.hh"
16 #include "paper-score.hh"
17 #include "paper-column.hh"
18 #include "command-request.hh"
19 #include "paper-def.hh"
20 #include "axis-group-interface.hh"
21 #include "translator-def.hh"
22
23 #include "staff-spacing.hh"
24 #include "note-spacing.hh"
25
26 /*
27   TODO: the column creation logic is rather hairy. Revise it.
28  */
29 Score_engraver::Score_engraver ()
30 {
31   system_ =0;
32   command_column_ =0;
33   musical_column_ =0;
34   breaks_ =0;
35   pscore_ = 0;
36 }
37
38 void
39 Score_engraver::make_columns ()
40 {
41   /*
42     ugh.
43    */
44   if (!command_column_)
45     {
46       set_columns (new Paper_column (get_property ("NonMusicalPaperColumn")),
47                    new Paper_column (get_property ("PaperColumn")));
48   
49       command_column_->set_grob_property ("breakable", SCM_BOOL_T);
50
51
52       Grob_info i1 (command_column_);
53       i1.origin_trans_ = this;
54   
55       Grob_info i2 (musical_column_);
56       i2.origin_trans_ = this;
57
58   
59       announce_grob (i1);
60       announce_grob (i2);
61
62       
63     }
64 }
65
66 void
67 Score_engraver::prepare (Moment w)
68 {
69   Global_translator::prepare (w);
70
71   /*
72     TODO: don't make columns when skipTypesetting is true.
73    */
74   make_columns ();
75   
76   command_column_->set_grob_property ("when", now_mom_.smobbed_copy ());
77   musical_column_->set_grob_property ("when", now_mom_.smobbed_copy ());
78
79   
80   Translator_group::start_translation_timestep();
81 }
82
83 void
84 Score_engraver::finish ()
85 {
86   if ((breaks_%8))
87     progress_indication ("[" + to_string (breaks_) + "]");
88    
89   check_removal ();
90   removal_processing ();
91
92 }
93
94 /*
95   use start/finish?
96  */
97 void
98 Score_engraver::initialize ()
99 {
100   Font_metric *fm = all_fonts_global->find_afm("feta20");
101   if (!fm)
102     error (_f ("can't find `%s'", "feta20.afm")
103            + "\n" +_ ("Fonts have not been installed properly.  Aborting"));
104    
105   unsmob_translator_def (definition_)->apply_property_operations (this);
106
107   assert (dynamic_cast<Paper_def *> (output_def_));
108   assert (!daddy_trans_);
109   pscore_ = new Paper_score;
110   pscore_->paper_ = dynamic_cast<Paper_def*> (output_def_);
111
112   SCM props = get_property ("System");
113
114   pscore_->typeset_line (new System (props));
115   
116   make_columns ();
117   system_ = pscore_->system_;
118   system_->set_bound (LEFT, command_column_);
119   command_column_->set_grob_property ("breakable", SCM_BOOL_T);
120
121   Engraver_group_engraver::initialize ();
122 }
123
124
125 void
126 Score_engraver::finalize ()
127 {
128   Engraver_group_engraver::finalize ();
129
130   Grob * cc
131     = unsmob_grob (get_property ("currentCommandColumn"));
132   system_->set_bound (RIGHT, cc);
133   cc->set_grob_property ("breakable", SCM_BOOL_T);
134   
135   typeset_all ();
136 }
137
138 void
139 Score_engraver::one_time_step ()
140 {
141   if (!to_boolean (get_property ("skipTypesetting")))
142     {
143       process_music ();
144       do_announces ();
145     }
146   
147   stop_translation_timestep ();
148   check_removal ();
149
150
151   for (int i = announce_infos_.size(); i--;)
152     {
153       Grob *g = announce_infos_[i].grob_;
154       if (!dynamic_cast<Paper_column*> (g)) // ugh.
155         {
156       
157           String msg= "Grob "
158             + g->name()
159             + " was created too late!";
160           g->programming_error (msg);
161         }
162     }
163   announce_infos_.clear ();
164 }
165
166 void
167 Score_engraver::announce_grob (Grob_info info)
168 {
169   announce_infos_.push (info);
170   pscore_->system_->typeset_grob (info.grob_);
171 }
172
173 void
174 Score_engraver::typeset_grob (Grob *elem)
175 {
176   if (!elem)
177     programming_error ("Score_engraver: empty elt\n");
178   else
179
180     elems_.push (elem);
181 }
182
183 void
184 Score_engraver::typeset_all ()
185 {
186   for (int i =0; i < elems_.size (); i++) 
187     {
188       Grob * elem = elems_[i];
189       
190       if (Spanner *s = dynamic_cast <Spanner *> (elem))
191         {
192           /*
193             do something sensible if spanner not 
194             spanned on 2 items.
195           */
196           Direction d = LEFT;
197           do {
198             if (!s->get_bound (d))
199               {
200                 s->set_bound (d, command_column_);
201                 /* don't warn for empty/suicided spanners,
202                    it makes real warningsinvisible.
203                    maybe should be junked earlier? */
204                 if (!elem->live())
205                   ; // gdb hook
206                 else
207                   elem->warning (_f ("unbound spanner `%s'", s->name ().to_str0 ()));
208               }
209           }
210           while (flip (&d) != LEFT);
211
212           if (dynamic_cast<Item*> (s->get_parent (Y_AXIS)))
213             programming_error ("Spanner Y-parent is an item.");
214         }
215       else 
216         {
217           if (!elem->get_parent (X_AXIS))
218             {
219               bool br = to_boolean (elem->get_grob_property ("breakable"));
220               Axis_group_interface::add_element (br ? command_column_ : musical_column_, elem);
221
222             }
223         }
224       if (!elem->get_parent (Y_AXIS))
225         Axis_group_interface::add_element (system_, elem);
226     }
227   elems_.clear ();
228 }
229
230 void
231 Score_engraver::stop_translation_timestep ()
232 {
233   // this generates all items.
234   Engraver_group_engraver::stop_translation_timestep ();
235   
236   typeset_all ();
237   if (to_boolean (command_column_->get_grob_property ("breakable")))
238     {
239       breaks_ ++;
240       if (! (breaks_%8))
241         progress_indication ("[" + to_string (breaks_) + "]");
242     }
243
244
245   system_->add_column (command_column_);
246   system_->add_column (musical_column_);
247   
248   command_column_ = 0;
249   musical_column_ = 0;
250 }
251
252 void
253 Score_engraver::set_columns (Paper_column *new_command, 
254                              Paper_column *new_musical)
255 {
256   assert (!command_column_ && !musical_column_);
257
258   command_column_ = new_command;
259   musical_column_ = new_musical;
260   if (new_command)
261     {
262       set_property ("currentCommandColumn", new_command->self_scm ());  
263     }
264   
265   if (new_musical)
266     {
267       set_property ("currentMusicalColumn", new_musical->self_scm ());
268     }
269 }
270
271 Music_output*
272 Score_engraver::get_output ()
273 {
274   Music_output * o = pscore_;
275   pscore_=0;
276   return o;
277 }
278
279 bool
280 Score_engraver::try_music (Music*r)
281 {
282   bool gotcha = Engraver_group_engraver::try_music (r);  
283
284   if (!gotcha)
285     {
286       if (Break_req* b = dynamic_cast<Break_req *> (r))
287         {
288           gotcha = true;
289
290
291           SCM pen = command_column_->get_grob_property ("penalty");
292           Real total_penalty = gh_number_p (pen)
293             ? gh_scm2double (pen)
294             : 0.0;
295
296           SCM rpen = b->get_mus_property ("penalty");
297           if (gh_number_p (rpen))
298             total_penalty +=  gh_scm2double (rpen);
299           
300           if (total_penalty > 10000.0) //  ugh. arbitrary.
301             forbid_breaks ();
302
303           command_column_->set_grob_property ("penalty",
304                                                gh_double2scm (total_penalty));
305         }
306     }
307    return gotcha;
308 }
309
310 /*
311   TODO:  use property Score.breakForbidden = #t
312  */
313 void
314 Score_engraver::forbid_breaks ()
315 {
316   /*
317     result is junked.
318    */
319   if (command_column_)
320     command_column_->set_grob_property ("breakable", SCM_EOL);
321 }
322   
323 void
324 Score_engraver::acknowledge_grob (Grob_info gi)
325 {
326   if (Staff_spacing::has_interface (gi.grob_))
327     {
328       Pointer_group_interface::add_grob (command_column_,
329                                          ly_symbol2scm ("spacing-wishes"),
330                                          gi.grob_);
331     }
332   if (Note_spacing::has_interface (gi.grob_))
333     {
334       Pointer_group_interface::add_grob (musical_column_,
335                                          ly_symbol2scm ("spacing-wishes"),
336                                          gi.grob_);
337     }
338 }
339
340
341
342 ENTER_DESCRIPTION(Score_engraver,
343 /* descr */       "Top level engraver. Takes care of generating columns and the complete  system (ie. System)
344
345
346 This engraver decides whether a column is breakable. The default is
347 that a column is always breakable. However, when every Bar_engraver
348 that does not have a barline at a certain point will call
349 Score_engraver::forbid_breaks to stop linebreaks.  In practice, this
350 means that you can make a breakpoint by creating a barline (assuming
351 that there are no beams or notes that prevent a breakpoint.)
352
353
354 ",
355 /* creats*/       "System PaperColumn NonMusicalPaperColumn",
356 /* acks  */       "note-spacing-interface staff-spacing-interface",
357 /* reads */       "currentMusicalColumn currentCommandColumn",
358 /* write */       "");