]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
release: 1.3.81
[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 0  
286   if (cresc_p_)
287     {
288       typeset_element (cresc_p_ );
289       finished_cresc_p_ = cresc_p_;
290
291       current_cresc_req_->origin ()->warning (_ ("unterminated (de)crescendo"));
292     }
293   if (line_spanner_)
294     {
295       finished_line_spanner_ = line_spanner_;
296     }
297   typeset_all ();
298 #else
299   if (cresc_p_)
300     {
301       current_cresc_req_->origin ()->warning (_ ("unterminated (de)crescendo"));
302       cresc_p_->suicide ();
303     }
304   if (line_spanner_)
305     {
306       line_spanner_->suicide ();
307     }
308 #endif
309 }
310
311 void
312 Dynamic_engraver::typeset_all ()
313 {  
314   if (finished_cresc_p_)
315     {
316       typeset_element (finished_cresc_p_);
317       finished_cresc_p_ =0;
318     }
319   
320   if (text_p_)
321     {
322       typeset_element (text_p_);
323       text_p_ = 0;
324     }
325   if (finished_line_spanner_)
326     {
327       Side_position::add_staff_support (finished_line_spanner_);
328
329       if (!finished_line_spanner_->get_bound (LEFT))
330         {
331           Score_element * cmc
332             = unsmob_element (get_property ("currentMusicalColumn"));
333           finished_line_spanner_->set_bound (LEFT, cmc);
334         }
335       if (!finished_line_spanner_->get_bound (RIGHT))
336         finished_line_spanner_->set_bound (RIGHT,
337                                            finished_line_spanner_->get_bound (LEFT));
338       
339       
340       typeset_element (finished_line_spanner_);
341       finished_line_spanner_ = 0;
342     }
343 }
344
345 void
346 Dynamic_engraver::acknowledge_element (Score_element_info i)
347 {
348   if (Note_column::has_interface (i.elem_l_))
349     {
350       if (line_spanner_)
351         {
352           Side_position::add_support (line_spanner_,i.elem_l_);
353           add_bound_item (line_spanner_,dynamic_cast<Item*>(i.elem_l_));
354         }
355     }
356 }