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