]> git.donarmstrong.com Git - lilypond.git/blob - lily/figured-bass-engraver.cc
Issue 4550 (1/2) Avoid "using namespace std;" in included files
[lilypond.git] / lily / figured-bass-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2005--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "engraver.hh"
22
23 #include "align-interface.hh"
24 #include "axis-group-interface.hh"
25 #include "context.hh"
26 #include "grob-array.hh"
27 #include "item.hh"
28 #include "pointer-group-interface.hh"
29 #include "spanner.hh"
30 #include "stream-event.hh"
31 #include "text-interface.hh"
32
33 #include "translator.icc"
34
35 using std::vector;
36
37 struct Figure_group
38 {
39   Spanner *group_;
40   Spanner *continuation_line_;
41
42   SCM number_;
43   SCM alteration_;
44   SCM augmented_;
45   SCM diminished_;
46   SCM augmented_slash_;
47   SCM text_;
48
49   Item *figure_item_;
50   Stream_event *current_event_;
51
52   Figure_group ()
53   {
54     figure_item_ = 0;
55     continuation_line_ = 0;
56     reset_figure ();
57     group_ = 0;
58     current_event_ = 0;
59   }
60   /* Reset (or init) all figure information to FALSE */
61   void reset_figure ()
62   {
63     number_ = SCM_BOOL_F;
64     alteration_ = SCM_BOOL_F;
65     augmented_ = SCM_BOOL_F;
66     diminished_ = SCM_BOOL_F;
67     augmented_slash_ = SCM_BOOL_F;
68     text_ = SCM_BOOL_F;
69   }
70   /* Mark the members of the struct as used for the GUILE Garbage Collection */
71   void gc_mark () const
72   {
73     scm_gc_mark (number_);
74     scm_gc_mark (alteration_);
75     scm_gc_mark (augmented_);
76     scm_gc_mark (diminished_);
77     scm_gc_mark (augmented_slash_);
78     scm_gc_mark (text_);
79   }
80   bool group_is_equal_to (Stream_event *evt) const
81   {
82     return
83       ly_is_equal (number_, evt->get_property ("figure"))
84       && ly_is_equal (alteration_, evt->get_property ("alteration"))
85       && ly_is_equal (augmented_, evt->get_property ("augmented"))
86       && ly_is_equal (diminished_, evt->get_property ("diminished"))
87       && ly_is_equal (augmented_slash_, evt->get_property ("augmented-slash"))
88       && ly_is_equal (text_, evt->get_property ("text"));
89   }
90   bool is_continuation () const
91   {
92     return
93       current_event_
94       && group_is_equal_to (current_event_);
95   }
96   void assign_from_event (Stream_event *currevt, Item *item)
97   {
98     number_ = current_event_->get_property ("figure");
99     alteration_ = currevt->get_property ("alteration");
100     augmented_ = currevt->get_property ("augmented");
101     diminished_ = currevt->get_property ("diminished");
102     augmented_slash_ = currevt->get_property ("augmented-slash");
103     text_ = currevt->get_property ("text");
104     figure_item_ = item;
105   }
106 };
107
108 struct Figured_bass_engraver : public Engraver
109 {
110   TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
111   void clear_spanners ();
112   void add_brackets ();
113   void create_grobs ();
114
115   void center_continuations (vector<Spanner *> const &consecutive_lines);
116   void center_repeated_continuations ();
117 protected:
118   vector<Figure_group> groups_;
119   Spanner *alignment_;
120   vector<Stream_event *> new_events_;
121   bool continuation_;
122   bool new_event_found_;
123
124   Moment stop_moment_;
125   bool have_rest_;
126
127   DECLARE_TRANSLATOR_LISTENER (rest);
128   DECLARE_TRANSLATOR_LISTENER (bass_figure);
129
130   virtual void derived_mark () const;
131
132   void start_translation_timestep ();
133   void stop_translation_timestep ();
134   void process_music ();
135 };
136
137 Figured_bass_engraver::Figured_bass_engraver ()
138 {
139   alignment_ = 0;
140   continuation_ = false;
141   have_rest_ = 0;
142   new_event_found_ = false;
143 }
144
145 void
146 Figured_bass_engraver::derived_mark () const
147 {
148   for (vsize i = 0; i < groups_.size (); i++)
149     {
150       groups_[i].gc_mark ();
151     }
152 }
153
154 void
155 Figured_bass_engraver::start_translation_timestep ()
156 {
157   if (now_mom ().main_part_ < stop_moment_.main_part_
158       || now_mom ().grace_part_ < Rational (0))
159     return;
160
161   have_rest_ = 0;
162   new_events_.clear ();
163   for (vsize i = 0; i < groups_.size (); i++)
164     groups_[i].current_event_ = 0;
165
166   continuation_ = false;
167 }
168
169 void
170 Figured_bass_engraver::stop_translation_timestep ()
171 {
172   if (groups_.empty ()
173       || now_mom ().main_part_ < stop_moment_.main_part_
174       || now_mom ().grace_part_ < Rational (0))
175     return;
176
177   bool found = false;
178   for (vsize i = 0; !found && i < groups_.size (); i++)
179     found = found || groups_[i].current_event_;
180
181   if (!found)
182     clear_spanners ();
183 }
184
185 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
186 void
187 Figured_bass_engraver::listen_rest (Stream_event *)
188 {
189   have_rest_ = true;
190 }
191
192 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
193 void
194 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
195 {
196   new_event_found_ = true;
197   Moment stop = now_mom () + get_event_length (ev, now_mom ());
198   stop_moment_ = max (stop_moment_, stop);
199
200   // Handle no-continuation here, don't even add it to the already existing
201   // spanner... This fixes some layout issues (figure will be placed separately)
202   bool no_continuation = to_boolean (ev->get_property ("no-continuation"));
203   if (to_boolean (get_property ("useBassFigureExtenders")) && !no_continuation)
204     {
205       for (vsize i = 0; i < groups_.size (); i++)
206         {
207           if (!groups_[i].current_event_
208               && groups_[i].group_is_equal_to (ev))
209             {
210               groups_[i].current_event_ = ev;
211               continuation_ = true;
212               return;
213             }
214         }
215     }
216   new_events_.push_back (ev);
217 }
218
219 void
220 Figured_bass_engraver::center_continuations (vector<Spanner *> const &consecutive_lines)
221 {
222   vector<Grob *> left_figs;
223   for (vsize j = consecutive_lines.size (); j--;)
224     left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
225
226   SCM ga = Grob_array::make_array ();
227   unsmob<Grob_array> (ga)->set_array (left_figs);
228
229   for (vsize j = consecutive_lines.size (); j--;)
230     consecutive_lines[j]->set_object ("figures",
231                                       unsmob<Grob_array> (ga)->smobbed_copy ());
232 }
233
234 void
235 Figured_bass_engraver::center_repeated_continuations ()
236 {
237   vector<Spanner *> consecutive_lines;
238   for (vsize i = 0; i <= groups_.size (); i++)
239     {
240       if (i < groups_.size ()
241           && groups_[i].continuation_line_
242           && (consecutive_lines.empty ()
243               || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
244                   == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
245                   && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
246                   == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
247         consecutive_lines.push_back (groups_[i].continuation_line_);
248       else
249         {
250           center_continuations (consecutive_lines);
251           consecutive_lines.clear ();
252         }
253     }
254 }
255
256 void
257 Figured_bass_engraver::clear_spanners ()
258 {
259   if (!alignment_)
260     return;
261
262   announce_end_grob (alignment_, SCM_EOL);
263   alignment_ = 0;
264
265   if (to_boolean (get_property ("figuredBassCenterContinuations")))
266     center_repeated_continuations ();
267
268   for (vsize i = 0; i < groups_.size (); i++)
269     {
270       if (groups_[i].group_)
271         {
272           announce_end_grob (groups_[i].group_, SCM_EOL);
273           groups_[i].group_ = 0;
274         }
275
276       if (groups_[i].continuation_line_)
277         {
278           announce_end_grob (groups_[i].continuation_line_, SCM_EOL);
279           groups_[i].continuation_line_ = 0;
280         }
281     }
282
283   /* Check me, groups_.clear () ? */
284 }
285
286 void
287 Figured_bass_engraver::process_music ()
288 {
289   bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
290   if (alignment_ && !use_extenders)
291     clear_spanners ();
292
293   // If we have a rest, or we have no new or continued events, clear all spanners
294   bool ignore_rest = to_boolean (get_property ("ignoreFiguredBassRest"));
295   if ((ignore_rest && have_rest_)
296       || (!continuation_ && new_events_.empty ()))
297     {
298       clear_spanners ();
299       groups_.clear ();
300       return;
301     }
302
303   if (!new_event_found_)
304     return;
305
306   new_event_found_ = false;
307
308   /*
309     Don't need to sync alignments, if we're not using extenders.
310    */
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].reset_figure ();
336       groups_[k].current_event_ = new_events_[i];
337       groups_[k].figure_item_ = 0;
338       k++;
339     }
340
341   for (vsize i = 0; i < groups_.size (); i++)
342     {
343       if (!groups_[i].is_continuation ())
344         {
345           groups_[i].reset_figure ();
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
361                     = make_spanner ("BassFigureContinuation", SCM_EOL);
362                   Item *item = group.figure_item_;
363                   group.continuation_line_ = line;
364                   line->set_bound (LEFT, item);
365
366                   /*
367                     Don't add as child. This will cache the wrong
368                     (pre-break) stencil when callbacks are triggered.
369                   */
370                   line->set_parent (group.group_, Y_AXIS);
371                   Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
372
373                   group.figure_item_ = 0;
374                 }
375             }
376           else if (group.continuation_line_)
377             junk_continuations.push_back (i);
378         }
379
380       /*
381         Ugh, repeated code.
382        */
383       vector<Spanner *> consecutive;
384       if (to_boolean (get_property ("figuredBassCenterContinuations")))
385         {
386           for (vsize i = 0; i <= junk_continuations.size (); i++)
387             {
388               if (i < junk_continuations.size ()
389                   && (i == 0 || junk_continuations[i - 1] == junk_continuations[i] - 1))
390                 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
391               else
392                 {
393                   center_continuations (consecutive);
394                   consecutive.clear ();
395                   if (i < junk_continuations.size ())
396                     consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
397                 }
398             }
399         }
400       for (vsize i = 0; i < junk_continuations.size (); i++)
401         groups_[junk_continuations[i]].continuation_line_ = 0;
402     }
403
404   create_grobs ();
405   add_brackets ();
406 }
407
408 void
409 Figured_bass_engraver::create_grobs ()
410 {
411   Grob *muscol
412     = unsmob<Item> (get_property ("currentMusicalColumn"));
413   if (!alignment_)
414     {
415       alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
416       alignment_->set_bound (LEFT, muscol);
417     }
418   alignment_->set_bound (RIGHT, muscol);
419
420   SCM proc = get_property ("figuredBassFormatter");
421   for (vsize i = 0; i < groups_.size (); i++)
422     {
423       Figure_group &group = groups_[i];
424
425       if (group.current_event_)
426         {
427           Item *item
428             = make_item ("BassFigure", group.current_event_->self_scm ());
429           group.assign_from_event (group.current_event_, item);
430
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_, group.group_);
436             }
437
438           if (scm_is_true (scm_memq (group.number_, get_property ("implicitBassFigures"))))
439             {
440               item->set_property ("transparent", SCM_BOOL_T);
441               item->set_property ("implicit", SCM_BOOL_T);
442             }
443
444           SCM text = group.text_;
445           if (!Text_interface::is_markup (text)
446               && ly_is_procedure (proc))
447             {
448               text = scm_call_3 (proc, group.number_, group.current_event_->self_scm (),
449                                  context ()->self_scm ());
450             }
451
452           item->set_property ("text", text);
453
454           Axis_group_interface::add_element (group.group_, item);
455         }
456
457       if (group.continuation_line_)
458         {
459           /*
460             UGH should connect to the bass staff, and get the note heads.
461             For now, simply set the hidden figure to a default value to
462             ensure the extenders of different figures always end at the same
463             position, e.g. in <12 5> <12 5>
464           */
465           group.figure_item_->set_property ("transparent", SCM_BOOL_T);
466           group.figure_item_->set_property ("text", ly_string2scm ("0"));
467           group.continuation_line_->set_bound (RIGHT, group.figure_item_);
468         }
469
470       if (groups_[i].group_)
471         groups_[i].group_->set_bound (RIGHT, muscol);
472
473     }
474
475 }
476
477 void
478 Figured_bass_engraver::add_brackets ()
479 {
480   vector<Grob *> encompass;
481   bool inside = false;
482   for (vsize i = 0; i < groups_.size (); i++)
483     {
484       if (!groups_[i].current_event_)
485         continue;
486
487       if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
488         inside = true;
489
490       if (inside && groups_[i].figure_item_)
491         encompass.push_back (groups_[i].figure_item_);
492
493       if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
494         {
495           inside = false;
496
497           Item *brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
498           for (vsize j = 0; j < encompass.size (); j++)
499             {
500               Pointer_group_interface::add_grob (brack,
501                                                  ly_symbol2scm ("elements"),
502                                                  encompass[j]);
503             }
504           encompass.clear ();
505         }
506     }
507 }
508
509 ADD_TRANSLATOR (Figured_bass_engraver,
510                 /* doc */
511                 "Make figured bass numbers.",
512
513                 /* create */
514                 "BassFigure "
515                 "BassFigureAlignment "
516                 "BassFigureBracket "
517                 "BassFigureContinuation "
518                 "BassFigureLine ",
519
520                 /* read */
521                 "figuredBassAlterationDirection "
522                 "figuredBassCenterContinuations "
523                 "figuredBassFormatter "
524                 "implicitBassFigures "
525                 "useBassFigureExtenders "
526                 "ignoreFiguredBassRest ",
527
528                 /* write */
529                 ""
530                );