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