]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
(lilypond-all): clear anonymous modules after
[lilypond.git] / lily / ligature-engraver.cc
1 /*
2   ligature-engraver.cc -- implement Ligature_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2002--2005 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
9 #include "ligature-engraver.hh"
10
11 #include "spanner.hh"
12 #include "score-engraver.hh"
13 #include "note-head.hh"
14 #include "rest.hh"
15 #include "warn.hh"
16 #include "context.hh"
17
18 #include "translator.icc"
19
20 /*
21  * This abstract class provides the general framework for ligatures of
22  * any kind.  It cares for handling start/stop ligatures events and
23  * collecting all noteheads inbetween, but delegates creation of a
24  * ligature spanner for each start/stop pair and typesetting of the
25  * ligature spanner to a concrete subclass.
26  *
27  * A concrete ligature engraver must subclass this class and provide
28  * functions create_ligature_spanner () and typeset_ligature
29  * (Spanner *, Array<Grob_info>).  Subclasses of this class basically
30  * fall into two categories.
31  *
32  * The first category consists of engravers that engrave ligatures in
33  * a way that really deserves the name ligature.  That is, they
34  * produce a single connected graphical object of fixed width,
35  * consisting of noteheads and other primitives.  Space may be
36  * inserted only after each ligature, if necessary, but in no case
37  * between the primitives of the ligature.  Accidentals have to be put
38  * to the left of the ligature, and not to the left of individual
39  * noteheads.  Class Coherent_ligature_engraver is the common
40  * superclass for all of these engravers.
41  *
42  * The second category is for engravers that are relaxed in the sense
43  * that they do not require to produce a single connected graphical
44  * object.  For example, in contemporary editions, ligatures are often
45  * marked, but otherwise use contemporary notation and spacing.  In
46  * this category, there is currently only a single class,
47  * Ligature_bracket_engraver, which marks each ligature with a
48  * horizontal sqare bracket, but otherwise leaves the appearance
49  * untouched.
50  */
51
52 /*
53  * TODO: lyrics/melisma/syllables: there should be at most one
54  * syllable of lyrics per ligature (i.e. for the lyrics context, a
55  * ligature should count as a single note, regardless of how many
56  * heads the ligature consists of).
57  *
58  * TODO: currently, you have to add/remove the proper
59  * Ligature_engraver (Ligature_bracket_engraver,
60  * Mensural_ligature_engraver) to the proper translator
61  * (e.g. VoiceContext) to choose between various representations.
62  * Since adding/removing an engraver to a translator is a global
63  * action in the layout block, you cannot mix various representations
64  * _within_ the same score.  Hence, for selecting a representation,
65  * one would rather like to have a property that can be set e.g. for
66  * several staves individually.  However, it seems that this approach
67  * would require to have a single, complicated Ligature_engraver that
68  * consists of all the code...  This needs further thoughts.
69  */
70 Ligature_engraver::Ligature_engraver ()
71 {
72   ligature_ = 0;
73   finished_ligature_ = 0;
74   events_drul_[LEFT] = events_drul_[RIGHT] = 0;
75   prev_start_event_ = 0;
76   last_bound_ = 0;
77   brew_ligature_primitive_proc = SCM_EOL;
78 }
79
80 bool
81 Ligature_engraver::try_music (Music *m)
82 {
83   if (m->is_mus_type ("ligature-event"))
84     {
85       Direction d = to_dir (m->get_property ("span-direction"));
86       events_drul_[d] = m;
87       return true;
88     }
89   return false;
90 }
91
92 Spanner *
93 Ligature_engraver::create_ligature_spanner ()
94 {
95   programming_error ("Ligature_engraver::create_ligature_spanner (): "
96                      "this is an abstract method that should not be called, "
97                      "but overridden by a subclass");
98   return 0;
99 }
100
101 /*
102  * This method should do something that comes close to the following
103  * .ly snippet:
104  *
105  * \property Voice.NoteHead \override #'print-function =
106  *     < value of #'ligature-primitive-callback of Voice.NoteHead >
107  *
108  * TODO: What we are doing here on the c++ level, should actually be
109  * performed on the SCM level.  However, I do not know how to teach
110  * lilypond to apply an \override and \revert on #'print-function,
111  * whenever lily encounters a \[ and \] in an .ly file, respectively.
112  * Also encounter, that lily should not crash if a user erronously
113  * nests \[ and \].
114  */
115 void
116 Ligature_engraver::override_stencil_callback ()
117 {
118   SCM target_callback = ly_symbol2scm ("print-function");
119   SCM source_callback = ly_symbol2scm ("ligature-primitive-callback");
120   SCM noteHeadProperties = updated_grob_properties (context (), ly_symbol2scm ("NoteHead"));
121   SCM value = scm_cdr (scm_sloppy_assq (source_callback, noteHeadProperties));
122   execute_pushpop_property (context (), ly_symbol2scm ("NoteHead"),
123                             target_callback, value);
124 }
125
126 /*
127  * This method should do something that comes close to the following
128  * .ly snippet:
129  *
130  * \property Voice.NoteHead \revert #'print-function
131  *
132  * TODO: What we are doing here on the c++ level, should actually be
133  * performed on the SCM level.  However, I do not know how to teach
134  * lilypond to apply an \override and \revert on #'print-function,
135  * whenever lily encounters a \[ and \] in an .ly file, respectively.
136  * Also encounter, that lily should not crash if a user erronously
137  * nests \[ and \].
138  */
139 void
140 Ligature_engraver::revert_stencil_callback ()
141 {
142   SCM symbol = ly_symbol2scm ("NoteHead");
143   SCM key = ly_symbol2scm ("print-function");
144   execute_pushpop_property (context (), symbol, key, SCM_UNDEFINED);
145 }
146
147 void
148 Ligature_engraver::process_music ()
149 {
150   if (events_drul_[STOP])
151     {
152       if (!ligature_)
153         {
154           events_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
155           return;
156         }
157
158       if (!last_bound_)
159         {
160           events_drul_[STOP]->origin ()->warning (_ ("no right bound"));
161         }
162       else
163         {
164           ligature_->set_bound (RIGHT, last_bound_);
165         }
166
167       prev_start_event_ = 0;
168       finished_primitives_ = primitives_;
169       finished_ligature_ = ligature_;
170       primitives_.clear ();
171       ligature_ = 0;
172       revert_stencil_callback ();
173     }
174   last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
175
176   if (ligature_)
177     {
178       // TODO: maybe forbid breaks only if not transcribing
179       get_score_engraver ()->forbid_breaks ();
180     }
181
182   if (events_drul_[START])
183     {
184       if (ligature_)
185         {
186           events_drul_[START]->origin ()->warning (_ ("already have a ligature"));
187           return;
188         }
189
190       prev_start_event_ = events_drul_[START];
191       ligature_ = create_ligature_spanner ();
192       brew_ligature_primitive_proc
193         = ligature_->get_property ("ligature-primitive-callback");
194       if (brew_ligature_primitive_proc == SCM_EOL)
195         {
196           programming_error ("Ligature_engraver: ligature-primitive-callback undefined");
197         }
198
199       Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
200       if (!bound)
201         {
202           events_drul_[START]->origin ()->warning (_ ("no left bound"));
203         }
204       else
205         {
206           ligature_->set_bound (LEFT, bound);
207         }
208
209       ligature_start_mom_ = now_mom ();
210
211       // TODO: dump cause into make_item/spanner. 
212       // announce_grob (ligature_, events_drul_[START]->self_scm ());
213       override_stencil_callback ();
214     }
215 }
216
217 void
218 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
219 {
220   programming_error ("Ligature_engraver::typeset_ligature (): "
221                      "this is an abstract method that should not be called, "
222                      "but overridden by a subclass");
223 }
224
225 void
226 Ligature_engraver::stop_translation_timestep ()
227 {
228   if (finished_ligature_)
229     {
230       if (!finished_primitives_.size ())
231         {
232           finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): "
233                                                  "junking empty ligature");
234         }
235       else
236         {
237           typeset_ligature (finished_ligature_, finished_primitives_);
238           finished_primitives_.clear ();
239         }
240       finished_ligature_ = 0;
241     }
242
243   events_drul_[START] = 0;
244   events_drul_[STOP] = 0;
245 }
246
247 void
248 Ligature_engraver::finalize ()
249 {
250   if (finished_ligature_)
251     {
252       typeset_ligature (finished_ligature_, finished_primitives_);
253       finished_primitives_.clear ();
254       finished_ligature_ = 0;
255     }
256   if (ligature_)
257     {
258       prev_start_event_->origin ()->warning (_ ("unterminated ligature"));
259       ligature_->suicide ();
260     }
261 }
262
263 Spanner *
264 Ligature_engraver::current_ligature ()
265 {
266   return ligature_;
267 }
268
269 void
270 Ligature_engraver::acknowledge_note_head (Grob_info info)
271 {
272   if (ligature_)
273     {
274       primitives_.push (info);
275       info.grob ()->set_property ("print-function",
276                                   brew_ligature_primitive_proc);
277     }
278 }
279
280 void
281 Ligature_engraver::acknowledge_rest (Grob_info info)
282 {
283   if (ligature_)
284     {
285       info.music_cause ()->origin ()->warning (_ ("ignoring rest: ligature may not contain rest"));
286       prev_start_event_->origin ()->warning (_ ("ligature was started here"));
287       // TODO: maybe better should stop ligature here rather than
288       // ignoring the rest?
289     }
290 }
291
292
293 ADD_ACKNOWLEDGER(Ligature_engraver, rest);
294 ADD_ACKNOWLEDGER(Ligature_engraver, note_head);
295 ADD_TRANSLATOR (Ligature_engraver,
296                 /* descr */ "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
297                 /* creats */ "",
298                 /* accepts */ "ligature-event",
299                 /* reads */ "",
300                 /* write */ "");