2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1998--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "engraver.hh"
22 #include "international.hh"
24 #include "rhythmic-head.hh"
25 #include "script-interface.hh"
26 #include "self-alignment-interface.hh"
27 #include "side-position-interface.hh"
29 #include "stream-event.hh"
32 #include "translator.icc"
39 Stream_event
*note_event_
;
40 Stream_event
*finger_event_
;
41 bool follow_into_staff_
;
48 note_event_
= finger_event_
= 0;
49 follow_into_staff_
= false;
54 operator< (Finger_tuple
const &a
, Finger_tuple
const &b
)
56 return a
.position_
< b
.position_
;
59 class New_fingering_engraver
: public Engraver
61 vector
<Finger_tuple
> fingerings_
;
62 vector
<Finger_tuple
> stroke_fingerings_
;
63 vector
<Finger_tuple
> articulations_
;
64 vector
<Finger_tuple
> string_numbers_
;
66 vector
<Grob
*> heads_
;
71 TRANSLATOR_DECLARATIONS (New_fingering_engraver
);
73 void stop_translation_timestep ();
74 DECLARE_ACKNOWLEDGER (rhythmic_head
);
75 DECLARE_ACKNOWLEDGER (stem
);
76 void add_fingering (Grob
*, SCM
,
77 vector
<Finger_tuple
> *,
78 Stream_event
*, Stream_event
*);
79 void add_script (Grob
*, Stream_event
*, Stream_event
*);
80 void add_string (Grob
*, Stream_event
*, Stream_event
*);
81 void position_scripts (SCM orientations
, vector
<Finger_tuple
> *);
85 New_fingering_engraver::acknowledge_rhythmic_head (Grob_info inf
)
87 Stream_event
*note_ev
= inf
.event_cause ();
91 SCM arts
= note_ev
->get_property ("articulations");
93 for (SCM s
= arts
; scm_is_pair (s
); s
= scm_cdr (s
))
95 Stream_event
*ev
= unsmob_stream_event (scm_car (s
));
100 if (ev
->in_event_class ("fingering-event"))
101 add_fingering (inf
.grob (),
102 ly_symbol2scm ("Fingering"),
105 else if (ev
->in_event_class ("text-script-event"))
106 ev
->origin ()->warning (_ ("cannot add text scripts to individual note heads"));
107 else if (ev
->in_event_class ("script-event"))
108 add_script (inf
.grob (), ev
, note_ev
);
109 else if (ev
->in_event_class ("string-number-event"))
110 add_fingering (inf
.grob (),
111 ly_symbol2scm ("StringNumber"), &string_numbers_
,
113 else if (ev
->in_event_class ("stroke-finger-event"))
114 add_fingering (inf
.grob (),
115 ly_symbol2scm ("StrokeFinger"), &stroke_fingerings_
,
117 else if (ev
->in_event_class ("harmonic-event"))
119 inf
.grob ()->set_property ("style", ly_symbol2scm ("harmonic"));
120 Grob
*d
= unsmob_grob (inf
.grob ()->get_object ("dot"));
121 if (d
&& !to_boolean (get_property ("harmonicDots")))
126 heads_
.push_back (inf
.grob ());
130 New_fingering_engraver::acknowledge_stem (Grob_info inf
)
136 New_fingering_engraver::add_script (Grob
*head
,
138 Stream_event
* /* note */)
142 Grob
*g
= make_item ("Script", event
->self_scm ());
143 make_script_from_event (g
, context (),
144 event
->get_property ("articulation-type"), 0);
146 ft
.script_
->set_parent (head
, X_AXIS
);
148 SCM forced_dir
= event
->get_property ("direction");
149 if (to_dir (forced_dir
))
150 ft
.script_
->set_property ("direction", forced_dir
);
152 articulations_
.push_back (ft
);
156 New_fingering_engraver::add_fingering (Grob
*head
,
158 vector
<Finger_tuple
> *tuple_vector
,
160 Stream_event
*hevent
)
164 ft
.script_
= internal_make_item (grob_sym
, event
->self_scm (),
165 ly_symbol2string (grob_sym
).c_str (),
166 __FILE__
, __LINE__
, __FUNCTION__
);
168 Side_position_interface::add_support (ft
.script_
, head
);
170 ft
.finger_event_
= event
;
171 ft
.note_event_
= hevent
;
174 tuple_vector
->push_back (ft
);
178 New_fingering_engraver::position_scripts (SCM orientations
,
179 vector
<Finger_tuple
> *scripts
)
181 for (vsize i
= 0; i
< scripts
->size (); i
++)
182 if (stem_
&& to_boolean (scripts
->at (i
).script_
->get_property ("add-stem-support")))
183 Side_position_interface::add_support (scripts
->at (i
).script_
, stem_
);
186 This is not extremely elegant, but we have to do a little
187 formatting here, because the parent/child relations should be
188 known before we move on to the next time step.
190 A more sophisticated approach would be to set both X and Y parents
191 to the note head, and write a more flexible function for
192 positioning the fingerings, setting both X and Y coordinates.
194 for (vsize i
= 0; i
< scripts
->size (); i
++)
195 (*scripts
)[i
].position_
= scm_to_int ((*scripts
)[i
].head_
->get_property ("staff-position"));
197 for (vsize i
= scripts
->size (); i
--;)
198 for (vsize j
= heads_
.size (); j
--;)
199 Side_position_interface::add_support ((*scripts
)[i
].script_
, heads_
[j
]);
201 vector
<Finger_tuple
> up
, down
, horiz
;
202 for (vsize i
= scripts
->size (); i
--;)
204 SCM d
= (*scripts
)[i
].finger_event_
->get_property ("direction");
207 ((to_dir (d
) == UP
) ? up
: down
).push_back ((*scripts
)[i
]);
208 scripts
->erase (scripts
->begin () + i
);
212 vector_sort (*scripts
, less
<Finger_tuple
> ());
214 bool up_p
= scm_c_memq (ly_symbol2scm ("up"), orientations
) != SCM_BOOL_F
;
215 bool down_p
= scm_c_memq (ly_symbol2scm ("down"), orientations
) != SCM_BOOL_F
;
216 bool left_p
= scm_c_memq (ly_symbol2scm ("left"), orientations
) != SCM_BOOL_F
;
217 bool right_p
= scm_c_memq (ly_symbol2scm ("right"), orientations
) != SCM_BOOL_F
;
218 Direction hordir
= (right_p
) ? RIGHT
: LEFT
;
219 if (left_p
|| right_p
)
221 if (up_p
&& !up
.size () && scripts
->size ())
223 up
.push_back (scripts
->back ());
224 scripts
->pop_back ();
227 if (down_p
&& !down
.size () && scripts
->size ())
229 down
.push_back ((*scripts
)[0]);
230 scripts
->erase (scripts
->begin ());
233 horiz
.insert (horiz
.end (), scripts
->begin (), scripts
->end ());
235 else if (up_p
&& down_p
)
237 int center
= scripts
->size () / 2;
238 down
.insert (down
.end (), scripts
->begin (), scripts
->begin () + center
);
239 up
.insert (up
.end (), scripts
->begin () + center
, scripts
->end ());
243 up
.insert (up
.end (), scripts
->begin (), scripts
->end ());
250 warning (_ ("no placement found for fingerings"));
251 warning (_ ("placing below"));
253 down
.insert (down
.end (), scripts
->begin (), scripts
->end ());
257 for (vsize i
= 0; i
< horiz
.size (); i
++)
259 Finger_tuple ft
= horiz
[i
];
260 Grob
*f
= ft
.script_
;
261 f
->set_parent (ft
.head_
, X_AXIS
);
262 f
->set_parent (ft
.head_
, Y_AXIS
);
263 f
->set_property ("avoid-slur", SCM_BOOL_F
);
265 && unsmob_grob (ft
.head_
->get_object ("accidental-grob")))
266 Side_position_interface::add_support (f
,
267 unsmob_grob (ft
.head_
->get_object ("accidental-grob")));
268 else if (unsmob_grob (ft
.head_
->get_object ("dot")))
269 Side_position_interface::add_support (f
,
270 unsmob_grob (ft
.head_
->get_object ("dot")));
272 Self_alignment_interface::set_align_self (f
, Y_AXIS
);
273 Self_alignment_interface::set_center_parent (f
, Y_AXIS
);
274 Side_position_interface::set_axis (f
, X_AXIS
);
276 f
->set_property ("direction", scm_from_int (hordir
));
280 Drul_array
< vector
<Finger_tuple
> > vertical (down
, up
);
283 for (vsize i
= 0; i
< vertical
[d
].size (); i
++)
285 Finger_tuple ft
= vertical
[d
][i
];
286 Grob
*f
= ft
.script_
;
287 int finger_prio
= robust_scm2int (f
->get_property ("script-priority"), 200);
288 f
->set_parent (ft
.head_
, X_AXIS
);
289 f
->set_property ("script-priority",
290 scm_from_int (finger_prio
+ d
* ft
.position_
));
292 Self_alignment_interface::set_align_self (f
, X_AXIS
);
293 Self_alignment_interface::set_center_parent (f
, X_AXIS
);
294 Side_position_interface::set_axis (f
, Y_AXIS
);
296 f
->set_property ("direction", scm_from_int (d
));
299 while (flip (&d
) != DOWN
);
303 New_fingering_engraver::stop_translation_timestep ()
311 New_fingering_engraver::position_all ()
313 if (fingerings_
.size ())
315 position_scripts (get_property ("fingeringOrientations"),
317 fingerings_
.clear ();
320 if (string_numbers_
.size ())
322 position_scripts (get_property ("stringNumberOrientations"),
324 string_numbers_
.clear ();
327 if (stroke_fingerings_
.size ())
329 position_scripts (get_property ("strokeFingerOrientations"),
330 &stroke_fingerings_
);
331 stroke_fingerings_
.clear ();
334 for (vsize i
= articulations_
.size (); i
--;)
336 Grob
*script
= articulations_
[i
].script_
;
338 for (vsize j
= heads_
.size (); j
--;)
339 Side_position_interface::add_support (script
, heads_
[j
]);
341 if (stem_
&& to_dir (script
->get_property ("side-relative-direction")))
342 script
->set_object ("direction-source", stem_
->self_scm ());
344 if (stem_
&& to_boolean (script
->get_property ("add-stem-support")))
345 Side_position_interface::add_support (script
, stem_
);
347 articulations_
.clear ();
350 New_fingering_engraver::New_fingering_engraver ()
355 ADD_ACKNOWLEDGER (New_fingering_engraver
, rhythmic_head
);
356 ADD_ACKNOWLEDGER (New_fingering_engraver
, stem
);
358 ADD_TRANSLATOR (New_fingering_engraver
,
360 "Create fingering scripts for notes in a new chord. This"
361 " engraver is ill-named, since it also takes care of"
362 " articulations and harmonic note heads.",
371 "fingeringOrientations "
373 "strokeFingerOrientations "
374 "stringNumberOrientations ",