]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
patch::: 1.3.35.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] || 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       for (int i = 0; i < pending_element_arr_.size (); i++)
215         {
216           Score_element* e = pending_element_arr_[i];
217           side_position (e).set_axis (Y_AXIS);
218           side_position (e).add_staff_support ();
219
220           /*
221             UGH UGH 
222            */
223           Direction d = directional_element (e).get ();
224           if (!d)
225             {
226               SCM s = get_property ("dynamicDirection");
227               if (!isdir_b (s))
228                 s = get_property ("verticalDirection");
229               if (isdir_b (s))
230                 d = to_dir (s);
231               directional_element (e).set (d);
232             }
233           
234           SCM s = get_property ("dynamicPadding");
235           if (gh_number_p (s))
236             e->set_elt_property ("padding", s);
237           s = get_property ("dynamicMinimumSpace");
238           if (gh_number_p (s))
239             e->set_elt_property ("minimum-space", s);
240         }
241       pending_element_arr_.clear ();
242     } 
243
244   if (text_req_l_)
245     {
246       String loud = text_req_l_->text_str_;
247
248       text_p_ = new Text_item;
249       text_p_->set_elt_property ("text",
250                                           ly_str02scm (loud.ch_C ()));
251       text_p_->set_elt_property ("style", gh_str02scm ("dynamic"));
252       text_p_->set_elt_property ("script-priority",
253                                           gh_int2scm (100));
254       if (Direction d=text_req_l_->get_direction ())
255         directional_element (text_p_).set (d);
256       pending_element_arr_.push (text_p_);
257       text_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
258       text_p_->add_offset_callback (Side_position_interface::aligned_on_self,
259                                     Y_AXIS);
260       announce_element (Score_element_info (text_p_, text_req_l_));
261     }
262
263   if (span_req_l_drul_[STOP])
264     {
265       if (!cresc_p_)
266         {
267           span_req_l_drul_[STOP]->warning
268             (_ ("can't find start of (de)crescendo"));
269         }
270       else
271         {
272           assert (!finished_cresc_p_);
273           cresc_p_->set_bound(RIGHT, get_staff_info ().musical_pcol_l ());
274           finished_cresc_p_ = cresc_p_;
275           cresc_p_ = 0;
276           span_start_req_l_ = 0;
277         }
278     }
279
280   if (span_req_l_drul_[START])
281     {
282       if (span_start_req_l_)
283         {
284           span_req_l_drul_[START]->warning
285             (span_start_req_l_->span_dir_ == 1
286              ?
287              _ ("already have a crescendo")
288              : _ ("already have a decrescendo"));
289         }
290       else
291         {
292           span_start_req_l_ = span_req_l_drul_[START];
293           cresc_p_  = new Crescendo;
294           cresc_p_->set_elt_property
295             ("grow-direction",
296              gh_int2scm ((span_req_l_drul_[START]->span_type_str_ == "crescendo")
297                          ? BIGGER : SMALLER));
298               
299           SCM s = get_property (span_req_l_drul_[START]->span_type_str_ + "Text");
300           if (gh_string_p (s))
301             {
302               cresc_p_->set_elt_property ("start-text", s);
303               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
304                                             + "Text", SCM_UNDEFINED);
305             }
306
307           s = get_property (span_req_l_drul_[START]->span_type_str_ + "Spanner");
308           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
309             {
310               cresc_p_->set_elt_property ("spanner", s);
311               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
312                                             + "Spanner", SCM_UNDEFINED);
313             }
314
315           cresc_p_->set_bound(LEFT, get_staff_info ().musical_pcol_l ());
316
317
318           /* 
319               We know how wide the text is, if we can be sure that the
320               text already has relevant pointers into the paperdef,
321               and it has its font-size property set.
322
323               Since font-size may be set by a context higher up, we
324               can not be sure of the size.
325
326
327               We shouldn't try to do this stuff here, the Item should
328               do it when the score is finished.  We could maybe
329               set a callback to have the Item do the alignment if
330               it is not a special symbol, like Crescendo.
331           */
332
333              
334           if (text_p_)
335             {
336               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
337                               LEFT, SCM_BOOL_T);
338               if (finished_cresc_p_)
339                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
340                                 RIGHT, SCM_BOOL_T);
341             }
342           pending_element_arr_.push (cresc_p_);
343           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
344           cresc_p_->add_offset_callback
345             (Side_position_interface::aligned_on_self, Y_AXIS);
346           announce_element (Score_element_info (cresc_p_, span_req_l_drul_[START]));
347         }
348     }
349 }
350
351 void
352 Dynamic_engraver::do_pre_move_processing ()
353 {
354   typeset_all ();
355 }
356
357 void
358 Dynamic_engraver::do_removal_processing ()
359 {
360   if (cresc_p_)
361     {
362       typeset_element (cresc_p_ );
363       span_start_req_l_->warning (_ ("unterminated (de)crescendo"));
364       cresc_p_ =0;
365     }
366   typeset_all ();
367   if (line_spanner_)
368     {
369       side_position (line_spanner_).add_staff_support ();
370       typeset_element (line_spanner_);
371       line_spanner_ = 0;
372     }
373 }
374
375 void
376 Dynamic_engraver::typeset_all ()
377 {  
378   if (finished_cresc_p_)
379     {
380       typeset_element (finished_cresc_p_);
381       finished_cresc_p_ =0;
382     }
383   
384   if (text_p_)
385     {
386       typeset_element (text_p_);
387       text_p_ = 0;
388     }
389
390   /*
391     TODO: This should be optionised:
392       * break when group of dynamic requests ends
393       * break now 
394       * continue through piece */
395   if (line_spanner_ && last_request_mom_ < now_mom ())
396     {
397       side_position (line_spanner_).add_staff_support ();
398       typeset_element (line_spanner_);
399       line_spanner_ = 0;
400     }
401 }
402
403 void
404 Dynamic_engraver::acknowledge_element (Score_element_info i)
405 {
406   if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
407     {
408       if (line_spanner_)
409         {
410           side_position (line_spanner_).add_support (n);
411           line_spanner_->add_column (n);
412         }
413       else
414         {
415           pending_column_ = n;
416         }
417     }
418 }