]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
* lily/my-lily-lexer.cc: add \accacciatura and \appoggiatura
[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--2003 Juergen Reuter <reuter@ipd.uka.de>
7   
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"
16
17 /*
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.
23  *
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.
28  *
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.
38  *
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.
47  */
48
49 /*
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).
54  *
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.
66  */
67 Ligature_engraver::Ligature_engraver ()
68 {
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;
75 }
76
77 bool
78 Ligature_engraver::try_music (Music *m)
79 {
80   if (m->is_mus_type ("abort-event"))
81     {
82       reqs_drul_[START] = 0;
83       reqs_drul_[STOP] = 0;
84       if (ligature_)
85         ligature_->suicide ();
86       ligature_ = 0;
87     }
88   else if (m->is_mus_type ("ligature-event"))
89     {
90       Direction d = to_dir (m->get_mus_property ("span-direction"));
91       reqs_drul_[d] = m;
92       return true;
93     }
94   return false;
95 }
96
97 Spanner *
98 Ligature_engraver::create_ligature_spanner ()
99 {
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;
104 }
105
106 /*
107  * This method should do something that comes close to the following
108  * .ly snippet:
109  *
110  * \property Voice.NoteHead \override #'molecule-callback =
111  *     < value of #'ligature-primitive-callback of Voice.NoteHead >
112  *
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 \].
119  */
120 void
121 Ligature_engraver::override_molecule_callback ()
122 {
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);
129 }
130
131 /*
132  * This method should do something that comes close to the following
133  * .ly snippet:
134  *
135  * \property Voice.NoteHead \revert #'molecule-callback
136  *
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 \].
143  */
144 void
145 Ligature_engraver::revert_molecule_callback ()
146 {
147   SCM symbol = ly_symbol2scm ("NoteHead");
148   SCM key = ly_symbol2scm ("molecule-callback");
149   daddy_trans_->execute_pushpop_property (symbol, key, SCM_UNDEFINED);
150 }
151
152 void
153 Ligature_engraver::process_music ()
154 {
155   if (reqs_drul_[STOP])
156     {
157       if (!ligature_)
158         {
159           reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
160           return;
161         }
162
163       if (!last_bound_)
164         {
165           reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
166         }
167       else
168         {
169           ligature_->set_bound (RIGHT, last_bound_);
170         }
171
172       prev_start_req_ = 0;
173       finished_primitives_ = primitives_;
174       finished_ligature_ = ligature_;
175       primitives_.clear ();
176       ligature_ = 0;
177       revert_molecule_callback ();
178     }
179   last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
180
181   if (ligature_)
182     {
183       // TODO: maybe forbid breaks only if not transcribing
184       top_engraver ()->forbid_breaks ();
185     }
186
187   if (reqs_drul_[START])
188     {
189       if (ligature_)
190         {
191           reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
192           return;
193         }
194
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)
200         {
201           warning ("Ligature_engraver: ligature-primitive-callback undefined");
202         }
203
204       Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
205       if (!bound)
206         {
207           reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
208         }
209       else
210         {
211           ligature_->set_bound (LEFT, bound);
212         }
213
214       ligature_start_mom_ = now_mom ();
215       
216       announce_grob(ligature_, reqs_drul_[START]->self_scm());
217       override_molecule_callback ();
218     }
219 }
220
221 void
222 Ligature_engraver::start_translation_timestep ()
223 {
224   reqs_drul_[START] = 0;
225   reqs_drul_[STOP] = 0;
226 }
227
228 void
229 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
230 {
231   programming_error ("Ligature_engraver::typeset_ligature (): "
232                      "this is an abstract method that should not be called, "
233                      "but overridden by a subclass");
234 }
235
236 void
237 Ligature_engraver::stop_translation_timestep ()
238 {
239   if (finished_ligature_)
240     {
241       typeset_ligature (finished_ligature_, finished_primitives_);
242       finished_primitives_.clear ();
243       finished_ligature_ = 0;
244     }
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_req_->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_grob (Grob_info info)
271 {
272   if (ligature_)
273     {
274       if (Note_head::has_interface (info.grob_))
275         {
276           primitives_.push (info);
277           info.grob_->set_grob_property ("molecule-callback",
278                                          brew_ligature_primitive_proc);
279         }
280       if (Rest::has_interface (info.grob_))
281         {
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?
286         }
287     }
288 }
289
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 */       "");