]> git.donarmstrong.com Git - lilypond.git/blob - lily/volta-engraver.cc
2590d0ed5397d9e0a8a06686f0edda4b5bbea5b0
[lilypond.git] / lily / volta-engraver.cc
1 /*
2   volta-engraver.cc -- implement Volta_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2000--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "engraver.hh"
10 #include "context.hh"
11 #include "volta-bracket.hh"
12 #include "note-column.hh"
13 #include "bar-line.hh"
14 #include "side-position-interface.hh"
15 #include "warn.hh"
16 #include "staff-symbol.hh"
17
18 /*
19   Create Volta spanners, by reading repeatCommands  property, usually
20   set by Unfolded_repeat_iterator.
21 */
22 class Volta_engraver : public Engraver
23 {
24 public:
25   TRANSLATOR_DECLARATIONS (Volta_engraver);
26 protected:
27
28   virtual void acknowledge_grob (Grob_info);
29   virtual void finalize ();
30   virtual void stop_translation_timestep ();
31   virtual void process_music ();
32
33   virtual void derived_mark () const;
34   Moment started_mom_;
35   Spanner *volta_span_;
36   Spanner *end_volta_span_;
37   SCM staff_;
38   SCM start_string_;
39
40   bool staff_eligible ();
41 };
42
43 void
44 Volta_engraver::derived_mark () const
45 {
46   scm_gc_mark (staff_);
47   scm_gc_mark (start_string_);
48 }
49
50 Volta_engraver::Volta_engraver ()
51 {
52   start_string_ = SCM_EOL;
53   staff_ = SCM_EOL;
54   volta_span_ = 0;
55   end_volta_span_ = 0;
56 }
57
58 /*
59   TODO: this logic should be rewritten, it is buggy.
60
61   One of the problems is that we can't determine wether or not to
62   print the volta bracket during the first step, since that requires
63   acknowledging the staff.
64 */
65 bool
66 Volta_engraver::staff_eligible ()
67 {
68   SCM doit = get_property ("voltaOnThisStaff");
69   if (scm_is_bool (doit))
70     {
71       return to_boolean (doit);
72     }
73
74   if (!unsmob_grob (staff_))
75     return false;
76
77   /*
78     TODO: this does weird things when you open a piece with a
79     volta spanner.
80   */
81   SCM staffs = get_property ("stavesFound");
82
83   /* Only put a volta on the top staff.
84      Maybe this is a bit convoluted, and we should have a single
85      volta engraver in score context or somesuch. */
86   if (!scm_is_pair (staffs))
87     {
88       programming_error ("volta engraver can't find staffs");
89       return false;
90     }
91   else if (scm_car (scm_last_pair (staffs)) != staff_)
92     return false;
93   return true;
94 }
95
96 void
97 Volta_engraver::process_music ()
98 {
99   SCM cs = get_property ("repeatCommands");
100
101   if (!staff_eligible ())
102     return;
103
104   bool end = false;
105   start_string_ = SCM_EOL;
106   while (scm_is_pair (cs))
107     {
108       SCM c = scm_car (cs);
109
110       if (scm_is_pair (c)
111           && scm_car (c) == ly_symbol2scm ("volta")
112           && scm_is_pair (scm_cdr (c)))
113         {
114           if (scm_cadr (c) == SCM_BOOL_F)
115             end = true;
116           else
117             start_string_ = scm_cadr (c);
118         }
119
120       cs = scm_cdr (cs);
121     }
122
123   if (volta_span_)
124     {
125       SCM l (get_property ("voltaSpannerDuration"));
126       Moment now = now_mom ();
127
128       bool early_stop = unsmob_moment (l)
129         && *unsmob_moment (l) <= now - started_mom_;
130
131       end = end || early_stop;
132     }
133
134   if (end && !volta_span_)
135     /* fixme: be more verbose.  */
136     warning (_ ("can't end volta spanner"));
137   else if (end)
138     {
139       end_volta_span_ = volta_span_;
140       volta_span_ = 0;
141     }
142
143   if (volta_span_
144       && (scm_is_string (start_string_) || scm_is_pair (start_string_)))
145     {
146       warning (_ ("already have a volta spanner, ending that one prematurely"));
147
148       if (end_volta_span_)
149         {
150           warning (_ ("also already have an ended spanner"));
151           warning (_ ("giving up"));
152           return;
153         }
154
155       end_volta_span_ = volta_span_;
156       volta_span_ = 0;
157     }
158
159   if (!volta_span_
160       && (scm_is_string (start_string_) || scm_is_pair (start_string_)))
161     {
162       started_mom_ = now_mom ();
163
164       volta_span_ = make_spanner ("VoltaBracket", SCM_EOL);
165
166       volta_span_->set_property ("text", start_string_);
167     }
168 }
169
170 void
171 Volta_engraver::acknowledge_grob (Grob_info i)
172 {
173   if (Item *item = dynamic_cast<Item *> (i.grob ()))
174     {
175       if (Note_column::has_interface (item))
176         {
177           if (volta_span_)
178             Volta_bracket_interface::add_column (volta_span_, item);
179         }
180       if (Bar_line::has_interface (item))
181         {
182           if (volta_span_)
183             Volta_bracket_interface::add_bar (volta_span_, item);
184           if (end_volta_span_)
185             Volta_bracket_interface::add_bar (end_volta_span_, item);
186         }
187     }
188   else if (Staff_symbol::has_interface (i.grob ()))
189     {
190       /*
191         We only want to know about a single staff: then we add to the
192         support.  */
193       if (staff_ != SCM_EOL)
194         staff_ = SCM_UNDEFINED;
195
196       if (staff_ != SCM_UNDEFINED)
197         staff_ = i.grob ()->self_scm ();
198     }
199 }
200
201 void
202 Volta_engraver::finalize ()
203 {
204 }
205
206 void
207 Volta_engraver::stop_translation_timestep ()
208 {
209   if (volta_span_ && !staff_eligible ())
210     {
211       /*
212         THIS IS A KLUDGE.
213
214         we need to do this here, because STAFF_ is not initialized yet
215         in the 1st call of process_music ()
216       */
217
218       volta_span_->suicide ();
219       volta_span_ = 0;
220     }
221
222   if (end_volta_span_ && !end_volta_span_->get_bound (RIGHT))
223     {
224       Grob *cc = unsmob_grob (get_property ("currentCommandColumn"));
225       Item *ci = dynamic_cast<Item *> (cc);
226       end_volta_span_->set_bound (RIGHT, ci);
227     }
228
229   end_volta_span_ = 0;
230
231   if (volta_span_ && !volta_span_->get_bound (LEFT))
232     {
233       Grob *cc = unsmob_grob (get_property ("currentCommandColumn"));
234       Item *ci = dynamic_cast<Item *> (cc);
235       volta_span_->set_bound (LEFT, ci);
236     }
237 }
238
239 /*
240   TODO: should attach volta to paper-column if no bar is found.
241 */
242
243 ADD_TRANSLATOR (Volta_engraver,
244                 /* descr */ "Make volta brackets.",
245                 /* creats*/ "VoltaBracket",
246                 /* accepts */ "",
247                 /* acks  */ "bar-line-interface staff-symbol-interface note-column-interface",
248                 /* reads */ "repeatCommands voltaSpannerDuration stavesFound",
249                 /* write */ "");