]> git.donarmstrong.com Git - lilypond.git/blob - lily/volta-engraver.cc
Merge branch 'master' of ssh+git://gpercival@git.sv.gnu.org/srv/git/lilypond
[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--2007 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 "item.hh"
16 #include "side-position-interface.hh"
17 #include "staff-symbol.hh"
18 #include "text-interface.hh"
19 #include "volta-bracket.hh"
20 #include "warn.hh"
21
22 #include "translator.icc"
23
24 /*
25   Create Volta spanners, by reading repeatCommands  property, usually
26   set by Volta_repeat_iterator.
27 */
28 class Volta_engraver : public Engraver
29 {
30 public:
31   TRANSLATOR_DECLARATIONS (Volta_engraver);
32 protected:
33
34   DECLARE_END_ACKNOWLEDGER (staff_symbol);
35   DECLARE_ACKNOWLEDGER (staff_symbol);
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 (_ ("cannot 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_bar_line (Grob_info i)
179 {
180   if (volta_span_)
181     Volta_bracket_interface::add_bar (volta_span_, i.item ());
182   if (end_volta_span_)
183     Volta_bracket_interface::add_bar (end_volta_span_, i.item ());
184 }
185
186 void
187 Volta_engraver::acknowledge_end_staff_symbol (Grob_info i)
188 {
189   if (i.grob ()->self_scm () == staff_)
190     staff_ = SCM_EOL;
191 }
192
193 void
194 Volta_engraver::acknowledge_staff_symbol (Grob_info i)
195 {
196   /*
197     We only want to know about a single staff: then we add to the
198     support.  */
199   if (staff_ != SCM_EOL)
200     staff_ = SCM_UNDEFINED;
201
202   if (staff_ != SCM_UNDEFINED)
203     staff_ = i.grob ()->self_scm ();
204 }
205
206
207 void
208 Volta_engraver::finalize ()
209 {
210 }
211
212 void
213 Volta_engraver::stop_translation_timestep ()
214 {
215   if (volta_span_ && !staff_eligible ())
216     {
217       /*
218         THIS IS A KLUDGE.
219
220         we need to do this here, because STAFF_ is not initialized yet
221         in the 1st call of process_music ()
222       */
223
224       volta_span_->suicide ();
225       volta_span_ = 0;
226     }
227
228   if (end_volta_span_ && !end_volta_span_->get_bound (RIGHT))
229     {
230       Grob *cc = unsmob_grob (get_property ("currentCommandColumn"));
231       Item *ci = dynamic_cast<Item *> (cc);
232       end_volta_span_->set_bound (RIGHT, ci);
233     }
234
235   end_volta_span_ = 0;
236
237   if (volta_span_ && !volta_span_->get_bound (LEFT))
238     {
239       Grob *cc = unsmob_grob (get_property ("currentCommandColumn"));
240       Item *ci = dynamic_cast<Item *> (cc);
241       volta_span_->set_bound (LEFT, ci);
242     }
243 }
244
245 /*
246   TODO: should attach volta to paper-column if no bar is found.
247 */
248 ADD_ACKNOWLEDGER (Volta_engraver, staff_symbol);
249 ADD_END_ACKNOWLEDGER (Volta_engraver, staff_symbol);
250 ADD_ACKNOWLEDGER (Volta_engraver, bar_line);
251 ADD_TRANSLATOR (Volta_engraver,
252                 /* doc */ "Make volta brackets.",
253                 /* create */ "VoltaBracket",
254                 /* read */ "repeatCommands voltaSpannerDuration stavesFound",
255                 /* write */ "");