2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1998--2010 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 DECLARE_ACKNOWLEDGER (note_column
);
67 DECLARE_TRANSLATOR_LISTENER (tuplet_span
);
68 virtual void finalize ();
69 void start_translation_timestep ();
70 void process_music ();
73 IMPLEMENT_TRANSLATOR_LISTENER (Tuplet_engraver
, tuplet_span
);
75 Tuplet_engraver::listen_tuplet_span (Stream_event
*ev
)
77 Direction dir
= to_dir (ev
->get_property ("span-direction"));
83 d
.length_
= robust_scm2moment (d
.event_
->get_property ("length"),
85 d
.start_moment_
= now_mom ();
86 d
.stop_moment_
= now_mom () + d
.length_
;
88 for (vsize i
=0; i
< new_tuplets_
.size (); i
++)
93 if (new_tuplets_
[i
].stop_moment_
== d
.stop_moment_
)
97 new_tuplets_
.push_back (d
);
101 if (tuplets_
.size ())
103 stopped_tuplets_
.push_back (tuplets_
.back ());
104 tuplets_
.pop_back ();
106 else if (!to_boolean (get_property ("skipTypesetting")))
107 ev
->origin ()->warning (_ ("No tuplet to end"));
110 ev
->origin ()->programming_error ("direction tuplet-span-event_ invalid.");
114 Tuplet_engraver::process_music ()
117 This may happen if the end of a tuplet is part of a quoted voice.
119 Moment now
= now_mom ();
120 for (vsize i
= tuplets_
.size (); i
--; )
122 if (tuplets_
[i
].stop_moment_
== now
)
124 stopped_tuplets_
.push_back (tuplets_
[i
]);
125 tuplets_
.erase (tuplets_
.begin () + i
);
129 for (vsize i
= 0; i
< stopped_tuplets_
.size (); i
++)
131 Spanner
*bracket
= stopped_tuplets_
[i
].bracket_
;
132 Spanner
*number
= stopped_tuplets_
[i
].number_
;
135 if (stopped_tuplets_
[i
].full_length_
)
138 unsmob_item (stopped_tuplets_
[i
].full_length_note_
139 ? get_property ("currentMusicalColumn")
140 : get_property ("currentCommandColumn"));
142 bracket
->set_bound (RIGHT
, col
);
143 number
->set_bound (RIGHT
, col
);
145 else if (!bracket
->get_bound (RIGHT
))
147 if (bracket
->get_bound (LEFT
))
149 bracket
->set_bound (RIGHT
,
150 bracket
->get_bound (LEFT
));
151 number
->set_bound (RIGHT
,
152 stopped_tuplets_
[i
].bracket_
->get_bound (LEFT
));
155 programming_error ("stopped tuplet bracket has neither left nor right bound");
157 // todo: scrap last_tuplets_, use stopped_tuplets_ only.
158 // clear stopped_tuplets_ at start_translation_timestep
159 last_tuplets_
.push_back (bracket
);
160 last_tuplets_
.push_back (number
);
163 stopped_tuplets_
.clear ();
165 concat (tuplets_
, new_tuplets_
);
166 new_tuplets_
.clear ();
167 for (vsize j
= tuplets_
.size (); j
> 0; j
--)
169 /* i goes from size-1 downto 0, inclusively */
173 if (tuplets_
[i
].bracket_
)
176 tuplets_
[i
].full_length_
= to_boolean (get_property ("tupletFullLength"));
177 tuplets_
[i
].full_length_note_
178 = to_boolean (get_property ("tupletFullLengthNote"));
180 tuplets_
[i
].bracket_
= make_spanner ("TupletBracket",
181 tuplets_
[i
].event_
->self_scm ());
182 tuplets_
[i
].number_
= make_spanner ("TupletNumber",
183 tuplets_
[i
].event_
->self_scm ());
184 tuplets_
[i
].number_
->set_object ("bracket", tuplets_
[i
].bracket_
->self_scm ());
185 tuplets_
[i
].bracket_
->set_object ("tuplet-number", tuplets_
[i
].number_
->self_scm ());
186 tuplets_
[i
].stop_moment_
.grace_part_
= 0;
189 if (i
+ 1 < tuplets_
.size () && tuplets_
[i
+ 1].bracket_
)
190 Tuplet_bracket::add_tuplet_bracket (tuplets_
[i
].bracket_
, tuplets_
[i
+ 1].bracket_
);
192 if (i
> 0 && tuplets_
[i
- 1].bracket_
)
193 Tuplet_bracket::add_tuplet_bracket (tuplets_
[i
- 1].bracket_
, tuplets_
[i
].bracket_
);
199 Tuplet_engraver::acknowledge_note_column (Grob_info inf
)
201 for (vsize j
= 0; j
< tuplets_
.size (); j
++)
202 if (tuplets_
[j
].bracket_
)
204 Item
*i
= dynamic_cast<Item
*> (inf
.grob ());
205 Tuplet_bracket::add_column (tuplets_
[j
].bracket_
, i
);
206 add_bound_item (tuplets_
[j
].number_
, i
);
211 Tuplet_engraver::start_translation_timestep ()
213 last_tuplets_
.clear ();
215 May seem superfluous, but necessary for skipTypesetting.
217 new_tuplets_
.clear ();
221 Tuplet_engraver::finalize ()
223 if (to_boolean (get_property ("tupletFullLength")))
224 for (vsize i
= 0; i
< last_tuplets_
.size (); i
++)
226 Item
*col
= unsmob_item (get_property ("currentCommandColumn"));
227 last_tuplets_
[i
]->set_bound (RIGHT
, col
);
231 Tuplet_engraver::Tuplet_engraver ()
235 ADD_ACKNOWLEDGER (Tuplet_engraver
, note_column
);
236 ADD_TRANSLATOR (Tuplet_engraver
,
238 "Catch tuplet events and generate appropriate bracket.",
246 "tupletFullLengthNote ",