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