]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
(LY_DEFINE): use Scheme style naming for
[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--2004 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 "context.hh"
16
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 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.
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 (daddy_context_, ly_symbol2scm ("NoteHead"));
119   SCM value = ly_cdr (scm_sloppy_assq (source_callback, noteHeadProperties));
120   execute_pushpop_property (daddy_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 (daddy_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       announce_grob(ligature_, reqs_drul_[START]->self_scm());
210       override_stencil_callback ();
211     }
212 }
213
214 void
215 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
216 {
217   programming_error ("Ligature_engraver::typeset_ligature (): "
218                      "this is an abstract method that should not be called, "
219                      "but overridden by a subclass");
220 }
221
222 void
223 Ligature_engraver::stop_translation_timestep ()
224 {
225   if (finished_ligature_)
226     {
227       if (!finished_primitives_.size ())
228         {
229           finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): "
230                                                  "junking empty ligature");
231         }
232       else
233         {
234           typeset_ligature (finished_ligature_, finished_primitives_);
235           finished_primitives_.clear ();
236         }
237       finished_ligature_ = 0;
238     }
239
240   reqs_drul_[START] = 0;
241   reqs_drul_[STOP] = 0;
242 }
243
244 void
245 Ligature_engraver::finalize ()
246 {
247   if (finished_ligature_)
248     {
249       typeset_ligature (finished_ligature_, finished_primitives_);
250       finished_primitives_.clear ();
251       finished_ligature_ = 0;
252     }
253   if (ligature_)
254     {
255       prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
256       ligature_->suicide ();
257     }
258 }
259
260 Spanner *
261 Ligature_engraver::current_ligature ()
262 {
263   return ligature_;
264 }
265
266 void
267 Ligature_engraver::acknowledge_grob (Grob_info info)
268 {
269   if (ligature_)
270     {
271       if (Note_head::has_interface (info.grob_))
272         {
273           primitives_.push (info);
274           info.grob_->set_property ("print-function",
275                                          brew_ligature_primitive_proc);
276         }
277       if (Rest::has_interface (info.grob_))
278         {
279           info.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest"));
280           prev_start_req_->origin ()->warning (_ ("ligature was started here"));
281           // TODO: maybe better should stop ligature here rather than
282           // ignoring the rest?
283         }
284     }
285 }
286
287 ENTER_DESCRIPTION (Ligature_engraver,
288 /* descr */       "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
289 /* creats */      "",
290 /* accepts */     "ligature-event",
291 /* acks  */      "note-head-interface rest-interface",
292 /* reads */       "",
293 /* write */       "");