* lily/paper-book.cc: remove copyright & tagline. Remove
[lilypond.git] / lily / ligature-engraver.cc
blob0e221aa38d86911a27de38af9e9a0c122ec29f6a
1 /*
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>
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 "context.hh"
19 * This abstract class provides the general framework for ligatures of
20 * any kind. It cares for handling start/stop ligatures requests and
21 * collecting all noteheads inbetween, but delegates creation of a
22 * ligature spanner for each start/stop pair and typesetting of the
23 * ligature spanner to a concrete subclass.
25 * A concrete ligature engraver must subclass this class and provide
26 * functions create_ligature_spanner () and typeset_ligature
27 * (Spanner *, Array<Grob_info>). Subclasses of this class basically
28 * fall into two categories.
30 * The first category consists of engravers that engrave ligatures in
31 * a way that really deserves the name ligature. That is, they
32 * produce a single connected graphical object of fixed width,
33 * consisting of noteheads and other primitives. Space may be
34 * inserted only after each ligature, if necessary, but in no case
35 * between the primitives of the ligature. Accidentals have to be put
36 * to the left of the ligature, and not to the left of individual
37 * noteheads. Class Coherent_ligature_engraver is the common
38 * superclass for all of these engravers.
40 * The second category is for engravers that are relaxed in the sense
41 * that they do not require to produce a single connected graphical
42 * object. For example, in contemporary editions, ligatures are often
43 * marked, but otherwise use contemporary notation and spacing. In
44 * this category, there is currently only a single class,
45 * Ligature_bracket_engraver, which marks each ligature with a
46 * horizontal sqare bracket, but otherwise leaves the appearance
47 * untouched.
51 * TODO: lyrics/melisma/syllables: there should be at most one
52 * syllable of lyrics per ligature (i.e. for the lyrics context, a
53 * ligature should count as a single note, regardless of how many
54 * heads the ligature consists of).
56 * TODO: currently, you have to add/remove the proper
57 * Ligature_engraver (Ligature_bracket_engraver,
58 * Mensural_ligature_engraver) to the proper translator
59 * (e.g. VoiceContext) to choose between various representations.
60 * Since adding/removing an engraver to a translator is a global
61 * action in the paper block, you can not mix various representations
62 * _within_ the same score. Hence, for selecting a representation,
63 * one would rather like to have a property that can be set e.g. for
64 * several staves individually. However, it seems that this approach
65 * would require to have a single, complicated Ligature_engraver that
66 * consists of all the code... This needs further thoughts.
68 Ligature_engraver::Ligature_engraver ()
70 ligature_ = 0;
71 finished_ligature_ = 0;
72 reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0;
73 prev_start_req_ = 0;
74 last_bound_ = 0;
75 brew_ligature_primitive_proc = SCM_EOL;
78 bool
79 Ligature_engraver::try_music (Music *m)
81 if (m->is_mus_type ("ligature-event"))
83 Direction d = to_dir (m->get_property ("span-direction"));
84 reqs_drul_[d] = m;
85 return true;
87 return false;
90 Spanner *
91 Ligature_engraver::create_ligature_spanner ()
93 programming_error ("Ligature_engraver::create_ligature_spanner (): "
94 "this is an abstract method that should not be called, "
95 "but overridden by a subclass");
96 return 0;
100 * This method should do something that comes close to the following
101 * .ly snippet:
103 * \property Voice.NoteHead \override #'print-function =
104 * < value of #'ligature-primitive-callback of Voice.NoteHead >
106 * TODO: What we are doing here on the c++ level, should actually be
107 * performed on the SCM level. However, I do not know how to teach
108 * lilypond to apply an \override and \revert on #'print-function,
109 * whenever lily encounters a \[ and \] in an .ly file, respectively.
110 * Also encounter, that lily should not crash if a user erronously
111 * nests \[ and \].
113 void
114 Ligature_engraver::override_stencil_callback ()
116 SCM target_callback = ly_symbol2scm ("print-function");
117 SCM source_callback = ly_symbol2scm ("ligature-primitive-callback");
118 SCM noteHeadProperties = updated_grob_properties (context (), ly_symbol2scm ("NoteHead"));
119 SCM value = ly_cdr (scm_sloppy_assq (source_callback, noteHeadProperties));
120 execute_pushpop_property (context (), ly_symbol2scm ("NoteHead"),
121 target_callback, value);
125 * This method should do something that comes close to the following
126 * .ly snippet:
128 * \property Voice.NoteHead \revert #'print-function
130 * TODO: What we are doing here on the c++ level, should actually be
131 * performed on the SCM level. However, I do not know how to teach
132 * lilypond to apply an \override and \revert on #'print-function,
133 * whenever lily encounters a \[ and \] in an .ly file, respectively.
134 * Also encounter, that lily should not crash if a user erronously
135 * nests \[ and \].
137 void
138 Ligature_engraver::revert_stencil_callback ()
140 SCM symbol = ly_symbol2scm ("NoteHead");
141 SCM key = ly_symbol2scm ("print-function");
142 execute_pushpop_property (context (), symbol, key, SCM_UNDEFINED);
145 void
146 Ligature_engraver::process_music ()
148 if (reqs_drul_[STOP])
150 if (!ligature_)
152 reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
153 return;
156 if (!last_bound_)
158 reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
160 else
162 ligature_->set_bound (RIGHT, last_bound_);
165 prev_start_req_ = 0;
166 finished_primitives_ = primitives_;
167 finished_ligature_ = ligature_;
168 primitives_.clear ();
169 ligature_ = 0;
170 revert_stencil_callback ();
172 last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
174 if (ligature_)
176 // TODO: maybe forbid breaks only if not transcribing
177 get_score_engraver ()->forbid_breaks ();
180 if (reqs_drul_[START])
182 if (ligature_)
184 reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
185 return;
188 prev_start_req_ = reqs_drul_[START];
189 ligature_ = create_ligature_spanner ();
190 brew_ligature_primitive_proc =
191 ligature_->get_property ("ligature-primitive-callback");
192 if (brew_ligature_primitive_proc == SCM_EOL)
194 warning ("Ligature_engraver: ligature-primitive-callback undefined");
197 Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
198 if (!bound)
200 reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
202 else
204 ligature_->set_bound (LEFT, bound);
207 ligature_start_mom_ = now_mom ();
209 // TODO: dump cause into make_item/spanner.
210 // announce_grob (ligature_, reqs_drul_[START]->self_scm ());
211 override_stencil_callback ();
215 void
216 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
218 programming_error ("Ligature_engraver::typeset_ligature (): "
219 "this is an abstract method that should not be called, "
220 "but overridden by a subclass");
223 void
224 Ligature_engraver::stop_translation_timestep ()
226 if (finished_ligature_)
228 if (!finished_primitives_.size ())
230 finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): "
231 "junking empty ligature");
233 else
235 typeset_ligature (finished_ligature_, finished_primitives_);
236 finished_primitives_.clear ();
238 finished_ligature_ = 0;
241 reqs_drul_[START] = 0;
242 reqs_drul_[STOP] = 0;
245 void
246 Ligature_engraver::finalize ()
248 if (finished_ligature_)
250 typeset_ligature (finished_ligature_, finished_primitives_);
251 finished_primitives_.clear ();
252 finished_ligature_ = 0;
254 if (ligature_)
256 prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
257 ligature_->suicide ();
261 Spanner *
262 Ligature_engraver::current_ligature ()
264 return ligature_;
267 void
268 Ligature_engraver::acknowledge_grob (Grob_info info)
270 if (ligature_)
272 if (Note_head::has_interface (info.grob_))
274 primitives_.push (info);
275 info.grob_->set_property ("print-function",
276 brew_ligature_primitive_proc);
278 if (Rest::has_interface (info.grob_))
280 info.music_cause ()->origin ()->warning (_ ("ignoring rest: ligature may not contain rest"));
281 prev_start_req_->origin ()->warning (_ ("ligature was started here"));
282 // TODO: maybe better should stop ligature here rather than
283 // ignoring the rest?
288 ENTER_DESCRIPTION (Ligature_engraver,
289 /* descr */ "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
290 /* creats */ "",
291 /* accepts */ "ligature-event",
292 /* acks */ "note-head-interface rest-interface",
293 /* reads */ "",
294 /* write */ "");