]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
patch::: 1.3.36.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 "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 "text-item.hh"
19 #include "side-position-interface.hh"
20 #include "engraver.hh"
21 #include "stem.hh"
22 #include "note-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
28
29 /*
30   TODO:
31
32   * direction of text-dynamic-request if not equalt to direction
33   of line-spanner
34  */
35
36 class Dynamic_line_spanner : public Spanner
37 {
38 public:
39   Dynamic_line_spanner ();
40   VIRTUAL_COPY_CONS(Score_element);
41   void add_column (Item*);
42
43 protected:
44   virtual void after_line_breaking ();
45 };
46
47 Dynamic_line_spanner::Dynamic_line_spanner ()
48 {
49   set_elt_property ("transparent", SCM_BOOL_T);
50   side_position (this).set_axis (Y_AXIS);
51 }
52
53 void
54 Dynamic_line_spanner::add_column (Item* n)
55 {
56   if (!get_bound (LEFT))
57     set_bound (LEFT, n);
58   else
59     set_bound (RIGHT, n);
60
61   add_dependency (n);
62 }
63
64 void
65 Dynamic_line_spanner::after_line_breaking ()
66 {
67 #if 0
68
69   /*
70     We hebben hier een probleempje: er is een verschil tussen
71     dynamics zonder en met line-spanner.
72     Allen zijn gecentreerd (aligned-on-self), wat okee is, 
73     maar de losse hebben zelf een padding tov de staff.
74
75     Deze padding werkt niet op items die in line-spanner zitten:
76     de padding werkt op line-spanner zelf.
77     De line-spanner moet dus eigenlijk zoveel naar beneden of boven
78     als er items uitsteken, maar Hmm.
79    */
80 #endif
81   Direction dir = directional_element (this).get ();
82   if (!dir)
83     dir = DOWN;
84   //Hmm. inf
85   //translate_axis (extent (Y_AXIS)[dir], Y_AXIS);
86   translate_axis (staff_symbol_referencer (this).staff_space () * dir, Y_AXIS);
87 }
88
89 /**
90    print text & hairpin dynamics.
91  */
92 class Dynamic_engraver : public Engraver
93 {
94   Text_item * text_p_;
95   Crescendo * finished_cresc_p_;
96   Crescendo * cresc_p_;
97
98   Text_script_req* text_req_l_;
99   Span_req * span_start_req_l_;
100   Drul_array<Span_req*> span_req_l_drul_;
101
102   Dynamic_line_spanner* line_spanner_;
103   Moment last_request_mom_;
104
105   Note_column* pending_column_;
106   Link_array<Score_element> pending_element_arr_;
107   
108   void  typeset_all ();
109
110 public:
111   VIRTUAL_COPY_CONS(Translator);
112   Dynamic_engraver ();
113   
114 protected:
115   void announce_element (Score_element_info);
116   
117   virtual void do_removal_processing ();
118   virtual void acknowledge_element (Score_element_info);
119   virtual bool do_try_music (Music *req_l);
120   virtual void do_process_music ();
121   virtual void do_pre_move_processing ();
122   virtual void do_post_move_processing ();
123 };
124
125 ADD_THIS_TRANSLATOR (Dynamic_engraver);
126
127 void
128 Dynamic_engraver::announce_element (Score_element_info i)
129 {
130   group (i.elem_l_, "interfaces").add_thing (ly_symbol2scm ("dynamic"));
131   
132   Engraver::announce_element (i);
133 }
134
135
136 Dynamic_engraver::Dynamic_engraver ()
137 {
138   text_p_ = 0;
139   finished_cresc_p_ = 0;
140   line_spanner_ = 0;
141   span_start_req_l_ = 0;
142   cresc_p_ =0;
143   pending_column_ = 0;
144
145   text_req_l_ = 0;
146   span_req_l_drul_[START] = 0;
147   span_req_l_drul_[STOP] = 0;
148 }
149
150 void
151 Dynamic_engraver::do_post_move_processing ()
152 {
153   text_req_l_ = 0;
154   span_req_l_drul_[START] = 0;
155   span_req_l_drul_[STOP] = 0;
156
157   /* ugr; we must attach the Dynamic_line_spanner to something
158      to be sure that the linebreaker will not be confused
159   */
160   // if (line_spanner_)
161   // line_spanner_->add_column (LEFT, get_staff_info ().command_pcol_l ());
162 }
163
164 bool
165 Dynamic_engraver::do_try_music (Music * m)
166 {
167   if (Text_script_req* d = dynamic_cast <Text_script_req*> (m))
168     {
169       if (d->style_str_ == "dynamic")
170         {
171           text_req_l_ = d;
172           return true;
173         }
174     }
175   else if (Span_req* s =  dynamic_cast <Span_req*> (m))
176     {
177       if ((s->span_type_str_ == "crescendo"
178            || s->span_type_str_ == "decrescendo"))
179         {
180           span_req_l_drul_[s->span_dir_] = s;
181           return true;
182         }
183     }
184   return false;
185 }
186
187 void
188 Dynamic_engraver::do_process_music ()
189 {
190   if ((span_req_l_drul_[START] || span_req_l_drul_[STOP] || text_req_l_)
191       && !line_spanner_
192       && pending_element_arr_.size ())
193     {
194       line_spanner_ = new Dynamic_line_spanner;
195       assert (pending_column_);
196       line_spanner_->add_column (pending_column_);
197       announce_element (Score_element_info
198                         (line_spanner_,
199                          text_req_l_ ? text_req_l_ : span_req_l_drul_[START]));
200
201     }
202
203   if (line_spanner_ && pending_element_arr_.size ())
204     {
205       for (int i = 0; i < pending_element_arr_.size (); i++)
206         pending_element_arr_[i]->set_parent (line_spanner_, Y_AXIS);
207       pending_element_arr_.clear ();
208     }
209
210   if (span_req_l_drul_[START] || span_req_l_drul_[STOP] || text_req_l_)
211     last_request_mom_ = now_mom ();
212   else
213     {
214
215 #if 1
216       /*
217         Maybe always creating a line-spanner for a (de)crescendo (see
218         below) is not a good idea:
219
220             a\< b\p \!c
221
222         the \p will be centred on the line-spanner, and thus clash
223         with the hairpin.  When axis-group code is in place, the \p
224         should move below the hairpin, which is probably better?
225        */
226       if (now > last_request_mom_)
227 #else
228       /*
229         During a (de)crescendo, pending request will not be cleared,
230         and a line-spanner will always be created, as \< \! are already
231         two requests.
232        */
233       if (now > last_request_mom_ && !span_start_req_l_)
234 #endif  
235         {
236           for (int i = 0; i < pending_element_arr_.size (); i++)
237             {
238               Score_element* e = pending_element_arr_[i];
239               side_position (e).set_axis (Y_AXIS);
240               side_position (e).add_staff_support ();
241
242               /*
243                 UGH UGH 
244               */
245               Direction d = directional_element (e).get ();
246               if (!d)
247                 {
248                   SCM s = get_property ("dynamicDirection");
249                   if (!isdir_b (s))
250                     s = get_property ("verticalDirection");
251                   if (isdir_b (s))
252                     d = to_dir (s);
253                   directional_element (e).set (d);
254                 }
255           
256               SCM s = get_property ("dynamicPadding");
257               if (gh_number_p (s))
258                 e->set_elt_property ("padding", s);
259               s = get_property ("dynamicMinimumSpace");
260               if (gh_number_p (s))
261                 e->set_elt_property ("minimum-space", s);
262             }
263           pending_element_arr_.clear ();
264         }
265     } 
266
267   if (text_req_l_)
268     {
269       String loud = text_req_l_->text_str_;
270
271       text_p_ = new Text_item;
272       text_p_->set_elt_property ("text",
273                                           ly_str02scm (loud.ch_C ()));
274       text_p_->set_elt_property ("style", gh_str02scm ("dynamic"));
275       text_p_->set_elt_property ("script-priority",
276                                           gh_int2scm (100));
277       if (Direction d=text_req_l_->get_direction ())
278         directional_element (text_p_).set (d);
279       pending_element_arr_.push (text_p_);
280       text_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
281       text_p_->add_offset_callback (Side_position_interface::aligned_on_self,
282                                     Y_AXIS);
283       announce_element (Score_element_info (text_p_, text_req_l_));
284     }
285
286   if (span_req_l_drul_[STOP])
287     {
288       if (!cresc_p_)
289         {
290           span_req_l_drul_[STOP]->warning
291             (_ ("can't find start of (de)crescendo"));
292         }
293       else
294         {
295           assert (!finished_cresc_p_);
296           cresc_p_->set_bound(RIGHT, get_staff_info ().musical_pcol_l ());
297           finished_cresc_p_ = cresc_p_;
298           cresc_p_ = 0;
299           span_start_req_l_ = 0;
300         }
301     }
302
303   if (span_req_l_drul_[START])
304     {
305       if (span_start_req_l_)
306         {
307           span_req_l_drul_[START]->warning
308             (span_start_req_l_->span_dir_ == 1
309              ?
310              _ ("already have a crescendo")
311              : _ ("already have a decrescendo"));
312         }
313       else
314         {
315           span_start_req_l_ = span_req_l_drul_[START];
316           cresc_p_  = new Crescendo;
317           cresc_p_->set_elt_property
318             ("grow-direction",
319              gh_int2scm ((span_req_l_drul_[START]->span_type_str_ == "crescendo")
320                          ? BIGGER : SMALLER));
321               
322           SCM s = get_property (span_req_l_drul_[START]->span_type_str_ + "Text");
323           if (gh_string_p (s))
324             {
325               cresc_p_->set_elt_property ("start-text", s);
326               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
327                                             + "Text", SCM_UNDEFINED);
328             }
329
330           s = get_property (span_req_l_drul_[START]->span_type_str_ + "Spanner");
331           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
332             {
333               cresc_p_->set_elt_property ("spanner", s);
334               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
335                                             + "Spanner", SCM_UNDEFINED);
336             }
337
338           cresc_p_->set_bound(LEFT, get_staff_info ().musical_pcol_l ());
339
340
341           /* 
342               We know how wide the text is, if we can be sure that the
343               text already has relevant pointers into the paperdef,
344               and it has its font-size property set.
345
346               Since font-size may be set by a context higher up, we
347               can not be sure of the size.
348
349
350               We shouldn't try to do this stuff here, the Item should
351               do it when the score is finished.  We could maybe
352               set a callback to have the Item do the alignment if
353               it is not a special symbol, like Crescendo.
354           */
355
356              
357           if (text_p_)
358             {
359               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
360                               LEFT, SCM_BOOL_T);
361               if (finished_cresc_p_)
362                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
363                                 RIGHT, SCM_BOOL_T);
364             }
365           pending_element_arr_.push (cresc_p_);
366           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
367           cresc_p_->add_offset_callback
368             (Side_position_interface::aligned_on_self, Y_AXIS);
369           announce_element (Score_element_info (cresc_p_, span_req_l_drul_[START]));
370         }
371     }
372 }
373
374 void
375 Dynamic_engraver::do_pre_move_processing ()
376 {
377   typeset_all ();
378 }
379
380 void
381 Dynamic_engraver::do_removal_processing ()
382 {
383   if (cresc_p_)
384     {
385       typeset_element (cresc_p_ );
386       span_start_req_l_->warning (_ ("unterminated (de)crescendo"));
387       cresc_p_ =0;
388     }
389   typeset_all ();
390   if (line_spanner_)
391     {
392       side_position (line_spanner_).add_staff_support ();
393       typeset_element (line_spanner_);
394       line_spanner_ = 0;
395     }
396 }
397
398 void
399 Dynamic_engraver::typeset_all ()
400 {  
401   if (finished_cresc_p_)
402     {
403       typeset_element (finished_cresc_p_);
404       finished_cresc_p_ =0;
405     }
406   
407   if (text_p_)
408     {
409       typeset_element (text_p_);
410       text_p_ = 0;
411     }
412
413   /*
414     TODO: This should be optionised:
415       * break when group of dynamic requests ends
416       * break now 
417       * continue through piece */
418   if (line_spanner_ && last_request_mom_ < now_mom ())
419     {
420       side_position (line_spanner_).add_staff_support ();
421       typeset_element (line_spanner_);
422       line_spanner_ = 0;
423     }
424 }
425
426 void
427 Dynamic_engraver::acknowledge_element (Score_element_info i)
428 {
429   if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
430     {
431       if (line_spanner_)
432         {
433           side_position (line_spanner_).add_support (n);
434           line_spanner_->add_column (n);
435         }
436       else
437         {
438           pending_column_ = n;
439         }
440     }
441 }