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