2 ligature-engraver.cc -- implement Ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2004 Juergen Reuter <reuter@ipd.uka.de>
9 #include "ligature-engraver.hh"
11 #include "score-engraver.hh"
12 #include "note-head.hh"
15 #include "translator-group.hh"
18 * This abstract class provides the general framework for ligatures of
19 * any kind. It cares for handling start/stop ligatures requests and
20 * collecting all noteheads inbetween, but delegates creation of a
21 * ligature spanner for each start/stop pair and typesetting of the
22 * ligature spanner to a concrete subclass.
24 * A concrete ligature engraver must subclass this class and provide
25 * functions create_ligature_spanner () and typeset_ligature
26 * (Spanner *, Array<Grob_info>). Subclasses of this class basically
27 * fall into two categories.
29 * The first category consists of engravers that engrave ligatures in
30 * a way that really deserves the name ligature. That is, they
31 * produce a single connected graphical object of fixed width,
32 * consisting of noteheads and other primitives. Space may be
33 * inserted only after each ligature, if necessary, but in no case
34 * between the primitives of the ligature. Accidentals have to be put
35 * to the left of the ligature, and not to the left of individual
36 * noteheads. Class Coherent_ligature_engraver is the common
37 * superclass for all of these engravers.
39 * The second category is for engravers that are relaxed in the sense
40 * that they do not require to produce a single connected graphical
41 * object. For example, in contemporary editions, ligatures are often
42 * marked, but otherwise use contemporary notation and spacing. In
43 * this category, there is currently only a single class,
44 * Ligature_bracket_engraver, which marks each ligature with a
45 * horizontal sqare bracket, but otherwise leaves the appearance
50 * TODO: lyrics/melisma/syllables: there should be at most one
51 * syllable of lyrics per ligature (i.e. for the lyrics context, a
52 * ligature should count as a single note, regardless of how many
53 * heads the ligature consists of).
55 * TODO: currently, you have to add/remove the proper
56 * Ligature_engraver (Ligature_bracket_engraver,
57 * Mensural_ligature_engraver) to the proper translator
58 * (e.g. VoiceContext) to choose between various representations.
59 * Since adding/removing an engraver to a translator is a global
60 * action in the paper block, you can not mix various representations
61 * _within_ the same score. Hence, for selecting a representation,
62 * one would rather like to have a property that can be set e.g. for
63 * several staves individually. However, it seems that this approach
64 * would require to have a single, complicated Ligature_engraver that
65 * consists of all the code... This needs further thoughts.
67 Ligature_engraver::Ligature_engraver ()
70 finished_ligature_
= 0;
71 reqs_drul_
[LEFT
] = reqs_drul_
[RIGHT
] = 0;
74 brew_ligature_primitive_proc
= SCM_EOL
;
78 Ligature_engraver::try_music (Music
*m
)
80 if (m
->is_mus_type ("ligature-event"))
82 Direction d
= to_dir (m
->get_mus_property ("span-direction"));
90 Ligature_engraver::create_ligature_spanner ()
92 programming_error ("Ligature_engraver::create_ligature_spanner (): "
93 "this is an abstract method that should not be called, "
94 "but overridden by a subclass");
99 * This method should do something that comes close to the following
102 * \property Voice.NoteHead \override #'molecule-callback =
103 * < value of #'ligature-primitive-callback of Voice.NoteHead >
105 * TODO: What we are doing here on the c++ level, should actually be
106 * performed on the SCM level. However, I do not know how to teach
107 * lilypond to apply an \override and \revert on #'molecule-callback,
108 * whenever lily encounters a \[ and \] in an .ly file, respectively.
109 * Also encounter, that lily should not crash if a user erronously
113 Ligature_engraver::override_molecule_callback ()
115 SCM symbol
= ly_symbol2scm ("NoteHead");
116 SCM target_callback
= ly_symbol2scm ("molecule-callback");
117 SCM source_callback
= ly_symbol2scm ("ligature-primitive-callback");
118 SCM noteHeadProperties
= daddy_trans_
->get_property ("NoteHead");
119 SCM value
= ly_cdr (scm_sloppy_assq (source_callback
, noteHeadProperties
));
120 execute_pushpop_property (daddy_trans_
, symbol
, target_callback
, value
);
124 * This method should do something that comes close to the following
127 * \property Voice.NoteHead \revert #'molecule-callback
129 * TODO: What we are doing here on the c++ level, should actually be
130 * performed on the SCM level. However, I do not know how to teach
131 * lilypond to apply an \override and \revert on #'molecule-callback,
132 * whenever lily encounters a \[ and \] in an .ly file, respectively.
133 * Also encounter, that lily should not crash if a user erronously
137 Ligature_engraver::revert_molecule_callback ()
139 SCM symbol
= ly_symbol2scm ("NoteHead");
140 SCM key
= ly_symbol2scm ("molecule-callback");
141 execute_pushpop_property (daddy_trans_
, symbol
, key
, SCM_UNDEFINED
);
145 Ligature_engraver::process_music ()
147 if (reqs_drul_
[STOP
])
151 reqs_drul_
[STOP
]->origin ()->warning (_ ("can't find start of ligature"));
157 reqs_drul_
[STOP
]->origin ()->warning (_ ("no right bound"));
161 ligature_
->set_bound (RIGHT
, last_bound_
);
165 finished_primitives_
= primitives_
;
166 finished_ligature_
= ligature_
;
167 primitives_
.clear ();
169 revert_molecule_callback ();
171 last_bound_
= unsmob_grob (get_property ("currentMusicalColumn"));
175 // TODO: maybe forbid breaks only if not transcribing
176 top_engraver ()->forbid_breaks ();
179 if (reqs_drul_
[START
])
183 reqs_drul_
[START
]->origin ()->warning (_ ("already have a ligature"));
187 prev_start_req_
= reqs_drul_
[START
];
188 ligature_
= create_ligature_spanner ();
189 brew_ligature_primitive_proc
=
190 ligature_
->get_grob_property ("ligature-primitive-callback");
191 if (brew_ligature_primitive_proc
== SCM_EOL
)
193 warning ("Ligature_engraver: ligature-primitive-callback undefined");
196 Grob
*bound
= unsmob_grob (get_property ("currentMusicalColumn"));
199 reqs_drul_
[START
]->origin ()->warning (_ ("no left bound"));
203 ligature_
->set_bound (LEFT
, bound
);
206 ligature_start_mom_
= now_mom ();
208 announce_grob(ligature_
, reqs_drul_
[START
]->self_scm());
209 override_molecule_callback ();
214 Ligature_engraver::typeset_ligature (Spanner
*, Array
<Grob_info
>)
216 programming_error ("Ligature_engraver::typeset_ligature (): "
217 "this is an abstract method that should not be called, "
218 "but overridden by a subclass");
222 Ligature_engraver::stop_translation_timestep ()
224 if (finished_ligature_
)
226 if (!finished_primitives_
.size ())
228 finished_ligature_
->programming_error ("Ligature_engraver::stop_translation_timestep (): "
229 "junking empty ligature");
233 typeset_ligature (finished_ligature_
, finished_primitives_
);
234 finished_primitives_
.clear ();
236 finished_ligature_
= 0;
239 reqs_drul_
[START
] = 0;
240 reqs_drul_
[STOP
] = 0;
244 Ligature_engraver::finalize ()
246 if (finished_ligature_
)
248 typeset_ligature (finished_ligature_
, finished_primitives_
);
249 finished_primitives_
.clear ();
250 finished_ligature_
= 0;
254 prev_start_req_
->origin ()->warning (_ ("unterminated ligature"));
255 ligature_
->suicide ();
260 Ligature_engraver::current_ligature ()
266 Ligature_engraver::acknowledge_grob (Grob_info info
)
270 if (Note_head::has_interface (info
.grob_
))
272 primitives_
.push (info
);
273 info
.grob_
->set_grob_property ("molecule-callback",
274 brew_ligature_primitive_proc
);
276 if (Rest::has_interface (info
.grob_
))
278 info
.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest"));
279 prev_start_req_
->origin ()->warning (_ ("ligature was started here"));
280 // TODO: maybe better should stop ligature here rather than
281 // ignoring the rest?
286 ENTER_DESCRIPTION (Ligature_engraver
,
287 /* descr */ "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
289 /* accepts */ "ligature-event",
290 /* acks */ "note-head-interface rest-interface",