2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--2009 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 bool is_continuation () const
69 && !force_no_continuation_
70 && ly_is_equal (number_
,
71 current_event_
->get_property ("figure"))
72 && ly_is_equal (alteration_
,
73 current_event_
->get_property ("alteration"))
74 && ly_is_equal (augmented_
,
75 current_event_
->get_property ("augmented"))
76 && ly_is_equal (diminished_
,
77 current_event_
->get_property ("diminished"))
78 && ly_is_equal (augmented_slash_
,
79 current_event_
->get_property ("augmented-slash"))
80 && ly_is_equal (text_
,
81 current_event_
->get_property ("text"));
85 struct Figured_bass_engraver
: public Engraver
87 TRANSLATOR_DECLARATIONS (Figured_bass_engraver
);
88 void clear_spanners ();
92 void center_continuations (vector
<Spanner
*> const &consecutive_lines
);
93 void center_repeated_continuations ();
95 vector
<Figure_group
> groups_
;
97 vector
<Stream_event
*> new_events_
;
99 bool new_event_found_
;
102 Stream_event
*rest_event_
;
104 DECLARE_TRANSLATOR_LISTENER (rest
);
105 DECLARE_TRANSLATOR_LISTENER (bass_figure
);
107 virtual void derived_mark () const;
109 void start_translation_timestep ();
110 void stop_translation_timestep ();
111 void process_music ();
115 Figured_bass_engraver::derived_mark () const
117 for (vsize i
= 0; i
< groups_
.size (); i
++)
119 scm_gc_mark (groups_
[i
].number_
);
120 scm_gc_mark (groups_
[i
].alteration_
);
121 scm_gc_mark (groups_
[i
].augmented_
);
122 scm_gc_mark (groups_
[i
].diminished_
);
123 scm_gc_mark (groups_
[i
].augmented_slash_
);
124 scm_gc_mark (groups_
[i
].text_
);
129 Figured_bass_engraver::stop_translation_timestep ()
132 || now_mom ().main_part_
< stop_moment_
.main_part_
133 || now_mom ().grace_part_
< Rational (0))
137 for (vsize i
= 0; !found
&& i
< groups_
.size (); i
++)
138 found
= found
|| groups_
[i
].current_event_
;
144 Figured_bass_engraver::Figured_bass_engraver ()
147 continuation_
= false;
149 new_event_found_
= false;
153 Figured_bass_engraver::start_translation_timestep ()
155 if (now_mom ().main_part_
< stop_moment_
.main_part_
156 || now_mom ().grace_part_
< Rational (0))
160 new_events_
.clear ();
161 for (vsize i
= 0; i
< groups_
.size (); i
++)
162 groups_
[i
].current_event_
= 0;
164 continuation_
= false;
169 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver
, rest
);
171 Figured_bass_engraver::listen_rest (Stream_event
*ev
)
173 if (to_boolean (get_property ("ignoreFiguredBassRest")))
175 new_event_found_
= true;
178 No ASSIGN_EVENT_ONCE () ; otherwise we get warnings about
185 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver
, bass_figure
);
187 Figured_bass_engraver::listen_bass_figure (Stream_event
*ev
)
189 new_event_found_
= true;
190 Moment stop
= now_mom () + get_event_length (ev
, now_mom ());
191 stop_moment_
= max (stop_moment_
, stop
);
193 if (to_boolean (get_property ("useBassFigureExtenders")))
195 SCM fig
= ev
->get_property ("figure");
196 SCM txt
= ev
->get_property ("text");
197 for (vsize i
= 0; i
< groups_
.size (); i
++)
199 if (!groups_
[i
].current_event_
200 && ly_is_equal (groups_
[i
].number_
, fig
)
201 && ly_is_equal (groups_
[i
].text_
, txt
))
203 groups_
[i
].current_event_
= ev
;
204 groups_
[i
].force_no_continuation_
205 = to_boolean (ev
->get_property ("no-continuation"));
206 continuation_
= true;
211 new_events_
.push_back (ev
);
215 Figured_bass_engraver::center_continuations (vector
<Spanner
*> const &consecutive_lines
)
217 if (consecutive_lines
.size () == 2)
219 vector
<Grob
*> left_figs
;
220 for (vsize j
= consecutive_lines
.size (); j
--;)
221 left_figs
.push_back (consecutive_lines
[j
]->get_bound (LEFT
));
223 SCM ga
= Grob_array::make_array ();
224 unsmob_grob_array (ga
)->set_array (left_figs
);
226 for (vsize j
= consecutive_lines
.size (); j
--;)
227 consecutive_lines
[j
]->set_object ("figures",
228 unsmob_grob_array (ga
)->smobbed_copy ());
233 Figured_bass_engraver::center_repeated_continuations ()
235 vector
<Spanner
*> consecutive_lines
;
236 for (vsize i
= 0; i
<= groups_
.size (); i
++)
238 if (i
< groups_
.size ()
239 && groups_
[i
].continuation_line_
240 && (consecutive_lines
.empty ()
241 || (consecutive_lines
[0]->get_bound (LEFT
)->get_column ()
242 == groups_
[i
].continuation_line_
->get_bound (LEFT
)->get_column ()
243 && consecutive_lines
[0]->get_bound (RIGHT
)->get_column ()
244 == groups_
[i
].continuation_line_
->get_bound (RIGHT
)->get_column ())))
245 consecutive_lines
.push_back (groups_
[i
].continuation_line_
);
248 center_continuations (consecutive_lines
);
249 consecutive_lines
.clear ();
255 Figured_bass_engraver::clear_spanners ()
262 announce_end_grob (alignment_
, SCM_EOL
);
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 () ? */
288 Figured_bass_engraver::add_brackets ()
290 vector
<Grob
*> encompass
;
292 for (vsize i
= 0; i
< groups_
.size (); i
++)
294 if (!groups_
[i
].current_event_
)
297 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-start")))
300 if (inside
&& groups_
[i
].figure_item_
)
301 encompass
.push_back (groups_
[i
].figure_item_
);
303 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-stop")))
307 Item
* brack
= make_item ("BassFigureBracket", groups_
[i
].current_event_
->self_scm ());
308 for (vsize j
= 0; j
< encompass
.size (); j
++)
310 Pointer_group_interface::add_grob (brack
,
311 ly_symbol2scm ("elements"),
320 Figured_bass_engraver::process_music ()
322 if (alignment_
&& !to_boolean (get_property ("useBassFigureExtenders")))
333 && new_events_
.empty ())
340 if (!new_event_found_
)
343 new_event_found_
= false;
346 Don't need to sync alignments, if we're not using extenders.
348 bool use_extenders
= to_boolean (get_property ("useBassFigureExtenders"));
361 for (vsize i
= 0; i
< new_events_
.size (); i
++)
363 while (k
< groups_
.size ()
364 && groups_
[k
].current_event_
)
367 if (k
>= groups_
.size ())
370 groups_
.push_back (group
);
373 groups_
[k
].current_event_
= new_events_
[i
];
374 groups_
[k
].figure_item_
= 0;
378 for (vsize i
= 0; i
< groups_
.size (); i
++)
380 if (!groups_
[i
].is_continuation ())
382 groups_
[i
].number_
= SCM_BOOL_F
;
383 groups_
[i
].alteration_
= SCM_BOOL_F
;
384 groups_
[i
].augmented_
= SCM_BOOL_F
;
385 groups_
[i
].diminished_
= SCM_BOOL_F
;
386 groups_
[i
].augmented_slash_
= SCM_BOOL_F
;
387 groups_
[i
].text_
= SCM_BOOL_F
;
393 vector
<int> junk_continuations
;
394 for (vsize i
= 0; i
< groups_
.size (); i
++)
396 Figure_group
&group
= groups_
[i
];
398 if (group
.is_continuation ())
400 if (!group
.continuation_line_
)
403 = make_spanner ("BassFigureContinuation", SCM_EOL
);
404 Item
* item
= group
.figure_item_
;
405 group
.continuation_line_
= line
;
406 line
->set_bound (LEFT
, item
);
409 Don't add as child. This will cache the wrong
410 (pre-break) stencil when callbacks are triggered.
412 line
->set_parent (group
.group_
, Y_AXIS
);
413 Pointer_group_interface::add_grob (line
, ly_symbol2scm ("figures"), item
);
415 group
.figure_item_
= 0;
418 else if (group
.continuation_line_
)
419 junk_continuations
.push_back (i
);
425 vector
<Spanner
*> consecutive
;
426 if (to_boolean (get_property ("figuredBassCenterContinuations")))
428 for (vsize i
= 0; i
<= junk_continuations
.size (); i
++)
430 if (i
< junk_continuations
.size ()
431 && (i
== 0 || junk_continuations
[i
-1] == junk_continuations
[i
] - 1))
432 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
435 center_continuations (consecutive
);
436 consecutive
.clear ();
437 if (i
< junk_continuations
.size ())
438 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
442 for (vsize i
= 0; i
< junk_continuations
.size (); i
++)
443 groups_
[junk_continuations
[i
]].continuation_line_
= 0;
451 Figured_bass_engraver::create_grobs ()
454 = dynamic_cast<Item
*> (unsmob_grob (get_property ("currentMusicalColumn")));
457 alignment_
= make_spanner ("BassFigureAlignment", SCM_EOL
);
458 alignment_
->set_bound (LEFT
, muscol
);
460 alignment_
->set_bound (RIGHT
, muscol
);
462 SCM proc
= get_property ("figuredBassFormatter");
463 for (vsize i
= 0; i
< groups_
.size (); i
++)
465 Figure_group
&group
= groups_
[i
];
467 if (group
.current_event_
)
470 = make_item ("BassFigure",
471 group
.current_event_
->self_scm ());
474 SCM fig
= group
.current_event_
->get_property ("figure");
477 group
.group_
= make_spanner ("BassFigureLine", SCM_EOL
);
478 group
.group_
->set_bound (LEFT
, muscol
);
479 Align_interface::add_element (alignment_
,
483 if (scm_memq (fig
, get_property ("implicitBassFigures")) != SCM_BOOL_F
)
485 item
->set_property ("transparent", SCM_BOOL_T
);
486 item
->set_property ("implicit", SCM_BOOL_T
);
490 group
.alteration_
= group
.current_event_
->get_property ("alteration");
491 group
.augmented_
= group
.current_event_
->get_property ("augmented");
492 group
.diminished_
= group
.current_event_
->get_property ("diminished");
493 group
.augmented_slash_
= group
.current_event_
->get_property ("augmented-slash");
494 group
.text_
= group
.current_event_
->get_property ("text");
496 SCM text
= group
.text_
;
497 if (!Text_interface::is_markup (text
)
498 && ly_is_procedure (proc
))
500 text
= scm_call_3 (proc
, fig
, group
.current_event_
->self_scm (),
501 context ()->self_scm ());
504 item
->set_property ("text", text
);
506 Axis_group_interface::add_element (group
.group_
, item
);
507 group
.figure_item_
= item
;
510 if (group
.continuation_line_
)
513 UGH should connect to the bass staff, and get the note heads.
515 group
.figure_item_
->set_property ("transparent", SCM_BOOL_T
);
516 group
.continuation_line_
->set_bound (RIGHT
, group
.figure_item_
);
519 if (groups_
[i
].group_
)
520 groups_
[i
].group_
->set_bound (RIGHT
, muscol
);
526 ADD_TRANSLATOR (Figured_bass_engraver
,
528 "Make figured bass numbers.",
532 "BassFigureAlignment "
534 "BassFigureContinuation "
538 "figuredBassAlterationDirection "
539 "figuredBassCenterContinuations "
540 "figuredBassFormatter "
541 "implicitBassFigures "
542 "useBassFigureExtenders "
543 "ignoreFiguredBassRest ",