2 fingering-engraver.cc -- implement New_fingering_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
10 #include "engraver.hh"
11 #include "side-position-interface.hh"
13 #include "rhythmic-head.hh"
14 #include "self-alignment-interface.hh"
15 #include "script-interface.hh"
18 #include "translator.icc"
26 bool follow_into_staff_
;
33 note_event_
= finger_event_
= 0;
34 follow_into_staff_
= false;
36 static int compare (Finger_tuple
const &c1
, Finger_tuple
const &c2
)
38 return c1
.position_
- c2
.position_
;
42 class New_fingering_engraver
: public Engraver
44 Array
<Finger_tuple
> fingerings_
;
45 Array
<Finger_tuple
> articulations_
;
46 Array
<Finger_tuple
> string_numbers_
;
48 Link_array
<Grob
> heads_
;
52 TRANSLATOR_DECLARATIONS (New_fingering_engraver
);
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
> *);
64 New_fingering_engraver::acknowledge_rhythmic_head (Grob_info inf
)
66 Music
*note_ev
= inf
.music_cause ();
70 SCM arts
= note_ev
->get_property ("articulations");
72 for (SCM s
= arts
; scm_is_pair (s
); s
= scm_cdr (s
))
74 Music
*m
= unsmob_music (scm_car (s
));
79 if (m
->is_mus_type ("fingering-event"))
80 add_fingering (inf
.grob (), m
, note_ev
);
81 else if (m
->is_mus_type ("text-script-event"))
82 m
->origin ()->warning (_ ("can't add text scripts to individual note heads"));
83 else if (m
->is_mus_type ("script-event"))
84 add_script (inf
.grob (), m
, note_ev
);
85 else if (m
->is_mus_type ("string-number-event"))
86 add_string (inf
.grob (), m
, note_ev
);
87 else if (m
->is_mus_type ("harmonic-event"))
89 inf
.grob ()->set_property ("style", ly_symbol2scm ("harmonic"));
90 Grob
*d
= unsmob_grob (inf
.grob ()->get_object ("dot"));
96 heads_
.push (inf
.grob ());
100 New_fingering_engraver::acknowledge_stem (Grob_info inf
)
106 New_fingering_engraver::add_script (Grob
*head
,
114 Grob
*g
= make_item ("Script", event
->self_scm ());
115 make_script_from_event (g
, &ft
.follow_into_staff_
, context (),
116 event
->get_property ("articulation-type"), 0);
121 articulations_
.push (ft
);
123 ft
.script_
->set_parent (head
, X_AXIS
);
128 New_fingering_engraver::add_fingering (Grob
*head
,
134 ft
.script_
= make_item ("Fingering", event
->self_scm ());
136 Side_position_interface::add_support (ft
.script_
, head
);
138 int d
= scm_to_int (event
->get_property ("digit"));
143 Should add support for thumb. It's a little involved, since
144 the thumb lives in a different font. Maybe it should be moved?
150 music for the softenon children?
152 event
->origin ()->warning (_ ("music for the martians."));
154 SCM sstr
= scm_number_to_string (scm_from_int (d
), scm_from_int (10));
155 ft
.script_
->set_property ("text", sstr
);
157 ft
.finger_event_
= event
;
158 ft
.note_event_
= hevent
;
161 fingerings_
.push (ft
);
165 New_fingering_engraver::add_string (Grob
*head
,
171 ft
.script_
= make_item ("StringNumber", event
->self_scm ());
173 Side_position_interface::add_support (ft
.script_
, head
);
175 int d
= scm_to_int (event
->get_property ("string-number"));
177 SCM sstr
= scm_number_to_string (scm_from_int (d
), scm_from_int (10));
178 ft
.script_
->set_property ("text", sstr
);
180 ft
.finger_event_
= event
;
181 ft
.note_event_
= hevent
;
184 string_numbers_
.push (ft
);
188 New_fingering_engraver::position_scripts (SCM orientations
,
189 Array
<Finger_tuple
> *scripts
)
191 for (int i
= 0; i
< scripts
->size (); i
++)
192 if (stem_
&& to_boolean (scripts
->elem (i
).script_
->get_property ("add-stem-support")))
193 Side_position_interface::add_support (scripts
->elem (i
).script_
, stem_
);
196 This is not extremely elegant, but we have to do a little
197 formatting here, because the parent/child relations should be
198 known before we move on to the next time step.
200 A more sophisticated approach would be to set both X and Y parents
201 to the note head, and write a more flexible function for
202 positioning the fingerings, setting both X and Y coordinates.
204 for (int i
= 0; i
< scripts
->size (); i
++)
205 (*scripts
)[i
].position_
= scm_to_int ((*scripts
)[i
].head_
->get_property ("staff-position"));
207 for (int i
= scripts
->size (); i
--;)
208 for (int j
= heads_
.size (); j
--;)
209 Side_position_interface::add_support ((*scripts
)[i
].script_
, heads_
[j
]);
211 Array
<Finger_tuple
> up
, down
, horiz
;
212 for (int i
= scripts
->size (); i
--;)
214 SCM d
= (*scripts
)[i
].finger_event_
->get_property ("direction");
217 ((to_dir (d
) == UP
) ? up
: down
).push ((*scripts
)[i
]);
222 scripts
->sort (&Finger_tuple::compare
);
224 bool up_p
= scm_c_memq (ly_symbol2scm ("up"), orientations
) != SCM_BOOL_F
;
225 bool down_p
= scm_c_memq (ly_symbol2scm ("down"), orientations
) != SCM_BOOL_F
;
226 bool left_p
= scm_c_memq (ly_symbol2scm ("left"), orientations
) != SCM_BOOL_F
;
227 bool right_p
= scm_c_memq (ly_symbol2scm ("right"), orientations
) != SCM_BOOL_F
;
228 Direction hordir
= (right_p
) ? RIGHT
: LEFT
;
229 if (left_p
|| right_p
)
231 if (up_p
&& !up
.size () && scripts
->size ())
232 up
.push (scripts
->pop ());
234 if (down_p
&& !down
.size () && scripts
->size ())
236 down
.push ((*scripts
)[0]);
240 horiz
.concat (*scripts
);
242 else if (up_p
&& down_p
)
244 int center
= scripts
->size () / 2;
245 down
.concat (scripts
->slice (0, center
));
246 up
.concat (scripts
->slice (center
, scripts
->size ()));
250 up
.concat (*scripts
);
257 warning (_ ("no placement found for fingerings"));
258 warning (_ ("placing below"));
260 down
.concat (*scripts
);
264 for (int i
= 0; i
< horiz
.size (); i
++)
266 Finger_tuple ft
= horiz
[i
];
267 Grob
*f
= ft
.script_
;
268 f
->set_parent (ft
.head_
, X_AXIS
);
269 f
->set_parent (ft
.head_
, Y_AXIS
);
270 f
->add_offset_callback (Self_alignment_interface::centered_on_parent_proc
, Y_AXIS
);
271 f
->add_offset_callback (Self_alignment_interface::aligned_on_self_proc
, Y_AXIS
);
272 f
->add_offset_callback (Side_position_interface::aligned_side_proc
, X_AXIS
);
274 f
->set_property ("direction", scm_from_int (hordir
));
277 int finger_prio
= 200;
278 for (int i
= 0; i
< up
.size (); i
++)
280 Finger_tuple ft
= up
[i
];
281 Grob
*f
= ft
.script_
;
282 f
->set_parent (ft
.head_
, X_AXIS
);
283 f
->set_property ("script-priority",
284 scm_from_int (finger_prio
+ ft
.position_
));
285 f
->add_offset_callback (Side_position_interface::aligned_side_proc
, Y_AXIS
);
286 f
->add_offset_callback (Self_alignment_interface::centered_on_parent_proc
, X_AXIS
);
287 f
->add_offset_callback (Self_alignment_interface::aligned_on_self_proc
, X_AXIS
);
289 f
->set_property ("direction", scm_from_int (UP
));
292 for (int i
= 0; i
< down
.size (); i
++)
294 Finger_tuple ft
= down
[i
];
295 Grob
*f
= ft
.script_
;
296 f
->set_parent (ft
.head_
, X_AXIS
);
297 f
->set_property ("script-priority",
298 scm_from_int (finger_prio
+ down
.size () - ft
.position_
));
300 f
->add_offset_callback (Self_alignment_interface::centered_on_parent_proc
, X_AXIS
);
301 f
->add_offset_callback (Self_alignment_interface::aligned_on_self_proc
, X_AXIS
);
302 f
->add_offset_callback (Side_position_interface::aligned_side_proc
, Y_AXIS
);
303 f
->set_property ("direction", scm_from_int (DOWN
));
308 New_fingering_engraver::stop_translation_timestep ()
310 if (fingerings_
.size ())
312 position_scripts (get_property ("fingeringOrientations"),
314 fingerings_
.clear ();
317 if (string_numbers_
.size ())
319 position_scripts (get_property ("stringNumberOrientations"),
321 string_numbers_
.clear ();
324 for (int i
= articulations_
.size (); i
--;)
326 Grob
*script
= articulations_
[i
].script_
;
328 for (int j
= heads_
.size (); j
--;)
329 Side_position_interface::add_support (script
, heads_
[j
]);
331 if (stem_
&& to_dir (script
->get_property ("side-relative-direction")))
332 script
->set_object ("direction-source", stem_
->self_scm ());
334 if (stem_
&& to_boolean (script
->get_property ("add-stem-support")))
335 Side_position_interface::add_support (script
, stem_
);
337 if (articulations_
[i
].follow_into_staff_
)
339 script
->add_offset_callback (Side_position_interface::quantised_position_proc
, Y_AXIS
);
340 script
->set_property ("staff-padding", SCM_EOL
);
346 articulations_
.clear ();
349 New_fingering_engraver::New_fingering_engraver ()
353 ADD_ACKNOWLEDGER (New_fingering_engraver
, rhythmic_head
);
354 ADD_ACKNOWLEDGER (New_fingering_engraver
, stem
);
356 ADD_TRANSLATOR (New_fingering_engraver
,
357 /* doc */ "Create fingering-scripts for notes in a new chord. "
358 "This engraver is ill-named, since it "
359 "also takes care of articulations and harmonic note heads",
360 /* create */ "Fingering",
362 /* read */ "fingeringOrientations",