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