]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-fingering-engraver.cc
2a6930bec394e969548dcb8b938a161a49622375
[lilypond.git] / lily / new-fingering-engraver.cc
1 /*
2   fingering-engraver.cc -- implement New_fingering_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "warn.hh"
10 #include "engraver.hh"
11 #include "side-position-interface.hh"
12 #include "stem.hh"
13 #include "rhythmic-head.hh"
14 #include "self-alignment-interface.hh"
15 #include "script-interface.hh"
16 #include "stem.hh"
17
18 struct Finger_tuple
19 {
20   Grob *head_;
21   Grob *script_;
22   Music *note_event_;
23   Music *finger_event_;
24   bool follow_into_staff_;
25   int position_;
26
27   Finger_tuple ()
28   {
29     position_ = 0;
30     head_ = script_ = 0;
31     note_event_ = finger_event_ = 0;
32     follow_into_staff_ = false;
33   }
34   static int compare (Finger_tuple const &c1, Finger_tuple const &c2)
35   {
36     return c1.position_- c2.position_;
37   }
38 };
39
40 class New_fingering_engraver : public Engraver
41 {
42   Array<Finger_tuple> fingerings_;
43   Array<Finger_tuple> articulations_;
44   Link_array<Grob> heads_;
45   Grob *stem_;
46
47 public:
48   TRANSLATOR_DECLARATIONS (New_fingering_engraver);
49 protected:
50   virtual void stop_translation_timestep ();
51   virtual void acknowledge_grob (Grob_info);
52   void add_fingering (Grob *, Music *, Music *);
53   void add_script (Grob *, Music *, Music *);
54   void position_scripts ();
55 };
56
57 void
58 New_fingering_engraver::acknowledge_grob (Grob_info inf)
59 {
60   if (Rhythmic_head::has_interface (inf.grob_))
61     {
62       Music *note_ev = inf.music_cause ();
63       if (!note_ev)
64         return;
65
66       SCM arts = note_ev->get_property ("articulations");
67
68       for (SCM s = arts; scm_is_pair (s); s = scm_cdr (s))
69         {
70           Music *m = unsmob_music (scm_car (s));
71
72           if (!m)
73             continue;
74
75           if (m->is_mus_type ("fingering-event"))
76             {
77               add_fingering (inf.grob_, m, note_ev);
78             }
79           else if (m->is_mus_type ("text-script-event"))
80             {
81               m->origin ()->warning ("Can not add text scripts to individual note heads");
82             }
83           else if (m->is_mus_type ("script-event"))
84             {
85               add_script (inf.grob_, m, note_ev);
86             }
87           else if (m->is_mus_type ("harmonic-event"))
88             {
89               inf.grob_->set_property ("style", ly_symbol2scm ("harmonic"));
90               Grob *d = unsmob_grob (inf.grob_->get_property ("dot"));
91               if (d)
92                 d->suicide ();
93             }
94         }
95
96       heads_.push (inf.grob_);
97     }
98   else if (Stem::has_interface (inf.grob_))
99     {
100       stem_ = inf.grob_;
101     }
102 }
103
104 void
105 New_fingering_engraver::add_script (Grob *head,
106                                     Music *event,
107                                     Music *)
108 {
109   Finger_tuple ft;
110
111   Grob *g = make_item ("Script", event->self_scm ());
112   make_script_from_event (g, &ft.follow_into_staff_, context (),
113                           event->get_property ("articulation-type"), 0);
114   if (g)
115     {
116       ft.script_ = g;
117
118       articulations_.push (ft);
119
120       ft.script_->set_parent (head, X_AXIS);
121     }
122 }
123
124 void
125 New_fingering_engraver::add_fingering (Grob *head,
126                                        Music *event,
127                                        Music *hevent)
128 {
129   Finger_tuple ft;
130
131   ft.script_ = make_item ("Fingering", event->self_scm ());
132
133   Side_position_interface::add_support (ft.script_, head);
134
135   int d = scm_to_int (event->get_property ("digit"));
136
137   /*
138     TODO:
139
140     Should add support for thumb.  It's a little involved, since
141     the thumb lives in a different font. Maybe it should be moved?
142
143   */
144   if (d > 5)
145     {
146       /*
147         music for the softenon children?
148       */
149       event->origin ()->warning (_ ("music for the martians."));
150     }
151   SCM sstr = scm_number_to_string (scm_int2num (d), scm_int2num (10));
152   ft.script_->set_property ("text", sstr);
153
154   ft.finger_event_ = event;
155   ft.note_event_ = hevent;
156   ft.head_ = head;
157
158   fingerings_.push (ft);
159 }
160
161 void
162 New_fingering_engraver::position_scripts ()
163 {
164
165   /*
166     This is not extremely elegant, but we have to do a little
167     formatting here, because the parent/child relations should be
168     known before we move on to the next time step.
169
170     A more sophisticated approach would be to set both X and Y parents
171     to the note head, and write a more flexible function for
172     positioning the fingerings, setting both X and Y coordinates.
173   */
174   for (int i = 0; i < fingerings_.size (); i++)
175     {
176       fingerings_[i].position_ = scm_to_int (fingerings_[i].head_->get_property ("staff-position"));
177     }
178
179   for (int i = fingerings_.size (); i--;)
180     for (int j = heads_.size (); j--;)
181       Side_position_interface::add_support (fingerings_[i].script_, heads_[j]);
182
183   Array<Finger_tuple> up, down, horiz;
184   for (int i = fingerings_.size (); i--;)
185     {
186       SCM d = fingerings_[i].finger_event_->get_property ("direction");
187       if (to_dir (d))
188         {
189           ((to_dir (d) == UP) ? up : down).push (fingerings_[i]);
190           fingerings_.del (i);
191         }
192     }
193
194   fingerings_.sort (&Finger_tuple::compare);
195   SCM orientations = get_property ("fingeringOrientations");
196
197   bool up_p = scm_c_memq (ly_symbol2scm ("up"), orientations) != SCM_BOOL_F;
198   bool down_p = scm_c_memq (ly_symbol2scm ("down"), orientations) != SCM_BOOL_F;
199   bool left_p = scm_c_memq (ly_symbol2scm ("left"), orientations) != SCM_BOOL_F;
200   bool right_p = scm_c_memq (ly_symbol2scm ("right"), orientations) != SCM_BOOL_F;
201   Direction hordir = (right_p) ? RIGHT : LEFT;
202   if (left_p || right_p)
203     {
204       if (up_p && !up.size () && fingerings_.size ())
205         up.push (fingerings_.pop ());
206
207       if (down_p && !down.size () && fingerings_.size ())
208         {
209           down.push (fingerings_[0]);
210           fingerings_.del (0);
211         }
212
213       horiz.concat (fingerings_);
214     }
215   else if (up_p && down_p)
216     {
217       int center = fingerings_.size () / 2;
218       down.concat (fingerings_.slice (0, center));
219       up.concat (fingerings_.slice (center, fingerings_.size ()));
220     }
221   else if (up_p)
222     {
223       up.concat (fingerings_);
224       fingerings_.clear ();
225     }
226   else
227     {
228       if (!down_p)
229         warning (_ ("Fingerings are also not down?! Putting them down anyway."));
230       down.concat (fingerings_);
231       fingerings_.clear ();
232     }
233
234   for (int i = 0; i < horiz.size (); i++)
235     {
236       Finger_tuple ft = horiz[i];
237       Grob *f = ft.script_;
238       f->set_parent (ft.head_, X_AXIS);
239       f->set_parent (ft.head_, Y_AXIS);
240       f->add_offset_callback (Self_alignment_interface::centered_on_parent_proc, Y_AXIS);
241       f->add_offset_callback (Self_alignment_interface::aligned_on_self_proc, Y_AXIS);
242       f->add_offset_callback (Side_position_interface::aligned_side_proc, X_AXIS);
243
244       f->set_property ("direction", scm_int2num (hordir));
245     }
246
247   int finger_prio = 200;
248   for (int i = 0; i < up.size (); i++)
249     {
250       Finger_tuple ft = up[i];
251       Grob *f = ft.script_;
252       f->set_parent (ft.head_, X_AXIS);
253       f->set_property ("script-priority",
254                        scm_int2num (finger_prio + ft.position_));
255       f->add_offset_callback (Side_position_interface::aligned_side_proc, Y_AXIS);
256       f->add_offset_callback (Self_alignment_interface::centered_on_parent_proc, X_AXIS);
257       f->add_offset_callback (Self_alignment_interface::aligned_on_self_proc, X_AXIS);
258
259       f->set_property ("direction", scm_int2num (UP));
260     }
261
262   for (int i = 0; i < down.size (); i++)
263     {
264       Finger_tuple ft = down[i];
265       Grob *f = ft.script_;
266       f->set_parent (ft.head_, X_AXIS);
267       f->set_property ("script-priority",
268                        scm_int2num (finger_prio + down.size () - ft.position_));
269
270       f->add_offset_callback (Self_alignment_interface::centered_on_parent_proc, X_AXIS);
271       f->add_offset_callback (Self_alignment_interface::aligned_on_self_proc, X_AXIS);
272       f->add_offset_callback (Side_position_interface::aligned_side_proc, Y_AXIS);
273       f->set_property ("direction", scm_int2num (DOWN));
274     }
275 }
276
277 void
278 New_fingering_engraver::stop_translation_timestep ()
279 {
280   if (fingerings_.size ())
281     {
282       for (int i = 0; i < fingerings_.size (); i++)
283         if (stem_ && to_boolean (fingerings_[i].script_->get_property ("add-stem-support")))
284           Side_position_interface::add_support (fingerings_[i].script_, stem_);
285       position_scripts ();
286       fingerings_.clear ();
287     }
288
289   for (int i = articulations_.size (); i--;)
290     {
291       Grob *script = articulations_[i].script_;
292
293       for (int j = heads_.size (); j--;)
294         Side_position_interface::add_support (script, heads_[j]);
295
296       if (stem_ && to_dir (script->get_property ("side-relative-direction")))
297         script->set_property ("direction-source", stem_->self_scm ());
298
299       if (stem_ && to_boolean (script->get_property ("add-stem-support")))
300         Side_position_interface::add_support (script, stem_);
301
302       if (articulations_[i].follow_into_staff_)
303         {
304           script->add_offset_callback (Side_position_interface::quantised_position_proc, Y_AXIS);
305           script->set_property ("staff-padding", SCM_EOL);
306         }
307     }
308
309   stem_ = 0;
310   heads_.clear ();
311   articulations_.clear ();
312 }
313
314 New_fingering_engraver::New_fingering_engraver ()
315 {
316   stem_ = 0;
317 }
318
319 ADD_TRANSLATOR (New_fingering_engraver,
320                 /* descr */ "Create fingering-scripts for notes in a new chord.  "
321                 "This engraver is ill-named, since it "
322                 "also takes care of articulations and harmonic note heads",
323                 /* creats*/ "Fingering",
324                 /* accepts */ "",
325                 /* acks  */ "rhythmic-head-interface stem-interface",
326                 /* reads */ "fingeringOrientations",
327                 /* write */ "");