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