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