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