2002->2003
[lilypond.git] / lily / ligature-engraver.cc
blob17414179cb3db1064de6c6e0b3cc102117c3f6d0
1 /*
2 ligature-engraver.cc -- implement Ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
8 */
9 #include "ligature-engraver.hh"
10 #include "ligature-head.hh"
11 #include "spanner.hh"
12 #include "score-engraver.hh"
13 #include "note-head.hh"
14 #include "rest.hh"
15 #include "warn.hh"
18 * TODO: lyrics/melisma/syllables: there should be at most one
19 * syllable of lyrics per ligature (i.e. for the lyrics context, a
20 * ligature should count as a single note, regardless of how many
21 * heads the ligature consists of).
23 * TODO: currently, you have to add/remove the proper
24 * Ligature_engraver (Ligature_bracket_engraver,
25 * Mensural_ligature_engraver) to the proper translator
26 * (e.g. VoiceContext) to choose between various representations.
27 * Since adding/removing an engraver to a translator is a global
28 * action in the paper block, you can not mix various representations
29 * _within_ the same score. Hence, for selecting a representation,
30 * one would rather like to have a property that can be set e.g. for
31 * several staves individually. However, it seems that this approach
32 * would require to have a single, complicated Ligature_engraver that
33 * consists of all the code... This needs further thoughts.
35 Ligature_engraver::Ligature_engraver ()
37 ligature_ = 0;
38 finished_ligature_ = 0;
39 reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0;
40 prev_start_req_ = 0;
41 last_bound_ = 0;
42 brew_ligature_primitive_proc = SCM_EOL;
45 bool
46 Ligature_engraver::try_music (Music *m)
48 if (m->is_mus_type ("abort-event"))
50 reqs_drul_[START] = 0;
51 reqs_drul_[STOP] = 0;
52 if (ligature_)
53 ligature_->suicide ();
54 ligature_ = 0;
56 else if (m->is_mus_type ("ligature-event"))
58 Direction d = to_dir (m->get_mus_property ("span-direction"));
59 reqs_drul_[d] = m;
60 return true;
62 return false;
65 Spanner *
66 Ligature_engraver::create_ligature_spanner ()
68 programming_error ("Ligature_engraver::create_ligature_spanner (): "
69 "this is an abstract method that should not be called, "
70 "but overridden by a subclass");
71 return 0;
74 void
75 Ligature_engraver::process_music ()
77 if (reqs_drul_[STOP])
79 if (!ligature_)
80 reqs_drul_[STOP]->origin ()->warning (_ ("can't find start of ligature"));
81 else
83 if (!last_bound_)
85 reqs_drul_[STOP]->origin ()->warning (_ ("no right bound"));
87 else
89 ligature_->set_bound (RIGHT, last_bound_);
92 prev_start_req_ = 0;
93 finished_primitives_ = primitives_;
94 finished_ligature_ = ligature_;
95 primitives_.clear ();
96 ligature_ = 0;
98 last_bound_ = unsmob_grob (get_property ("currentMusicalColumn"));
100 if (ligature_)
102 // TODO: maybe forbid breaks only if not transcribing
103 top_engraver ()->forbid_breaks ();
105 if (reqs_drul_[START])
107 if (ligature_)
109 reqs_drul_[START]->origin ()->warning (_ ("already have a ligature"));
110 return;
113 prev_start_req_ = reqs_drul_[START];
114 ligature_ = create_ligature_spanner ();
115 brew_ligature_primitive_proc =
116 ligature_->get_grob_property ("ligature-primitive-callback");
117 if (brew_ligature_primitive_proc == SCM_EOL)
119 warning ("Ligature_engraver: ligature-primitive-callback undefined");
122 Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
123 if (!bound)
125 reqs_drul_[START]->origin ()->warning (_ ("no left bound"));
127 else
129 ligature_->set_bound (LEFT, bound);
132 ligature_start_mom_ = now_mom ();
134 announce_grob(ligature_, reqs_drul_[START]->self_scm());
138 void
139 Ligature_engraver::start_translation_timestep ()
141 reqs_drul_[START] = 0;
142 reqs_drul_[STOP] = 0;
145 void
146 Ligature_engraver::typeset_ligature (Spanner *, Array<Grob_info>)
148 programming_error ("Ligature_engraver::typeset_ligature (): "
149 "this is an abstract method that should not be called, "
150 "but overridden by a subclass");
153 void
154 Ligature_engraver::stop_translation_timestep ()
156 if (finished_ligature_)
158 typeset_ligature (finished_ligature_, finished_primitives_);
159 finished_primitives_.clear ();
160 finished_ligature_ = 0;
164 void
165 Ligature_engraver::finalize ()
167 if (finished_ligature_)
169 typeset_ligature (finished_ligature_, finished_primitives_);
170 finished_primitives_.clear ();
171 finished_ligature_ = 0;
173 if (ligature_)
175 prev_start_req_->origin ()->warning (_ ("unterminated ligature"));
176 ligature_->suicide ();
180 Spanner *
181 Ligature_engraver::current_ligature ()
183 return ligature_;
186 void
187 Ligature_engraver::acknowledge_grob (Grob_info info)
189 if (ligature_)
191 if (Note_head::has_interface (info.grob_))
193 primitives_.push (info);
195 if (Ligature_head::has_interface (info.grob_))
197 info.grob_->set_grob_property ("ligature-primitive-callback",
198 brew_ligature_primitive_proc);
200 else if (Rest::has_interface (info.grob_))
202 info.music_cause ()->origin ()->warning (_ ("ligature may not contain rest; ignoring rest"));
203 prev_start_req_->origin ()->warning (_ ("ligature was started here"));
204 // TODO: maybe better should stop ligature here rather than
205 // ignoring the rest?
210 ENTER_DESCRIPTION (Ligature_engraver,
211 /* descr */ "Abstract class; a concrete subclass handles Ligature_events by engraving Ligatures in a concrete style.",
212 /* creats */ "",
213 /* accepts */ "ligature-event abort-event",
214 /* acks */ "ligature-head-interface rest-interface",
215 /* reads */ "",
216 /* write */ "");