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