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_
;
53 continuation_line_
= 0;
58 /* Reset (or init) all figure information to FALSE */
62 alteration_
= SCM_BOOL_F
;
63 augmented_
= SCM_BOOL_F
;
64 diminished_
= SCM_BOOL_F
;
65 augmented_slash_
= SCM_BOOL_F
;
68 /* Mark the members of the struct as used for the GUILE Garbage Collection */
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_
);
78 bool group_is_equal_to (Stream_event
*evt
) const
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
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");
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 ();
116 vector
<Figure_group
> groups_
;
118 vector
<Stream_event
*> new_events_
;
120 bool new_event_found_
;
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 ()
138 continuation_
= false;
140 new_event_found_
= false;
144 Figured_bass_engraver::derived_mark () const
146 for (vsize i
= 0; i
< groups_
.size (); i
++)
148 groups_
[i
].gc_mark ();
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;
168 Figured_bass_engraver::stop_translation_timestep ()
171 || now_mom ().main_part_
< stop_moment_
.main_part_
172 || now_mom ().grace_part_
< Rational (0))
176 for (vsize i
= 0; !found
&& i
< groups_
.size (); i
++)
177 found
= found
|| groups_
[i
].current_event_
;
183 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver
, rest
);
185 Figured_bass_engraver::listen_rest (Stream_event
*ev
)
190 IMPLEMENT_TRANSLATOR_LISTENER (Figured_bass_engraver
, bass_figure
);
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;
214 new_events_
.push_back (ev
);
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 ());
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_
);
251 center_continuations (consecutive_lines
);
252 consecutive_lines
.clear ();
258 Figured_bass_engraver::clear_spanners ()
263 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::process_music ()
290 bool use_extenders
= to_boolean (get_property ("useBassFigureExtenders"));
291 if (alignment_
&& !use_extenders
)
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 ()))
304 if (!new_event_found_
)
307 new_event_found_
= false;
310 Don't need to sync alignments, if we're not using extenders.
324 for (vsize i
= 0; i
< new_events_
.size (); i
++)
326 while (k
< groups_
.size ()
327 && groups_
[k
].current_event_
)
330 if (k
>= groups_
.size ())
333 groups_
.push_back (group
);
336 groups_
[k
].current_event_
= new_events_
[i
];
337 groups_
[k
].figure_item_
= 0;
341 for (vsize i
= 0; i
< groups_
.size (); i
++)
343 if (!groups_
[i
].is_continuation ())
345 groups_
[i
].reset_figure ();
351 vector
<int> junk_continuations
;
352 for (vsize i
= 0; i
< groups_
.size (); i
++)
354 Figure_group
&group
= groups_
[i
];
356 if (group
.is_continuation ())
358 if (!group
.continuation_line_
)
361 = make_spanner ("BassFigureContinuation", SCM_EOL
);
362 Item
* item
= group
.figure_item_
;
363 group
.continuation_line_
= line
;
364 line
->set_bound (LEFT
, item
);
367 Don't add as child. This will cache the wrong
368 (pre-break) stencil when callbacks are triggered.
370 line
->set_parent (group
.group_
, Y_AXIS
);
371 Pointer_group_interface::add_grob (line
, ly_symbol2scm ("figures"), item
);
373 group
.figure_item_
= 0;
376 else if (group
.continuation_line_
)
377 junk_continuations
.push_back (i
);
383 vector
<Spanner
*> consecutive
;
384 if (to_boolean (get_property ("figuredBassCenterContinuations")))
386 for (vsize i
= 0; i
<= junk_continuations
.size (); i
++)
388 if (i
< junk_continuations
.size ()
389 && (i
== 0 || junk_continuations
[i
-1] == junk_continuations
[i
] - 1))
390 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
393 center_continuations (consecutive
);
394 consecutive
.clear ();
395 if (i
< junk_continuations
.size ())
396 consecutive
.push_back (groups_
[junk_continuations
[i
]].continuation_line_
);
400 for (vsize i
= 0; i
< junk_continuations
.size (); i
++)
401 groups_
[junk_continuations
[i
]].continuation_line_
= 0;
409 Figured_bass_engraver::create_grobs ()
412 = dynamic_cast<Item
*> (unsmob_grob (get_property ("currentMusicalColumn")));
415 alignment_
= make_spanner ("BassFigureAlignment", SCM_EOL
);
416 alignment_
->set_bound (LEFT
, muscol
);
418 alignment_
->set_bound (RIGHT
, muscol
);
420 SCM proc
= get_property ("figuredBassFormatter");
421 for (vsize i
= 0; i
< groups_
.size (); i
++)
423 Figure_group
&group
= groups_
[i
];
425 if (group
.current_event_
)
428 = make_item ("BassFigure", group
.current_event_
->self_scm ());
429 group
.assign_from_event (group
.current_event_
, item
);
433 group
.group_
= make_spanner ("BassFigureLine", SCM_EOL
);
434 group
.group_
->set_bound (LEFT
, muscol
);
435 Align_interface::add_element (alignment_
, group
.group_
);
438 if (scm_memq (group
.number_
, get_property ("implicitBassFigures")) != SCM_BOOL_F
)
440 item
->set_property ("transparent", SCM_BOOL_T
);
441 item
->set_property ("implicit", SCM_BOOL_T
);
444 SCM text
= group
.text_
;
445 if (!Text_interface::is_markup (text
)
446 && ly_is_procedure (proc
))
448 text
= scm_call_3 (proc
, group
.number_
, group
.current_event_
->self_scm (),
449 context ()->self_scm ());
452 item
->set_property ("text", text
);
454 Axis_group_interface::add_element (group
.group_
, item
);
457 if (group
.continuation_line_
)
460 UGH should connect to the bass staff, and get the note heads.
462 group
.figure_item_
->set_property ("transparent", SCM_BOOL_T
);
463 group
.continuation_line_
->set_bound (RIGHT
, group
.figure_item_
);
466 if (groups_
[i
].group_
)
467 groups_
[i
].group_
->set_bound (RIGHT
, muscol
);
474 Figured_bass_engraver::add_brackets ()
476 vector
<Grob
*> encompass
;
478 for (vsize i
= 0; i
< groups_
.size (); i
++)
480 if (!groups_
[i
].current_event_
)
483 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-start")))
486 if (inside
&& groups_
[i
].figure_item_
)
487 encompass
.push_back (groups_
[i
].figure_item_
);
489 if (to_boolean (groups_
[i
].current_event_
->get_property ("bracket-stop")))
493 Item
* brack
= make_item ("BassFigureBracket", groups_
[i
].current_event_
->self_scm ());
494 for (vsize j
= 0; j
< encompass
.size (); j
++)
496 Pointer_group_interface::add_grob (brack
,
497 ly_symbol2scm ("elements"),
505 ADD_TRANSLATOR (Figured_bass_engraver
,
507 "Make figured bass numbers.",
511 "BassFigureAlignment "
513 "BassFigureContinuation "
517 "figuredBassAlterationDirection "
518 "figuredBassCenterContinuations "
519 "figuredBassFormatter "
520 "implicitBassFigures "
521 "useBassFigureExtenders "
522 "ignoreFiguredBassRest ",