(process_acknowledged_grobs):
[lilypond.git] / lily / ligature-engraver.cc
blobe355e5c3ece1ed2f50bd1315fbfc0b0ece872f19
1 /*
2 ligature-engraver.cc -- implement Ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
8 */
9 #include "ligature-engraver.hh"
10 #include "spanner.hh"
11 #include "score-engraver.hh"
12 #include "note-head.hh"
13 #include "rest.hh"
14 #include "warn.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
46 * untouched.
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 ()
69 ligature_ = 0;
70 finished_ligature_ = 0;
71 reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0;
72 prev_start_req_ = 0;
73 last_bound_ = 0;
74 brew_ligature_primitive_proc = SCM_EOL;
77 bool
78 Ligature_engraver::try_music (Music *m)
80 if (m->is_mus_type ("abort-event"))
82 reqs_drul_[START] = 0;
83 reqs_drul_[STOP] = 0;
84 if (ligature_)
85 ligature_->suicide ();
86 ligature_ = 0;
88 else if (m->is_mus_type ("ligature-event"))
90 Direction d = to_dir (m->get_mus_property ("span-direction"));
91 reqs_drul_[d] = m;
92 return true;
94 return false;
97 Spanner *
98 Ligature_engraver::create_ligature_spanner ()
100 programming_error ("Ligature_engraver::create_ligature_spanner (): "
101 "this is an abstract method that should not be called, "
102 "but overridden by a subclass");
103 return 0;
107 * This method should do something that comes close to the following
108 * .ly snippet:
110 * \property Voice.NoteHead \override #'molecule-callback =
111 * < value of #'ligature-primitive-callback of Voice.NoteHead >
113 * TODO: What we are doing here on the c++ level, should actually be
114 * performed on the SCM level. However, I do not know how to teach
115 * lilypond to apply an \override and \revert on #'molecule-callback,
116 * whenever lily encounters a \[ and \] in an .ly file, respectively.
117 * Also encounter, that lily should not crash if a user erronously
118 * nests \[ and \].
120 void
121 Ligature_engraver::override_molecule_callback ()
123 SCM symbol = ly_symbol2scm ("NoteHead");
124 SCM target_callback = ly_symbol2scm ("molecule-callback");
125 SCM source_callback = ly_symbol2scm ("ligature-primitive-callback");
126 SCM noteHeadProperties = daddy_trans_->get_property ("NoteHead");
127 SCM value = ly_cdr (scm_sloppy_assq (source_callback, noteHeadProperties));
128 daddy_trans_->execute_pushpop_property (symbol, target_callback, value);
132 * This method should do something that comes close to the following
133 * .ly snippet:
135 * \property Voice.NoteHead \revert #'molecule-callback
137 * TODO: What we are doing here on the c++ level, should actually be
138 * performed on the SCM level. However, I do not know how to teach
139 * lilypond to apply an \override and \revert on #'molecule-callback,
140 * whenever lily encounters a \[ and \] in an .ly file, respectively.
141 * Also encounter, that lily should not crash if a user erronously
142 * nests \[ and \].
144 void
145 Ligature_engraver::revert_molecule_callback ()
147 SCM symbol = ly_symbol2scm ("NoteHead");
148 SCM key = ly_symbol2scm ("molecule-callback");
149 daddy_trans_->execute_pushpop_property (symbol, key, SCM_UNDEFINED);
152 void
153 Ligature_engraver::process_music ()
155 if (reqs_drul_[STOP])
157 if (!ligature_)
159 reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
160 return;
163 if (!last_bound_)
165 reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
167 else
169 ligature_->set_bound (RIGHT, last_bound_);
172 prev_start_req_ = 0;
173 finished_primitives_ = primitives_;
174 finished_ligature_ = ligature_;
175 primitives_.clear ();
176 ligature_ = 0;
177 revert_molecule_callback ();
179 last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
181 if (ligature_)
183 // TODO: maybe forbid breaks only if not transcribing
184 top_engraver ()->forbid_breaks ();
187 if (reqs_drul_[START])
189 if (ligature_)
191 reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
192 return;
195 prev_start_req_ = reqs_drul_[START];
196 ligature_ = create_ligature_spanner ();
197 brew_ligature_primitive_proc =
198 ligature_->get_grob_property ("ligature-primitive-callback");
199 if (brew_ligature_primitive_proc == SCM_EOL)
201 warning ("Ligature_engraver: ligature-primitive-callback undefined");
204 Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
205 if (!bound)
207 reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
209 else
211 ligature_->set_bound (LEFT, bound);
214 ligature_start_mom_ = now_mom ();
216 announce_grob(ligature_, reqs_drul_[START]->self_scm());
217 override_molecule_callback ();
221 void
222 Ligature_engraver::start_translation_timestep ()
224 reqs_drul_[START] = 0;
225 reqs_drul_[STOP] = 0;
228 void
229 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
231 programming_error ("Ligature_engraver::typeset_ligature (): "
232 "this is an abstract method that should not be called, "
233 "but overridden by a subclass");
236 void
237 Ligature_engraver::stop_translation_timestep ()
239 if (finished_ligature_)
241 typeset_ligature (finished_ligature_, finished_primitives_);
242 finished_primitives_.clear ();
243 finished_ligature_ = 0;
247 void
248 Ligature_engraver::finalize ()
250 if (finished_ligature_)
252 typeset_ligature (finished_ligature_, finished_primitives_);
253 finished_primitives_.clear ();
254 finished_ligature_ = 0;
256 if (ligature_)
258 prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
259 ligature_->suicide ();
263 Spanner *
264 Ligature_engraver::current_ligature ()
266 return ligature_;
269 void
270 Ligature_engraver::acknowledge_grob (Grob_info info)
272 if (ligature_)
274 if (Note_head::has_interface (info.grob_))
276 primitives_.push (info);
277 info.grob_->set_grob_property ("molecule-callback",
278 brew_ligature_primitive_proc);
280 if (Rest::has_interface (info.grob_))
282 info.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest"));
283 prev_start_req_->origin ()->warning (_ ("ligature was started here"));
284 // TODO: maybe better should stop ligature here rather than
285 // ignoring the rest?
290 ENTER_DESCRIPTION (Ligature_engraver,
291 /* descr */ "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
292 /* creats */ "",
293 /* accepts */ "ligature-event abort-event",
294 /* acks */ "note-head-interface rest-interface",
295 /* reads */ "",
296 /* write */ "");