]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
8224b1f6f6939f39d2691f2c9764fd4b213ea6d9
[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 "dimension-cache.hh"
11 #include "crescendo.hh"
12 #include "musical-request.hh"
13 #include "lookup.hh"
14 #include "paper-def.hh"
15 #include "paper-column.hh"
16 #include "staff-symbol.hh"
17 #include "note-column.hh"
18 #include "item.hh"
19 #include "side-position-interface.hh"
20 #include "engraver.hh"
21 #include "stem.hh"
22 #include "rhythmic-head.hh"
23 #include "group-interface.hh"
24 #include "directional-element-interface.hh"
25 #include "staff-symbol-referencer.hh"
26 #include "translator-group.hh"
27 #include "axis-group-interface.hh"
28
29
30 /*
31   TODO:
32
33   * direction of text-dynamic-request if not equal to direction of
34   line-spanner
35 */
36
37 /**
38    print text & hairpin dynamics.
39  */
40 class Dynamic_engraver : public Engraver
41 {
42   Item * text_p_;
43   Crescendo * finished_cresc_p_;
44   Crescendo * cresc_p_;
45
46   Text_script_req* text_req_l_;
47   
48   Span_req * current_cresc_req_;
49   Drul_array<Span_req*> accepted_spanreqs_drul_;
50
51   Spanner* line_spanner_;
52   Spanner* finished_line_spanner_;
53
54   Link_array<Note_column> pending_column_arr_;
55   Link_array<Score_element> pending_element_arr_;
56   
57   void typeset_all ();
58
59 public:
60   VIRTUAL_COPY_CONS(Translator);
61   Dynamic_engraver ();
62   
63 protected:
64   virtual void do_removal_processing ();
65   virtual void acknowledge_element (Score_element_info);
66   virtual bool do_try_music (Music *req_l);
67   virtual void do_process_music ();
68   virtual void do_pre_move_processing ();
69   virtual void do_post_move_processing ();
70 };
71
72 ADD_THIS_TRANSLATOR (Dynamic_engraver);
73
74
75 Dynamic_engraver::Dynamic_engraver ()
76 {
77   text_p_ = 0;
78   finished_cresc_p_ = 0;
79   line_spanner_ = 0;
80   finished_line_spanner_ = 0;
81   current_cresc_req_ = 0;
82   cresc_p_ =0;
83
84   text_req_l_ = 0;
85   accepted_spanreqs_drul_[START] = 0;
86   accepted_spanreqs_drul_[STOP] = 0;
87 }
88
89 void
90 Dynamic_engraver::do_post_move_processing ()
91 {
92   text_req_l_ = 0;
93   accepted_spanreqs_drul_[START] = 0;
94   accepted_spanreqs_drul_[STOP] = 0;
95 }
96
97 bool
98 Dynamic_engraver::do_try_music (Music * m)
99 {
100   if (Text_script_req* d = dynamic_cast <Text_script_req*> (m))
101     {
102       if (d->style_str_ == "dynamic")
103         {
104           text_req_l_ = d;
105           return true;
106         }
107     }
108   else if (Span_req* s =  dynamic_cast <Span_req*> (m))
109     {
110       if ((s->span_type_str_ == "crescendo"
111            || s->span_type_str_ == "decrescendo"))
112         {
113           accepted_spanreqs_drul_[s->span_dir_] = s;
114           return true;
115         }
116     }
117   return false;
118 }
119
120 void
121 Dynamic_engraver::do_process_music ()
122 {
123   if (accepted_spanreqs_drul_[START] || accepted_spanreqs_drul_[STOP] || text_req_l_)
124     
125     {
126       if (!line_spanner_)
127         {
128           line_spanner_ = new Spanner (get_property ("basicDynamicLineSpannerProperties"));
129
130           Side_position_interface (line_spanner_).set_axis (Y_AXIS);
131           Axis_group_interface (line_spanner_).set_interface ();
132           Axis_group_interface (line_spanner_).set_axes (Y_AXIS, Y_AXIS);
133           announce_element (Score_element_info
134                             (line_spanner_,
135                              text_req_l_ ? text_req_l_ : accepted_spanreqs_drul_[START]));
136
137         }
138     }
139
140   /*
141     TODO: should finish and create new spanner if vertical dyn-direction is changed.
142    */
143   else if (!accepted_spanreqs_drul_[START] && !text_req_l_)
144     {
145       finished_line_spanner_ = line_spanner_;
146       line_spanner_ = 0;
147     }
148
149         /*
150         todo: resurrect  dynamic{direction, padding,minimumspace}
151         */
152         /*
153         During a (de)crescendo, pending request will not be cleared,
154         and a line-spanner will always be created, as \< \! are already
155         two requests.
156
157         Maybe always creating a line-spanner for a (de)crescendo (see
158         below) is not a good idea:
159
160             a\< b\p \!c
161
162         the \p will be centred on the line-spanner, and thus clash
163         with the hairpin.  When axis-group code is in place, the \p
164         should move below the hairpin, which is probably better?
165
166         Urg, but line-spanner must always have at least same duration
167         as (de)crecsendo, b.o. line-breaking.
168         */
169
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 (line_spanner_).set (d);
179
180       Axis_group_interface (line_spanner_).add_element (text_p_);
181
182       text_p_->add_offset_callback (Side_position_interface::aligned_on_self,
183                                     Y_AXIS);
184       announce_element (Score_element_info (text_p_, text_req_l_));
185     }
186
187   if (accepted_spanreqs_drul_[STOP])
188     {
189       if (!cresc_p_)
190         {
191           accepted_spanreqs_drul_[STOP]->warning
192             (_ ("can't find start of (de)crescendo"));
193         }
194       else
195         {
196           assert (!finished_cresc_p_);
197           cresc_p_->set_bound (RIGHT, unsmob_element (get_property ("currentMusicalColumn")));
198           finished_cresc_p_ = cresc_p_;
199           cresc_p_ = 0;
200           current_cresc_req_ = 0;
201         }
202     }
203
204   if (accepted_spanreqs_drul_[START])
205     {
206       if (current_cresc_req_)
207         {
208           accepted_spanreqs_drul_[START]->warning
209             (current_cresc_req_->span_dir_ == 1
210              ?
211              _ ("already have a crescendo")
212              : _ ("already have a decrescendo"));
213         }
214       else
215         {
216           current_cresc_req_ = accepted_spanreqs_drul_[START];
217           cresc_p_  = new Crescendo (get_property ("basicCrescendoProperties"));
218           cresc_p_->set_elt_property
219             ("grow-direction",
220              gh_int2scm ((accepted_spanreqs_drul_[START]->span_type_str_ == "crescendo")
221                          ? BIGGER : SMALLER));
222               
223           SCM s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Text").ch_C());
224           if (gh_string_p (s))
225             {
226               cresc_p_->set_elt_property ("start-text", s);
227               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
228                                             + "Text", SCM_UNDEFINED);
229             }
230
231           s = get_property ((accepted_spanreqs_drul_[START]->span_type_str_ + "Spanner").ch_C());
232
233
234           /*
235             TODO: Use symbols.
236            */
237           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
238             {
239               cresc_p_->set_elt_property ("spanner", s);
240               daddy_trans_l_->set_property (accepted_spanreqs_drul_[START]->span_type_str_
241                                             + "Spanner", SCM_UNDEFINED);
242             }
243
244           cresc_p_->set_bound (LEFT, unsmob_element (get_property ("currentMusicalColumn")));
245
246
247           /* 
248               We know how wide the text is, if we can be sure that the
249               text already has relevant pointers into the paperdef,
250               and it has its font-size property set.
251
252               Since font-size may be set by a context higher up, we
253               can not be sure of the size.
254
255
256               We shouldn't try to do this stuff here, the Item should
257               do it when the score is finished.  We could maybe
258               set a callback to have the Item do the alignment if
259               it is not a special symbol, like Crescendo.
260           */
261
262           
263           if (text_p_)
264             {
265               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
266                               LEFT, SCM_BOOL_T);
267               if (finished_cresc_p_)
268                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
269                                 RIGHT, SCM_BOOL_T);
270             }
271
272           Axis_group_interface (line_spanner_).add_element (cresc_p_);
273           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
274           cresc_p_->add_offset_callback
275             (Side_position_interface::aligned_on_self, Y_AXIS);
276           announce_element (Score_element_info (cresc_p_, accepted_spanreqs_drul_[START]));
277         }
278     }
279 }
280
281 void
282 Dynamic_engraver::do_pre_move_processing ()
283 {
284   typeset_all ();
285 }
286
287 void
288 Dynamic_engraver::do_removal_processing ()
289 {
290   typeset_all ();
291   
292   if (cresc_p_)
293     {
294       typeset_element (cresc_p_ );
295       finished_cresc_p_ = cresc_p_;
296       current_cresc_req_->warning (_ ("unterminated (de)crescendo"));
297     }
298   if (line_spanner_)
299     {
300       finished_line_spanner_ = line_spanner_;
301     }
302   typeset_all ();
303 }
304
305 void
306 Dynamic_engraver::typeset_all ()
307 {  
308   if (finished_cresc_p_)
309     {
310       typeset_element (finished_cresc_p_);
311       finished_cresc_p_ =0;
312     }
313   
314   if (text_p_)
315     {
316       typeset_element (text_p_);
317       text_p_ = 0;
318     }
319   if (finished_line_spanner_)
320     {
321       Side_position_interface (finished_line_spanner_).add_staff_support ();
322
323       if (!finished_line_spanner_->get_bound (LEFT))
324         {
325           Score_element * cmc
326             = unsmob_element (get_property ("currentMusicalColumn"));
327           finished_line_spanner_->set_bound (LEFT, cmc);
328         }
329       if (!finished_line_spanner_->get_bound (RIGHT))
330         finished_line_spanner_->set_bound (RIGHT,
331                                            finished_line_spanner_->get_bound (LEFT));
332       
333       
334       typeset_element (finished_line_spanner_);
335       finished_line_spanner_ = 0;
336     }
337 }
338
339 void
340 Dynamic_engraver::acknowledge_element (Score_element_info i)
341 {
342   if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
343     {
344       if (line_spanner_)
345         {
346           Side_position_interface (line_spanner_).add_support (n);
347           add_bound_item (line_spanner_,n);
348         }
349     }
350 }