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