]> git.donarmstrong.com Git - lilypond.git/blob - lily/dynamic-engraver.cc
patch::: 1.3.34.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   * padding of orphaned items
33   * direction of orphaned items
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   Direction get_default_dir () const;
43 };
44
45 Dynamic_line_spanner::Dynamic_line_spanner ()
46 {
47   set_elt_property ("transparent", SCM_BOOL_T);
48   side_position (this).set_axis (Y_AXIS);
49 }
50
51 void
52 Dynamic_line_spanner::add_column (Item* n)
53 {
54   if (!get_bound (LEFT))
55     set_bound (LEFT, n);
56   else
57     set_bound (RIGHT, n);
58
59   add_dependency (n);
60 }
61
62 Direction
63 Dynamic_line_spanner::get_default_dir () const
64 {
65   return DOWN;
66 }
67
68 /**
69    print text & hairpin dynamics.
70  */
71 class Dynamic_engraver : public Engraver
72 {
73   Text_item * text_p_;
74   Crescendo * finished_cresc_p_;
75   Crescendo * cresc_p_;
76
77   Text_script_req* text_req_l_;
78   Span_req * span_start_req_l_;
79   Drul_array<Span_req*> span_req_l_drul_;
80
81   Dynamic_line_spanner* line_spanner_;
82   Moment last_request_mom_;
83
84   Note_column* pending_column_;
85   Link_array<Score_element> pending_element_arr_;
86   
87   void  typeset_all ();
88
89 public:
90   VIRTUAL_COPY_CONS(Translator);
91   Dynamic_engraver ();
92   
93 protected:
94   void announce_element (Score_element_info);
95   
96   virtual void do_removal_processing ();
97   virtual void acknowledge_element (Score_element_info);
98   virtual bool do_try_music (Music *req_l);
99   virtual void do_process_music ();
100   virtual void do_pre_move_processing ();
101   virtual void do_post_move_processing ();
102 };
103
104 ADD_THIS_TRANSLATOR (Dynamic_engraver);
105
106 void
107 Dynamic_engraver::announce_element (Score_element_info i)
108 {
109   group (i.elem_l_, "interfaces").add_thing (ly_symbol2scm ("dynamic"));
110   
111   Engraver::announce_element (i);
112 }
113
114
115 Dynamic_engraver::Dynamic_engraver ()
116 {
117   text_p_ = 0;
118   finished_cresc_p_ = 0;
119   line_spanner_ = 0;
120   span_start_req_l_ = 0;
121   cresc_p_ =0;
122   pending_column_ = 0;
123
124   text_req_l_ = 0;
125   span_req_l_drul_[START] = 0;
126   span_req_l_drul_[STOP] = 0;
127 }
128
129 void
130 Dynamic_engraver::do_post_move_processing ()
131 {
132   text_req_l_ = 0;
133   span_req_l_drul_[START] = 0;
134   span_req_l_drul_[STOP] = 0;
135
136   /* ugr; we must attach the Dynamic_line_spanner to something
137      to be sure that the linebreaker will not be confused
138   */
139   // if (line_spanner_)
140   // line_spanner_->add_column (LEFT, get_staff_info ().command_pcol_l ());
141 }
142
143 bool
144 Dynamic_engraver::do_try_music (Music * m)
145 {
146   if (Text_script_req* d = dynamic_cast <Text_script_req*> (m))
147     {
148       if (d->style_str_ == "dynamic")
149         {
150           text_req_l_ = d;
151           return true;
152         }
153     }
154   else if (Span_req* s =  dynamic_cast <Span_req*> (m))
155     {
156       if ((s->span_type_str_ == "crescendo"
157            || s->span_type_str_ == "decrescendo"))
158         {
159           span_req_l_drul_[s->span_dir_] = s;
160           return true;
161         }
162     }
163   return false;
164 }
165
166 void
167 Dynamic_engraver::do_process_music ()
168 {
169   if ((span_req_l_drul_[START] || text_req_l_)
170       && !line_spanner_
171       && pending_element_arr_.size ())
172     {
173       line_spanner_ = new Dynamic_line_spanner;
174       assert (pending_column_);
175       line_spanner_->add_column (pending_column_);
176       announce_element (Score_element_info
177                         (line_spanner_,
178                          text_req_l_ ? text_req_l_ : span_req_l_drul_[START]));
179
180     }
181
182   if (line_spanner_ && pending_element_arr_.size ())
183     {
184       for (int i = 0; i < pending_element_arr_.size (); i++)
185         pending_element_arr_[i]->set_parent (line_spanner_, Y_AXIS);
186       pending_element_arr_.clear ();
187     }
188
189   if (span_req_l_drul_[START] || span_req_l_drul_[STOP] || text_req_l_)
190     last_request_mom_ = now_mom ();
191   else
192     {
193       for (int i = 0; i < pending_element_arr_.size (); i++)
194         {
195           Score_element* e = pending_element_arr_[i];
196           side_position (e).set_axis (Y_AXIS);
197           side_position (e).add_staff_support ();
198
199           /*
200             UGH UGH 
201            */
202           Direction d = directional_element (e).get ();
203           if (!d)
204             {
205               SCM s = get_property ("dynamicDirection");
206               if (!isdir_b (s))
207                 s = get_property ("verticalDirection");
208               if (isdir_b (s))
209                 d = to_dir (s);
210               directional_element (e).set (d);
211             }
212           
213           SCM s = get_property ("dynamicPadding");
214           if (gh_number_p (s))
215             e->set_elt_property ("padding", s);
216         }
217       pending_element_arr_.clear ();
218     } 
219
220   if (text_req_l_)
221     {
222       String loud = text_req_l_->text_str_;
223
224       text_p_ = new Text_item;
225       text_p_->set_elt_property ("text",
226                                           ly_str02scm (loud.ch_C ()));
227       text_p_->set_elt_property ("style", gh_str02scm ("dynamic"));
228       text_p_->set_elt_property ("script-priority",
229                                           gh_int2scm (100));
230       if (Direction d=text_req_l_->get_direction ())
231         directional_element (text_p_).set (d);
232       pending_element_arr_.push (text_p_);
233       text_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
234       text_p_->add_offset_callback (Side_position_interface::aligned_on_self,
235                                     Y_AXIS);
236       announce_element (Score_element_info (text_p_, text_req_l_));
237     }
238
239   if (span_req_l_drul_[STOP])
240     {
241       if (!cresc_p_)
242         {
243           span_req_l_drul_[STOP]->warning
244             (_ ("can't find start of (de)crescendo"));
245         }
246       else
247         {
248           assert (!finished_cresc_p_);
249           cresc_p_->set_bound(RIGHT, get_staff_info ().musical_pcol_l ());
250           finished_cresc_p_ = cresc_p_;
251           cresc_p_ = 0;
252           span_start_req_l_ = 0;
253         }
254     }
255
256   if (span_req_l_drul_[START])
257     {
258       if (span_start_req_l_)
259         {
260           span_req_l_drul_[START]->warning
261             (span_start_req_l_->span_dir_ == 1
262              ?
263              _ ("already have a crescendo")
264              : _ ("already have a decrescendo"));
265         }
266       else
267         {
268           span_start_req_l_ = span_req_l_drul_[START];
269           cresc_p_  = new Crescendo;
270           cresc_p_->set_elt_property
271             ("grow-direction",
272              gh_int2scm ((span_req_l_drul_[START]->span_type_str_ == "crescendo")
273                          ? BIGGER : SMALLER));
274               
275           SCM s = get_property (span_req_l_drul_[START]->span_type_str_ + "Text");
276           if (gh_string_p (s))
277             {
278               cresc_p_->set_elt_property ("start-text", s);
279               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
280                                             + "Text", SCM_UNDEFINED);
281             }
282
283           s = get_property (span_req_l_drul_[START]->span_type_str_ + "Spanner");
284           if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
285             {
286               cresc_p_->set_elt_property ("spanner", s);
287               daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
288                                             + "Spanner", SCM_UNDEFINED);
289             }
290
291           cresc_p_->set_bound(LEFT, get_staff_info ().musical_pcol_l ());
292
293
294           /* 
295               We know how wide the text is, if we can be sure that the
296               text already has relevant pointers into the paperdef,
297               and it has its font-size property set.
298
299               Since font-size may be set by a context higher up, we
300               can not be sure of the size.
301
302
303               We shouldn't try to do this stuff here, the Item should
304               do it when the score is finished.  We could maybe
305               set a callback to have the Item do the alignment if
306               it is not a special symbol, like Crescendo.
307           */
308
309              
310           if (text_p_)
311             {
312               index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
313                               LEFT, SCM_BOOL_T);
314               if (finished_cresc_p_)
315                 index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
316                                 RIGHT, SCM_BOOL_T);
317             }
318           pending_element_arr_.push (cresc_p_);
319           cresc_p_->set_elt_property ("self-alignment-Y", gh_int2scm (0));
320           cresc_p_->add_offset_callback
321             (Side_position_interface::aligned_on_self, Y_AXIS);
322           announce_element (Score_element_info (cresc_p_, span_req_l_drul_[START]));
323         }
324     }
325 }
326
327 void
328 Dynamic_engraver::do_pre_move_processing ()
329 {
330   typeset_all ();
331 }
332
333 void
334 Dynamic_engraver::do_removal_processing ()
335 {
336   if (cresc_p_)
337     {
338       typeset_element (cresc_p_ );
339       span_start_req_l_->warning (_ ("unterminated (de)crescendo"));
340       cresc_p_ =0;
341     }
342   typeset_all ();
343   if (line_spanner_)
344     {
345       side_position (line_spanner_).add_staff_support ();
346       typeset_element (line_spanner_);
347       line_spanner_ = 0;
348     }
349 }
350
351 void
352 Dynamic_engraver::typeset_all ()
353 {  
354   if (finished_cresc_p_)
355     {
356       typeset_element (finished_cresc_p_);
357       finished_cresc_p_ =0;
358     }
359   
360   if (text_p_)
361     {
362       typeset_element (text_p_);
363       text_p_ = 0;
364     }
365
366   /*
367     TODO: This should be optionised:
368       * break when group of dynamic requests ends
369       * break now 
370       * continue through piece */
371   if (line_spanner_ && last_request_mom_ < now_mom ())
372     {
373       side_position (line_spanner_).add_staff_support ();
374       typeset_element (line_spanner_);
375       line_spanner_ = 0;
376     }
377 }
378
379 void
380 Dynamic_engraver::acknowledge_element (Score_element_info i)
381 {
382   if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
383     {
384       if (line_spanner_)
385         {
386           side_position (line_spanner_).add_support (n);
387           line_spanner_->add_column (n);
388         }
389       else
390         {
391           pending_column_ = n;
392         }
393     }
394 }