]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-align-engraver.cc
f62c45f0dc6d3adec09cccf6e700fa1722806a24
[lilypond.git] / lily / dynamic-align-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2008--2011 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_TRANSLATOR_LISTENER (break_span);
38   DECLARE_ACKNOWLEDGER (note_column);
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   Spanner *line_;
49   vector<Spanner *> ended_;
50   vector<Spanner *> started_;
51   vector<Grob *> scripts_;
52   vector<Grob *> support_;
53
54   set<Spanner *> running_;
55
56   bool early_end_;
57 };
58
59 Dynamic_align_engraver::Dynamic_align_engraver ()
60 {
61   line_ = 0;
62   early_end_ = false;
63 }
64
65 ADD_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
66 ADD_ACKNOWLEDGER (Dynamic_align_engraver, note_column);
67 ADD_ACKNOWLEDGER (Dynamic_align_engraver, footnote_spanner);
68 ADD_END_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
69
70 void
71 Dynamic_align_engraver::create_line_spanner (Stream_event *event)
72 {
73   if (!line_)
74     line_ = make_spanner ("DynamicLineSpanner",
75                           event ? event->self_scm () : SCM_EOL);
76 }
77
78 void
79 Dynamic_align_engraver::acknowledge_end_dynamic (Grob_info info)
80 {
81   if (Spanner::has_interface (info.grob ()))
82     ended_.push_back (info.spanner ());
83 }
84
85 void
86 Dynamic_align_engraver::acknowledge_footnote_spanner (Grob_info info)
87 {
88   Grob *parent = info.grob ()->get_parent (Y_AXIS);
89   if (line_ && parent
90       && parent->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
91     Axis_group_interface::add_element (line_, info.grob ());
92 }
93
94 void
95 Dynamic_align_engraver::acknowledge_note_column (Grob_info info)
96 {
97   support_.push_back (info.grob ());
98 }
99
100 IMPLEMENT_TRANSLATOR_LISTENER (Dynamic_align_engraver, break_span);
101 void
102 Dynamic_align_engraver::listen_break_span (Stream_event *event)
103 {
104   if (event->in_event_class ("break-dynamic-span-event"))
105     early_end_ = true;
106 }
107
108 void
109 Dynamic_align_engraver::acknowledge_dynamic (Grob_info info)
110 {
111   Stream_event *cause = info.event_cause ();
112   create_line_spanner (cause);
113   if (Spanner::has_interface (info.grob ()))
114     {
115       started_.push_back (info.spanner ());
116       /*
117         If we are using text spans instead of hairpins and the line
118         is hidden, end the alignment spanner early: this allows dynamics
119         to be spaced individually instead of being linked together.
120       */
121       if (info.grob ()->internal_has_interface (ly_symbol2scm ("dynamic-text-spanner-interface"))
122           && (info.grob ()->get_property ("style") == ly_symbol2scm ("none")))
123         early_end_ = true;
124     }
125   else if (info.item ())
126     scripts_.push_back (info.item ());
127   else
128     info.grob ()->programming_error ("unknown dynamic grob");
129
130   Axis_group_interface::add_element (line_, info.grob ());
131
132   if (cause)
133     {
134       if (Direction d = to_dir (cause->get_property ("direction")))
135         set_grob_direction (line_, d);
136     }
137 }
138
139 void
140 Dynamic_align_engraver::stop_translation_timestep ()
141 {
142   for (vsize i = 0; i < started_.size (); i++)
143     running_.insert (started_[i]);
144   for (vsize i = 0; i < ended_.size (); i++)
145     {
146       Spanner *sp = ended_[i];
147
148       set<Spanner *>::iterator it = running_.find (sp);
149       if (it != running_.end ())
150         running_.erase (it);
151       else
152         started_[i]->programming_error ("lost track of this dynamic spanner");
153     }
154
155   bool end = line_ && (running_.empty ()
156                        || early_end_);
157   Direction d = LEFT;
158   do
159     {
160       if (line_
161           && ((d == LEFT && !line_->get_bound (LEFT))
162               || (end && d == RIGHT && !line_->get_bound (RIGHT))))
163         {
164           vector<Spanner *> const &spanners
165             = (d == LEFT) ? started_ : ended_;
166
167           Grob *bound = 0;
168           if (scripts_.size ())
169             bound = scripts_[0];
170           else if (spanners.size ())
171             bound = spanners[0]->get_bound (d);
172           else
173             {
174               bound = unsmob_grob (get_property ("currentMusicalColumn"));
175               if (!early_end_)
176                 programming_error ("started DynamicLineSpanner but have no left bound");
177             }
178
179           line_->set_bound (d, bound);
180         }
181     }
182   while (flip (&d) != LEFT);
183
184   for (vsize i = 0; line_ && i < support_.size (); i++)
185     Side_position_interface::add_support (line_, support_[i]);
186
187   if (end)
188     {
189       line_ = 0;
190       early_end_ = false;
191     }
192
193   ended_.clear ();
194   started_.clear ();
195   scripts_.clear ();
196   support_.clear ();
197 }
198
199 ADD_TRANSLATOR (Dynamic_align_engraver,
200                 /* doc */
201                 "Align hairpins and dynamic texts on a horizontal line.",
202
203                 /* create */
204                 "DynamicLineSpanner ",
205
206                 /* read */
207                 "currentMusicalColumn ",
208
209                 /* write */
210                 ""
211                 );