]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-fingering-engraver.cc
* lily/side-position-interface.cc (out_of_staff): move contents of
[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--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9
10 #include "warn.hh"
11 #include "engraver.hh"
12 #include "side-position-interface.hh"
13 #include "item.hh"
14 #include "event.hh"
15 #include "stem.hh"
16 #include "rhythmic-head.hh"
17 #include "self-alignment-interface.hh"
18 #include "script.hh"
19 #include "stem.hh"
20
21 struct Finger_tuple
22 {
23   Grob *head_;
24   Grob *script_;
25   Music *note_event_;
26   Music *finger_event_;
27   SCM description_;
28   int position_;
29
30   Finger_tuple ()
31   {
32     position_ = 0;
33     head_ = script_ = 0;
34     note_event_ = finger_event_ = 0;
35     description_ = SCM_EOL;
36   }
37   static int compare (Finger_tuple const & c1, Finger_tuple const & c2)
38   {
39     return c1.position_-  c2.position_;
40   }
41                
42 };
43
44 class New_fingering_engraver : public Engraver
45 {
46   Array<Finger_tuple> fingerings_;
47   Array<Finger_tuple> articulations_;
48   Link_array<Grob> heads_;
49   Grob *stem_;
50   
51 public:
52   TRANSLATOR_DECLARATIONS(New_fingering_engraver);
53 protected:
54   virtual void stop_translation_timestep ();
55   virtual void acknowledge_grob (Grob_info);
56   void add_fingering (Grob*, Music*,Music*);
57   void add_script  (Grob*, Music*,Music*);
58   void position_scripts();
59 };
60
61 void
62 New_fingering_engraver::acknowledge_grob (Grob_info inf)
63 {
64   if (Rhythmic_head::has_interface (inf.grob_))
65     {
66       Music * note_ev =inf.music_cause ();
67
68       SCM arts = note_ev->get_mus_property ("articulations");
69
70       for (SCM s = arts; gh_pair_p (s); s = gh_cdr  (s))
71         {
72           Music * m = unsmob_music (gh_car (s));
73
74           if (!m)
75             continue;
76           
77
78           if (m->is_mus_type ("fingering-event"))
79             {
80               add_fingering (inf.grob_ , m, note_ev);
81             }
82           else if (m->is_mus_type ("text-script-event"))
83             {
84               m->origin ()->warning ("Can not add text scripts to individual note heads");
85             }
86           else if (m->is_mus_type ("script-event"))
87             {
88               add_script (inf.grob_, m, note_ev);
89             }
90         }
91
92       heads_.push (inf.grob_);
93     }
94   else if (Stem::has_interface (inf.grob_))
95     {
96       stem_ = inf.grob_;
97     }
98 }
99
100 extern void make_script_from_event (Grob *, SCM * descr, Translator_group*tg, Music * event,
101                                      int index);
102 void
103 New_fingering_engraver::add_script (Grob * head,
104                                     Music * event,
105                                     Music * )
106 {
107   Finger_tuple ft ;
108
109   Grob * g=  make_item ("Script");
110   make_script_from_event (g, &ft.description_, daddy_trans_, event, 0);
111   if (g)
112     {
113       ft.script_ =g ;
114       
115       articulations_.push (ft);
116       announce_grob (g, event->self_scm ());
117  
118       ft.script_->set_parent (head, X_AXIS);
119     }
120 }                                   
121                                     
122
123 void
124 New_fingering_engraver::add_fingering (Grob * head,
125                                        Music * event,
126                                        Music *hevent)
127 {
128   Finger_tuple ft;
129
130   ft.script_ = make_item ("Fingering");
131   announce_grob (ft.script_, event->self_scm());
132   
133   Side_position_interface::add_support (ft.script_, head);
134
135   int d = gh_scm2int ( event->get_mus_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 (gh_int2scm (d), gh_int2scm (10)) ;
152   ft.script_->set_grob_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_ = gh_scm2int (fingerings_[i].head_ -> get_grob_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_mus_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_memq (ly_symbol2scm ("up"), orientations) != SCM_BOOL_F;
198   bool down_p = scm_memq (ly_symbol2scm ("down"), orientations) != SCM_BOOL_F;
199   bool left_p = scm_memq (ly_symbol2scm ("left"), orientations) != SCM_BOOL_F;
200   bool right_p = scm_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_grob_property ("direction", gh_int2scm (hordir));
245       typeset_grob (f);
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_grob_property ("script-priority",
255                             gh_int2scm (finger_prio + i));
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_grob_property ("direction", gh_int2scm (UP));
261
262       typeset_grob (f);
263     }
264   
265   for (int i = 0; i < down.size(); i++)
266     {
267       Finger_tuple ft = down[i];
268       Grob* f = ft.script_;
269       f->set_parent (ft.head_, X_AXIS);
270       f->set_grob_property ("script-priority",
271                             gh_int2scm (finger_prio + down.size() - i));
272
273       f->add_offset_callback (Self_alignment_interface::centered_on_parent_proc, X_AXIS);
274       f->add_offset_callback (Self_alignment_interface::aligned_on_self_proc, X_AXIS);
275       f->add_offset_callback (Side_position_interface::aligned_side_proc, Y_AXIS);
276       f->set_grob_property ("direction", gh_int2scm (DOWN));
277       typeset_grob (f);
278     }
279 }
280
281 void
282 New_fingering_engraver::stop_translation_timestep ()
283 {
284   if (fingerings_.size ())
285     {
286       position_scripts();
287       fingerings_.clear ();
288     }
289   
290   for (int i =  articulations_.size(); i--;)
291     {
292       Grob *sc = articulations_[i].script_;
293    
294       for (int j = heads_.size() ; j--;)
295         Side_position_interface::add_support (sc, heads_[j]);
296
297       if (stem_ && to_dir (sc->get_grob_property ("side-relative-direction")))
298         sc->set_grob_property ("direction-source", stem_->self_scm ());
299       
300       SCM follow = scm_assoc (ly_symbol2scm ("follow-into-staff"), articulations_[i].description_);
301       if (gh_pair_p (follow) && to_boolean (gh_cdr (follow)))
302         sc->add_offset_callback (Side_position_interface::quantised_position_proc, Y_AXIS);
303       else
304         Side_position_interface::add_staff_support (sc);
305       typeset_grob (sc);
306     }
307
308   stem_ = 0;
309   heads_.clear ();
310   articulations_.clear();
311 }
312
313
314 New_fingering_engraver::New_fingering_engraver()
315 {
316   stem_ = 0;  
317 }
318
319 ENTER_DESCRIPTION(New_fingering_engraver,
320 /* descr */       "Create fingering-scripts for notes in a new chord.",
321 /* creats*/       "Fingering",
322 /* accepts */     "",
323 /* acks  */       "rhythmic-head-interface stem-interface",
324 /* reads */       "fingeringOrientations",
325 /* write */       "");