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