]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-fingering-engraver.cc
* input/regression/ambitus.ly: move file.
[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 ("script-event"))
83             {
84               add_script (inf.grob_, m, note_ev);
85             }
86         }
87
88       heads_.push (inf.grob_);
89     }
90   else if (Stem::has_interface (inf.grob_))
91     {
92       stem_ = inf.grob_;
93     }
94 }
95
96 extern Grob *make_script_from_event (SCM * descr, Translator_group*tg, Music * event,
97                               int index);
98 void
99 New_fingering_engraver::add_script (Grob * head,
100                                     Music * event,
101                                     Music * head_event)
102 {
103   Finger_tuple ft ;
104
105   ft.script_ =make_script_from_event (&ft.description_, daddy_trans_, event, 0);
106
107   articulations_.push (ft);
108   announce_grob (ft.script_, event->self_scm ());
109   
110  
111   ft.script_->set_parent (head, X_AXIS);
112 }
113                                     
114                                     
115
116 void
117 New_fingering_engraver::add_fingering (Grob * head,
118                                        Music * event,
119                                        Music *hevent)
120 {
121   Finger_tuple ft;
122
123   ft.script_ = new Item (get_property ("Fingering"));
124   announce_grob (ft.script_, event->self_scm());
125   
126   Side_position_interface::add_support (ft.script_, head);
127
128   int d = gh_scm2int ( event->get_mus_property ("digit"));
129   
130   /*
131     TODO:
132     
133     Should add support for thumb.  It's a little involved, since
134     the thumb lives in a different font. Maybe it should be moved?
135     
136    */
137
138   if (d > 5)
139     {
140       /*
141         music for the softenon children? 
142        */
143       event->origin()->warning (_("music for the martians."));
144     }
145   SCM sstr = scm_number_to_string (gh_int2scm (d), gh_int2scm (10)) ;
146   ft.script_->set_grob_property ("text", sstr);
147        
148   ft.finger_event_ = event;
149   ft.note_event_ = hevent;
150   ft.head_ = head;
151
152   fingerings_.push (ft);
153 }
154
155 void
156 New_fingering_engraver::position_scripts ()
157 {
158
159   /*
160     This is not extremely elegant, but we have to do a little
161     formatting here, because the parent/child relations should be
162     known before we move on to the next time step.
163
164     A more sophisticated approach would be to set both X and Y parents
165     to the note head, and write a more flexible function for
166     positioning the fingerings, setting both X and Y coordinates.
167   */
168   for (int i = 0; i < fingerings_.size(); i++)
169     {      
170       fingerings_[i].position_ = gh_scm2int (fingerings_[i].head_ -> get_grob_property( "staff-position"));
171     }
172
173   SCM fhd = get_property ("fingerHorizontalDirection");
174   
175   Array<Finger_tuple> up, down, horiz;
176   for (int i = fingerings_.size(); i--;)
177     {
178       SCM d = fingerings_[i].finger_event_->get_mus_property ("direction");
179       if (to_dir (d))
180         {
181           if (to_dir (d) == UP)
182             {
183               up.push (fingerings_[i]);
184             }
185           else
186             down.push (fingerings_[i]);
187           fingerings_.del (i);
188         }
189     }
190   
191   fingerings_.sort (&Finger_tuple::compare);
192   
193   if (ly_dir_p (fhd))
194     {
195       if (!up.size())
196         up.push (fingerings_.pop());
197       if (!down.size())
198         {
199           down.push (fingerings_[0]);
200           fingerings_.del(0);
201         }
202
203       horiz.concat (fingerings_);
204     }
205   else
206     {
207       int center = fingerings_.size() / 2;
208       down.concat (fingerings_.slice (0,center));
209       up.concat (fingerings_.slice (center, fingerings_.size()));
210     }
211
212   for (int i = 0; i < horiz.size(); i++)
213     {
214       Finger_tuple ft = horiz[i];
215       Grob* f = ft.script_;
216       f->set_parent (ft.head_, X_AXIS);
217       f->set_parent (ft.head_, Y_AXIS);
218       f->add_offset_callback (Self_alignment_interface::centered_on_parent_proc, Y_AXIS);
219       f->add_offset_callback (Self_alignment_interface::aligned_on_self_proc, Y_AXIS);
220       f->add_offset_callback (Side_position_interface::aligned_side_proc, X_AXIS);
221
222       f->set_grob_property( "direction", fhd);
223       typeset_grob (f);
224     }
225
226   int finger_prio = 200;
227   for (int i = 0; i < up.size(); i++)
228     {
229       Finger_tuple ft = up[i];
230       Grob* f = ft.script_;
231       f->set_parent (ft.head_, X_AXIS);
232       f->set_grob_property ("script-priority",
233                             gh_int2scm (finger_prio + i));
234       f->add_offset_callback (Side_position_interface::aligned_side_proc, Y_AXIS);
235       f->add_offset_callback (Self_alignment_interface::centered_on_parent_proc, X_AXIS);
236       f->add_offset_callback (Self_alignment_interface::aligned_on_self_proc, X_AXIS);
237       
238       f->set_grob_property ("direction", gh_int2scm (UP));
239
240       Side_position_interface::add_staff_support (f);
241       typeset_grob (f);
242     }
243   
244   for (int i = 0; i < down.size(); i++)
245     {
246       Finger_tuple ft = down[i];
247       Grob* f = ft.script_;
248       f->set_parent (ft.head_, X_AXIS);
249       f->set_grob_property ("script-priority",
250                             gh_int2scm (finger_prio + down.size() - i));
251
252       f->add_offset_callback (Self_alignment_interface::centered_on_parent_proc, X_AXIS);
253       f->add_offset_callback (Self_alignment_interface::aligned_on_self_proc, X_AXIS);
254       f->add_offset_callback (Side_position_interface::aligned_side_proc, Y_AXIS);
255       f->set_grob_property ("direction", gh_int2scm (DOWN));
256       Side_position_interface::add_staff_support (f);
257       typeset_grob (f);
258     }
259 }
260
261 void
262 New_fingering_engraver::stop_translation_timestep ()
263 {
264   if (fingerings_.size ())
265     {
266       position_scripts();
267       fingerings_.clear ();
268     }
269   
270   for (int i =  articulations_.size(); i--;)
271     {
272       Grob *sc = articulations_[i].script_;
273    
274       for (int j = heads_.size() ; j--;)
275         Side_position_interface::add_support (sc, heads_[j]);
276
277       if (stem_ && to_dir (sc->get_grob_property ("side-relative-direction")))
278         sc->set_grob_property ("direction-source", stem_->self_scm ());
279       
280       SCM follow = scm_assoc (ly_symbol2scm ("follow-into-staff"), articulations_[i].description_);
281       if (gh_pair_p (follow) && to_boolean (gh_cdr (follow)))
282         sc->add_offset_callback (Side_position_interface::quantised_position_proc, Y_AXIS);
283       else
284         Side_position_interface::add_staff_support (sc);
285       typeset_grob (sc);
286     }
287
288   stem_ = 0;
289   heads_.clear ();
290   articulations_.clear();
291 }
292
293
294 New_fingering_engraver::New_fingering_engraver()
295 {
296   stem_ = 0;  
297 }
298
299 ENTER_DESCRIPTION(New_fingering_engraver,
300 /* descr */       "Create fingering-scripts for notes in a New Chord.",
301 /* creats*/       "Fingering",
302 /* accepts */     "text-script-event",
303 /* acks  */       "rhythmic-head-interface stem-interface",
304 /* reads */       "fingerHorizontalDirection",
305 /* write */       "");