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