2 ligature-engraver.cc -- implement Ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2006 Juergen Reuter <reuter@ipd.uka.de>
9 #include "ligature-engraver.hh"
12 #include "international.hh"
13 #include "note-head.hh"
16 #include "stream-event.hh"
19 #include "translator.icc"
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.
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.
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.
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
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).
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.
71 Ligature_engraver::Ligature_engraver ()
74 finished_ligature_ = 0;
75 events_drul_[LEFT] = events_drul_[RIGHT] = 0;
76 prev_start_event_ = 0;
78 brew_ligature_primitive_proc = SCM_EOL;
82 Ligature_engraver::listen_ligature (Stream_event *ev)
84 Direction d = to_dir (ev->get_property ("span-direction"));
89 Ligature_engraver::process_music ()
91 if (events_drul_[STOP])
95 events_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
100 events_drul_[STOP]->origin ()->warning (_ ("no right bound"));
102 ligature_->set_bound (RIGHT, last_bound_);
104 prev_start_event_ = 0;
105 finished_primitives_ = primitives_;
106 finished_ligature_ = ligature_;
107 primitives_.clear ();
110 last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
114 // TODO: maybe forbid breaks only if not transcribing
115 context ()->get_score_context ()->set_property ("forbidBreak", SCM_BOOL_T);
118 if (events_drul_[START])
122 events_drul_[START]->origin ()->warning (_ ("already have a ligature"));
126 prev_start_event_ = events_drul_[START];
127 ligature_ = create_ligature_spanner ();
129 Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
131 events_drul_[START]->origin ()->warning (_ ("no left bound"));
133 ligature_->set_bound (LEFT, bound);
135 ligature_start_mom_ = now_mom ();
137 // TODO: dump cause into make_item/spanner.
138 // announce_grob (ligature_, events_drul_[START]->self_scm ());
143 Ligature_engraver::stop_translation_timestep ()
145 if (finished_ligature_)
147 if (!finished_primitives_.size ())
149 finished_ligature_->programming_error ("Ligature_engraver::stop_translation_timestep (): "
150 "junking empty ligature");
154 typeset_ligature (finished_ligature_, finished_primitives_);
155 finished_primitives_.clear ();
157 finished_ligature_ = 0;
160 events_drul_[START] = 0;
161 events_drul_[STOP] = 0;
165 Ligature_engraver::finalize ()
167 if (finished_ligature_)
169 typeset_ligature (finished_ligature_, finished_primitives_);
170 finished_primitives_.clear ();
171 finished_ligature_ = 0;
175 prev_start_event_->origin ()->warning (_ ("unterminated ligature"));
176 ligature_->suicide ();
181 Ligature_engraver::current_ligature ()
187 Ligature_engraver::acknowledge_note_head (Grob_info info)
191 primitives_.push_back (info);
192 if (info.grob () && brew_ligature_primitive_proc != SCM_EOL)
194 info.grob ()->set_property ("stencil", brew_ligature_primitive_proc);
200 Ligature_engraver::acknowledge_rest (Grob_info info)
204 info.event_cause ()->origin ()->warning (_ ("ignoring rest: ligature may not contain rest"));
205 prev_start_event_->origin ()->warning (_ ("ligature was started here"));
206 // TODO: maybe better should stop ligature here rather than
207 // ignoring the rest?
211 // no ADD_ACKNOWLEDGER / ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls
212 // since this class is abstract