]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-engraver.cc
* input/test/slur-shape.ly (x): remove file.
[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       
159           String msg= "Grob "
160             + g->name()
161             + " was created too late!";
162           g->programming_error (msg);
163         }
164     }
165   announce_infos_.clear ();
166 }
167
168 void
169 Score_engraver::announce_grob (Grob_info info)
170 {
171   announce_infos_.push (info);
172   pscore_->system_->typeset_grob (info.grob_);
173 }
174
175 void
176 Score_engraver::typeset_grob (Grob *elem)
177 {
178   if (!elem)
179     programming_error ("Score_engraver: empty elt\n");
180   else
181     elems_.push (elem);
182 }
183
184 void
185 Score_engraver::typeset_all ()
186 {
187   for (int i =0; i < elems_.size (); i++) 
188     {
189       Grob * elem = elems_[i];
190       
191       if (Spanner *s = dynamic_cast <Spanner *> (elem))
192         {
193           /*
194             do something sensible if spanner not 
195             spanned on 2 items.
196           */
197           Direction d = LEFT;
198           do {
199             if (!s->get_bound (d))
200               {
201                 s->set_bound (d, command_column_);
202                 /* don't warn for empty/suicided spanners,
203                    it makes real warningsinvisible.
204                    maybe should be junked earlier? */
205                 if (!elem->live())
206                   ; // gdb hook
207                 else
208                   elem->warning (_f ("unbound spanner `%s'", s->name ().to_str0 ()));
209               }
210           }
211           while (flip (&d) != LEFT);
212
213           if (dynamic_cast<Item*> (s->get_parent (Y_AXIS)))
214             programming_error ("Spanner Y-parent is an item.");
215         }
216       else 
217         {
218           if (!elem->get_parent (X_AXIS))
219             {
220               bool br = to_boolean (elem->get_grob_property ("breakable"));
221               Axis_group_interface::add_element (br ? command_column_ : musical_column_, elem);
222
223             }
224         }
225       if (!elem->get_parent (Y_AXIS))
226         Axis_group_interface::add_element (system_, elem);
227     }
228   elems_.clear ();
229 }
230
231 void
232 Score_engraver::stop_translation_timestep ()
233 {
234   // this generates all items.
235   Engraver_group_engraver::stop_translation_timestep ();
236   
237   typeset_all ();
238   if (to_boolean (command_column_->get_grob_property ("breakable")))
239     {
240       breaks_ ++;
241       if (! (breaks_%8))
242         progress_indication ("[" + to_string (breaks_) + "]");
243     }
244
245
246   system_->add_column (command_column_);
247   system_->add_column (musical_column_);
248   
249   command_column_ = 0;
250   musical_column_ = 0;
251 }
252
253 void
254 Score_engraver::set_columns (Paper_column *new_command, 
255                              Paper_column *new_musical)
256 {
257   assert (!command_column_ && !musical_column_);
258
259   command_column_ = new_command;
260   musical_column_ = new_musical;
261   if (new_command)
262     {
263       set_property ("currentCommandColumn", new_command->self_scm ());  
264     }
265   
266   if (new_musical)
267     {
268       set_property ("currentMusicalColumn", new_musical->self_scm ());
269     }
270 }
271
272 Music_output*
273 Score_engraver::get_output ()
274 {
275   Music_output * o = pscore_;
276   pscore_=0;
277   return o;
278 }
279
280 bool
281 Score_engraver::try_music (Music*r)
282 {
283   bool gotcha = Engraver_group_engraver::try_music (r);  
284
285   if (!gotcha && r->is_mus_type ("break-event"))
286     {
287       gotcha = true;
288
289       SCM pen = command_column_->get_grob_property ("penalty");
290       Real total_penalty = gh_number_p (pen)
291         ? gh_scm2double (pen)
292         : 0.0;
293
294       SCM rpen = r->get_mus_property ("penalty");
295       if (gh_number_p (rpen))
296         total_penalty +=  gh_scm2double (rpen);
297           
298       if (total_penalty > 10000.0) //  ugh. arbitrary.
299         forbid_breaks ();
300
301       command_column_->set_grob_property ("penalty",
302                                           gh_double2scm (total_penalty));
303     }
304   return gotcha;
305 }
306
307 /*
308   TODO:  use property Score.breakForbidden = #t
309  */
310 void
311 Score_engraver::forbid_breaks ()
312 {
313   /*
314     result is junked.
315    */
316   if (command_column_)
317     command_column_->set_grob_property ("breakable", SCM_EOL);
318 }
319   
320 void
321 Score_engraver::acknowledge_grob (Grob_info gi)
322 {
323   if (Staff_spacing::has_interface (gi.grob_))
324     {
325       Pointer_group_interface::add_grob (command_column_,
326                                          ly_symbol2scm ("spacing-wishes"),
327                                          gi.grob_);
328     }
329   if (Note_spacing::has_interface (gi.grob_))
330     {
331       Pointer_group_interface::add_grob (musical_column_,
332                                          ly_symbol2scm ("spacing-wishes"),
333                                          gi.grob_);
334     }
335 }
336
337
338
339 ENTER_DESCRIPTION(Score_engraver,
340 /* descr */       "Top level engraver. Takes care of generating columns and the complete  system (ie. System) "
341 "\n\n "
342 "This engraver decides whether a column is breakable. The default is "
343 "that a column is always breakable. However, when every Bar_engraver "
344 "that does not have a barline at a certain point will call "
345 "Score_engraver::forbid_breaks to stop linebreaks.  In practice, this "
346 "means that you can make a breakpoint by creating a barline (assuming "
347 "that there are no beams or notes that prevent a breakpoint.) "
348 ,
349 /* creats*/       "System PaperColumn NonMusicalPaperColumn", 
350 /* accepts */     "break-event",
351 /* acks  */       "note-spacing-interface staff-spacing-interface",
352 /* reads */       "currentMusicalColumn currentCommandColumn",
353 /* write */       "");