]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
patch::: 1.3.95.jcn1
[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           if (cresc_p_)
111             cresc_p_->suicide ();
112           cresc_p_ = 0;
113         }
114       else if ((s->span_type_str_ == "crescendo"
115            || s->span_type_str_ == "decrescendo"))
116         {
117           accepted_spanreqs_drul_[s->span_dir_] = s;
118           return true;
119         }
120     }
121   return false;
122 }
123
124 void
125 Dynamic_engraver::do_process_music ()
126 {
127   if (accepted_spanreqs_drul_[START] || accepted_spanreqs_drul_[STOP] || text_req_l_)
128     
129     {
130       if (!line_spanner_)
131         {
132           line_spanner_ = new Spanner (get_property ("DynamicLineSpanner"));
133
134           Side_position::set_axis (line_spanner_, Y_AXIS);
135           Axis_group_interface::set_interface (line_spanner_);
136           Axis_group_interface::set_axes (line_spanner_, Y_AXIS, Y_AXIS);
137
138           Request * rq = accepted_spanreqs_drul_[START];
139           if (text_req_l_) rq =  text_req_l_ ;
140           announce_element (line_spanner_, rq);
141                          
142
143         }
144     }
145
146   /*
147     finish side position alignment if the (de)cresc ends here, and
148     there are no new dynamics.
149     
150    */
151   else if (accepted_spanreqs_drul_[STOP]
152            && !accepted_spanreqs_drul_[START] && !text_req_l_)
153     {
154       finished_line_spanner_ = line_spanner_;
155       line_spanner_ = 0;
156     }
157
158         /*
159         todo: resurrect  dynamic{direction, padding,minimumspace}
160         */
161         /*
162         During a (de)crescendo, pending request will not be cleared,
163         and a line-spanner will always be created, as \< \! are already
164         two requests.
165
166         Maybe always creating a line-spanner for a (de)crescendo (see
167         below) is not a good idea:
168
169             a\< b\p \!c
170
171         the \p will be centred on the line-spanner, and thus clash
172         with the hairpin.  When axis-group code is in place, the \p
173         should move below the hairpin, which is probably better?
174
175         Urg, but line-spanner must always have at least same duration
176         as (de)crecsendo, b.o. line-breaking.
177         */
178
179   
180
181   /*
182     maybe we should leave dynamic texts to the text-engraver and
183     simply acknowledge them?
184   */
185   if (text_req_l_)
186     {
187       String loud = text_req_l_->text_str_;
188
189       text_p_ = new Item (get_property ("DynamicText"));
190       text_p_->set_elt_property ("text", ly_str02scm (loud.ch_C ()));
191       if (Direction d=text_req_l_->get_direction ())
192         Directional_element_interface::set (line_spanner_, d);
193
194       Axis_group_interface::add_element (line_spanner_, text_p_);
195
196       announce_element (text_p_, text_req_l_);
197     }
198
199   if (accepted_spanreqs_drul_[STOP])
200     {
201       if (!cresc_p_)
202         {
203           accepted_spanreqs_drul_[STOP]->origin ()->warning
204             (_ ("can't find start of (de)crescendo"));
205         }
206       else
207         {
208           assert (!finished_cresc_p_);
209           Score_element* cc = unsmob_element (get_property ("currentMusicalColumn"));
210           
211           cresc_p_->set_bound (RIGHT, cc);
212
213           finished_cresc_p_ = cresc_p_;
214           cresc_p_ = 0;
215           current_cresc_req_ = 0;
216         }
217     }
218
219   if (accepted_spanreqs_drul_[START])
220     {
221       if (current_cresc_req_)
222         {
223           accepted_spanreqs_drul_[START]->origin ()->warning
224             (current_cresc_req_->span_dir_ == 1
225              ? _ ("already have a crescendo")
226              : _ ("already have a decrescendo"));
227         }
228       else
229         {
230           current_cresc_req_ = accepted_spanreqs_drul_[START];
231           cresc_p_  = new Spanner (get_property ("Crescendo"));
232           Crescendo::set_interface (cresc_p_);
233           cresc_p_->set_elt_property
234             ("grow-direction",
235              gh_int2scm ((accepted_spanreqs_drul_[START]->span_type_str_ == "crescendo")
236                          ? BIGGER : SMALLER));
237               
238           SCM s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Text").ch_C());
239           if (gh_string_p (s))
240             {
241               cresc_p_->set_elt_property ("start-text", s);
242               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
243                                             + "Text", SCM_UNDEFINED);
244             }
245
246           s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Spanner").ch_C());
247
248
249           /*
250             TODO: Use symbols.
251            */
252           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
253             {
254               cresc_p_->set_elt_property ("spanner", s);
255               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
256                                             + "Spanner", SCM_UNDEFINED);
257             }
258
259           Score_element *cc = unsmob_element (get_property ("currentMusicalColumn"));
260           cresc_p_->set_bound (LEFT, cc);
261
262
263           /* 
264               We know how wide the text is, if we can be sure that the
265               text already has relevant pointers into the paperdef,
266               and it has its font-size property set.
267
268               Since font-size may be set by a context higher up, we
269               can not be sure of the size.
270
271
272               We shouldn't try to do this stuff here, the Item should
273               do it when the score is finished.  We could maybe
274               set a callback to have the Item do the alignment if
275               it is not a special symbol, like Crescendo.
276           */
277
278           
279           if (text_p_)
280             {
281               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
282                               LEFT, text_p_->self_scm ());
283
284               if (finished_cresc_p_
285               // I don't see why, but we need this check
286                   && gh_pair_p (finished_cresc_p_->get_elt_property ("dynamic-drul")))
287                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
288                                 RIGHT, text_p_->self_scm ());
289             }
290
291           Axis_group_interface::add_element (line_spanner_, cresc_p_);
292           announce_element (cresc_p_, accepted_spanreqs_drul_[START]);
293         }
294     }
295 }
296
297 void
298 Dynamic_engraver::do_pre_move_processing ()
299 {
300   typeset_all ();
301 }
302
303 void
304 Dynamic_engraver::do_removal_processing ()
305 {
306   typeset_all ();
307   if (line_spanner_)
308     {
309       finished_line_spanner_ = line_spanner_;
310       typeset_all ();
311     }
312
313   if (cresc_p_)
314     {
315       current_cresc_req_->origin ()->warning (_ ("unterminated (de)crescendo"));
316       cresc_p_->suicide ();
317       cresc_p_ = 0;
318     }
319 }
320
321 void
322 Dynamic_engraver::typeset_all ()
323 {  
324   if (finished_cresc_p_)
325     {
326       typeset_element (finished_cresc_p_);
327       finished_cresc_p_ =0;
328     }
329   
330   if (text_p_)
331     {
332       typeset_element (text_p_);
333       text_p_ = 0;
334     }
335   if (finished_line_spanner_)
336     {
337       Side_position::add_staff_support (finished_line_spanner_);
338       extend_spanner_over_elements (finished_line_spanner_);
339       typeset_element (finished_line_spanner_);
340       finished_line_spanner_ = 0;
341     }
342 }
343
344 void
345 Dynamic_engraver::acknowledge_element (Score_element_info i)
346 {
347   if (Note_column::has_interface (i.elem_l_))
348     {
349       if (line_spanner_)
350         {
351           Side_position::add_support (line_spanner_,i.elem_l_);
352           add_bound_item (line_spanner_,dynamic_cast<Item*>(i.elem_l_));
353         }
354     }
355 }