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