]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-engraver.cc
2003 -> 2004
[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--2004 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 "context-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   use start/finish?
95  */
96 void
97 Score_engraver::initialize ()
98 {
99   Font_metric *fm = all_fonts_global->find_afm("feta20");
100   if (!fm)
101     error (_f ("can't find `%s'", "feta20.afm")
102            + "\n" +_ ("Fonts have not been installed properly.  Aborting"));
103    
104   unsmob_context_def (definition_)->apply_default_property_operations (this);
105
106   assert (dynamic_cast<Paper_def *> (output_def_));
107   assert (!daddy_trans_);
108   pscore_ = new Paper_score;
109   pscore_->paper_ = dynamic_cast<Paper_def*> (output_def_);
110
111   SCM props = get_property ("System");
112
113   pscore_->typeset_line (new System (props));
114   
115   make_columns ();
116   system_ = pscore_->system_;
117   system_->set_bound (LEFT, command_column_);
118   command_column_->set_grob_property ("breakable", SCM_BOOL_T);
119
120   Engraver_group_engraver::initialize ();
121 }
122
123
124 void
125 Score_engraver::finalize ()
126 {
127   Engraver_group_engraver::finalize ();
128
129   Grob * cc
130     = unsmob_grob (get_property ("currentCommandColumn"));
131   system_->set_bound (RIGHT, cc);
132   cc->set_grob_property ("breakable", SCM_BOOL_T);
133   
134   typeset_all ();
135 }
136
137 void
138 Score_engraver::one_time_step ()
139 {
140   if (!to_boolean (get_property ("skipTypesetting")))
141     {
142       process_music ();
143       do_announces ();
144     }
145   
146   stop_translation_timestep ();
147   
148   apply_finalizations ();
149   check_removal ();
150
151
152   for (int i = announce_infos_.size(); i--;)
153     {
154       Grob *g = announce_infos_[i].grob_;
155       if (!dynamic_cast<Paper_column*> (g)) // ugh.
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     elems_.push (elem);
180 }
181
182 void
183 Score_engraver::typeset_all ()
184 {
185   for (int i =0; i < elems_.size (); i++) 
186     {
187       Grob * elem = elems_[i];
188       
189       if (Spanner *s = dynamic_cast <Spanner *> (elem))
190         {
191           /*
192             do something sensible if spanner not 
193             spanned on 2 items.
194           */
195           Direction d = LEFT;
196           do {
197             if (!s->get_bound (d))
198               {
199                 s->set_bound (d, command_column_);
200                 /* don't warn for empty/suicided spanners,
201                    it makes real warningsinvisible.
202                    maybe should be junked earlier? */
203                 if (!elem->live())
204                   ; // gdb hook
205                 else
206                   elem->warning (_f ("unbound spanner `%s'", s->name ().to_str0 ()));
207               }
208           }
209           while (flip (&d) != LEFT);
210
211           if (dynamic_cast<Item*> (s->get_parent (Y_AXIS)))
212             programming_error ("Spanner Y-parent is an item.");
213         }
214       else 
215         {
216           if (!elem->get_parent (X_AXIS))
217             {
218               bool br = to_boolean (elem->get_grob_property ("breakable"));
219               Axis_group_interface::add_element (br ? command_column_ : musical_column_, elem);
220
221             }
222         }
223       if (!elem->get_parent (Y_AXIS))
224         Axis_group_interface::add_element (system_, elem);
225     }
226   elems_.clear ();
227 }
228
229 void
230 Score_engraver::stop_translation_timestep ()
231 {
232   // this generates all items.
233   Engraver_group_engraver::stop_translation_timestep ();
234   
235   typeset_all ();
236   if (to_boolean (command_column_->get_grob_property ("breakable")))
237     {
238       breaks_ ++;
239       if (! (breaks_%8))
240         progress_indication ("[" + to_string (breaks_) + "]");
241     }
242
243
244   system_->add_column (command_column_);
245   system_->add_column (musical_column_);
246   
247   command_column_ = 0;
248   musical_column_ = 0;
249 }
250
251 void
252 Score_engraver::set_columns (Paper_column *new_command, 
253                              Paper_column *new_musical)
254 {
255   assert (!command_column_ && !musical_column_);
256
257   command_column_ = new_command;
258   musical_column_ = new_musical;
259   if (new_command)
260     {
261       set_property ("currentCommandColumn", new_command->self_scm ());  
262     }
263   
264   if (new_musical)
265     {
266       set_property ("currentMusicalColumn", new_musical->self_scm ());
267     }
268 }
269
270 Music_output*
271 Score_engraver::get_output ()
272 {
273   Music_output * o = pscore_;
274   pscore_=0;
275   return o;
276 }
277
278 bool
279 Score_engraver::try_music (Music*r)
280 {
281   bool gotcha = Engraver_group_engraver::try_music (r);  
282
283   if (!gotcha && r->is_mus_type ("break-event"))
284     {
285       gotcha = true;
286
287       SCM pen = command_column_->get_grob_property ("penalty");
288       Real total_penalty = gh_number_p (pen)
289         ? gh_scm2double (pen)
290         : 0.0;
291
292       SCM rpen = r->get_mus_property ("penalty");
293       if (gh_number_p (rpen))
294         total_penalty +=  gh_scm2double (rpen);
295           
296       if (total_penalty > 10000.0) //  ugh. arbitrary.
297         forbid_breaks ();
298
299       command_column_->set_grob_property ("penalty",
300                                           gh_double2scm (total_penalty));
301     }
302   return gotcha;
303 }
304
305 /*
306   TODO:  use property Score.breakForbidden = #t
307  */
308 void
309 Score_engraver::forbid_breaks ()
310 {
311   /*
312     result is junked.
313    */
314   if (command_column_)
315     command_column_->set_grob_property ("breakable", SCM_EOL);
316 }
317   
318 void
319 Score_engraver::acknowledge_grob (Grob_info gi)
320 {
321   if (Staff_spacing::has_interface (gi.grob_))
322     {
323       Pointer_group_interface::add_grob (command_column_,
324                                          ly_symbol2scm ("spacing-wishes"),
325                                          gi.grob_);
326     }
327   if (Note_spacing::has_interface (gi.grob_))
328     {
329       Pointer_group_interface::add_grob (musical_column_,
330                                          ly_symbol2scm ("spacing-wishes"),
331                                          gi.grob_);
332     }
333 }
334
335
336
337 ENTER_DESCRIPTION(Score_engraver,
338 /* descr */       "Top level engraver. Takes care of generating columns and the complete  system (ie. System) "
339 "\n\n "
340 "This engraver decides whether a column is breakable. The default is "
341 "that a column is always breakable. However, when every Bar_engraver "
342 "that does not have a barline at a certain point will call "
343 "Score_engraver::forbid_breaks to stop linebreaks.  In practice, this "
344 "means that you can make a breakpoint by creating a barline (assuming "
345 "that there are no beams or notes that prevent a breakpoint.) "
346 ,
347 /* creats*/       "System PaperColumn NonMusicalPaperColumn", 
348 /* accepts */     "break-event",
349 /* acks  */       "note-spacing-interface staff-spacing-interface",
350 /* reads */       "currentMusicalColumn currentCommandColumn",
351 /* write */       "");