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