]> git.donarmstrong.com Git - lilypond.git/blob - lily/figured-bass-engraver.cc
Fix UTF-8 validation checks
[lilypond.git] / lily / figured-bass-engraver.cc
1 /*
2   figured-bass-engraver.cc -- implement Figured_bass_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2005--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7
8 */
9
10 #include "engraver.hh"
11
12 #include "align-interface.hh"
13 #include "axis-group-interface.hh"
14 #include "context.hh"
15 #include "grob-array.hh"
16 #include "item.hh"
17 #include "pointer-group-interface.hh"
18 #include "spanner.hh"
19 #include "stream-event.hh"
20 #include "text-interface.hh"
21
22 #include "translator.icc"
23
24 struct Figure_group
25 {
26   Spanner *group_;
27   Spanner *continuation_line_;
28   
29   SCM number_;
30   SCM alteration_;
31   SCM augmented_;
32   SCM diminished_;
33   SCM augmented_slash_;
34   SCM text_;
35   
36   Item *figure_item_; 
37   Stream_event *current_event_;
38   bool force_no_continuation_;
39   
40   Figure_group ()
41   {
42     figure_item_ = 0;
43     force_no_continuation_ = false;
44     continuation_line_ = 0;
45     number_ = SCM_EOL;
46     alteration_ = SCM_EOL;
47     augmented_ = SCM_EOL;
48     diminished_ = SCM_EOL;
49     augmented_slash_ = SCM_EOL;
50     text_ = SCM_EOL;
51     group_ = 0;
52     current_event_ = 0;
53   }
54   bool is_continuation () const
55   {
56     return
57       current_event_
58       && !force_no_continuation_
59       && ly_is_equal (number_,
60                       current_event_->get_property ("figure"))
61       && ly_is_equal (alteration_,
62                       current_event_->get_property ("alteration"))
63       && ly_is_equal (augmented_,
64                       current_event_->get_property ("augmented"))
65       && ly_is_equal (diminished_,
66                       current_event_->get_property ("diminished"))
67       && ly_is_equal (augmented_slash_,
68                       current_event_->get_property ("augmented-slash"))
69       && ly_is_equal (text_,
70                       current_event_->get_property ("text"));
71   }
72 };
73
74 struct Figured_bass_engraver : public Engraver
75 {
76   TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
77   void clear_spanners ();
78   void add_brackets ();
79   void create_grobs ();
80
81   void center_continuations (vector<Spanner*> const &consecutive_lines);
82   void center_repeated_continuations ();
83 protected:
84   vector<Figure_group> groups_;
85   Spanner *alignment_;
86   vector<Stream_event *> new_events_;
87   bool continuation_;
88   bool new_event_found_;
89   
90   Moment stop_moment_;
91   Stream_event *rest_event_; 
92
93   DECLARE_TRANSLATOR_LISTENER (rest);
94   DECLARE_TRANSLATOR_LISTENER (bass_figure);
95
96   virtual void derived_mark () const; 
97
98   void start_translation_timestep ();
99   void stop_translation_timestep ();
100   void process_music ();
101 };
102
103 void
104 Figured_bass_engraver::derived_mark () const
105 {
106   for (vsize i = 0; i < groups_.size (); i++)
107     {
108       scm_gc_mark (groups_[i].number_);
109       scm_gc_mark (groups_[i].alteration_);
110       scm_gc_mark (groups_[i].augmented_);
111       scm_gc_mark (groups_[i].diminished_);
112       scm_gc_mark (groups_[i].augmented_slash_);
113       scm_gc_mark (groups_[i].text_);
114     }
115 }
116
117 void
118 Figured_bass_engraver::stop_translation_timestep ()
119 {
120   if (groups_.empty ()
121       || now_mom ().main_part_ < stop_moment_.main_part_
122       || now_mom ().grace_part_ < Rational (0))
123     return ;
124   
125   bool found = false;
126   for (vsize i = 0; !found && i < groups_.size (); i++)
127     found  = found  || groups_[i].current_event_;
128
129   if (!found)
130     clear_spanners ();
131 }
132
133 Figured_bass_engraver::Figured_bass_engraver ()
134 {
135   alignment_ = 0;
136   continuation_ = false;
137   rest_event_ = 0;
138   new_event_found_ = false;
139 }
140
141 void
142 Figured_bass_engraver::start_translation_timestep ()
143 {
144   if (now_mom ().main_part_ < stop_moment_.main_part_
145       || now_mom ().grace_part_ < Rational (0))
146     return ;
147   
148   rest_event_ = 0;
149   new_events_.clear ();
150   for (vsize i = 0; i < groups_.size (); i++)
151     groups_[i].current_event_ = 0;
152
153   continuation_ = false;
154
155   
156 }
157
158 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
159 void
160 Figured_bass_engraver::listen_rest (Stream_event *ev)
161 {
162   if (to_boolean (get_property ("ignoreFiguredBassRest")))
163     {
164       new_event_found_ = true;
165
166       /*
167         No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
168         polyphonic rests.
169        */
170       rest_event_ = ev;
171     }
172 }
173
174 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
175 void
176 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
177 {
178   new_event_found_ = true;
179   Moment stop  = now_mom () + get_event_length (ev, now_mom ());
180   stop_moment_ = max (stop_moment_, stop);
181
182   if (to_boolean (get_property ("useBassFigureExtenders")))
183     {
184       SCM fig = ev->get_property ("figure");
185       SCM txt = ev->get_property ("text");
186       for (vsize i = 0; i < groups_.size (); i++)
187         {
188           if (!groups_[i].current_event_
189               && ly_is_equal (groups_[i].number_, fig)
190               && ly_is_equal (groups_[i].text_, txt))
191             {
192               groups_[i].current_event_ = ev;
193               groups_[i].force_no_continuation_
194                 = to_boolean (ev->get_property ("no-continuation"));
195               continuation_ = true;
196               return; 
197             }
198         }
199     }  
200   new_events_.push_back (ev);
201 }
202
203 void
204 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
205 {
206   if (consecutive_lines.size () == 2)
207     {
208       vector<Grob*> left_figs;
209       for (vsize j = consecutive_lines.size (); j--;)
210         left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
211
212       SCM  ga = Grob_array::make_array ();
213       unsmob_grob_array (ga)->set_array (left_figs);
214
215       for (vsize j = consecutive_lines.size (); j--;)
216         consecutive_lines[j]->set_object ("figures",
217                                           unsmob_grob_array (ga)->smobbed_copy ());
218     }
219 }
220
221 void
222 Figured_bass_engraver::center_repeated_continuations ()
223 {  
224   vector<Spanner*> consecutive_lines;
225   for (vsize i = 0; i <= groups_.size (); i++)
226     {
227       if (i < groups_.size ()
228           && groups_[i].continuation_line_
229           && (consecutive_lines.empty ()
230               || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
231                   == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
232                   && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
233                   == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
234         consecutive_lines.push_back (groups_[i].continuation_line_);      
235       else 
236         {
237           center_continuations (consecutive_lines);
238           consecutive_lines.clear ();
239         }
240     }
241 }
242
243 void
244 Figured_bass_engraver::clear_spanners ()
245 {
246   if (!alignment_)
247     return;
248
249   if (alignment_)
250     {
251       announce_end_grob (alignment_, SCM_EOL);
252       alignment_ = 0;
253     }
254
255   if (to_boolean (get_property ("figuredBassCenterContinuations")))
256     center_repeated_continuations ();
257   
258   for (vsize i = 0; i < groups_.size (); i++)
259     {
260       if (groups_[i].group_)
261         {
262           announce_end_grob (groups_[i].group_ , SCM_EOL);
263           groups_[i].group_ = 0;
264         }
265       
266       if (groups_[i].continuation_line_)
267         {
268           announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
269           groups_[i].continuation_line_ = 0;
270         }
271     }
272
273   /* Check me, groups_.clear () ? */
274 }
275
276 void
277 Figured_bass_engraver::add_brackets ()
278 {
279   vector<Grob*> encompass;
280   bool inside = false;
281   for (vsize i = 0; i < groups_.size (); i ++)
282     {
283       if (!groups_[i].current_event_)
284         continue;
285       
286       if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))       
287         inside = true;
288
289       if (inside && groups_[i].figure_item_)
290         encompass.push_back (groups_[i].figure_item_);
291
292        if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
293         {
294           inside = false;
295
296           Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
297           for (vsize j = 0; j < encompass.size (); j++)
298             {
299               Pointer_group_interface::add_grob (brack,
300                                                  ly_symbol2scm ("elements"),
301                                                  encompass[j]);
302             }
303           encompass.clear ();
304         }
305     }
306 }
307
308 void
309 Figured_bass_engraver::process_music ()
310 {
311   if (alignment_ && !to_boolean (get_property ("useBassFigureExtenders")))
312     clear_spanners ();
313         
314   if (rest_event_)
315     {
316       clear_spanners ();
317       groups_.clear ();
318       return;
319     }
320   
321   if (!continuation_
322       && new_events_.empty ())
323     {
324       clear_spanners ();
325       groups_.clear ();
326       return;
327     }
328
329   if (!new_event_found_)
330     return;
331   
332   new_event_found_ = false;
333
334   /*
335     Don't need to sync alignments, if we're not using extenders. 
336    */
337   bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
338   if (!use_extenders)
339     {
340       clear_spanners ();
341     }
342   
343   if (!continuation_)
344     {
345       clear_spanners ();
346       groups_.clear ();
347     }
348
349   vsize k = 0;
350   for (vsize i = 0; i < new_events_.size (); i++)
351     {
352       while (k < groups_.size ()
353              && groups_[k].current_event_)
354         k++;
355       
356       if (k >= groups_.size ())
357         {
358           Figure_group group;
359           groups_.push_back (group);
360         }
361       
362       groups_[k].current_event_ = new_events_[i];
363       groups_[k].figure_item_ = 0;
364       k++;
365     }
366
367   for (vsize i = 0; i < groups_.size (); i++)
368     {
369       if (!groups_[i].is_continuation ())
370         {
371           groups_[i].number_ = SCM_BOOL_F;
372           groups_[i].alteration_ = SCM_BOOL_F;
373           groups_[i].augmented_ = SCM_BOOL_F;
374           groups_[i].diminished_ = SCM_BOOL_F;
375           groups_[i].augmented_slash_ = SCM_BOOL_F;
376           groups_[i].text_ = SCM_BOOL_F;
377         }
378     }
379
380   if (use_extenders)
381     {
382       vector<int> junk_continuations;
383       for (vsize i = 0; i < groups_.size (); i++)
384         {
385           Figure_group &group = groups_[i];
386
387           if (group.is_continuation ())
388             {
389               if (!group.continuation_line_)
390                 {
391                   Spanner * line
392                     = make_spanner ("BassFigureContinuation", SCM_EOL);
393                   Item * item = group.figure_item_;
394                   group.continuation_line_ = line;
395                   line->set_bound (LEFT, item);
396
397                   /*
398                     Don't add as child. This will cache the wrong
399                     (pre-break) stencil when callbacks are triggered.
400                   */
401                   line->set_parent (group.group_, Y_AXIS);
402                   Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
403
404                   group.figure_item_ = 0;
405                 }
406             }
407           else if (group.continuation_line_) 
408             junk_continuations.push_back (i); 
409         }
410
411       /*
412         Ugh, repeated code.
413        */
414       vector<Spanner*> consecutive;
415       if (to_boolean (get_property ("figuredBassCenterContinuations")))
416         {
417           for (vsize i = 0; i <= junk_continuations.size (); i++)
418             {
419               if (i < junk_continuations.size ()
420                   && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
421                 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
422               else 
423                 {
424                   center_continuations (consecutive);
425                   consecutive.clear ();
426                   if (i < junk_continuations.size ())
427                     consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
428                 }
429             }
430         }
431       for (vsize i = 0; i < junk_continuations.size (); i++)
432         groups_[junk_continuations[i]].continuation_line_ = 0;
433     }
434   
435   create_grobs ();
436   add_brackets ();
437 }
438
439 void
440 Figured_bass_engraver::create_grobs () 
441 {
442   Grob *muscol
443     = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
444   if (!alignment_)
445     {
446       alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
447       alignment_->set_bound (LEFT, muscol);
448     }
449   alignment_->set_bound (RIGHT, muscol);
450
451   SCM proc = get_property ("figuredBassFormatter");
452   for (vsize i = 0; i < groups_.size (); i++)
453     {
454       Figure_group &group = groups_[i];
455       
456       if (group.current_event_)
457         {
458           Item *item
459             = make_item ("BassFigure",
460                          group.current_event_->self_scm ());
461
462           
463           SCM fig = group.current_event_->get_property ("figure");
464           if (!group.group_)
465             {
466               group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
467               group.group_->set_bound (LEFT, muscol);
468               Align_interface::add_element (alignment_,
469                                             group.group_);
470             }
471
472           if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
473             {
474               item->set_property ("transparent", SCM_BOOL_T); 
475               item->set_property ("implicit", SCM_BOOL_T);
476             }
477           
478           group.number_ = fig;
479           group.alteration_ = group.current_event_->get_property ("alteration");
480           group.augmented_ = group.current_event_->get_property ("augmented");
481           group.diminished_ = group.current_event_->get_property ("diminished");
482           group.augmented_slash_ = group.current_event_->get_property ("augmented-slash");
483           group.text_ = group.current_event_->get_property ("text");
484
485           SCM text = group.text_;
486           if (!Text_interface::is_markup (text)
487               && ly_is_procedure (proc))
488             {
489               text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
490                                  context ()->self_scm ());
491             }
492
493           item->set_property ("text", text);
494           
495           Axis_group_interface::add_element (group.group_, item);
496           group.figure_item_ = item;
497         }
498
499       if (group.continuation_line_)
500         {
501           /*
502             UGH should connect to the bass staff, and get the note heads. 
503           */
504           group.figure_item_->set_property ("transparent", SCM_BOOL_T);
505           group.continuation_line_->set_bound (RIGHT, group.figure_item_);
506         }
507       
508       if (groups_[i].group_)
509         groups_[i].group_->set_bound (RIGHT, muscol);
510
511     }
512
513 }
514
515 ADD_TRANSLATOR (Figured_bass_engraver,
516                 /* doc */
517                 "Make figured bass numbers.",
518
519                 /* create */
520                 "BassFigure "
521                 "BassFigureAlignment "
522                 "BassFigureBracket "
523                 "BassFigureContinuation "
524                 "BassFigureLine ",
525
526                 /* read */
527                 "figuredBassAlterationDirection "
528                 "figuredBassCenterContinuations "
529                 "figuredBassFormatter "
530                 "implicitBassFigures "
531                 "useBassFigureExtenders "
532                 "ignoreFiguredBassRest ",
533
534                 /* write */
535                 ""
536                 );