]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
patch::: 1.3.91.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         }
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 ("basicDynamicLineSpannerProperties"));
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 ("basicDynamicTextProperties"));
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       text_p_->add_offset_callback (Side_position::aligned_on_self,
194                                     Y_AXIS);
195       announce_element (text_p_, text_req_l_);
196     }
197
198   if (accepted_spanreqs_drul_[STOP])
199     {
200       if (!cresc_p_)
201         {
202           accepted_spanreqs_drul_[STOP]->origin ()->warning
203             (_ ("can't find start of (de)crescendo"));
204         }
205       else
206         {
207           assert (!finished_cresc_p_);
208           Score_element* cc = unsmob_element (get_property ("currentMusicalColumn"));
209           
210           cresc_p_->set_bound (RIGHT, cc);
211
212           finished_cresc_p_ = cresc_p_;
213           cresc_p_ = 0;
214           current_cresc_req_ = 0;
215         }
216     }
217
218   if (accepted_spanreqs_drul_[START])
219     {
220       if (current_cresc_req_)
221         {
222           accepted_spanreqs_drul_[START]->origin ()->warning
223             (current_cresc_req_->span_dir_ == 1
224              ? _ ("already have a crescendo")
225              : _ ("already have a decrescendo"));
226         }
227       else
228         {
229           current_cresc_req_ = accepted_spanreqs_drul_[START];
230           cresc_p_  = new Spanner (get_property ("basicCrescendoProperties"));
231           Crescendo::set_interface (cresc_p_);
232           cresc_p_->set_elt_property
233             ("grow-direction",
234              gh_int2scm ((accepted_spanreqs_drul_[START]->span_type_str_ == "crescendo")
235                          ? BIGGER : SMALLER));
236               
237           SCM s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Text").ch_C());
238           if (gh_string_p (s))
239             {
240               cresc_p_->set_elt_property ("start-text", s);
241               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
242                                             + "Text", SCM_UNDEFINED);
243             }
244
245           s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Spanner").ch_C());
246
247
248           /*
249             TODO: Use symbols.
250            */
251           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
252             {
253               cresc_p_->set_elt_property ("spanner", s);
254               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
255                                             + "Spanner", SCM_UNDEFINED);
256             }
257
258           Score_element *cc = unsmob_element (get_property ("currentMusicalColumn"));
259           cresc_p_->set_bound (LEFT, cc);
260
261
262           /* 
263               We know how wide the text is, if we can be sure that the
264               text already has relevant pointers into the paperdef,
265               and it has its font-size property set.
266
267               Since font-size may be set by a context higher up, we
268               can not be sure of the size.
269
270
271               We shouldn't try to do this stuff here, the Item should
272               do it when the score is finished.  We could maybe
273               set a callback to have the Item do the alignment if
274               it is not a special symbol, like Crescendo.
275           */
276
277           
278           if (text_p_)
279             {
280               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
281                               LEFT, text_p_->self_scm ());
282
283               if (finished_cresc_p_
284               // I don't see why, but we need this check
285                   && gh_pair_p (finished_cresc_p_->get_elt_property ("dynamic-drul")))
286                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
287                                 RIGHT, text_p_->self_scm ());
288             }
289
290           Axis_group_interface::add_element (line_spanner_, cresc_p_);
291           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
292           cresc_p_->add_offset_callback
293             (Side_position::aligned_on_self, Y_AXIS);
294           announce_element (cresc_p_, accepted_spanreqs_drul_[START]);
295         }
296     }
297 }
298
299 void
300 Dynamic_engraver::do_pre_move_processing ()
301 {
302   typeset_all ();
303 }
304
305 void
306 Dynamic_engraver::do_removal_processing ()
307 {
308   typeset_all ();
309   if (line_spanner_)
310     {
311       finished_line_spanner_ = line_spanner_;
312       typeset_all ();
313     }
314
315   if (cresc_p_)
316     {
317       current_cresc_req_->origin ()->warning (_ ("unterminated (de)crescendo"));
318       cresc_p_->suicide ();
319       cresc_p_ = 0;
320     }
321 }
322
323 void
324 Dynamic_engraver::typeset_all ()
325 {  
326   if (finished_cresc_p_)
327     {
328       typeset_element (finished_cresc_p_);
329       finished_cresc_p_ =0;
330     }
331   
332   if (text_p_)
333     {
334       typeset_element (text_p_);
335       text_p_ = 0;
336     }
337   if (finished_line_spanner_)
338     {
339       Side_position::add_staff_support (finished_line_spanner_);
340       extend_spanner_over_elements (finished_line_spanner_);
341       typeset_element (finished_line_spanner_);
342       finished_line_spanner_ = 0;
343     }
344 }
345
346 void
347 Dynamic_engraver::acknowledge_element (Score_element_info i)
348 {
349   if (Note_column::has_interface (i.elem_l_))
350     {
351       if (line_spanner_)
352         {
353           Side_position::add_support (line_spanner_,i.elem_l_);
354           add_bound_item (line_spanner_,dynamic_cast<Item*>(i.elem_l_));
355         }
356     }
357 }