2 ligature-engraver.cc -- implement Ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2009 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"
17 #include "translator.icc"
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.
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.
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.
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
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).
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.
69 Ligature_engraver::Ligature_engraver ()
72 finished_ligature_
= 0;
73 events_drul_
[LEFT
] = events_drul_
[RIGHT
] = 0;
74 prev_start_event_
= 0;
76 brew_ligature_primitive_proc
= SCM_EOL
;
80 Ligature_engraver::listen_ligature (Stream_event
*ev
)
82 Direction d
= to_dir (ev
->get_property ("span-direction"));
83 ASSIGN_EVENT_ONCE (events_drul_
[d
], ev
);
87 Ligature_engraver::process_music ()
89 if (events_drul_
[STOP
])
93 events_drul_
[STOP
]->origin ()->warning (_ ("cannot find start of ligature"));
98 events_drul_
[STOP
]->origin ()->warning (_ ("no right bound"));
100 ligature_
->set_bound (RIGHT
, last_bound_
);
102 prev_start_event_
= 0;
103 finished_primitives_
= primitives_
;
104 finished_ligature_
= ligature_
;
105 primitives_
.clear ();
108 last_bound_
= unsmob_grob (get_property ("currentMusicalColumn"));
112 // TODO: maybe forbid breaks only if not transcribing
113 context ()->get_score_context ()->set_property ("forbidBreak", SCM_BOOL_T
);
116 if (events_drul_
[START
])
120 events_drul_
[START
]->origin ()->warning (_ ("already have a ligature"));
124 prev_start_event_
= events_drul_
[START
];
125 ligature_
= create_ligature_spanner ();
127 Grob
*bound
= unsmob_grob (get_property ("currentMusicalColumn"));
129 events_drul_
[START
]->origin ()->warning (_ ("no left bound"));
131 ligature_
->set_bound (LEFT
, bound
);
133 ligature_start_mom_
= now_mom ();
135 // TODO: dump cause into make_item/spanner.
136 // announce_grob (ligature_, events_drul_[START]->self_scm ());
141 Ligature_engraver::stop_translation_timestep ()
143 if (finished_ligature_
)
145 if (!finished_primitives_
.size ())
147 finished_ligature_
->programming_error ("Ligature_engraver::stop_translation_timestep (): "
148 "junking empty ligature");
152 typeset_ligature (finished_ligature_
, finished_primitives_
);
153 finished_primitives_
.clear ();
155 finished_ligature_
= 0;
158 events_drul_
[START
] = 0;
159 events_drul_
[STOP
] = 0;
163 Ligature_engraver::finalize ()
165 if (finished_ligature_
)
167 typeset_ligature (finished_ligature_
, finished_primitives_
);
168 finished_primitives_
.clear ();
169 finished_ligature_
= 0;
173 prev_start_event_
->origin ()->warning (_ ("unterminated ligature"));
174 ligature_
->suicide ();
179 Ligature_engraver::current_ligature ()
185 Ligature_engraver::acknowledge_note_head (Grob_info info
)
189 primitives_
.push_back (info
);
190 if (info
.grob () && brew_ligature_primitive_proc
!= SCM_EOL
)
192 info
.grob ()->set_property ("stencil", brew_ligature_primitive_proc
);
198 Ligature_engraver::acknowledge_rest (Grob_info info
)
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?
209 // no ADD_ACKNOWLEDGER / ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls
210 // since this class is abstract