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