Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / figured-bass-engraver.cc
blobc0b4c09b92584dcb01cb6e597954256c474b7259
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--2011 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"
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"
33 #include "translator.icc"
35 struct Figure_group
37 Spanner *group_;
38 Spanner *continuation_line_;
40 SCM number_;
41 SCM alteration_;
42 SCM augmented_;
43 SCM diminished_;
44 SCM augmented_slash_;
45 SCM text_;
47 Item *figure_item_;
48 Stream_event *current_event_;
50 Figure_group ()
52 figure_item_ = 0;
53 continuation_line_ = 0;
54 reset_figure ();
55 group_ = 0;
56 current_event_ = 0;
58 /* Reset (or init) all figure information to FALSE */
59 void reset_figure ()
61 number_ = SCM_BOOL_F;
62 alteration_ = SCM_BOOL_F;
63 augmented_ = SCM_BOOL_F;
64 diminished_ = SCM_BOOL_F;
65 augmented_slash_ = SCM_BOOL_F;
66 text_ = SCM_BOOL_F;
68 /* Mark the members of the struct as used for the GUILE Garbage Collection */
69 void gc_mark () const
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_);
76 scm_gc_mark (text_);
78 bool group_is_equal_to (Stream_event *evt) const
80 return
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
90 return
91 current_event_
92 && group_is_equal_to (current_event_);
94 void assign_from_event (Stream_event *currevt, Item *item)
96 number_ = current_event_->get_property ("figure");
97 alteration_ = currevt->get_property ("alteration");
98 augmented_ = currevt->get_property ("augmented");
99 diminished_ = currevt->get_property ("diminished");
100 augmented_slash_ = currevt->get_property ("augmented-slash");
101 text_ = currevt->get_property ("text");
102 figure_item_ = item;
106 struct Figured_bass_engraver : public Engraver
108 TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
109 void clear_spanners ();
110 void add_brackets ();
111 void create_grobs ();
113 void center_continuations (vector<Spanner*> const &consecutive_lines);
114 void center_repeated_continuations ();
115 protected:
116 vector<Figure_group> groups_;
117 Spanner *alignment_;
118 vector<Stream_event *> new_events_;
119 bool continuation_;
120 bool new_event_found_;
122 Moment stop_moment_;
123 bool have_rest_;
125 DECLARE_TRANSLATOR_LISTENER (rest);
126 DECLARE_TRANSLATOR_LISTENER (bass_figure);
128 virtual void derived_mark () const;
130 void start_translation_timestep ();
131 void stop_translation_timestep ();
132 void process_music ();
135 Figured_bass_engraver::Figured_bass_engraver ()
137 alignment_ = 0;
138 continuation_ = false;
139 have_rest_ = 0;
140 new_event_found_ = false;
143 void
144 Figured_bass_engraver::derived_mark () const
146 for (vsize i = 0; i < groups_.size (); i++)
148 groups_[i].gc_mark ();
152 void
153 Figured_bass_engraver::start_translation_timestep ()
155 if (now_mom ().main_part_ < stop_moment_.main_part_
156 || now_mom ().grace_part_ < Rational (0))
157 return ;
159 have_rest_ = 0;
160 new_events_.clear ();
161 for (vsize i = 0; i < groups_.size (); i++)
162 groups_[i].current_event_ = 0;
164 continuation_ = false;
167 void
168 Figured_bass_engraver::stop_translation_timestep ()
170 if (groups_.empty ()
171 || now_mom ().main_part_ < stop_moment_.main_part_
172 || now_mom ().grace_part_ < Rational (0))
173 return ;
175 bool found = false;
176 for (vsize i = 0; !found && i < groups_.size (); i++)
177 found = found || groups_[i].current_event_;
179 if (!found)
180 clear_spanners ();
183 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
184 void
185 Figured_bass_engraver::listen_rest (Stream_event *)
187 have_rest_ = true;
190 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
191 void
192 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
194 new_event_found_ = true;
195 Moment stop = now_mom () + get_event_length (ev, now_mom ());
196 stop_moment_ = max (stop_moment_, stop);
198 // Handle no-continuation here, don't even add it to the already existing
199 // spanner... This fixes some layout issues (figure will be placed separately)
200 bool no_continuation = to_boolean (ev->get_property ("no-continuation"));
201 if (to_boolean (get_property ("useBassFigureExtenders")) && !no_continuation)
203 for (vsize i = 0; i < groups_.size (); i++)
205 if (!groups_[i].current_event_
206 && groups_[i].group_is_equal_to (ev))
208 groups_[i].current_event_ = ev;
209 continuation_ = true;
210 return;
214 new_events_.push_back (ev);
217 void
218 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
220 if (consecutive_lines.size () == 2)
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 void
236 Figured_bass_engraver::center_repeated_continuations ()
238 vector<Spanner*> consecutive_lines;
239 for (vsize i = 0; i <= groups_.size (); i++)
241 if (i < groups_.size ()
242 && groups_[i].continuation_line_
243 && (consecutive_lines.empty ()
244 || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
245 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
246 && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
247 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
248 consecutive_lines.push_back (groups_[i].continuation_line_);
249 else
251 center_continuations (consecutive_lines);
252 consecutive_lines.clear ();
257 void
258 Figured_bass_engraver::clear_spanners ()
260 if (!alignment_)
261 return;
263 announce_end_grob (alignment_, SCM_EOL);
264 alignment_ = 0;
266 if (to_boolean (get_property ("figuredBassCenterContinuations")))
267 center_repeated_continuations ();
269 for (vsize i = 0; i < groups_.size (); i++)
271 if (groups_[i].group_)
273 announce_end_grob (groups_[i].group_ , SCM_EOL);
274 groups_[i].group_ = 0;
277 if (groups_[i].continuation_line_)
279 announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
280 groups_[i].continuation_line_ = 0;
284 /* Check me, groups_.clear () ? */
287 void
288 Figured_bass_engraver::process_music ()
290 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
291 if (alignment_ && !use_extenders)
292 clear_spanners ();
294 // If we have a rest, or we have no new or continued events, clear all spanners
295 bool ignore_rest = to_boolean (get_property ("ignoreFiguredBassRest"));
296 if ((ignore_rest && have_rest_) ||
297 (!continuation_ && new_events_.empty ()))
299 clear_spanners ();
300 groups_.clear ();
301 return;
304 if (!new_event_found_)
305 return;
307 new_event_found_ = false;
310 Don't need to sync alignments, if we're not using extenders.
312 if (!use_extenders)
314 clear_spanners ();
317 if (!continuation_)
319 clear_spanners ();
320 groups_.clear ();
323 vsize k = 0;
324 for (vsize i = 0; i < new_events_.size (); i++)
326 while (k < groups_.size ()
327 && groups_[k].current_event_)
328 k++;
330 if (k >= groups_.size ())
332 Figure_group group;
333 groups_.push_back (group);
336 groups_[k].reset_figure ();
337 groups_[k].current_event_ = new_events_[i];
338 groups_[k].figure_item_ = 0;
339 k++;
342 for (vsize i = 0; i < groups_.size (); i++)
344 if (!groups_[i].is_continuation ())
346 groups_[i].reset_figure ();
350 if (use_extenders)
352 vector<int> junk_continuations;
353 for (vsize i = 0; i < groups_.size (); i++)
355 Figure_group &group = groups_[i];
357 if (group.is_continuation ())
359 if (!group.continuation_line_)
361 Spanner * line
362 = make_spanner ("BassFigureContinuation", SCM_EOL);
363 Item * item = group.figure_item_;
364 group.continuation_line_ = line;
365 line->set_bound (LEFT, item);
368 Don't add as child. This will cache the wrong
369 (pre-break) stencil when callbacks are triggered.
371 line->set_parent (group.group_, Y_AXIS);
372 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
374 group.figure_item_ = 0;
377 else if (group.continuation_line_)
378 junk_continuations.push_back (i);
382 Ugh, repeated code.
384 vector<Spanner*> consecutive;
385 if (to_boolean (get_property ("figuredBassCenterContinuations")))
387 for (vsize i = 0; i <= junk_continuations.size (); i++)
389 if (i < junk_continuations.size ()
390 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
391 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
392 else
394 center_continuations (consecutive);
395 consecutive.clear ();
396 if (i < junk_continuations.size ())
397 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
401 for (vsize i = 0; i < junk_continuations.size (); i++)
402 groups_[junk_continuations[i]].continuation_line_ = 0;
405 create_grobs ();
406 add_brackets ();
409 void
410 Figured_bass_engraver::create_grobs ()
412 Grob *muscol
413 = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
414 if (!alignment_)
416 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
417 alignment_->set_bound (LEFT, muscol);
419 alignment_->set_bound (RIGHT, muscol);
421 SCM proc = get_property ("figuredBassFormatter");
422 for (vsize i = 0; i < groups_.size (); i++)
424 Figure_group &group = groups_[i];
426 if (group.current_event_)
428 Item *item
429 = make_item ("BassFigure", group.current_event_->self_scm ());
430 group.assign_from_event (group.current_event_, item);
432 if (!group.group_)
434 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
435 group.group_->set_bound (LEFT, muscol);
436 Align_interface::add_element (alignment_, group.group_);
439 if (scm_memq (group.number_, get_property ("implicitBassFigures")) != SCM_BOOL_F)
441 item->set_property ("transparent", SCM_BOOL_T);
442 item->set_property ("implicit", SCM_BOOL_T);
445 SCM text = group.text_;
446 if (!Text_interface::is_markup (text)
447 && ly_is_procedure (proc))
449 text = scm_call_3 (proc, group.number_, group.current_event_->self_scm (),
450 context ()->self_scm ());
453 item->set_property ("text", text);
455 Axis_group_interface::add_element (group.group_, item);
458 if (group.continuation_line_)
461 UGH should connect to the bass staff, and get the note heads.
462 For now, simply set the hidden figure to a default value to
463 ensure the extenders of different figures always end at the same
464 position, e.g. in <12 5> <12 5>
466 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
467 group.figure_item_->set_property ("text", ly_string2scm ("0"));
468 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
471 if (groups_[i].group_)
472 groups_[i].group_->set_bound (RIGHT, muscol);
478 void
479 Figured_bass_engraver::add_brackets ()
481 vector<Grob*> encompass;
482 bool inside = false;
483 for (vsize i = 0; i < groups_.size (); i ++)
485 if (!groups_[i].current_event_)
486 continue;
488 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
489 inside = true;
491 if (inside && groups_[i].figure_item_)
492 encompass.push_back (groups_[i].figure_item_);
494 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
496 inside = false;
498 Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
499 for (vsize j = 0; j < encompass.size (); j++)
501 Pointer_group_interface::add_grob (brack,
502 ly_symbol2scm ("elements"),
503 encompass[j]);
505 encompass.clear ();
510 ADD_TRANSLATOR (Figured_bass_engraver,
511 /* doc */
512 "Make figured bass numbers.",
514 /* create */
515 "BassFigure "
516 "BassFigureAlignment "
517 "BassFigureBracket "
518 "BassFigureContinuation "
519 "BassFigureLine ",
521 /* read */
522 "figuredBassAlterationDirection "
523 "figuredBassCenterContinuations "
524 "figuredBassFormatter "
525 "implicitBassFigures "
526 "useBassFigureExtenders "
527 "ignoreFiguredBassRest ",
529 /* write */