2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--2010 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"
38 Spanner *continuation_line_;
48 Stream_event *current_event_;
53 continuation_line_ = 0;
58 /* Reset (or init) all figure information to FALSE */
62 alteration_ = SCM_BOOL_F;
63 augmented_ = SCM_BOOL_F;
64 diminished_ = SCM_BOOL_F;
65 augmented_slash_ = SCM_BOOL_F;
68 /* Mark the members of the struct as used for the GUILE Garbage Collection */
71 scm_gc_mark (number_);
72 scm_gc_mark (alteration_);
73 scm_gc_mark (augmented_);
74 scm_gc_mark (diminished_);
75 scm_gc_mark (augmented_slash_);
78 bool group_is_equal_to (Stream_event *evt) const
81 ly_is_equal (number_, evt->get_property ("figure"))
82 && ly_is_equal (alteration_, evt->get_property ("alteration"))
83 && ly_is_equal (augmented_, evt->get_property ("augmented"))
84 && ly_is_equal (diminished_, evt->get_property ("diminished"))
85 && ly_is_equal (augmented_slash_, evt->get_property ("augmented-slash"))
86 && ly_is_equal (text_, evt->get_property ("text"));
88 bool is_continuation () const
92 && group_is_equal_to (current_event_);
96 struct Figured_bass_engraver : public Engraver
98 TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
99 void clear_spanners ();
100 void add_brackets ();
101 void create_grobs ();
103 void center_continuations (vector<Spanner*> const &consecutive_lines);
104 void center_repeated_continuations ();
106 vector<Figure_group> groups_;
108 vector<Stream_event *> new_events_;
110 bool new_event_found_;
115 DECLARE_TRANSLATOR_LISTENER (rest);
116 DECLARE_TRANSLATOR_LISTENER (bass_figure);
118 virtual void derived_mark () const;
120 void start_translation_timestep ();
121 void stop_translation_timestep ();
122 void process_music ();
126 Figured_bass_engraver::derived_mark () const
128 for (vsize i = 0; i < groups_.size (); i++)
130 groups_[i].gc_mark ();
135 Figured_bass_engraver::stop_translation_timestep ()
138 || now_mom ().main_part_ < stop_moment_.main_part_
139 || now_mom ().grace_part_ < Rational (0))
143 for (vsize i = 0; !found && i < groups_.size (); i++)
144 found = found || groups_[i].current_event_;
150 Figured_bass_engraver::Figured_bass_engraver ()
153 continuation_ = false;
155 new_event_found_ = false;
159 Figured_bass_engraver::start_translation_timestep ()
161 if (now_mom ().main_part_ < stop_moment_.main_part_
162 || now_mom ().grace_part_ < Rational (0))
166 new_events_.clear ();
167 for (vsize i = 0; i < groups_.size (); i++)
168 groups_[i].current_event_ = 0;
170 continuation_ = false;
175 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
177 Figured_bass_engraver::listen_rest (Stream_event *ev)
182 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
184 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
186 new_event_found_ = true;
187 Moment stop = now_mom () + get_event_length (ev, now_mom ());
188 stop_moment_ = max (stop_moment_, stop);
190 // Handle no-continuation here, don't even add it to the already existing
191 // spanner... This fixes some layout issues (figure will be placed separately)
192 bool no_continuation = to_boolean (ev->get_property ("no-continuation"));
193 if (to_boolean (get_property ("useBassFigureExtenders")) && !no_continuation)
195 for (vsize i = 0; i < groups_.size (); i++)
197 if (!groups_[i].current_event_
198 && groups_[i].group_is_equal_to (ev))
200 groups_[i].current_event_ = ev;
201 continuation_ = true;
206 new_events_.push_back (ev);
210 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
212 if (consecutive_lines.size () == 2)
214 vector<Grob*> left_figs;
215 for (vsize j = consecutive_lines.size (); j--;)
216 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
218 SCM ga = Grob_array::make_array ();
219 unsmob_grob_array (ga)->set_array (left_figs);
221 for (vsize j = consecutive_lines.size (); j--;)
222 consecutive_lines[j]->set_object ("figures",
223 unsmob_grob_array (ga)->smobbed_copy ());
228 Figured_bass_engraver::center_repeated_continuations ()
230 vector<Spanner*> consecutive_lines;
231 for (vsize i = 0; i <= groups_.size (); i++)
233 if (i < groups_.size ()
234 && groups_[i].continuation_line_
235 && (consecutive_lines.empty ()
236 || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
237 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
238 && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
239 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
240 consecutive_lines.push_back (groups_[i].continuation_line_);
243 center_continuations (consecutive_lines);
244 consecutive_lines.clear ();
250 Figured_bass_engraver::clear_spanners ()
255 announce_end_grob (alignment_, SCM_EOL);
258 if (to_boolean (get_property ("figuredBassCenterContinuations")))
259 center_repeated_continuations ();
261 for (vsize i = 0; i < groups_.size (); i++)
263 if (groups_[i].group_)
265 announce_end_grob (groups_[i].group_ , SCM_EOL);
266 groups_[i].group_ = 0;
269 if (groups_[i].continuation_line_)
271 announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
272 groups_[i].continuation_line_ = 0;
276 /* Check me, groups_.clear () ? */
280 Figured_bass_engraver::add_brackets ()
282 vector<Grob*> encompass;
284 for (vsize i = 0; i < groups_.size (); i ++)
286 if (!groups_[i].current_event_)
289 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
292 if (inside && groups_[i].figure_item_)
293 encompass.push_back (groups_[i].figure_item_);
295 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
299 Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
300 for (vsize j = 0; j < encompass.size (); j++)
302 Pointer_group_interface::add_grob (brack,
303 ly_symbol2scm ("elements"),
312 Figured_bass_engraver::process_music ()
314 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
315 if (alignment_ && !use_extenders)
318 // If we have a rest, or we have no new or continued events, clear all spanners
319 bool ignore_rest = to_boolean (get_property ("ignoreFiguredBassRest"));
320 if ((ignore_rest && have_rest_) ||
321 (!continuation_ && new_events_.empty ()))
328 if (!new_event_found_)
331 new_event_found_ = false;
334 Don't need to sync alignments, if we're not using extenders.
348 for (vsize i = 0; i < new_events_.size (); i++)
350 while (k < groups_.size ()
351 && groups_[k].current_event_)
354 if (k >= groups_.size ())
357 groups_.push_back (group);
360 groups_[k].current_event_ = new_events_[i];
361 groups_[k].figure_item_ = 0;
365 for (vsize i = 0; i < groups_.size (); i++)
367 if (!groups_[i].is_continuation ())
369 groups_[i].reset_figure ();
375 vector<int> junk_continuations;
376 for (vsize i = 0; i < groups_.size (); i++)
378 Figure_group &group = groups_[i];
380 if (group.is_continuation ())
382 if (!group.continuation_line_)
385 = make_spanner ("BassFigureContinuation", SCM_EOL);
386 Item * item = group.figure_item_;
387 group.continuation_line_ = line;
388 line->set_bound (LEFT, item);
391 Don't add as child. This will cache the wrong
392 (pre-break) stencil when callbacks are triggered.
394 line->set_parent (group.group_, Y_AXIS);
395 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
397 group.figure_item_ = 0;
400 else if (group.continuation_line_)
401 junk_continuations.push_back (i);
407 vector<Spanner*> consecutive;
408 if (to_boolean (get_property ("figuredBassCenterContinuations")))
410 for (vsize i = 0; i <= junk_continuations.size (); i++)
412 if (i < junk_continuations.size ()
413 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
414 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
417 center_continuations (consecutive);
418 consecutive.clear ();
419 if (i < junk_continuations.size ())
420 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
424 for (vsize i = 0; i < junk_continuations.size (); i++)
425 groups_[junk_continuations[i]].continuation_line_ = 0;
433 Figured_bass_engraver::create_grobs ()
436 = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
439 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
440 alignment_->set_bound (LEFT, muscol);
442 alignment_->set_bound (RIGHT, muscol);
444 SCM proc = get_property ("figuredBassFormatter");
445 for (vsize i = 0; i < groups_.size (); i++)
447 Figure_group &group = groups_[i];
449 if (group.current_event_)
452 = make_item ("BassFigure",
453 group.current_event_->self_scm ());
456 SCM fig = group.current_event_->get_property ("figure");
459 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
460 group.group_->set_bound (LEFT, muscol);
461 Align_interface::add_element (alignment_,
465 if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
467 item->set_property ("transparent", SCM_BOOL_T);
468 item->set_property ("implicit", SCM_BOOL_T);
472 group.alteration_ = group.current_event_->get_property ("alteration");
473 group.augmented_ = group.current_event_->get_property ("augmented");
474 group.diminished_ = group.current_event_->get_property ("diminished");
475 group.augmented_slash_ = group.current_event_->get_property ("augmented-slash");
476 group.text_ = group.current_event_->get_property ("text");
478 SCM text = group.text_;
479 if (!Text_interface::is_markup (text)
480 && ly_is_procedure (proc))
482 text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
483 context ()->self_scm ());
486 item->set_property ("text", text);
488 Axis_group_interface::add_element (group.group_, item);
489 group.figure_item_ = item;
492 if (group.continuation_line_)
495 UGH should connect to the bass staff, and get the note heads.
497 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
498 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
501 if (groups_[i].group_)
502 groups_[i].group_->set_bound (RIGHT, muscol);
508 ADD_TRANSLATOR (Figured_bass_engraver,
510 "Make figured bass numbers.",
514 "BassFigureAlignment "
516 "BassFigureContinuation "
520 "figuredBassAlterationDirection "
521 "figuredBassCenterContinuations "
522 "figuredBassFormatter "
523 "implicitBassFigures "
524 "useBassFigureExtenders "
525 "ignoreFiguredBassRest ",