2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1998--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
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.
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.
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/>.
21 #include "engraver.hh"
22 #include "international.hh"
23 #include "note-column.hh"
25 #include "stream-event.hh"
26 #include "tuplet-bracket.hh"
31 #include "translator.icc"
33 struct Tuplet_description
40 bool full_length_note_;
48 full_length_note_ = false;
55 class Tuplet_engraver : public Engraver
58 TRANSLATOR_DECLARATIONS (Tuplet_engraver);
61 vector<Tuplet_description> tuplets_;
62 vector<Tuplet_description> new_tuplets_;
63 vector<Tuplet_description> stopped_tuplets_;
64 vector<Spanner *> last_tuplets_;
66 void acknowledge_note_column (Grob_info);
67 void acknowledge_script (Grob_info);
68 void acknowledge_finger (Grob_info);
69 void acknowledge_string_number (Grob_info);
70 void listen_tuplet_span (Stream_event *);
71 virtual void finalize ();
72 void start_translation_timestep ();
73 void process_music ();
77 Tuplet_engraver::listen_tuplet_span (Stream_event *ev)
79 Direction dir = to_dir (ev->get_property ("span-direction"));
85 d.length_ = robust_scm2moment (d.event_->get_property ("length"),
87 d.start_moment_ = now_mom ();
88 d.stop_moment_ = now_mom () + d.length_;
90 for (vsize i = 0; i < new_tuplets_.size (); i++)
95 if (new_tuplets_[i].stop_moment_ == d.stop_moment_)
99 new_tuplets_.push_back (d);
101 else if (dir == STOP)
103 if (tuplets_.size ())
105 stopped_tuplets_.push_back (tuplets_.back ());
106 tuplets_.pop_back ();
108 else if (!to_boolean (get_property ("skipTypesetting")))
109 ev->origin ()->debug_output (_ ("No tuplet to end"));
112 ev->origin ()->programming_error ("direction tuplet-span-event_ invalid.");
116 Tuplet_engraver::process_music ()
119 This may happen if the end of a tuplet is part of a quoted voice.
121 Moment now = now_mom ();
122 for (vsize i = tuplets_.size (); i--;)
124 if (tuplets_[i].stop_moment_ == now)
126 stopped_tuplets_.push_back (tuplets_[i]);
127 tuplets_.erase (tuplets_.begin () + i);
131 for (vsize i = 0; i < stopped_tuplets_.size (); i++)
133 Spanner *bracket = stopped_tuplets_[i].bracket_;
134 Spanner *number = stopped_tuplets_[i].number_;
137 if (stopped_tuplets_[i].full_length_)
140 = unsmob<Item> (stopped_tuplets_[i].full_length_note_
141 ? get_property ("currentMusicalColumn")
142 : get_property ("currentCommandColumn"));
144 bracket->set_bound (RIGHT, col);
145 number->set_bound (RIGHT, col);
147 else if (!bracket->get_bound (RIGHT))
149 if (bracket->get_bound (LEFT))
151 bracket->set_bound (RIGHT,
152 bracket->get_bound (LEFT));
153 number->set_bound (RIGHT,
154 stopped_tuplets_[i].bracket_->get_bound (LEFT));
158 warning ("omitting tuplet bracket with neither left nor right bound");
162 // todo: scrap last_tuplets_, use stopped_tuplets_ only.
163 // clear stopped_tuplets_ at start_translation_timestep
164 last_tuplets_.push_back (bracket);
165 last_tuplets_.push_back (number);
168 stopped_tuplets_.clear ();
170 concat (tuplets_, new_tuplets_);
171 new_tuplets_.clear ();
172 for (vsize j = tuplets_.size (); j > 0; j--)
174 /* i goes from size-1 downto 0, inclusively */
177 if (tuplets_[i].bracket_)
180 tuplets_[i].full_length_ = to_boolean (get_property ("tupletFullLength"));
181 tuplets_[i].full_length_note_
182 = to_boolean (get_property ("tupletFullLengthNote"));
184 tuplets_[i].bracket_ = make_spanner ("TupletBracket",
185 tuplets_[i].event_->self_scm ());
186 tuplets_[i].number_ = make_spanner ("TupletNumber",
187 tuplets_[i].event_->self_scm ());
188 tuplets_[i].number_->set_object ("bracket", tuplets_[i].bracket_->self_scm ());
189 tuplets_[i].number_->set_parent (tuplets_[i].bracket_, X_AXIS);
190 tuplets_[i].number_->set_parent (tuplets_[i].bracket_, Y_AXIS);
191 tuplets_[i].bracket_->set_object ("tuplet-number", tuplets_[i].number_->self_scm ());
192 tuplets_[i].stop_moment_.grace_part_ = 0;
194 if (i + 1 < tuplets_.size () && tuplets_[i + 1].bracket_)
195 Tuplet_bracket::add_tuplet_bracket (tuplets_[i].bracket_, tuplets_[i + 1].bracket_);
197 if (i > 0 && tuplets_[i - 1].bracket_)
198 Tuplet_bracket::add_tuplet_bracket (tuplets_[i - 1].bracket_, tuplets_[i].bracket_);
204 Tuplet_engraver::acknowledge_note_column (Grob_info inf)
206 for (vsize j = 0; j < tuplets_.size (); j++)
207 if (tuplets_[j].bracket_)
209 Item *i = dynamic_cast<Item *> (inf.grob ());
210 Tuplet_bracket::add_column (tuplets_[j].bracket_, i);
211 add_bound_item (tuplets_[j].number_, i);
216 Tuplet_engraver::acknowledge_script (Grob_info inf)
218 for (vsize j = 0; j < tuplets_.size (); j++)
219 if (tuplets_[j].bracket_)
221 Item *i = dynamic_cast<Item *> (inf.grob ());
222 if (!i->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
223 Tuplet_bracket::add_script (tuplets_[j].bracket_, i);
228 Tuplet_engraver::acknowledge_finger (Grob_info inf)
230 for (vsize j = 0; j < tuplets_.size (); j++)
231 if (tuplets_[j].bracket_)
233 Item *i = dynamic_cast<Item *> (inf.grob ());
234 Tuplet_bracket::add_script (tuplets_[j].bracket_, i);
239 Tuplet_engraver::acknowledge_string_number (Grob_info inf)
241 for (vsize j = 0; j < tuplets_.size (); j++)
242 if (tuplets_[j].bracket_)
244 Item *i = dynamic_cast<Item *> (inf.grob ());
245 Tuplet_bracket::add_script (tuplets_[j].bracket_, i);
250 Tuplet_engraver::start_translation_timestep ()
252 last_tuplets_.clear ();
254 May seem superfluous, but necessary for skipTypesetting.
256 new_tuplets_.clear ();
260 Tuplet_engraver::finalize ()
262 if (to_boolean (get_property ("tupletFullLength")))
263 for (vsize i = 0; i < last_tuplets_.size (); i++)
265 Item *col = unsmob<Item> (get_property ("currentCommandColumn"));
266 last_tuplets_[i]->set_bound (RIGHT, col);
270 Tuplet_engraver::Tuplet_engraver (Context *c)
276 Tuplet_engraver::boot ()
278 ADD_LISTENER (Tuplet_engraver, tuplet_span);
279 ADD_ACKNOWLEDGER (Tuplet_engraver, note_column);
280 ADD_ACKNOWLEDGER (Tuplet_engraver, script);
281 ADD_ACKNOWLEDGER (Tuplet_engraver, finger);
282 ADD_ACKNOWLEDGER (Tuplet_engraver, string_number);
285 ADD_TRANSLATOR (Tuplet_engraver,
287 "Catch tuplet events and generate appropriate bracket.",
295 "tupletFullLengthNote ",