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