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