]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
* flower
[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 /*
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.
24  *
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.
29  *
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.
39  *
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.
48  */
49
50 /*
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).
55  *
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 layout 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.
67  */
68 Ligature_engraver::Ligature_engraver ()
69 {
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;
76 }
77
78 bool
79 Ligature_engraver::try_music (Music *m)
80 {
81   if (m->is_mus_type ("ligature-event"))
82     {
83       Direction d = to_dir (m->get_property ("span-direction"));
84       reqs_drul_[d] = m;
85       return true;
86     }
87   return false;
88 }
89
90 Spanner *
91 Ligature_engraver::create_ligature_spanner ()
92 {
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;
97 }
98
99 /*
100  * This method should do something that comes close to the following
101  * .ly snippet:
102  *
103  * \property Voice.NoteHead \override #'print-function =
104  *     < value of #'ligature-primitive-callback of Voice.NoteHead >
105  *
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 \].
112  */
113 void
114 Ligature_engraver::override_stencil_callback ()
115 {
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 = scm_cdr (scm_sloppy_assq (source_callback, noteHeadProperties));
120   execute_pushpop_property (context (), ly_symbol2scm ("NoteHead"),
121                             target_callback, value);
122 }
123
124 /*
125  * This method should do something that comes close to the following
126  * .ly snippet:
127  *
128  * \property Voice.NoteHead \revert #'print-function
129  *
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 \].
136  */
137 void
138 Ligature_engraver::revert_stencil_callback ()
139 {
140   SCM symbol = ly_symbol2scm ("NoteHead");
141   SCM key = ly_symbol2scm ("print-function");
142   execute_pushpop_property (context (), symbol, key, SCM_UNDEFINED);
143 }
144
145 void
146 Ligature_engraver::process_music ()
147 {
148   if (reqs_drul_[STOP])
149     {
150       if (!ligature_)
151         {
152           reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
153           return;
154         }
155
156       if (!last_bound_)
157         {
158           reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
159         }
160       else
161         {
162           ligature_->set_bound (RIGHT, last_bound_);
163         }
164
165       prev_start_req_ = 0;
166       finished_primitives_ = primitives_;
167       finished_ligature_ = ligature_;
168       primitives_.clear ();
169       ligature_ = 0;
170       revert_stencil_callback ();
171     }
172   last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
173
174   if (ligature_)
175     {
176       // TODO: maybe forbid breaks only if not transcribing
177       get_score_engraver ()->forbid_breaks ();
178     }
179
180   if (reqs_drul_[START])
181     {
182       if (ligature_)
183         {
184           reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
185           return;
186         }
187
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)
193         {
194           warning ("Ligature_engraver: ligature-primitive-callback undefined");
195         }
196
197       Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
198       if (!bound)
199         {
200           reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
201         }
202       else
203         {
204           ligature_->set_bound (LEFT, bound);
205         }
206
207       ligature_start_mom_ = now_mom ();
208
209       // TODO: dump cause into make_item/spanner. 
210       // announce_grob (ligature_, reqs_drul_[START]->self_scm ());
211       override_stencil_callback ();
212     }
213 }
214
215 void
216 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
217 {
218   programming_error ("Ligature_engraver::typeset_ligature (): "
219                      "this is an abstract method that should not be called, "
220                      "but overridden by a subclass");
221 }
222
223 void
224 Ligature_engraver::stop_translation_timestep ()
225 {
226   if (finished_ligature_)
227     {
228       if (!finished_primitives_.size ())
229         {
230           finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): "
231                                                  "junking empty ligature");
232         }
233       else
234         {
235           typeset_ligature (finished_ligature_, finished_primitives_);
236           finished_primitives_.clear ();
237         }
238       finished_ligature_ = 0;
239     }
240
241   reqs_drul_[START] = 0;
242   reqs_drul_[STOP] = 0;
243 }
244
245 void
246 Ligature_engraver::finalize ()
247 {
248   if (finished_ligature_)
249     {
250       typeset_ligature (finished_ligature_, finished_primitives_);
251       finished_primitives_.clear ();
252       finished_ligature_ = 0;
253     }
254   if (ligature_)
255     {
256       prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
257       ligature_->suicide ();
258     }
259 }
260
261 Spanner *
262 Ligature_engraver::current_ligature ()
263 {
264   return ligature_;
265 }
266
267 void
268 Ligature_engraver::acknowledge_grob (Grob_info info)
269 {
270   if (ligature_)
271     {
272       if (Note_head::has_interface (info.grob_))
273         {
274           primitives_.push (info);
275           info.grob_->set_property ("print-function",
276                                     brew_ligature_primitive_proc);
277         }
278       if (Rest::has_interface (info.grob_))
279         {
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?
284         }
285     }
286 }
287
288 ADD_TRANSLATOR (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 */ "");