]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-engraver.cc
release commit
[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--2003 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
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;
53       i1.grob_ = command_column_;
54       i1.origin_trans_ = this;
55   
56       announce_grob (i1);
57
58       Grob_info i2;
59       i2.grob_ = musical_column_;
60       i2.origin_trans_ = this;
61
62       announce_grob (i2);
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   finalize ();
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_default_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   
149   apply_finalizations ();
150   check_removal ();
151
152
153   for (int i = announce_infos_.size(); i--;)
154     {
155       Grob *g = announce_infos_[i].grob_;
156       if (!dynamic_cast<Paper_column*> (g)) // ugh.
157         {
158           String msg= "Grob "
159             + g->name()
160             + " was created too late!";
161           g->programming_error (msg);
162         }
163     }
164   announce_infos_.clear ();
165 }
166
167 void
168 Score_engraver::announce_grob (Grob_info info)
169 {
170   announce_infos_.push (info);
171   pscore_->system_->typeset_grob (info.grob_);
172 }
173
174 void
175 Score_engraver::typeset_grob (Grob *elem)
176 {
177   if (!elem)
178     programming_error ("Score_engraver: empty elt\n");
179   else
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 && r->is_mus_type ("break-event"))
285     {
286       gotcha = true;
287
288       SCM pen = command_column_->get_grob_property ("penalty");
289       Real total_penalty = gh_number_p (pen)
290         ? gh_scm2double (pen)
291         : 0.0;
292
293       SCM rpen = r->get_mus_property ("penalty");
294       if (gh_number_p (rpen))
295         total_penalty +=  gh_scm2double (rpen);
296           
297       if (total_penalty > 10000.0) //  ugh. arbitrary.
298         forbid_breaks ();
299
300       command_column_->set_grob_property ("penalty",
301                                           gh_double2scm (total_penalty));
302     }
303   return gotcha;
304 }
305
306 /*
307   TODO:  use property Score.breakForbidden = #t
308  */
309 void
310 Score_engraver::forbid_breaks ()
311 {
312   /*
313     result is junked.
314    */
315   if (command_column_)
316     command_column_->set_grob_property ("breakable", SCM_EOL);
317 }
318   
319 void
320 Score_engraver::acknowledge_grob (Grob_info gi)
321 {
322   if (Staff_spacing::has_interface (gi.grob_))
323     {
324       Pointer_group_interface::add_grob (command_column_,
325                                          ly_symbol2scm ("spacing-wishes"),
326                                          gi.grob_);
327     }
328   if (Note_spacing::has_interface (gi.grob_))
329     {
330       Pointer_group_interface::add_grob (musical_column_,
331                                          ly_symbol2scm ("spacing-wishes"),
332                                          gi.grob_);
333     }
334 }
335
336
337
338 ENTER_DESCRIPTION(Score_engraver,
339 /* descr */       "Top level engraver. Takes care of generating columns and the complete  system (ie. System) "
340 "\n\n "
341 "This engraver decides whether a column is breakable. The default is "
342 "that a column is always breakable. However, when every Bar_engraver "
343 "that does not have a barline at a certain point will call "
344 "Score_engraver::forbid_breaks to stop linebreaks.  In practice, this "
345 "means that you can make a breakpoint by creating a barline (assuming "
346 "that there are no beams or notes that prevent a breakpoint.) "
347 ,
348 /* creats*/       "System PaperColumn NonMusicalPaperColumn", 
349 /* accepts */     "break-event",
350 /* acks  */       "note-spacing-interface staff-spacing-interface",
351 /* reads */       "currentMusicalColumn currentCommandColumn",
352 /* write */       "");