2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
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.
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.
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/>.
21 #include "engraver.hh"
23 #include "align-interface.hh"
24 #include "axis-group-interface.hh"
26 #include "grob-array.hh"
28 #include "pointer-group-interface.hh"
30 #include "stream-event.hh"
31 #include "text-interface.hh"
33 #include "translator.icc"
40 Spanner *continuation_line_;
50 Stream_event *current_event_;
55 continuation_line_ = 0;
60 /* Reset (or init) all figure information to FALSE */
64 alteration_ = SCM_BOOL_F;
65 augmented_ = SCM_BOOL_F;
66 diminished_ = SCM_BOOL_F;
67 augmented_slash_ = SCM_BOOL_F;
70 /* Mark the members of the struct as used for the GUILE Garbage Collection */
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_);
80 bool group_is_equal_to (Stream_event *evt) const
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"));
90 bool is_continuation () const
94 && group_is_equal_to (current_event_);
96 void assign_from_event (Stream_event *currevt, Item *item)
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");
108 struct Figured_bass_engraver : public Engraver
110 TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
111 void clear_spanners ();
112 void add_brackets ();
113 void create_grobs ();
115 void center_continuations (vector<Spanner *> const &consecutive_lines);
116 void center_repeated_continuations ();
118 vector<Figure_group> groups_;
120 vector<Stream_event *> new_events_;
122 bool new_event_found_;
127 DECLARE_TRANSLATOR_LISTENER (rest);
128 DECLARE_TRANSLATOR_LISTENER (bass_figure);
130 virtual void derived_mark () const;
132 void start_translation_timestep ();
133 void stop_translation_timestep ();
134 void process_music ();
137 Figured_bass_engraver::Figured_bass_engraver ()
140 continuation_ = false;
142 new_event_found_ = false;
146 Figured_bass_engraver::derived_mark () const
148 for (vsize i = 0; i < groups_.size (); i++)
150 groups_[i].gc_mark ();
155 Figured_bass_engraver::start_translation_timestep ()
157 if (now_mom ().main_part_ < stop_moment_.main_part_
158 || now_mom ().grace_part_ < Rational (0))
162 new_events_.clear ();
163 for (vsize i = 0; i < groups_.size (); i++)
164 groups_[i].current_event_ = 0;
166 continuation_ = false;
170 Figured_bass_engraver::stop_translation_timestep ()
173 || now_mom ().main_part_ < stop_moment_.main_part_
174 || now_mom ().grace_part_ < Rational (0))
178 for (vsize i = 0; !found && i < groups_.size (); i++)
179 found = found || groups_[i].current_event_;
185 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
187 Figured_bass_engraver::listen_rest (Stream_event *)
192 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
194 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
196 new_event_found_ = true;
197 Moment stop = now_mom () + get_event_length (ev, now_mom ());
198 stop_moment_ = std::max (stop_moment_, stop);
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)
205 for (vsize i = 0; i < groups_.size (); i++)
207 if (!groups_[i].current_event_
208 && groups_[i].group_is_equal_to (ev))
210 groups_[i].current_event_ = ev;
211 continuation_ = true;
216 new_events_.push_back (ev);
220 Figured_bass_engraver::center_continuations (vector<Spanner *> const &consecutive_lines)
222 vector<Grob *> left_figs;
223 for (vsize j = consecutive_lines.size (); j--;)
224 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
226 SCM ga = Grob_array::make_array ();
227 unsmob<Grob_array> (ga)->set_array (left_figs);
229 for (vsize j = consecutive_lines.size (); j--;)
230 consecutive_lines[j]->set_object ("figures",
231 unsmob<Grob_array> (ga)->smobbed_copy ());
235 Figured_bass_engraver::center_repeated_continuations ()
237 vector<Spanner *> consecutive_lines;
238 for (vsize i = 0; i <= groups_.size (); i++)
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_);
250 center_continuations (consecutive_lines);
251 consecutive_lines.clear ();
257 Figured_bass_engraver::clear_spanners ()
262 announce_end_grob (alignment_, SCM_EOL);
265 if (to_boolean (get_property ("figuredBassCenterContinuations")))
266 center_repeated_continuations ();
268 for (vsize i = 0; i < groups_.size (); i++)
270 if (groups_[i].group_)
272 announce_end_grob (groups_[i].group_, SCM_EOL);
273 groups_[i].group_ = 0;
276 if (groups_[i].continuation_line_)
278 announce_end_grob (groups_[i].continuation_line_, SCM_EOL);
279 groups_[i].continuation_line_ = 0;
283 /* Check me, groups_.clear () ? */
287 Figured_bass_engraver::process_music ()
289 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
290 if (alignment_ && !use_extenders)
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 ()))
303 if (!new_event_found_)
306 new_event_found_ = false;
309 Don't need to sync alignments, if we're not using extenders.
323 for (vsize i = 0; i < new_events_.size (); i++)
325 while (k < groups_.size ()
326 && groups_[k].current_event_)
329 if (k >= groups_.size ())
332 groups_.push_back (group);
335 groups_[k].reset_figure ();
336 groups_[k].current_event_ = new_events_[i];
337 groups_[k].figure_item_ = 0;
341 for (vsize i = 0; i < groups_.size (); i++)
343 if (!groups_[i].is_continuation ())
345 groups_[i].reset_figure ();
351 vector<int> junk_continuations;
352 for (vsize i = 0; i < groups_.size (); i++)
354 Figure_group &group = groups_[i];
356 if (group.is_continuation ())
358 if (!group.continuation_line_)
361 = make_spanner ("BassFigureContinuation", SCM_EOL);
362 Item *item = group.figure_item_;
363 group.continuation_line_ = line;
364 line->set_bound (LEFT, item);
367 Don't add as child. This will cache the wrong
368 (pre-break) stencil when callbacks are triggered.
370 line->set_parent (group.group_, Y_AXIS);
371 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
373 group.figure_item_ = 0;
376 else if (group.continuation_line_)
377 junk_continuations.push_back (i);
383 vector<Spanner *> consecutive;
384 if (to_boolean (get_property ("figuredBassCenterContinuations")))
386 for (vsize i = 0; i <= junk_continuations.size (); i++)
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_);
393 center_continuations (consecutive);
394 consecutive.clear ();
395 if (i < junk_continuations.size ())
396 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
400 for (vsize i = 0; i < junk_continuations.size (); i++)
401 groups_[junk_continuations[i]].continuation_line_ = 0;
409 Figured_bass_engraver::create_grobs ()
412 = unsmob<Item> (get_property ("currentMusicalColumn"));
415 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
416 alignment_->set_bound (LEFT, muscol);
418 alignment_->set_bound (RIGHT, muscol);
420 SCM proc = get_property ("figuredBassFormatter");
421 for (vsize i = 0; i < groups_.size (); i++)
423 Figure_group &group = groups_[i];
425 if (group.current_event_)
428 = make_item ("BassFigure", group.current_event_->self_scm ());
429 group.assign_from_event (group.current_event_, item);
433 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
434 group.group_->set_bound (LEFT, muscol);
435 Align_interface::add_element (alignment_, group.group_);
438 if (scm_is_true (scm_memq (group.number_, get_property ("implicitBassFigures"))))
440 item->set_property ("transparent", SCM_BOOL_T);
441 item->set_property ("implicit", SCM_BOOL_T);
444 SCM text = group.text_;
445 if (!Text_interface::is_markup (text)
446 && ly_is_procedure (proc))
448 text = scm_call_3 (proc, group.number_, group.current_event_->self_scm (),
449 context ()->self_scm ());
452 item->set_property ("text", text);
454 Axis_group_interface::add_element (group.group_, item);
457 if (group.continuation_line_)
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>
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_);
470 if (groups_[i].group_)
471 groups_[i].group_->set_bound (RIGHT, muscol);
478 Figured_bass_engraver::add_brackets ()
480 vector<Grob *> encompass;
482 for (vsize i = 0; i < groups_.size (); i++)
484 if (!groups_[i].current_event_)
487 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
490 if (inside && groups_[i].figure_item_)
491 encompass.push_back (groups_[i].figure_item_);
493 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
497 Item *brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
498 for (vsize j = 0; j < encompass.size (); j++)
500 Pointer_group_interface::add_grob (brack,
501 ly_symbol2scm ("elements"),
509 ADD_TRANSLATOR (Figured_bass_engraver,
511 "Make figured bass numbers.",
515 "BassFigureAlignment "
517 "BassFigureContinuation "
521 "figuredBassAlterationDirection "
522 "figuredBassCenterContinuations "
523 "figuredBassFormatter "
524 "implicitBassFigures "
525 "useBassFigureExtenders "
526 "ignoreFiguredBassRest ",