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