]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
* lily/coherent-ligature-engraver.cc,
[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 "ligature-head.hh"
11 #include "spanner.hh"
12 #include "score-engraver.hh"
13 #include "note-head.hh"
14 #include "rest.hh"
15 #include "warn.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 void
107 Ligature_engraver::process_music ()
108 {
109   if (reqs_drul_[STOP])
110     {
111       if (!ligature_)
112         reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
113       else
114         {
115           if (!last_bound_)
116             {
117               reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
118             }
119           else
120             {
121               ligature_->set_bound (RIGHT, last_bound_);
122             }
123         }
124       prev_start_req_ = 0;
125       finished_primitives_ = primitives_;
126       finished_ligature_ = ligature_;
127       primitives_.clear ();
128       ligature_ = 0;
129     }
130   last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
131
132   if (ligature_)
133     {
134       // TODO: maybe forbid breaks only if not transcribing
135       top_engraver ()->forbid_breaks ();
136     }
137   if (reqs_drul_[START])
138     {
139       if (ligature_)
140         {
141           reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
142           return;
143         }
144
145       prev_start_req_ = reqs_drul_[START];
146       ligature_ = create_ligature_spanner ();
147       brew_ligature_primitive_proc =
148         ligature_->get_grob_property ("ligature-primitive-callback");
149       if (brew_ligature_primitive_proc == SCM_EOL)
150         {
151           warning ("Ligature_engraver: ligature-primitive-callback undefined");
152         }
153
154       Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
155       if (!bound)
156         {
157           reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
158         }
159       else
160         {
161           ligature_->set_bound (LEFT, bound);
162         }
163
164       ligature_start_mom_ = now_mom ();
165       
166       announce_grob(ligature_, reqs_drul_[START]->self_scm());
167     }
168 }
169
170 void
171 Ligature_engraver::start_translation_timestep ()
172 {
173   reqs_drul_[START] = 0;
174   reqs_drul_[STOP] = 0;
175 }
176
177 void
178 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
179 {
180   programming_error ("Ligature_engraver::typeset_ligature (): "
181                      "this is an abstract method that should not be called, "
182                      "but overridden by a subclass");
183 }
184
185 void
186 Ligature_engraver::stop_translation_timestep ()
187 {
188   if (finished_ligature_)
189     {
190       typeset_ligature (finished_ligature_, finished_primitives_);
191       finished_primitives_.clear ();
192       finished_ligature_ = 0;
193     }
194 }
195
196 void
197 Ligature_engraver::finalize ()
198 {
199   if (finished_ligature_)
200     {
201       typeset_ligature (finished_ligature_, finished_primitives_);
202       finished_primitives_.clear ();
203       finished_ligature_ = 0;
204     }
205   if (ligature_)
206     {
207       prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
208       ligature_->suicide ();
209     }
210 }
211
212 Spanner *
213 Ligature_engraver::current_ligature ()
214 {
215   return ligature_;
216 }
217
218 void
219 Ligature_engraver::acknowledge_grob (Grob_info info)
220 {
221   if (ligature_)
222     {
223       if (Note_head::has_interface (info.grob_))
224         {
225           primitives_.push (info);
226         }
227       if (Ligature_head::has_interface (info.grob_))
228         {
229           info.grob_->set_grob_property ("ligature-primitive-callback",
230                                          brew_ligature_primitive_proc);
231         }
232       else if (Rest::has_interface (info.grob_))
233         {
234           info.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest"));
235           prev_start_req_->origin ()->warning (_ ("ligature was started here"));
236           // TODO: maybe better should stop ligature here rather than
237           // ignoring the rest?
238         }
239     }
240 }
241
242 ENTER_DESCRIPTION (Ligature_engraver,
243 /* descr */       "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
244 /* creats */      "",
245 /* accepts */     "ligature-event abort-event",
246 /* acks  */      "ligature-head-interface rest-interface",
247 /* reads */       "",
248 /* write */       "");