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_;
49 bool force_no_continuation_;
54 force_no_continuation_ = false;
55 continuation_line_ = 0;
57 alteration_ = SCM_EOL;
59 diminished_ = SCM_EOL;
60 augmented_slash_ = SCM_EOL;
65 /* Mark the members of the struct as used for the GUILE Garbage Collection */
68 scm_gc_mark (number_);
69 scm_gc_mark (alteration_);
70 scm_gc_mark (augmented_);
71 scm_gc_mark (diminished_);
72 scm_gc_mark (augmented_slash_);
75 bool group_is_equal_to (Stream_event *evt) const
78 ly_is_equal (number_, evt->get_property ("figure"))
79 && ly_is_equal (alteration_, evt->get_property ("alteration"))
80 && ly_is_equal (augmented_, evt->get_property ("augmented"))
81 && ly_is_equal (diminished_, evt->get_property ("diminished"))
82 && ly_is_equal (augmented_slash_, evt->get_property ("augmented-slash"))
83 && ly_is_equal (text_, evt->get_property ("text"));
85 bool is_continuation () const
89 && !force_no_continuation_
90 && group_is_equal_to (current_event_);
94 struct Figured_bass_engraver : public Engraver
96 TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
97 void clear_spanners ();
101 void center_continuations (vector<Spanner*> const &consecutive_lines);
102 void center_repeated_continuations ();
104 vector<Figure_group> groups_;
106 vector<Stream_event *> new_events_;
108 bool new_event_found_;
111 Stream_event *rest_event_;
113 DECLARE_TRANSLATOR_LISTENER (rest);
114 DECLARE_TRANSLATOR_LISTENER (bass_figure);
116 virtual void derived_mark () const;
118 void start_translation_timestep ();
119 void stop_translation_timestep ();
120 void process_music ();
124 Figured_bass_engraver::derived_mark () const
126 for (vsize i = 0; i < groups_.size (); i++)
128 groups_[i].gc_mark ();
133 Figured_bass_engraver::stop_translation_timestep ()
136 || now_mom ().main_part_ < stop_moment_.main_part_
137 || now_mom ().grace_part_ < Rational (0))
141 for (vsize i = 0; !found && i < groups_.size (); i++)
142 found = found || groups_[i].current_event_;
148 Figured_bass_engraver::Figured_bass_engraver ()
151 continuation_ = false;
153 new_event_found_ = false;
157 Figured_bass_engraver::start_translation_timestep ()
159 if (now_mom ().main_part_ < stop_moment_.main_part_
160 || now_mom ().grace_part_ < Rational (0))
164 new_events_.clear ();
165 for (vsize i = 0; i < groups_.size (); i++)
166 groups_[i].current_event_ = 0;
168 continuation_ = false;
173 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
175 Figured_bass_engraver::listen_rest (Stream_event *ev)
177 if (to_boolean (get_property ("ignoreFiguredBassRest")))
179 new_event_found_ = true;
182 No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
189 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
191 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
193 new_event_found_ = true;
194 Moment stop = now_mom () + get_event_length (ev, now_mom ());
195 stop_moment_ = max (stop_moment_, stop);
197 if (to_boolean (get_property ("useBassFigureExtenders")))
199 SCM fig = ev->get_property ("figure");
200 SCM txt = ev->get_property ("text");
201 for (vsize i = 0; i < groups_.size (); i++)
203 if (!groups_[i].current_event_
204 && ly_is_equal (groups_[i].number_, fig)
205 && ly_is_equal (groups_[i].text_, txt))
207 groups_[i].current_event_ = ev;
208 groups_[i].force_no_continuation_
209 = to_boolean (ev->get_property ("no-continuation"));
210 continuation_ = true;
215 new_events_.push_back (ev);
219 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
221 if (consecutive_lines.size () == 2)
223 vector<Grob*> left_figs;
224 for (vsize j = consecutive_lines.size (); j--;)
225 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
227 SCM ga = Grob_array::make_array ();
228 unsmob_grob_array (ga)->set_array (left_figs);
230 for (vsize j = consecutive_lines.size (); j--;)
231 consecutive_lines[j]->set_object ("figures",
232 unsmob_grob_array (ga)->smobbed_copy ());
237 Figured_bass_engraver::center_repeated_continuations ()
239 vector<Spanner*> consecutive_lines;
240 for (vsize i = 0; i <= groups_.size (); i++)
242 if (i < groups_.size ()
243 && groups_[i].continuation_line_
244 && (consecutive_lines.empty ()
245 || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
246 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
247 && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
248 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
249 consecutive_lines.push_back (groups_[i].continuation_line_);
252 center_continuations (consecutive_lines);
253 consecutive_lines.clear ();
259 Figured_bass_engraver::clear_spanners ()
266 announce_end_grob (alignment_, SCM_EOL);
270 if (to_boolean (get_property ("figuredBassCenterContinuations")))
271 center_repeated_continuations ();
273 for (vsize i = 0; i < groups_.size (); i++)
275 if (groups_[i].group_)
277 announce_end_grob (groups_[i].group_ , SCM_EOL);
278 groups_[i].group_ = 0;
281 if (groups_[i].continuation_line_)
283 announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
284 groups_[i].continuation_line_ = 0;
288 /* Check me, groups_.clear () ? */
292 Figured_bass_engraver::add_brackets ()
294 vector<Grob*> encompass;
296 for (vsize i = 0; i < groups_.size (); i ++)
298 if (!groups_[i].current_event_)
301 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
304 if (inside && groups_[i].figure_item_)
305 encompass.push_back (groups_[i].figure_item_);
307 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
311 Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
312 for (vsize j = 0; j < encompass.size (); j++)
314 Pointer_group_interface::add_grob (brack,
315 ly_symbol2scm ("elements"),
324 Figured_bass_engraver::process_music ()
326 if (alignment_ && !to_boolean (get_property ("useBassFigureExtenders")))
337 && new_events_.empty ())
344 if (!new_event_found_)
347 new_event_found_ = false;
350 Don't need to sync alignments, if we're not using extenders.
352 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
365 for (vsize i = 0; i < new_events_.size (); i++)
367 while (k < groups_.size ()
368 && groups_[k].current_event_)
371 if (k >= groups_.size ())
374 groups_.push_back (group);
377 groups_[k].current_event_ = new_events_[i];
378 groups_[k].figure_item_ = 0;
382 for (vsize i = 0; i < groups_.size (); i++)
384 if (!groups_[i].is_continuation ())
386 groups_[i].number_ = SCM_BOOL_F;
387 groups_[i].alteration_ = SCM_BOOL_F;
388 groups_[i].augmented_ = SCM_BOOL_F;
389 groups_[i].diminished_ = SCM_BOOL_F;
390 groups_[i].augmented_slash_ = SCM_BOOL_F;
391 groups_[i].text_ = SCM_BOOL_F;
397 vector<int> junk_continuations;
398 for (vsize i = 0; i < groups_.size (); i++)
400 Figure_group &group = groups_[i];
402 if (group.is_continuation ())
404 if (!group.continuation_line_)
407 = make_spanner ("BassFigureContinuation", SCM_EOL);
408 Item * item = group.figure_item_;
409 group.continuation_line_ = line;
410 line->set_bound (LEFT, item);
413 Don't add as child. This will cache the wrong
414 (pre-break) stencil when callbacks are triggered.
416 line->set_parent (group.group_, Y_AXIS);
417 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
419 group.figure_item_ = 0;
422 else if (group.continuation_line_)
423 junk_continuations.push_back (i);
429 vector<Spanner*> consecutive;
430 if (to_boolean (get_property ("figuredBassCenterContinuations")))
432 for (vsize i = 0; i <= junk_continuations.size (); i++)
434 if (i < junk_continuations.size ()
435 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
436 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
439 center_continuations (consecutive);
440 consecutive.clear ();
441 if (i < junk_continuations.size ())
442 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
446 for (vsize i = 0; i < junk_continuations.size (); i++)
447 groups_[junk_continuations[i]].continuation_line_ = 0;
455 Figured_bass_engraver::create_grobs ()
458 = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
461 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
462 alignment_->set_bound (LEFT, muscol);
464 alignment_->set_bound (RIGHT, muscol);
466 SCM proc = get_property ("figuredBassFormatter");
467 for (vsize i = 0; i < groups_.size (); i++)
469 Figure_group &group = groups_[i];
471 if (group.current_event_)
474 = make_item ("BassFigure",
475 group.current_event_->self_scm ());
478 SCM fig = group.current_event_->get_property ("figure");
481 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
482 group.group_->set_bound (LEFT, muscol);
483 Align_interface::add_element (alignment_,
487 if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
489 item->set_property ("transparent", SCM_BOOL_T);
490 item->set_property ("implicit", SCM_BOOL_T);
494 group.alteration_ = group.current_event_->get_property ("alteration");
495 group.augmented_ = group.current_event_->get_property ("augmented");
496 group.diminished_ = group.current_event_->get_property ("diminished");
497 group.augmented_slash_ = group.current_event_->get_property ("augmented-slash");
498 group.text_ = group.current_event_->get_property ("text");
500 SCM text = group.text_;
501 if (!Text_interface::is_markup (text)
502 && ly_is_procedure (proc))
504 text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
505 context ()->self_scm ());
508 item->set_property ("text", text);
510 Axis_group_interface::add_element (group.group_, item);
511 group.figure_item_ = item;
514 if (group.continuation_line_)
517 UGH should connect to the bass staff, and get the note heads.
519 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
520 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
523 if (groups_[i].group_)
524 groups_[i].group_->set_bound (RIGHT, muscol);
530 ADD_TRANSLATOR (Figured_bass_engraver,
532 "Make figured bass numbers.",
536 "BassFigureAlignment "
538 "BassFigureContinuation "
542 "figuredBassAlterationDirection "
543 "figuredBassCenterContinuations "
544 "figuredBassFormatter "
545 "implicitBassFigures "
546 "useBassFigureExtenders "
547 "ignoreFiguredBassRest ",