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