]> git.donarmstrong.com Git - lilypond.git/blob - lily/ligature-engraver.cc
Fix some bugs in the dynamic engraver and PostScript backend
[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--2006 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
9 #include "ligature-engraver.hh"
10
11 #include "context.hh"
12 #include "international.hh"
13 #include "note-head.hh"
14 #include "rest.hh"
15 #include "score-context.hh"
16 #include "spanner.hh"
17 #include "warn.hh"
18
19 #include "translator.icc"
20
21 /*
22  * This abstract class provides the general framework for ligatures of
23  * any kind.  It cares for handling start/stop ligatures events and
24  * collecting all noteheads inbetween, but delegates creation of a
25  * ligature spanner for each start/stop pair and typesetting of the
26  * ligature spanner to a concrete subclass.
27  *
28  * A concrete ligature engraver must subclass this class and provide
29  * functions create_ligature_spanner () and typeset_ligature
30  * (Spanner *, vector<Grob_info>).  Subclasses of this class basically
31  * fall into two categories.
32  *
33  * The first category consists of engravers that engrave ligatures in
34  * a way that really deserves the name ligature.  That is, they
35  * produce a single connected graphical object of fixed width,
36  * consisting of noteheads and other primitives.  Space may be
37  * inserted only after each ligature, if necessary, but in no case
38  * between the primitives of the ligature.  Accidentals have to be put
39  * to the left of the ligature, and not to the left of individual
40  * noteheads.  Class Coherent_ligature_engraver is the common
41  * superclass for all of these engravers.
42  *
43  * The second category is for engravers that are relaxed in the sense
44  * that they do not require to produce a single connected graphical
45  * object.  For example, in contemporary editions, ligatures are often
46  * marked, but otherwise use contemporary notation and spacing.  In
47  * this category, there is currently only a single class,
48  * Ligature_bracket_engraver, which marks each ligature with a
49  * horizontal sqare bracket, but otherwise leaves the appearance
50  * untouched.
51  */
52
53 /*
54  * TODO: lyrics/melisma/syllables: there should be at most one
55  * syllable of lyrics per ligature (i.e. for the lyrics context, a
56  * ligature should count as a single note, regardless of how many
57  * heads the ligature consists of).
58  *
59  * TODO: currently, you have to add/remove the proper
60  * Ligature_engraver (Ligature_bracket_engraver,
61  * Mensural_ligature_engraver) to the proper translator
62  * (e.g. VoiceContext) to choose between various representations.
63  * Since adding/removing an engraver to a translator is a global
64  * action in the layout block, you cannot mix various representations
65  * _within_ the same score.  Hence, for selecting a representation,
66  * one would rather like to have a property that can be set e.g. for
67  * several staves individually.  However, it seems that this approach
68  * would require to have a single, complicated Ligature_engraver that
69  * consists of all the code...  This needs further thoughts.
70  */
71 Ligature_engraver::Ligature_engraver ()
72 {
73   ligature_ = 0;
74   finished_ligature_ = 0;
75   events_drul_[LEFT] = events_drul_[RIGHT] = 0;
76   prev_start_event_ = 0;
77   last_bound_ = 0;
78   brew_ligature_primitive_proc = SCM_EOL;
79 }
80
81 bool
82 Ligature_engraver::try_music (Music *m)
83 {
84   if (m->is_mus_type ("ligature-event"))
85     {
86       Direction d = to_dir (m->get_property ("span-direction"));
87       events_drul_[d] = m;
88       return true;
89     }
90   return false;
91 }
92
93 void
94 Ligature_engraver::process_music ()
95 {
96   if (events_drul_[STOP])
97     {
98       if (!ligature_)
99         {
100           events_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
101           return;
102         }
103
104       if (!last_bound_)
105         events_drul_[STOP]->origin ()->warning (_ ("no right bound"));
106       else
107         ligature_->set_bound (RIGHT, last_bound_);
108
109       prev_start_event_ = 0;
110       finished_primitives_ = primitives_;
111       finished_ligature_ = ligature_;
112       primitives_.clear ();
113       ligature_ = 0;
114     }
115   last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
116
117   if (ligature_)
118     {
119       // TODO: maybe forbid breaks only if not transcribing
120       context ()->get_score_context ()->set_property ("forbidBreak", SCM_BOOL_T);
121     }
122
123   if (events_drul_[START])
124     {
125       if (ligature_)
126         {
127           events_drul_[START]->origin ()->warning (_ ("already have a ligature"));
128           return;
129         }
130
131       prev_start_event_ = events_drul_[START];
132       ligature_ = create_ligature_spanner ();
133
134       Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
135       if (!bound)
136         events_drul_[START]->origin ()->warning (_ ("no left bound"));
137       else
138         ligature_->set_bound (LEFT, bound);
139
140       ligature_start_mom_ = now_mom ();
141
142       // TODO: dump cause into make_item/spanner. 
143       // announce_grob (ligature_, events_drul_[START]->self_scm ());
144     }
145 }
146
147 void
148 Ligature_engraver::stop_translation_timestep ()
149 {
150   if (finished_ligature_)
151     {
152       if (!finished_primitives_.size ())
153         {
154           finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): "
155                                                  "junking empty ligature");
156         }
157       else
158         {
159           typeset_ligature (finished_ligature_, finished_primitives_);
160           finished_primitives_.clear ();
161         }
162       finished_ligature_ = 0;
163     }
164
165   events_drul_[START] = 0;
166   events_drul_[STOP] = 0;
167 }
168
169 void
170 Ligature_engraver::finalize ()
171 {
172   if (finished_ligature_)
173     {
174       typeset_ligature (finished_ligature_, finished_primitives_);
175       finished_primitives_.clear ();
176       finished_ligature_ = 0;
177     }
178   if (ligature_)
179     {
180       prev_start_event_->origin ()->warning (_ ("unterminated ligature"));
181       ligature_->suicide ();
182     }
183 }
184
185 Spanner *
186 Ligature_engraver::current_ligature ()
187 {
188   return ligature_;
189 }
190
191 void
192 Ligature_engraver::acknowledge_note_head (Grob_info info)
193 {
194   if (ligature_)
195     {
196       primitives_.push_back (info);
197       if (info.grob () && (brew_ligature_primitive_proc != SCM_EOL))
198         {
199           info.grob ()->set_property ("stencil", brew_ligature_primitive_proc);
200         }
201     }
202 }
203
204 void
205 Ligature_engraver::acknowledge_rest (Grob_info info)
206 {
207   if (ligature_)
208     {
209       info.music_cause ()->origin ()->warning (_ ("ignoring rest: ligature may not contain rest"));
210       prev_start_event_->origin ()->warning (_ ("ligature was started here"));
211       // TODO: maybe better should stop ligature here rather than
212       // ignoring the rest?
213     }
214 }
215
216 // no ADD_ACKNOWLEDGER / ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls
217 // since this class is abstract