]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
($(outdir)/%.pdf): add DVIPS_FLAGS. This will
[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 "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 ("ligature-event"))
81     {
82       Direction d = to_dir (m->get_mus_property ("span-direction"));
83       reqs_drul_[d] = m;
84       return true;
85     }
86   return false;
87 }
88
89 Spanner *
90 Ligature_engraver::create_ligature_spanner ()
91 {
92   programming_error ("Ligature_engraver::create_ligature_spanner (): "
93                      "this is an abstract method that should not be called, "
94                      "but overridden by a subclass");
95   return 0;
96 }
97
98 /*
99  * This method should do something that comes close to the following
100  * .ly snippet:
101  *
102  * \property Voice.NoteHead \override #'print-function =
103  *     < value of #'ligature-primitive-callback of Voice.NoteHead >
104  *
105  * TODO: What we are doing here on the c++ level, should actually be
106  * performed on the SCM level.  However, I do not know how to teach
107  * lilypond to apply an \override and \revert on #'print-function,
108  * whenever lily encounters a \[ and \] in an .ly file, respectively.
109  * Also encounter, that lily should not crash if a user erronously
110  * nests \[ and \].
111  */
112 void
113 Ligature_engraver::override_stencil_callback ()
114 {
115   SCM target_callback = ly_symbol2scm ("print-function");
116   SCM source_callback = ly_symbol2scm ("ligature-primitive-callback");
117   SCM noteHeadProperties = updated_grob_properties (daddy_trans_, ly_symbol2scm ("NoteHead"));
118   SCM value = ly_cdr (scm_sloppy_assq (source_callback, noteHeadProperties));
119   execute_pushpop_property (daddy_trans_, ly_symbol2scm ("NoteHead"),
120                             target_callback, value);
121 }
122
123 /*
124  * This method should do something that comes close to the following
125  * .ly snippet:
126  *
127  * \property Voice.NoteHead \revert #'print-function
128  *
129  * TODO: What we are doing here on the c++ level, should actually be
130  * performed on the SCM level.  However, I do not know how to teach
131  * lilypond to apply an \override and \revert on #'print-function,
132  * whenever lily encounters a \[ and \] in an .ly file, respectively.
133  * Also encounter, that lily should not crash if a user erronously
134  * nests \[ and \].
135  */
136 void
137 Ligature_engraver::revert_stencil_callback ()
138 {
139   SCM symbol = ly_symbol2scm ("NoteHead");
140   SCM key = ly_symbol2scm ("print-function");
141   execute_pushpop_property (daddy_trans_, symbol, key, SCM_UNDEFINED);
142 }
143
144 void
145 Ligature_engraver::process_music ()
146 {
147   if (reqs_drul_[STOP])
148     {
149       if (!ligature_)
150         {
151           reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
152           return;
153         }
154
155       if (!last_bound_)
156         {
157           reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
158         }
159       else
160         {
161           ligature_->set_bound (RIGHT, last_bound_);
162         }
163
164       prev_start_req_ = 0;
165       finished_primitives_ = primitives_;
166       finished_ligature_ = ligature_;
167       primitives_.clear ();
168       ligature_ = 0;
169       revert_stencil_callback ();
170     }
171   last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
172
173   if (ligature_)
174     {
175       // TODO: maybe forbid breaks only if not transcribing
176       top_engraver ()->forbid_breaks ();
177     }
178
179   if (reqs_drul_[START])
180     {
181       if (ligature_)
182         {
183           reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
184           return;
185         }
186
187       prev_start_req_ = reqs_drul_[START];
188       ligature_ = create_ligature_spanner ();
189       brew_ligature_primitive_proc =
190         ligature_->get_grob_property ("ligature-primitive-callback");
191       if (brew_ligature_primitive_proc == SCM_EOL)
192         {
193           warning ("Ligature_engraver: ligature-primitive-callback undefined");
194         }
195
196       Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
197       if (!bound)
198         {
199           reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
200         }
201       else
202         {
203           ligature_->set_bound (LEFT, bound);
204         }
205
206       ligature_start_mom_ = now_mom ();
207       
208       announce_grob(ligature_, reqs_drul_[START]->self_scm());
209       override_stencil_callback ();
210     }
211 }
212
213 void
214 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
215 {
216   programming_error ("Ligature_engraver::typeset_ligature (): "
217                      "this is an abstract method that should not be called, "
218                      "but overridden by a subclass");
219 }
220
221 void
222 Ligature_engraver::stop_translation_timestep ()
223 {
224   if (finished_ligature_)
225     {
226       if (!finished_primitives_.size ())
227         {
228           finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): "
229                                                  "junking empty ligature");
230         }
231       else
232         {
233           typeset_ligature (finished_ligature_, finished_primitives_);
234           finished_primitives_.clear ();
235         }
236       finished_ligature_ = 0;
237     }
238
239   reqs_drul_[START] = 0;
240   reqs_drul_[STOP] = 0;
241 }
242
243 void
244 Ligature_engraver::finalize ()
245 {
246   if (finished_ligature_)
247     {
248       typeset_ligature (finished_ligature_, finished_primitives_);
249       finished_primitives_.clear ();
250       finished_ligature_ = 0;
251     }
252   if (ligature_)
253     {
254       prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
255       ligature_->suicide ();
256     }
257 }
258
259 Spanner *
260 Ligature_engraver::current_ligature ()
261 {
262   return ligature_;
263 }
264
265 void
266 Ligature_engraver::acknowledge_grob (Grob_info info)
267 {
268   if (ligature_)
269     {
270       if (Note_head::has_interface (info.grob_))
271         {
272           primitives_.push (info);
273           info.grob_->set_grob_property ("print-function",
274                                          brew_ligature_primitive_proc);
275         }
276       if (Rest::has_interface (info.grob_))
277         {
278           info.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest"));
279           prev_start_req_->origin ()->warning (_ ("ligature was started here"));
280           // TODO: maybe better should stop ligature here rather than
281           // ignoring the rest?
282         }
283     }
284 }
285
286 ENTER_DESCRIPTION (Ligature_engraver,
287 /* descr */       "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
288 /* creats */      "",
289 /* accepts */     "ligature-event",
290 /* acks  */      "note-head-interface rest-interface",
291 /* reads */       "",
292 /* write */       "");