]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-fingering-engraver.cc
Update mailmap.
[lilypond.git] / lily / new-fingering-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1998--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "engraver.hh"
21
22 #include "international.hh"
23 #include "rhythmic-head.hh"
24 #include "script-interface.hh"
25 #include "self-alignment-interface.hh"
26 #include "side-position-interface.hh"
27 #include "stem.hh"
28 #include "stream-event.hh"
29 #include "item.hh"
30 #include "warn.hh"
31
32 #include "translator.icc"
33
34
35
36 struct Finger_tuple
37 {
38   Grob *head_;
39   Grob *script_;
40   Stream_event *note_event_;
41   Stream_event *finger_event_;
42   bool follow_into_staff_;
43   int position_;
44
45   Finger_tuple ()
46   {
47     position_ = 0;
48     head_ = script_ = 0;
49     note_event_ = finger_event_ = 0;
50     follow_into_staff_ = false;
51   }
52 };
53
54 bool
55 operator< (Finger_tuple const &a, Finger_tuple const &b)
56 {
57   return a.position_ < b.position_;
58 }
59
60 class New_fingering_engraver : public Engraver
61 {
62   vector<Finger_tuple> fingerings_;
63   vector<Finger_tuple> stroke_fingerings_;
64   vector<Finger_tuple> articulations_;
65   vector<Finger_tuple> string_numbers_;
66
67   vector<Grob*> heads_;
68   Grob *stem_;
69
70   void position_all ();
71 public:
72   TRANSLATOR_DECLARATIONS (New_fingering_engraver);
73 protected:
74   void stop_translation_timestep ();
75   DECLARE_ACKNOWLEDGER (rhythmic_head);
76   DECLARE_ACKNOWLEDGER (stem);
77   void add_fingering (Grob *, SCM,
78                       vector<Finger_tuple> *,
79                       Stream_event *, Stream_event *);
80   void add_script (Grob *, Stream_event *, Stream_event *);
81   void add_string (Grob *, Stream_event *, Stream_event *);
82   void position_scripts (SCM orientations, vector<Finger_tuple> *);
83 };
84
85 void
86 New_fingering_engraver::acknowledge_rhythmic_head (Grob_info inf)
87 {
88   Stream_event *note_ev = inf.event_cause ();
89   if (!note_ev)
90     return;
91
92   SCM arts = note_ev->get_property ("articulations");
93
94   for (SCM s = arts; scm_is_pair (s); s = scm_cdr (s))
95     {
96       Stream_event *ev = unsmob_stream_event (scm_car (s));
97
98       if (!ev)
99         continue;
100
101       if (ev->in_event_class ("fingering-event"))
102         add_fingering (inf.grob (),
103                        ly_symbol2scm ("Fingering"),
104                        &fingerings_,
105                        ev, note_ev);
106       else if (ev->in_event_class ("text-script-event"))
107         ev->origin ()->warning (_ ("cannot add text scripts to individual note heads"));
108       else if (ev->in_event_class ("script-event"))
109         add_script (inf.grob (), ev, note_ev);
110       else if (ev->in_event_class ("string-number-event"))
111         add_fingering (inf.grob (),
112                        ly_symbol2scm ("StringNumber"), &string_numbers_,
113                        ev, note_ev);
114       else if (ev->in_event_class ("stroke-finger-event"))
115         add_fingering (inf.grob (),
116                        ly_symbol2scm ("StrokeFinger"), &stroke_fingerings_,
117                        ev, note_ev);
118       else if (ev->in_event_class ("harmonic-event"))
119         {
120           inf.grob ()->set_property ("style", ly_symbol2scm ("harmonic"));
121           Grob *d = unsmob_grob (inf.grob ()->get_object ("dot"));
122           if (d && !to_boolean (get_property ("harmonicDots")))
123             d->suicide ();
124         }
125     }
126
127   heads_.push_back (inf.grob ());
128 }
129
130 void
131 New_fingering_engraver::acknowledge_stem (Grob_info inf)
132 {
133   stem_ = inf.grob ();
134 }
135
136 void
137 New_fingering_engraver::add_script (Grob *head,
138                                     Stream_event *event,
139                                     Stream_event * /* note */)
140 {
141   Finger_tuple ft;
142
143   Grob *g = make_item ("Script", event->self_scm ());
144   make_script_from_event (g, context (),
145                           event->get_property ("articulation-type"), 0);
146   ft.script_ = g;
147   ft.script_->set_parent (head, X_AXIS);
148   
149   articulations_.push_back (ft);
150 }
151
152 void
153 New_fingering_engraver::add_fingering (Grob *head,
154                                        SCM grob_sym,
155                                        vector<Finger_tuple> *tuple_vector,
156                                        Stream_event *event,
157                                        Stream_event *hevent)
158 {
159   Finger_tuple ft;
160
161   ft.script_ = internal_make_item (grob_sym, event->self_scm (),
162                                    ly_symbol2string (grob_sym).c_str (),
163                                    __FILE__, __LINE__, __FUNCTION__
164                                    );
165
166   Side_position_interface::add_support (ft.script_, head);
167
168   ft.finger_event_ = event;
169   ft.note_event_ = hevent;
170   ft.head_ = head;
171
172   tuple_vector->push_back (ft);
173 }
174
175 void
176 New_fingering_engraver::position_scripts (SCM orientations,
177                                           vector<Finger_tuple> *scripts)
178 {
179   for (vsize i = 0; i < scripts->size (); i++)
180     if (stem_ && to_boolean (scripts->at (i).script_->get_property ("add-stem-support")))
181       Side_position_interface::add_support (scripts->at (i).script_, stem_);
182
183   /*
184     This is not extremely elegant, but we have to do a little
185     formatting here, because the parent/child relations should be
186     known before we move on to the next time step.
187
188     A more sophisticated approach would be to set both X and Y parents
189     to the note head, and write a more flexible function for
190     positioning the fingerings, setting both X and Y coordinates.
191   */
192   for (vsize i = 0; i < scripts->size (); i++)
193     (*scripts)[i].position_ = scm_to_int ((*scripts)[i].head_->get_property ("staff-position"));
194
195   for (vsize i = scripts->size (); i--;)
196     for (vsize j = heads_.size (); j--;)
197       Side_position_interface::add_support ((*scripts)[i].script_, heads_[j]);
198
199   vector<Finger_tuple> up, down, horiz;
200   for (vsize i = scripts->size (); i--;)
201     {
202       SCM d = (*scripts)[i].finger_event_->get_property ("direction");
203       if (to_dir (d))
204         {
205           ((to_dir (d) == UP) ? up : down).push_back ((*scripts)[i]);
206           scripts->erase (scripts->begin () + i);
207         }
208     }
209
210   vector_sort (*scripts, less<Finger_tuple> ());
211
212   bool up_p = scm_c_memq (ly_symbol2scm ("up"), orientations) != SCM_BOOL_F;
213   bool down_p = scm_c_memq (ly_symbol2scm ("down"), orientations) != SCM_BOOL_F;
214   bool left_p = scm_c_memq (ly_symbol2scm ("left"), orientations) != SCM_BOOL_F;
215   bool right_p = scm_c_memq (ly_symbol2scm ("right"), orientations) != SCM_BOOL_F;
216   Direction hordir = (right_p) ? RIGHT : LEFT;
217   if (left_p || right_p)
218     {
219       if (up_p && !up.size () && scripts->size ())
220         {
221           up.push_back (scripts->back ());
222           scripts->pop_back ();
223         }
224
225       if (down_p && !down.size () && scripts->size ())
226         {
227           down.push_back ((*scripts)[0]);
228           scripts->erase (scripts->begin ());
229         }
230
231       horiz.insert (horiz.end (), scripts->begin (), scripts->end ());
232     }
233   else if (up_p && down_p)
234     {
235       int center = scripts->size () / 2;
236       down.insert (down.end (), scripts->begin (), scripts->begin () + center);
237       up.insert (up.end (), scripts->begin () + center, scripts->end ());
238     }
239   else if (up_p)
240     {
241       up.insert (up.end (), scripts->begin (), scripts->end ());
242       scripts->clear ();
243     }
244   else
245     {
246       if (!down_p)
247         {
248           warning (_ ("no placement found for fingerings"));
249           warning (_ ("placing below"));
250         }
251       down.insert (down.end (), scripts->begin (), scripts->end ());
252       scripts->clear ();
253     }
254
255   for (vsize i = 0; i < horiz.size (); i++)
256     {
257       Finger_tuple ft = horiz[i];
258       Grob *f = ft.script_;
259       f->set_parent (ft.head_, X_AXIS);
260       f->set_parent (ft.head_, Y_AXIS);
261       f->set_property ("avoid-slur", SCM_BOOL_F);
262       if (hordir == LEFT
263           && unsmob_grob (ft.head_->get_object ("accidental-grob")))
264         Side_position_interface::add_support (f,
265                                               unsmob_grob (ft.head_->get_object ("accidental-grob")));
266       else if (unsmob_grob (ft.head_->get_object ("dot")))
267         Side_position_interface::add_support (f,
268                                               unsmob_grob (ft.head_->get_object ("dot")));
269                                               
270       Self_alignment_interface::set_align_self (f, Y_AXIS);
271       Self_alignment_interface::set_center_parent (f, Y_AXIS);
272       Side_position_interface::set_axis (f, X_AXIS);
273
274       f->set_property ("direction", scm_from_int (hordir));
275     }
276
277   Direction d = DOWN;
278   Drul_array< vector<Finger_tuple> > vertical (down, up);
279   do
280     {
281       for (vsize i = 0; i < vertical[d].size (); i++)
282         {
283           Finger_tuple ft = vertical[d][i];
284           Grob *f = ft.script_;
285           int finger_prio = robust_scm2int (f->get_property ("script-priority"), 200);
286           f->set_parent (ft.head_, X_AXIS);
287           f->set_property ("script-priority",
288                            scm_from_int (finger_prio + d * ft.position_));
289
290           Self_alignment_interface::set_align_self (f, X_AXIS);
291           Self_alignment_interface::set_center_parent (f, X_AXIS);
292           Side_position_interface::set_axis (f, Y_AXIS);
293       
294           f->set_property ("direction", scm_from_int (d));
295         }
296     }
297   while (flip (&d) != DOWN);
298 }
299
300 void
301 New_fingering_engraver::stop_translation_timestep ()
302 {
303   position_all ();
304   stem_ = 0;
305   heads_.clear ();
306 }
307
308
309 void
310 New_fingering_engraver::position_all ()
311 {
312   if (fingerings_.size ())
313     {
314       position_scripts (get_property ("fingeringOrientations"),
315                         &fingerings_);
316       fingerings_.clear ();
317     }
318
319   if (string_numbers_.size ())
320     {
321       position_scripts (get_property ("stringNumberOrientations"),
322                         &string_numbers_);
323       string_numbers_.clear ();
324     }
325
326   if (stroke_fingerings_.size ())
327     {
328       position_scripts (get_property ("strokeFingerOrientations"),
329                         &stroke_fingerings_);
330       stroke_fingerings_.clear ();
331     }
332   
333   for (vsize i = articulations_.size (); i--;)
334     {
335       Grob *script = articulations_[i].script_;
336
337       for (vsize j = heads_.size (); j--;)
338         Side_position_interface::add_support (script, heads_[j]);
339
340       if (stem_ && to_dir (script->get_property ("side-relative-direction")))
341         script->set_object ("direction-source", stem_->self_scm ());
342
343       if (stem_ && to_boolean (script->get_property ("add-stem-support")))
344         Side_position_interface::add_support (script, stem_);
345     }
346   articulations_.clear ();
347 }
348
349 New_fingering_engraver::New_fingering_engraver ()
350 {
351   stem_ = 0;
352 }
353
354
355 ADD_ACKNOWLEDGER (New_fingering_engraver, rhythmic_head);
356 ADD_ACKNOWLEDGER (New_fingering_engraver, stem);
357
358 ADD_TRANSLATOR (New_fingering_engraver,
359                 /* doc */
360                 "Create fingering scripts for notes in a new chord.  This"
361                 " engraver is ill-named, since it also takes care of"
362                 " articulations and harmonic note heads.",
363
364                 /* create */
365                 "Fingering "
366                 "StringNumber "
367                 "StrokeFinger "
368                 "Script ",
369
370                 /* read */
371                 "fingeringOrientations "
372                 "harmonicDots "
373                 "strokeFingerOrientations "
374                 "stringNumberOrientations ",
375                 
376                 /* write */
377                 ""
378                 );