Fix typo in convert-ly.
[lilypond.git] / lily / figured-bass-engraver.cc
bloba0554eed5887a21d682605fa73d6087cc0d010f9
1 /*
2 figured-bass-engraver.cc -- implement Figured_bass_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2005--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
8 */
10 #include "engraver.hh"
12 #include "align-interface.hh"
13 #include "axis-group-interface.hh"
14 #include "context.hh"
15 #include "grob-array.hh"
16 #include "item.hh"
17 #include "pointer-group-interface.hh"
18 #include "spanner.hh"
19 #include "stream-event.hh"
20 #include "text-interface.hh"
22 #include "translator.icc"
24 struct Figure_group
26 Spanner *group_;
27 Spanner *continuation_line_;
29 SCM number_;
30 SCM alteration_;
31 SCM augmented_;
32 SCM diminished_;
33 SCM augmented_slash_;
35 Item *figure_item_;
36 Stream_event *current_event_;
37 bool force_no_continuation_;
39 Figure_group ()
41 figure_item_ = 0;
42 force_no_continuation_ = false;
43 continuation_line_ = 0;
44 number_ = SCM_EOL;
45 alteration_ = SCM_EOL;
46 augmented_ = SCM_EOL;
47 diminished_ = SCM_EOL;
48 augmented_slash_ = SCM_EOL;
49 group_ = 0;
50 current_event_ = 0;
52 bool is_continuation () const
54 return
55 current_event_
56 && !force_no_continuation_
57 && ly_is_equal (number_,
58 current_event_->get_property ("figure"))
59 && ly_is_equal (alteration_,
60 current_event_->get_property ("alteration"))
61 && ly_is_equal (augmented_,
62 current_event_->get_property ("augmented"))
63 && ly_is_equal (diminished_,
64 current_event_->get_property ("diminished"))
65 && ly_is_equal (augmented_slash_,
66 current_event_->get_property ("augmented-slash"));
70 struct Figured_bass_engraver : public Engraver
72 TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
73 void clear_spanners ();
74 void add_brackets ();
75 void create_grobs ();
77 void center_continuations (vector<Spanner*> const &consecutive_lines);
78 void center_repeated_continuations ();
79 protected:
80 vector<Figure_group> groups_;
81 Spanner *alignment_;
82 vector<Stream_event *> new_events_;
83 bool continuation_;
84 bool new_event_found_;
86 Moment stop_moment_;
87 Stream_event *rest_event_;
89 DECLARE_TRANSLATOR_LISTENER (rest);
90 DECLARE_TRANSLATOR_LISTENER (bass_figure);
92 virtual void derived_mark () const;
94 void start_translation_timestep ();
95 void stop_translation_timestep ();
96 void process_music ();
99 void
100 Figured_bass_engraver::derived_mark () const
102 for (vsize i = 0; i < groups_.size (); i++)
104 scm_gc_mark (groups_[i].number_);
105 scm_gc_mark (groups_[i].alteration_);
106 scm_gc_mark (groups_[i].augmented_);
107 scm_gc_mark (groups_[i].diminished_);
108 scm_gc_mark (groups_[i].augmented_slash_);
112 void
113 Figured_bass_engraver::stop_translation_timestep ()
115 if (groups_.empty ()
116 || now_mom ().main_part_ < stop_moment_.main_part_
117 || now_mom ().grace_part_ < Rational (0))
118 return ;
120 bool found = false;
121 for (vsize i = 0; !found && i < groups_.size (); i++)
122 found = found || groups_[i].current_event_;
124 if (!found)
125 clear_spanners ();
128 Figured_bass_engraver::Figured_bass_engraver ()
130 alignment_ = 0;
131 continuation_ = false;
132 rest_event_ = 0;
133 new_event_found_ = false;
136 void
137 Figured_bass_engraver::start_translation_timestep ()
139 if (now_mom ().main_part_ < stop_moment_.main_part_
140 || now_mom ().grace_part_ < Rational (0))
141 return ;
143 rest_event_ = 0;
144 new_events_.clear ();
145 for (vsize i = 0; i < groups_.size (); i++)
146 groups_[i].current_event_ = 0;
148 continuation_ = false;
153 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
154 void
155 Figured_bass_engraver::listen_rest (Stream_event *ev)
157 if (to_boolean (get_property ("ignoreFiguredBassRest")))
159 new_event_found_ = true;
162 No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
163 polyphonic rests.
165 rest_event_ = ev;
169 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
170 void
171 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
173 new_event_found_ = true;
174 Moment stop = now_mom () + get_event_length (ev, now_mom ());
175 stop_moment_ = max (stop_moment_, stop);
177 if (to_boolean (get_property ("useBassFigureExtenders")))
179 SCM fig = ev->get_property ("figure");
180 for (vsize i = 0; i < groups_.size (); i++)
182 if (!groups_[i].current_event_
183 && ly_is_equal (groups_[i].number_, fig))
185 groups_[i].current_event_ = ev;
186 groups_[i].force_no_continuation_
187 = to_boolean (ev->get_property ("no-continuation"));
188 continuation_ = true;
189 return;
193 new_events_.push_back (ev);
196 void
197 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
199 if (consecutive_lines.size () == 2)
201 vector<Grob*> left_figs;
202 for (vsize j = consecutive_lines.size (); j--;)
203 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
205 SCM ga = Grob_array::make_array ();
206 unsmob_grob_array (ga)->set_array (left_figs);
208 for (vsize j = consecutive_lines.size (); j--;)
209 consecutive_lines[j]->set_object ("figures",
210 unsmob_grob_array (ga)->smobbed_copy ());
214 void
215 Figured_bass_engraver::center_repeated_continuations ()
217 vector<Spanner*> consecutive_lines;
218 for (vsize i = 0; i <= groups_.size (); i++)
220 if (i < groups_.size ()
221 && groups_[i].continuation_line_
222 && (consecutive_lines.empty ()
223 || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
224 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
225 && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
226 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
227 consecutive_lines.push_back (groups_[i].continuation_line_);
228 else
230 center_continuations (consecutive_lines);
231 consecutive_lines.clear ();
236 void
237 Figured_bass_engraver::clear_spanners ()
239 if (!alignment_)
240 return;
242 if (alignment_)
244 announce_end_grob (alignment_, SCM_EOL);
245 alignment_ = 0;
248 if (to_boolean (get_property ("figuredBassCenterContinuations")))
249 center_repeated_continuations ();
251 for (vsize i = 0; i < groups_.size (); i++)
253 if (groups_[i].group_)
255 announce_end_grob (groups_[i].group_ , SCM_EOL);
256 groups_[i].group_ = 0;
259 if (groups_[i].continuation_line_)
261 announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
262 groups_[i].continuation_line_ = 0;
266 /* Check me, groups_.clear () ? */
269 void
270 Figured_bass_engraver::add_brackets ()
272 vector<Grob*> encompass;
273 bool inside = false;
274 for (vsize i = 0; i < groups_.size (); i ++)
276 if (!groups_[i].current_event_)
277 continue;
279 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
280 inside = true;
282 if (inside && groups_[i].figure_item_)
283 encompass.push_back (groups_[i].figure_item_);
285 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
287 inside = false;
289 Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
290 for (vsize j = 0; j < encompass.size (); j++)
292 Pointer_group_interface::add_grob (brack,
293 ly_symbol2scm ("elements"),
294 encompass[j]);
296 encompass.clear ();
301 void
302 Figured_bass_engraver::process_music ()
304 if (alignment_ && !to_boolean (get_property ("useBassFigureExtenders")))
305 clear_spanners ();
307 if (rest_event_)
309 clear_spanners ();
310 groups_.clear ();
311 return;
314 if (!continuation_
315 && new_events_.empty ())
317 clear_spanners ();
318 groups_.clear ();
319 return;
322 if (!new_event_found_)
323 return;
325 new_event_found_ = false;
328 Don't need to sync alignments, if we're not using extenders.
330 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
331 if (!use_extenders)
333 clear_spanners ();
336 if (!continuation_)
338 clear_spanners ();
339 groups_.clear ();
342 vsize k = 0;
343 for (vsize i = 0; i < new_events_.size (); i++)
345 while (k < groups_.size ()
346 && groups_[k].current_event_)
347 k++;
349 if (k >= groups_.size ())
351 Figure_group group;
352 groups_.push_back (group);
355 groups_[k].current_event_ = new_events_[i];
356 groups_[k].figure_item_ = 0;
357 k++;
360 for (vsize i = 0; i < groups_.size (); i++)
362 if (!groups_[i].is_continuation ())
364 groups_[i].number_ = SCM_BOOL_F;
365 groups_[i].alteration_ = SCM_BOOL_F;
366 groups_[i].augmented_ = SCM_BOOL_F;
367 groups_[i].diminished_ = SCM_BOOL_F;
368 groups_[i].augmented_slash_ = SCM_BOOL_F;
372 if (use_extenders)
374 vector<int> junk_continuations;
375 for (vsize i = 0; i < groups_.size (); i++)
377 Figure_group &group = groups_[i];
379 if (group.is_continuation ())
381 if (!group.continuation_line_)
383 Spanner * line
384 = make_spanner ("BassFigureContinuation", SCM_EOL);
385 Item * item = group.figure_item_;
386 group.continuation_line_ = line;
387 line->set_bound (LEFT, item);
390 Don't add as child. This will cache the wrong
391 (pre-break) stencil when callbacks are triggered.
393 line->set_parent (group.group_, Y_AXIS);
394 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
396 group.figure_item_ = 0;
399 else if (group.continuation_line_)
400 junk_continuations.push_back (i);
404 Ugh, repeated code.
406 vector<Spanner*> consecutive;
407 if (to_boolean (get_property ("figuredBassCenterContinuations")))
409 for (vsize i = 0; i <= junk_continuations.size (); i++)
411 if (i < junk_continuations.size ()
412 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
413 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
414 else
416 center_continuations (consecutive);
417 consecutive.clear ();
418 if (i < junk_continuations.size ())
419 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
423 for (vsize i = 0; i < junk_continuations.size (); i++)
424 groups_[junk_continuations[i]].continuation_line_ = 0;
427 create_grobs ();
428 add_brackets ();
431 void
432 Figured_bass_engraver::create_grobs ()
434 Grob *muscol
435 = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
436 if (!alignment_)
438 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
439 alignment_->set_bound (LEFT, muscol);
441 alignment_->set_bound (RIGHT, muscol);
443 SCM proc = get_property ("figuredBassFormatter");
444 for (vsize i = 0; i < groups_.size (); i++)
446 Figure_group &group = groups_[i];
448 if (group.current_event_)
450 Item *item
451 = make_item ("BassFigure",
452 group.current_event_->self_scm ());
455 SCM fig = group.current_event_->get_property ("figure");
456 if (!group.group_)
458 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
459 group.group_->set_bound (LEFT, muscol);
460 Align_interface::add_element (alignment_,
461 group.group_);
464 if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
466 item->set_property ("transparent", SCM_BOOL_T);
467 item->set_property ("implicit", SCM_BOOL_T);
470 group.number_ = fig;
471 group.alteration_ = group.current_event_->get_property ("alteration");
472 group.augmented_ = group.current_event_->get_property ("augmented");
473 group.diminished_ = group.current_event_->get_property ("diminished");
474 group.augmented_slash_ = group.current_event_->get_property ("augmented-slash");
476 SCM text = group.current_event_->get_property ("text");
477 if (!Text_interface::is_markup (text)
478 && ly_is_procedure (proc))
480 text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
481 context ()->self_scm ());
484 item->set_property ("text", text);
486 Axis_group_interface::add_element (group.group_, item);
487 group.figure_item_ = item;
490 if (group.continuation_line_)
493 UGH should connect to the bass staff, and get the note heads.
495 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
496 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
499 if (groups_[i].group_)
500 groups_[i].group_->set_bound (RIGHT, muscol);
506 ADD_TRANSLATOR (Figured_bass_engraver,
507 /* doc */
508 "Make figured bass numbers.",
510 /* create */
511 "BassFigure "
512 "BassFigureAlignment "
513 "BassFigureBracket "
514 "BassFigureContinuation "
515 "BassFigureLine ",
517 /* read */
518 "figuredBassAlterationDirection "
519 "figuredBassCenterContinuations "
520 "figuredBassFormatter "
521 "implicitBassFigures "
522 "useBassFigureExtenders "
523 "ignoreFiguredBassRest ",
525 /* write */