lilypond-1.3.145
[lilypond.git] / lily / local-key-engraver.cc
blob94e7980d04701022a93763183c084a986a7acb40
1 /*
2 local-key-engraver.cc -- implement Local_key_engraver
4 (c) 1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 */
7 #include "musical-request.hh"
8 #include "command-request.hh"
9 #include "local-key-item.hh"
10 #include "item.hh"
11 #include "tie.hh"
12 #include "rhythmic-head.hh"
13 #include "timing-translator.hh"
14 #include "engraver-group-engraver.hh"
15 #include "grace-align-item.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "side-position-interface.hh"
18 #include "engraver.hh"
19 #include "arpeggio.hh"
21 /**
24 FIXME: should not compute vertical positioning of accidentals, but
25 get them from the noteheads
27 The algorithm for accidentals should be documented, and made
28 tweakable.
33 struct Local_key_engraver : Engraver {
34 Item *key_item_p_;
35 protected:
36 VIRTUAL_COPY_CONS (Translator);
37 virtual void process_music ();
38 virtual void acknowledge_grob (Grob_info);
39 virtual void stop_translation_timestep ();
40 virtual void initialize ();
41 virtual void create_grobs ();
42 virtual void finalize ();
43 public:
45 // todo -> property
46 SCM last_keysig_;
49 Urgh. Since the accidentals depend on lots of variables, we have to
50 store all information before we can really create the accidentals.
52 Link_array<Grob> arpeggios_;
54 Link_array<Note_req> mel_l_arr_;
55 Link_array<Grob> support_l_arr_;
56 Link_array<Item> forced_l_arr_;
57 Link_array<Grob> tie_l_arr_;
58 Local_key_engraver ();
60 Item * grace_align_l_;
63 Local_key_engraver::Local_key_engraver ()
65 key_item_p_ =0;
66 grace_align_l_ =0;
67 last_keysig_ = SCM_EOL;
70 void
71 Local_key_engraver::initialize ()
73 last_keysig_ = get_property ("keySignature");
74 daddy_trans_l_->set_property ("localKeySignature", last_keysig_);
77 void
78 Local_key_engraver::create_grobs ()
80 if (!key_item_p_ && mel_l_arr_.size ())
82 SCM localsig = get_property ("localKeySignature");
84 for (int i=0; i < mel_l_arr_.size (); i++)
86 Grob * support_l = support_l_arr_[i];
87 Note_req * note_l = mel_l_arr_[i];
89 int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
90 int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
91 int a = unsmob_pitch (note_l->get_mus_property ("pitch"))->alteration_i_;
93 /* see if there's a tie that "changes" the accidental */
94 /* works because if there's a tie, the note to the left
95 is of the same pitch as the actual note */
97 SCM prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), localsig);
98 if (prev == SCM_BOOL_F)
99 prev = scm_assoc (gh_int2scm (n), localsig);
100 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : gh_cdr (prev);
101 bool different = !gh_equal_p (prev_acc , gh_int2scm (a));
102 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
104 Grob *tie_break_reminder = 0;
105 bool tie_changes = false;
106 for (int i=0; i < tie_l_arr_.size (); i++)
107 if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
109 tie_changes = different;
110 /* Enable accidentals for broken tie
112 We only want an accidental on a broken tie,
113 if the tie changes the accidental.
115 Maybe check property noTieBreakForceAccidental? */
116 if (different)
117 tie_break_reminder = tie_l_arr_[i];
118 break;
121 /* When do we want accidentals:
123 1. when property force-accidental is set, and not
124 tie_changes
125 2. when different and not tie-changes
126 3. maybe when at end of a tie: we must later see if
127 we're after a line break */
128 if (( (to_boolean (note_l->get_mus_property ("force-accidental"))
129 || different)
130 && !tie_changes)
131 || tie_break_reminder)
133 if (!key_item_p_)
135 key_item_p_ = new Item (get_property ("Accidentals"));
136 Local_key_item::set_interface (key_item_p_);
139 Staff_symbol_referencer::set_interface (key_item_p_);
140 SCM c0 = get_property ("centralCPosition");
141 if (gh_number_p (c0))
142 Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
144 announce_grob (key_item_p_, 0);
148 bool extra_natural =
149 sign (p) * (p - a) == 1
150 && abs (p) == 2;
152 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
153 to_boolean (note_l->get_mus_property ("cautionary")),
154 extra_natural,
155 tie_break_reminder);
156 Side_position_interface::add_support (key_item_p_,support_l);
160 We should not record the accidental if it is the first
161 note and it is tied from the previous measure.
163 Checking whether it is tied also works mostly, but will it
164 always do the correct thing?
167 bool forget = to_boolean (get_property ("forgetAccidentals"));
168 if (tie_changes)
171 Remember an alteration that is different both from
172 that of the tied note and of the key signature.
175 localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
176 gh_int2scm (n)),
177 SCM_BOOL_T);
180 else if (!forget)
183 not really really correct if there are more than one
184 noteheads with the same notename.
186 localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
187 gh_int2scm (n)),
188 gh_int2scm (a));
196 daddy_trans_l_->set_property ("localKeySignature", localsig);
199 if (key_item_p_ && grace_align_l_)
201 Side_position_interface::add_support (grace_align_l_,key_item_p_);
202 grace_align_l_ =0;
205 if (key_item_p_)
208 Hmm. Which one has to be on the left?
210 On which left, code or paper?
212 (Arpeggios are engraved left of accidentals, of course.)
214 for (int i=0; i < arpeggios_.size (); i++)
215 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
217 arpeggios_.clear ();
221 void
222 Local_key_engraver::finalize ()
224 // TODO: if grace ? signal accidentals to Local_key_engraver the
227 void
228 Local_key_engraver::stop_translation_timestep ()
230 if (key_item_p_)
232 for (int i=0; i < support_l_arr_.size (); i++)
233 Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
235 typeset_grob (key_item_p_);
236 key_item_p_ =0;
239 grace_align_l_ = 0;
240 mel_l_arr_.clear ();
241 arpeggios_.clear ();
242 tie_l_arr_.clear ();
243 support_l_arr_.clear ();
244 forced_l_arr_.clear ();
247 void
248 Local_key_engraver::acknowledge_grob (Grob_info info)
250 SCM wg= get_property ("weAreGraceContext");
252 bool selfgr = gh_boolean_p (wg) &&gh_scm2bool (wg);
253 bool he_gr = to_boolean (info.elem_l_->get_grob_property ("grace"));
255 Item * item = dynamic_cast<Item*> (info.elem_l_);
256 if (he_gr && !selfgr && item && Grace_align_item::has_interface (item))
258 grace_align_l_ = item;
260 if (he_gr != selfgr)
261 return;
263 Note_req * note_l = dynamic_cast <Note_req *> (info.req_l_);
265 if (note_l && Rhythmic_head::has_interface (info.elem_l_))
267 mel_l_arr_.push (note_l);
268 support_l_arr_.push (info.elem_l_);
270 else if (Tie::has_interface (info.elem_l_))
272 tie_l_arr_.push (info.elem_l_);
274 else if (Arpeggio::has_interface (info.elem_l_))
276 arpeggios_.push (info.elem_l_);
282 ugh. repeated deep_copy generates lots of garbage.
284 void
285 Local_key_engraver::process_music ()
287 SCM smp = get_property ("measurePosition");
288 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
290 SCM sig = get_property ("keySignature");
293 Detect key sig changes. If we haven't found any, check if at start
294 of measure, and set localKeySignature anyhow. */
295 if (last_keysig_ != sig)
297 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
298 last_keysig_ = sig;
300 else if (!mp)
302 if (!to_boolean (get_property ("noResetKey")))
303 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
309 ADD_THIS_TRANSLATOR (Local_key_engraver);