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