]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-align-engraver.cc
Doc-es: various updates.
[lilypond.git] / lily / dynamic-align-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2008--2015 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   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);
42
43 protected:
44   virtual void stop_translation_timestep ();
45
46 private:
47   void create_line_spanner (Grob *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 (Context *c)
61   : Engraver (c)
62 {
63   line_ = 0;
64   ended_line_ = 0;
65   current_dynamic_spanner_ = 0;
66 }
67
68
69 void
70 Dynamic_align_engraver::create_line_spanner (Grob *cause)
71 {
72   if (!line_)
73     line_ = make_spanner ("DynamicLineSpanner", cause->self_scm ());
74 }
75
76 void
77 Dynamic_align_engraver::acknowledge_end_dynamic (Grob_info info)
78 {
79   if (has_interface<Spanner> (info.grob ()))
80     ended_.push_back (info.spanner ());
81
82   /* If the break flag is set, store the current spanner and let new dynamics
83    * create a new spanner
84    */
85   bool spanner_broken = current_dynamic_spanner_ == info.spanner ()
86                         && to_boolean (current_dynamic_spanner_->get_property ("spanner-broken"));
87   if (spanner_broken && line_)
88     {
89       if (ended_line_)
90         programming_error ("already have a force-ended DynamicLineSpanner.");
91       ended_line_ = line_;
92       line_ = 0;
93       current_dynamic_spanner_ = 0;
94     }
95 }
96
97 void
98 Dynamic_align_engraver::acknowledge_footnote_spanner (Grob_info info)
99 {
100   Grob *parent = info.grob ()->get_parent (Y_AXIS);
101   if (line_ && parent
102       && parent->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
103     Axis_group_interface::add_element (line_, info.grob ());
104 }
105
106 void
107 Dynamic_align_engraver::acknowledge_rhythmic_head (Grob_info info)
108 {
109   support_.push_back (info.grob ());
110 }
111
112 void
113 Dynamic_align_engraver::acknowledge_stem (Grob_info info)
114 {
115   support_.push_back (info.grob ());
116 }
117
118 void
119 Dynamic_align_engraver::acknowledge_dynamic (Grob_info info)
120 {
121   Stream_event *cause = info.event_cause ();
122   // Check whether an existing line spanner has the same direction
123   if (line_ && cause)
124     {
125       Direction line_dir = get_grob_direction (line_);
126       Direction grob_dir = to_dir (cause->get_property ("direction"));
127
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))
131         {
132           if (!ended_line_)
133             ended_line_ = line_;
134           line_ = 0;
135           current_dynamic_spanner_ = 0;
136         }
137     }
138
139   create_line_spanner (info.grob ());
140   if (has_interface<Spanner> (info.grob ()))
141     {
142       started_.push_back (info.spanner ());
143       current_dynamic_spanner_ = info.spanner ();
144     }
145   else if (info.item ())
146     scripts_.push_back (info.item ());
147   else
148     info.grob ()->programming_error ("unknown dynamic grob");
149
150   Axis_group_interface::add_element (line_, info.grob ());
151
152   if (cause)
153     {
154       if (Direction d = to_dir (cause->get_property ("direction")))
155         set_grob_direction (line_, d);
156     }
157 }
158
159 void
160 Dynamic_align_engraver::set_spanner_bounds (Spanner *line, bool end)
161 {
162   if (!line)
163     return;
164
165   for (LEFT_and_RIGHT (d))
166     {
167       if ((d == LEFT && !line->get_bound (LEFT))
168           || (end && d == RIGHT && !line->get_bound (RIGHT)))
169         {
170           vector<Spanner *> const &spanners
171             = (d == LEFT) ? started_ : ended_;
172
173           Grob *bound = 0;
174           if (scripts_.size ())
175             bound = scripts_[0];
176           else if (spanners.size ())
177             bound = spanners[0]->get_bound (d);
178           else
179             {
180               bound = unsmob<Grob> (get_property ("currentMusicalColumn"));
181               programming_error ("started DynamicLineSpanner but have no left bound");
182             }
183
184           line->set_bound (d, bound);
185         }
186     }
187 }
188
189 void
190 Dynamic_align_engraver::stop_translation_timestep ()
191 {
192   for (vsize i = 0; i < started_.size (); i++)
193     running_.insert (started_[i]);
194   for (vsize i = 0; i < ended_.size (); i++)
195     {
196       Spanner *sp = ended_[i];
197
198       set<Spanner *>::iterator it = running_.find (sp);
199       if (it != running_.end ())
200         running_.erase (it);
201       else
202         started_[i]->programming_error ("lost track of this dynamic spanner");
203     }
204
205   bool end = line_ && running_.empty ();
206
207   // Set the proper bounds for the current spanner and for a spanner that
208   // is ended now
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]);
218
219   if (end)
220     {
221       line_ = 0;
222     }
223
224   ended_line_ = 0;
225   ended_.clear ();
226   started_.clear ();
227   scripts_.clear ();
228   support_.clear ();
229 }
230
231 void
232 Dynamic_align_engraver::boot ()
233 {
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);
239 }
240
241 ADD_TRANSLATOR (Dynamic_align_engraver,
242                 /* doc */
243                 "Align hairpins and dynamic texts on a horizontal line.",
244
245                 /* create */
246                 "DynamicLineSpanner ",
247
248                 /* read */
249                 "currentMusicalColumn ",
250
251                 /* write */
252                 ""
253                );