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