Update from Andrew: tweaks for cross-staff chords snippet.
[lilypond.git] / lily / figured-bass-engraver.cc
blob782574e5e6e97a491a977379d6f76a556e5199ec
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_;
32 Item *figure_item_;
33 Stream_event *current_event_;
34 bool force_no_continuation_;
36 Figure_group ()
38 figure_item_ = 0;
39 force_no_continuation_ = false;
40 continuation_line_ = 0;
41 number_ = SCM_EOL;
42 alteration_ = SCM_EOL;
43 group_ = 0;
44 current_event_ = 0;
46 bool is_continuation () const
48 return
49 current_event_
50 && !force_no_continuation_
51 && ly_is_equal (number_,
52 current_event_->get_property ("figure"))
53 && ly_is_equal (alteration_,
54 current_event_->get_property ("alteration"));
58 struct Figured_bass_engraver : public Engraver
60 TRANSLATOR_DECLARATIONS (Figured_bass_engraver);
61 void clear_spanners ();
62 void add_brackets ();
63 void create_grobs ();
65 void center_continuations (vector<Spanner*> const &consecutive_lines);
66 void center_repeated_continuations ();
67 protected:
68 vector<Figure_group> groups_;
69 Spanner *alignment_;
70 vector<Stream_event *> new_events_;
71 bool continuation_;
72 bool new_event_found_;
74 Moment stop_moment_;
75 Stream_event *rest_event_;
77 DECLARE_TRANSLATOR_LISTENER (rest);
78 DECLARE_TRANSLATOR_LISTENER (bass_figure);
80 virtual void derived_mark () const;
82 void start_translation_timestep ();
83 void stop_translation_timestep ();
84 void process_music ();
87 void
88 Figured_bass_engraver::derived_mark () const
90 for (vsize i = 0; i < groups_.size (); i++)
92 scm_gc_mark (groups_[i].number_);
93 scm_gc_mark (groups_[i].alteration_);
97 void
98 Figured_bass_engraver::stop_translation_timestep ()
100 if (groups_.empty ()
101 || now_mom ().main_part_ < stop_moment_.main_part_
102 || now_mom ().grace_part_ < Rational (0))
103 return ;
105 bool found = false;
106 for (vsize i = 0; !found && i < groups_.size (); i++)
107 found = found || groups_[i].current_event_;
109 if (!found)
110 clear_spanners ();
113 Figured_bass_engraver::Figured_bass_engraver ()
115 alignment_ = 0;
116 continuation_ = false;
117 rest_event_ = 0;
118 new_event_found_ = false;
121 void
122 Figured_bass_engraver::start_translation_timestep ()
124 if (now_mom ().main_part_ < stop_moment_.main_part_
125 || now_mom ().grace_part_ < Rational (0))
126 return ;
128 rest_event_ = 0;
129 new_events_.clear ();
130 for (vsize i = 0; i < groups_.size (); i++)
131 groups_[i].current_event_ = 0;
133 continuation_ = false;
138 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, rest);
139 void
140 Figured_bass_engraver::listen_rest (Stream_event *ev)
142 if (to_boolean (get_property ("ignoreFiguredBassRest")))
144 new_event_found_ = true;
147 No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
148 polyphonic rests.
150 rest_event_ = ev;
154 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver, bass_figure);
155 void
156 Figured_bass_engraver::listen_bass_figure (Stream_event *ev)
158 new_event_found_ = true;
159 Moment stop = now_mom () + get_event_length (ev, now_mom ());
160 stop_moment_ = max (stop_moment_, stop);
162 if (to_boolean (get_property ("useBassFigureExtenders")))
164 SCM fig = ev->get_property ("figure");
165 for (vsize i = 0; i < groups_.size (); i++)
167 if (!groups_[i].current_event_
168 && ly_is_equal (groups_[i].number_, fig))
170 groups_[i].current_event_ = ev;
171 groups_[i].force_no_continuation_
172 = to_boolean (ev->get_property ("no-continuation"));
173 continuation_ = true;
174 return;
178 new_events_.push_back (ev);
181 void
182 Figured_bass_engraver::center_continuations (vector<Spanner*> const &consecutive_lines)
184 if (consecutive_lines.size () == 2)
186 vector<Grob*> left_figs;
187 for (vsize j = consecutive_lines.size (); j--;)
188 left_figs.push_back (consecutive_lines[j]->get_bound (LEFT));
190 SCM ga = Grob_array::make_array ();
191 unsmob_grob_array (ga)->set_array (left_figs);
193 for (vsize j = consecutive_lines.size (); j--;)
194 consecutive_lines[j]->set_object ("figures",
195 unsmob_grob_array (ga)->smobbed_copy ());
199 void
200 Figured_bass_engraver::center_repeated_continuations ()
202 vector<Spanner*> consecutive_lines;
203 for (vsize i = 0; i <= groups_.size (); i++)
205 if (i < groups_.size ()
206 && groups_[i].continuation_line_
207 && (consecutive_lines.empty ()
208 || (consecutive_lines[0]->get_bound (LEFT)->get_column ()
209 == groups_[i].continuation_line_->get_bound (LEFT)->get_column ()
210 && consecutive_lines[0]->get_bound (RIGHT)->get_column ()
211 == groups_[i].continuation_line_->get_bound (RIGHT)->get_column ())))
212 consecutive_lines.push_back (groups_[i].continuation_line_);
213 else
215 center_continuations (consecutive_lines);
216 consecutive_lines.clear ();
221 void
222 Figured_bass_engraver::clear_spanners ()
224 if (!alignment_)
225 return;
227 if (alignment_)
229 announce_end_grob (alignment_, SCM_EOL);
230 alignment_ = 0;
233 if (to_boolean (get_property ("figuredBassCenterContinuations")))
234 center_repeated_continuations ();
236 for (vsize i = 0; i < groups_.size (); i++)
238 if (groups_[i].group_)
240 announce_end_grob (groups_[i].group_ , SCM_EOL);
241 groups_[i].group_ = 0;
244 if (groups_[i].continuation_line_)
246 announce_end_grob (groups_[i].continuation_line_ , SCM_EOL);
247 groups_[i].continuation_line_ = 0;
251 /* Check me, groups_.clear () ? */
254 void
255 Figured_bass_engraver::add_brackets ()
257 vector<Grob*> encompass;
258 bool inside = false;
259 for (vsize i = 0; i < groups_.size (); i ++)
261 if (!groups_[i].current_event_)
262 continue;
264 if (to_boolean (groups_[i].current_event_->get_property ("bracket-start")))
265 inside = true;
267 if (inside && groups_[i].figure_item_)
268 encompass.push_back (groups_[i].figure_item_);
270 if (to_boolean (groups_[i].current_event_->get_property ("bracket-stop")))
272 inside = false;
274 Item * brack = make_item ("BassFigureBracket", groups_[i].current_event_->self_scm ());
275 for (vsize j = 0; j < encompass.size (); j++)
277 Pointer_group_interface::add_grob (brack,
278 ly_symbol2scm ("elements"),
279 encompass[j]);
281 encompass.clear ();
286 void
287 Figured_bass_engraver::process_music ()
289 if (alignment_ && !to_boolean (get_property ("useBassFigureExtenders")))
290 clear_spanners ();
292 if (rest_event_)
294 clear_spanners ();
295 groups_.clear ();
296 return;
299 if (!continuation_
300 && new_events_.empty ())
302 clear_spanners ();
303 groups_.clear ();
304 return;
307 if (!new_event_found_)
308 return;
310 new_event_found_ = false;
313 Don't need to sync alignments, if we're not using extenders.
315 bool use_extenders = to_boolean (get_property ("useBassFigureExtenders"));
316 if (!use_extenders)
318 clear_spanners ();
321 if (!continuation_)
323 clear_spanners ();
324 groups_.clear ();
327 vsize k = 0;
328 for (vsize i = 0; i < new_events_.size (); i++)
330 while (k < groups_.size ()
331 && groups_[k].current_event_)
332 k++;
334 if (k >= groups_.size ())
336 Figure_group group;
337 groups_.push_back (group);
340 groups_[k].current_event_ = new_events_[i];
341 groups_[k].figure_item_ = 0;
342 k++;
345 for (vsize i = 0; i < groups_.size (); i++)
347 if (!groups_[i].is_continuation ())
349 groups_[i].number_ = SCM_BOOL_F;
350 groups_[i].alteration_ = SCM_BOOL_F;
354 if (use_extenders)
356 vector<int> junk_continuations;
357 for (vsize i = 0; i < groups_.size (); i++)
359 Figure_group &group = groups_[i];
361 if (group.is_continuation ())
363 if (!group.continuation_line_)
365 Spanner * line
366 = make_spanner ("BassFigureContinuation", SCM_EOL);
367 Item * item = group.figure_item_;
368 group.continuation_line_ = line;
369 line->set_bound (LEFT, item);
372 Don't add as child. This will cache the wrong
373 (pre-break) stencil when callbacks are triggered.
375 line->set_parent (group.group_, Y_AXIS);
376 Pointer_group_interface::add_grob (line, ly_symbol2scm ("figures"), item);
378 group.figure_item_ = 0;
381 else if (group.continuation_line_)
382 junk_continuations.push_back (i);
386 Ugh, repeated code.
388 vector<Spanner*> consecutive;
389 if (to_boolean (get_property ("figuredBassCenterContinuations")))
391 for (vsize i = 0; i <= junk_continuations.size (); i++)
393 if (i < junk_continuations.size ()
394 && (i == 0 || junk_continuations[i-1] == junk_continuations[i] - 1))
395 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
396 else
398 center_continuations (consecutive);
399 consecutive.clear ();
400 if (i < junk_continuations.size ())
401 consecutive.push_back (groups_[junk_continuations[i]].continuation_line_);
405 for (vsize i = 0; i < junk_continuations.size (); i++)
406 groups_[junk_continuations[i]].continuation_line_ = 0;
409 create_grobs ();
410 add_brackets ();
413 void
414 Figured_bass_engraver::create_grobs ()
416 Grob *muscol
417 = dynamic_cast<Item*> (unsmob_grob (get_property ("currentMusicalColumn")));
418 if (!alignment_)
420 alignment_ = make_spanner ("BassFigureAlignment", SCM_EOL);
421 alignment_->set_bound (LEFT, muscol);
423 alignment_->set_bound (RIGHT, muscol);
425 SCM proc = get_property ("figuredBassFormatter");
426 for (vsize i = 0; i < groups_.size (); i++)
428 Figure_group &group = groups_[i];
430 if (group.current_event_)
432 Item *item
433 = make_item ("BassFigure",
434 group.current_event_->self_scm ());
437 SCM fig = group.current_event_->get_property ("figure");
438 if (!group.group_)
440 group.group_ = make_spanner ("BassFigureLine", SCM_EOL);
441 group.group_->set_bound (LEFT, muscol);
442 Align_interface::add_element (alignment_,
443 group.group_);
446 if (scm_memq (fig, get_property ("implicitBassFigures")) != SCM_BOOL_F)
448 item->set_property ("transparent", SCM_BOOL_T);
449 item->set_property ("implicit", SCM_BOOL_T);
452 group.number_ = fig;
453 group.alteration_ = group.current_event_->get_property ("alteration");
455 SCM text = group.current_event_->get_property ("text");
456 if (!Text_interface::is_markup (text)
457 && ly_is_procedure (proc))
459 text = scm_call_3 (proc, fig, group.current_event_->self_scm (),
460 context ()->self_scm ());
463 item->set_property ("text", text);
465 Axis_group_interface::add_element (group.group_, item);
466 group.figure_item_ = item;
469 if (group.continuation_line_)
472 UGH should connect to the bass staff, and get the note heads.
474 group.figure_item_->set_property ("transparent", SCM_BOOL_T);
475 group.continuation_line_->set_bound (RIGHT, group.figure_item_);
478 if (groups_[i].group_)
479 groups_[i].group_->set_bound (RIGHT, muscol);
485 ADD_TRANSLATOR (Figured_bass_engraver,
486 /* doc */
487 "Make figured bass numbers.",
489 /* create */
490 "BassFigure "
491 "BassFigureAlignment "
492 "BassFigureBracket "
493 "BassFigureContinuation "
494 "BassFigureLine ",
496 /* read */
497 "figuredBassAlterationDirection "
498 "figuredBassCenterContinuations "
499 "figuredBassFormatter "
500 "implicitBassFigures "
501 "useBassFigureExtenders "
502 "ignoreFiguredBassRest ",
504 /* write */