2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2008--2015 Han-Wen Nienhuys <hanwen@lilypond.org>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
23 #include "engraver.hh"
25 #include "axis-group-interface.hh"
26 #include "directional-element-interface.hh"
28 #include "side-position-interface.hh"
30 #include "stream-event.hh"
32 #include "translator.icc"
34 class Dynamic_align_engraver : public Engraver
36 TRANSLATOR_DECLARATIONS (Dynamic_align_engraver);
37 void acknowledge_rhythmic_head (Grob_info);
38 void acknowledge_stem (Grob_info);
39 void acknowledge_dynamic (Grob_info);
40 void acknowledge_footnote_spanner (Grob_info);
41 void acknowledge_end_dynamic (Grob_info);
44 virtual void stop_translation_timestep ();
47 void create_line_spanner (Grob *cause);
48 void set_spanner_bounds (Spanner *line, bool end);
50 Spanner *ended_line_; // Spanner manually broken, don't use it for new grobs
51 Spanner *current_dynamic_spanner_;
52 vector<Spanner *> ended_;
53 vector<Spanner *> started_;
54 vector<Grob *> scripts_;
55 vector<Grob *> support_;
57 set<Spanner *> running_;
60 Dynamic_align_engraver::Dynamic_align_engraver (Context *c)
65 current_dynamic_spanner_ = 0;
70 Dynamic_align_engraver::create_line_spanner (Grob *cause)
73 line_ = make_spanner ("DynamicLineSpanner", cause->self_scm ());
77 Dynamic_align_engraver::acknowledge_end_dynamic (Grob_info info)
79 if (has_interface<Spanner> (info.grob ()))
80 ended_.push_back (info.spanner ());
82 /* If the break flag is set, store the current spanner and let new dynamics
83 * create a new spanner
85 bool spanner_broken = current_dynamic_spanner_ == info.spanner ()
86 && to_boolean (current_dynamic_spanner_->get_property ("spanner-broken"));
87 if (spanner_broken && line_)
90 programming_error ("already have a force-ended DynamicLineSpanner.");
93 current_dynamic_spanner_ = 0;
98 Dynamic_align_engraver::acknowledge_footnote_spanner (Grob_info info)
100 Grob *parent = info.grob ()->get_parent (Y_AXIS);
102 && parent->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
103 Axis_group_interface::add_element (line_, info.grob ());
107 Dynamic_align_engraver::acknowledge_rhythmic_head (Grob_info info)
109 support_.push_back (info.grob ());
113 Dynamic_align_engraver::acknowledge_stem (Grob_info info)
115 support_.push_back (info.grob ());
119 Dynamic_align_engraver::acknowledge_dynamic (Grob_info info)
121 Stream_event *cause = info.event_cause ();
122 // Check whether an existing line spanner has the same direction
125 Direction line_dir = get_grob_direction (line_);
126 Direction grob_dir = to_dir (cause->get_property ("direction"));
128 // If we have an explicit direction for the new dynamic grob
129 // that differs from the current line spanner, break the spanner
130 if (grob_dir && (line_dir != grob_dir))
135 current_dynamic_spanner_ = 0;
139 create_line_spanner (info.grob ());
140 if (has_interface<Spanner> (info.grob ()))
142 started_.push_back (info.spanner ());
143 current_dynamic_spanner_ = info.spanner ();
145 else if (info.item ())
146 scripts_.push_back (info.item ());
148 info.grob ()->programming_error ("unknown dynamic grob");
150 Axis_group_interface::add_element (line_, info.grob ());
154 if (Direction d = to_dir (cause->get_property ("direction")))
155 set_grob_direction (line_, d);
160 Dynamic_align_engraver::set_spanner_bounds (Spanner *line, bool end)
165 for (LEFT_and_RIGHT (d))
167 if ((d == LEFT && !line->get_bound (LEFT))
168 || (end && d == RIGHT && !line->get_bound (RIGHT)))
170 vector<Spanner *> const &spanners
171 = (d == LEFT) ? started_ : ended_;
174 if (scripts_.size ())
176 else if (spanners.size ())
177 bound = spanners[0]->get_bound (d);
180 bound = unsmob<Grob> (get_property ("currentMusicalColumn"));
181 programming_error ("started DynamicLineSpanner but have no left bound");
184 line->set_bound (d, bound);
190 Dynamic_align_engraver::stop_translation_timestep ()
192 for (vsize i = 0; i < started_.size (); i++)
193 running_.insert (started_[i]);
194 for (vsize i = 0; i < ended_.size (); i++)
196 Spanner *sp = ended_[i];
198 set<Spanner *>::iterator it = running_.find (sp);
199 if (it != running_.end ())
202 started_[i]->programming_error ("lost track of this dynamic spanner");
205 bool end = line_ && running_.empty ();
207 // Set the proper bounds for the current spanner and for a spanner that
209 set_spanner_bounds (ended_line_, true);
210 set_spanner_bounds (line_, end);
211 // If the flag is set to break the spanner after the current child, don't
212 // add any more support points (needed e.g. for style=none, where the
213 // invisible spanner should NOT be shifted since we don't have a line).
214 bool spanner_broken = current_dynamic_spanner_
215 && to_boolean (current_dynamic_spanner_->get_property ("spanner-broken"));
216 for (vsize i = 0; line_ && !spanner_broken && i < support_.size (); i++)
217 Side_position_interface::add_support (line_, support_[i]);
232 Dynamic_align_engraver::boot ()
234 ADD_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
235 ADD_ACKNOWLEDGER (Dynamic_align_engraver, rhythmic_head);
236 ADD_ACKNOWLEDGER (Dynamic_align_engraver, stem);
237 ADD_ACKNOWLEDGER (Dynamic_align_engraver, footnote_spanner);
238 ADD_END_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
241 ADD_TRANSLATOR (Dynamic_align_engraver,
243 "Align hairpins and dynamic texts on a horizontal line.",
246 "DynamicLineSpanner ",
249 "currentMusicalColumn ",