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