]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-align-engraver.cc
Run grand-replace (issue 3765)
[lilypond.git] / lily / dynamic-align-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2008--2014 Han-Wen Nienhuys <hanwen@lilypond.org>
5
6
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.
11
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.
16
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/>.
19 */
20
21 #include <set>
22
23 #include "engraver.hh"
24
25 #include "axis-group-interface.hh"
26 #include "directional-element-interface.hh"
27 #include "item.hh"
28 #include "side-position-interface.hh"
29 #include "spanner.hh"
30 #include "stream-event.hh"
31
32 #include "translator.icc"
33
34 class Dynamic_align_engraver : public Engraver
35 {
36   TRANSLATOR_DECLARATIONS (Dynamic_align_engraver);
37   DECLARE_ACKNOWLEDGER (rhythmic_head);
38   DECLARE_ACKNOWLEDGER (stem);
39   DECLARE_ACKNOWLEDGER (dynamic);
40   DECLARE_ACKNOWLEDGER (footnote_spanner);
41   DECLARE_END_ACKNOWLEDGER (dynamic);
42
43 protected:
44   virtual void stop_translation_timestep ();
45
46 private:
47   void create_line_spanner (Stream_event *cause);
48   void set_spanner_bounds (Spanner *line, bool end);
49   Spanner *line_;
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_;
56
57   set<Spanner *> running_;
58 };
59
60 Dynamic_align_engraver::Dynamic_align_engraver ()
61 {
62   line_ = 0;
63   ended_line_ = 0;
64   current_dynamic_spanner_ = 0;
65 }
66
67 ADD_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
68 ADD_ACKNOWLEDGER (Dynamic_align_engraver, rhythmic_head);
69 ADD_ACKNOWLEDGER (Dynamic_align_engraver, stem);
70 ADD_ACKNOWLEDGER (Dynamic_align_engraver, footnote_spanner);
71 ADD_END_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
72
73 void
74 Dynamic_align_engraver::create_line_spanner (Stream_event *event)
75 {
76   if (!line_)
77     line_ = make_spanner ("DynamicLineSpanner",
78                           event ? event->self_scm () : SCM_EOL);
79 }
80
81 void
82 Dynamic_align_engraver::acknowledge_end_dynamic (Grob_info info)
83 {
84   if (Spanner::has_interface (info.grob ()))
85     ended_.push_back (info.spanner ());
86
87   /* If the break flag is set, store the current spanner and let new dynamics
88    * create a new spanner
89    */
90   bool spanner_broken = current_dynamic_spanner_ == info.spanner ()
91                         && to_boolean (current_dynamic_spanner_->get_property ("spanner-broken"));
92   if (spanner_broken && line_)
93     {
94       if (ended_line_)
95         programming_error ("already have a force-ended DynamicLineSpanner.");
96       ended_line_ = line_;
97       line_ = 0;
98       current_dynamic_spanner_ = 0;
99     }
100 }
101
102 void
103 Dynamic_align_engraver::acknowledge_footnote_spanner (Grob_info info)
104 {
105   Grob *parent = info.grob ()->get_parent (Y_AXIS);
106   if (line_ && parent
107       && parent->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
108     Axis_group_interface::add_element (line_, info.grob ());
109 }
110
111 void
112 Dynamic_align_engraver::acknowledge_rhythmic_head (Grob_info info)
113 {
114   support_.push_back (info.grob ());
115 }
116
117 void
118 Dynamic_align_engraver::acknowledge_stem (Grob_info info)
119 {
120   support_.push_back (info.grob ());
121 }
122
123 void
124 Dynamic_align_engraver::acknowledge_dynamic (Grob_info info)
125 {
126   Stream_event *cause = info.event_cause ();
127   // Check whether an existing line spanner has the same direction
128   if (line_ && cause)
129     {
130       Direction line_dir = get_grob_direction (line_);
131       Direction grob_dir = to_dir (cause->get_property ("direction"));
132
133       // If we have an explicit direction for the new dynamic grob
134       // that differs from the current line spanner, break the spanner
135       if (grob_dir && (line_dir != grob_dir))
136         {
137           if (!ended_line_)
138             ended_line_ = line_;
139           line_ = 0;
140           current_dynamic_spanner_ = 0;
141         }
142     }
143
144   create_line_spanner (cause);
145   if (Spanner::has_interface (info.grob ()))
146     {
147       started_.push_back (info.spanner ());
148       current_dynamic_spanner_ = info.spanner ();
149     }
150   else if (info.item ())
151     scripts_.push_back (info.item ());
152   else
153     info.grob ()->programming_error ("unknown dynamic grob");
154
155   Axis_group_interface::add_element (line_, info.grob ());
156
157   if (cause)
158     {
159       if (Direction d = to_dir (cause->get_property ("direction")))
160         set_grob_direction (line_, d);
161     }
162 }
163
164 void
165 Dynamic_align_engraver::set_spanner_bounds (Spanner *line, bool end)
166 {
167   if (!line)
168     return;
169
170   for (LEFT_and_RIGHT (d))
171     {
172       if ((d == LEFT && !line->get_bound (LEFT))
173           || (end && d == RIGHT && !line->get_bound (RIGHT)))
174         {
175           vector<Spanner *> const &spanners
176             = (d == LEFT) ? started_ : ended_;
177
178           Grob *bound = 0;
179           if (scripts_.size ())
180             bound = scripts_[0];
181           else if (spanners.size ())
182             bound = spanners[0]->get_bound (d);
183           else
184             {
185               bound = unsmob_grob (get_property ("currentMusicalColumn"));
186               programming_error ("started DynamicLineSpanner but have no left bound");
187             }
188
189           line->set_bound (d, bound);
190         }
191     }
192 }
193
194 void
195 Dynamic_align_engraver::stop_translation_timestep ()
196 {
197   for (vsize i = 0; i < started_.size (); i++)
198     running_.insert (started_[i]);
199   for (vsize i = 0; i < ended_.size (); i++)
200     {
201       Spanner *sp = ended_[i];
202
203       set<Spanner *>::iterator it = running_.find (sp);
204       if (it != running_.end ())
205         running_.erase (it);
206       else
207         started_[i]->programming_error ("lost track of this dynamic spanner");
208     }
209
210   bool end = line_ && running_.empty ();
211
212   // Set the proper bounds for the current spanner and for a spanner that
213   // is ended now
214   set_spanner_bounds (ended_line_, true);
215   set_spanner_bounds (line_, end);
216   // If the flag is set to break the spanner after the current child, don't
217   // add any more support points (needed e.g. for style=none, where the
218   // invisible spanner should NOT be shifted since we don't have a line).
219   bool spanner_broken = current_dynamic_spanner_
220                         && to_boolean (current_dynamic_spanner_->get_property ("spanner-broken"));
221   for (vsize i = 0; line_ && !spanner_broken && i < support_.size (); i++)
222     Side_position_interface::add_support (line_, support_[i]);
223
224   if (end)
225     {
226       line_ = 0;
227     }
228
229   ended_line_ = 0;
230   ended_.clear ();
231   started_.clear ();
232   scripts_.clear ();
233   support_.clear ();
234 }
235
236 ADD_TRANSLATOR (Dynamic_align_engraver,
237                 /* doc */
238                 "Align hairpins and dynamic texts on a horizontal line.",
239
240                 /* create */
241                 "DynamicLineSpanner ",
242
243                 /* read */
244                 "currentMusicalColumn ",
245
246                 /* write */
247                 ""
248                );