]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
release: 1.3.94
[lilypond.git] / lily / dynamic-engraver.cc
1 /*
2   dynamic-engraver.cc -- implement Dynamic_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include "debug.hh"
9 #include "dimensions.hh"
10 #include "crescendo.hh"
11 #include "musical-request.hh"
12 #include "paper-column.hh"
13 #include "note-column.hh"
14 #include "item.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "group-interface.hh"
18 #include "directional-element-interface.hh"
19 #include "translator-group.hh"
20 #include "axis-group-interface.hh"
21
22
23 /*
24   TODO:
25
26   * direction of text-dynamic-request if not equal to direction of
27   line-spanner
28 */
29
30 /**
31    print text & hairpin dynamics.
32  */
33 class Dynamic_engraver : public Engraver
34 {
35   Item * text_p_;
36   Spanner * finished_cresc_p_;
37   Spanner * cresc_p_;
38
39   Text_script_req* text_req_l_;
40   
41   Span_req * current_cresc_req_;
42   Drul_array<Span_req*> accepted_spanreqs_drul_;
43
44   Spanner* line_spanner_;
45   Spanner* finished_line_spanner_;
46
47   Link_array<Note_column> pending_column_arr_;
48   Link_array<Score_element> pending_element_arr_;
49   
50   void typeset_all ();
51
52 public:
53   VIRTUAL_COPY_CONS(Translator);
54   Dynamic_engraver ();
55   
56 protected:
57   virtual void do_removal_processing ();
58   virtual void acknowledge_element (Score_element_info);
59   virtual bool do_try_music (Music *req_l);
60   virtual void do_process_music ();
61   virtual void do_pre_move_processing ();
62   virtual void do_post_move_processing ();
63 };
64
65 ADD_THIS_TRANSLATOR (Dynamic_engraver);
66
67
68 Dynamic_engraver::Dynamic_engraver ()
69 {
70   text_p_ = 0;
71   finished_cresc_p_ = 0;
72   line_spanner_ = 0;
73   finished_line_spanner_ = 0;
74   current_cresc_req_ = 0;
75   cresc_p_ =0;
76
77   text_req_l_ = 0;
78   accepted_spanreqs_drul_[START] = 0;
79   accepted_spanreqs_drul_[STOP] = 0;
80 }
81
82 void
83 Dynamic_engraver::do_post_move_processing ()
84 {
85   text_req_l_ = 0;
86   accepted_spanreqs_drul_[START] = 0;
87   accepted_spanreqs_drul_[STOP] = 0;
88 }
89
90 bool
91 Dynamic_engraver::do_try_music (Music * m)
92 {
93   if (Text_script_req* d = dynamic_cast <Text_script_req*> (m))
94     {
95       if (d->style_str_ == "dynamic")
96         {
97           text_req_l_ = d;
98           return true;
99         }
100     }
101   else if (Span_req* s =  dynamic_cast <Span_req*> (m))
102     {
103       if (s->span_type_str_ == "abort")
104         {
105           accepted_spanreqs_drul_[LEFT] = 0;
106           accepted_spanreqs_drul_[RIGHT] = 0;
107           if (line_spanner_)
108             line_spanner_->suicide ();
109           line_spanner_ = 0;
110         }
111       else if ((s->span_type_str_ == "crescendo"
112            || s->span_type_str_ == "decrescendo"))
113         {
114           accepted_spanreqs_drul_[s->span_dir_] = s;
115           return true;
116         }
117     }
118   return false;
119 }
120
121 void
122 Dynamic_engraver::do_process_music ()
123 {
124   if (accepted_spanreqs_drul_[START] || accepted_spanreqs_drul_[STOP] || text_req_l_)
125     
126     {
127       if (!line_spanner_)
128         {
129           line_spanner_ = new Spanner (get_property ("DynamicLineSpanner"));
130
131           Side_position::set_axis (line_spanner_, Y_AXIS);
132           Axis_group_interface::set_interface (line_spanner_);
133           Axis_group_interface::set_axes (line_spanner_, Y_AXIS, Y_AXIS);
134
135           Request * rq = accepted_spanreqs_drul_[START];
136           if (text_req_l_) rq =  text_req_l_ ;
137           announce_element (line_spanner_, rq);
138                          
139
140         }
141     }
142
143   /*
144     finish side position alignment if the (de)cresc ends here, and
145     there are no new dynamics.
146     
147    */
148   else if (accepted_spanreqs_drul_[STOP]
149            && !accepted_spanreqs_drul_[START] && !text_req_l_)
150     {
151       finished_line_spanner_ = line_spanner_;
152       line_spanner_ = 0;
153     }
154
155         /*
156         todo: resurrect  dynamic{direction, padding,minimumspace}
157         */
158         /*
159         During a (de)crescendo, pending request will not be cleared,
160         and a line-spanner will always be created, as \< \! are already
161         two requests.
162
163         Maybe always creating a line-spanner for a (de)crescendo (see
164         below) is not a good idea:
165
166             a\< b\p \!c
167
168         the \p will be centred on the line-spanner, and thus clash
169         with the hairpin.  When axis-group code is in place, the \p
170         should move below the hairpin, which is probably better?
171
172         Urg, but line-spanner must always have at least same duration
173         as (de)crecsendo, b.o. line-breaking.
174         */
175
176   
177
178   /*
179     maybe we should leave dynamic texts to the text-engraver and
180     simply acknowledge them?
181   */
182   if (text_req_l_)
183     {
184       String loud = text_req_l_->text_str_;
185
186       text_p_ = new Item (get_property ("DynamicText"));
187       text_p_->set_elt_property ("text", ly_str02scm (loud.ch_C ()));
188       if (Direction d=text_req_l_->get_direction ())
189         Directional_element_interface::set (line_spanner_, d);
190
191       Axis_group_interface::add_element (line_spanner_, text_p_);
192
193       announce_element (text_p_, text_req_l_);
194     }
195
196   if (accepted_spanreqs_drul_[STOP])
197     {
198       if (!cresc_p_)
199         {
200           accepted_spanreqs_drul_[STOP]->origin ()->warning
201             (_ ("can't find start of (de)crescendo"));
202         }
203       else
204         {
205           assert (!finished_cresc_p_);
206           Score_element* cc = unsmob_element (get_property ("currentMusicalColumn"));
207           
208           cresc_p_->set_bound (RIGHT, cc);
209
210           finished_cresc_p_ = cresc_p_;
211           cresc_p_ = 0;
212           current_cresc_req_ = 0;
213         }
214     }
215
216   if (accepted_spanreqs_drul_[START])
217     {
218       if (current_cresc_req_)
219         {
220           accepted_spanreqs_drul_[START]->origin ()->warning
221             (current_cresc_req_->span_dir_ == 1
222              ? _ ("already have a crescendo")
223              : _ ("already have a decrescendo"));
224         }
225       else
226         {
227           current_cresc_req_ = accepted_spanreqs_drul_[START];
228           cresc_p_  = new Spanner (get_property ("Crescendo"));
229           Crescendo::set_interface (cresc_p_);
230           cresc_p_->set_elt_property
231             ("grow-direction",
232              gh_int2scm ((accepted_spanreqs_drul_[START]->span_type_str_ == "crescendo")
233                          ? BIGGER : SMALLER));
234               
235           SCM s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Text").ch_C());
236           if (gh_string_p (s))
237             {
238               cresc_p_->set_elt_property ("start-text", s);
239               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
240                                             + "Text", SCM_UNDEFINED);
241             }
242
243           s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Spanner").ch_C());
244
245
246           /*
247             TODO: Use symbols.
248            */
249           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
250             {
251               cresc_p_->set_elt_property ("spanner", s);
252               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
253                                             + "Spanner", SCM_UNDEFINED);
254             }
255
256           Score_element *cc = unsmob_element (get_property ("currentMusicalColumn"));
257           cresc_p_->set_bound (LEFT, cc);
258
259
260           /* 
261               We know how wide the text is, if we can be sure that the
262               text already has relevant pointers into the paperdef,
263               and it has its font-size property set.
264
265               Since font-size may be set by a context higher up, we
266               can not be sure of the size.
267
268
269               We shouldn't try to do this stuff here, the Item should
270               do it when the score is finished.  We could maybe
271               set a callback to have the Item do the alignment if
272               it is not a special symbol, like Crescendo.
273           */
274
275           
276           if (text_p_)
277             {
278               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
279                               LEFT, text_p_->self_scm ());
280
281               if (finished_cresc_p_
282               // I don't see why, but we need this check
283                   && gh_pair_p (finished_cresc_p_->get_elt_property ("dynamic-drul")))
284                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
285                                 RIGHT, text_p_->self_scm ());
286             }
287
288           Axis_group_interface::add_element (line_spanner_, cresc_p_);
289           announce_element (cresc_p_, accepted_spanreqs_drul_[START]);
290         }
291     }
292 }
293
294 void
295 Dynamic_engraver::do_pre_move_processing ()
296 {
297   typeset_all ();
298 }
299
300 void
301 Dynamic_engraver::do_removal_processing ()
302 {
303   typeset_all ();
304   if (line_spanner_)
305     {
306       finished_line_spanner_ = line_spanner_;
307       typeset_all ();
308     }
309
310   if (cresc_p_)
311     {
312       current_cresc_req_->origin ()->warning (_ ("unterminated (de)crescendo"));
313       cresc_p_->suicide ();
314       cresc_p_ = 0;
315     }
316 }
317
318 void
319 Dynamic_engraver::typeset_all ()
320 {  
321   if (finished_cresc_p_)
322     {
323       typeset_element (finished_cresc_p_);
324       finished_cresc_p_ =0;
325     }
326   
327   if (text_p_)
328     {
329       typeset_element (text_p_);
330       text_p_ = 0;
331     }
332   if (finished_line_spanner_)
333     {
334       Side_position::add_staff_support (finished_line_spanner_);
335       extend_spanner_over_elements (finished_line_spanner_);
336       typeset_element (finished_line_spanner_);
337       finished_line_spanner_ = 0;
338     }
339 }
340
341 void
342 Dynamic_engraver::acknowledge_element (Score_element_info i)
343 {
344   if (Note_column::has_interface (i.elem_l_))
345     {
346       if (line_spanner_)
347         {
348           Side_position::add_support (line_spanner_,i.elem_l_);
349           add_bound_item (line_spanner_,dynamic_cast<Item*>(i.elem_l_));
350         }
351     }
352 }