]> git.donarmstrong.com Git - lilypond.git/blob - lily/local-key-engraver.cc
patch::: 1.3.137.jcn2
[lilypond.git] / lily / local-key-engraver.cc
1 /*
2   local-key-engraver.cc -- implement Local_key_engraver
3
4   (c)  1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 */
6
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"
20
21 /**
22
23
24    FIXME: should not compute vertical positioning of accidentals, but
25    get them from the noteheads
26
27    The algorithm for accidentals should be documented, and made
28    tweakable.
29
30 */
31
32
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:
44
45   // todo -> property
46   SCM last_keysig_;
47
48   /*
49     Urgh. Since the accidentals depend on lots of variables, we have to
50     store all information before we can really create the accidentals.
51    */
52   Link_array<Grob> arpeggios_;
53   
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 ();
59
60   Item * grace_align_l_;
61 };
62
63 Local_key_engraver::Local_key_engraver ()
64 {
65   key_item_p_ =0;
66   grace_align_l_ =0;
67   last_keysig_ = SCM_EOL;
68 }
69
70 void
71 Local_key_engraver::initialize ()
72 {
73   last_keysig_ = get_property ("keySignature");
74   daddy_trans_l_->set_property ("localKeySignature",  last_keysig_);  
75 }
76
77 void
78 Local_key_engraver::create_grobs ()
79 {
80   if (!key_item_p_ && mel_l_arr_.size ()) 
81     {
82       SCM localsig = get_property ("localKeySignature");
83   
84       for (int i=0; i  < mel_l_arr_.size (); i++) 
85         {
86           Grob * support_l = support_l_arr_[i];
87           Note_req * note_l = mel_l_arr_[i];
88
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_;
92           
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 */
96
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;
103
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))
108               {
109                 tie_changes = different;
110                 /* Enable accidentals for broken tie
111
112                    We only want an accidental on a broken tie,
113                    if the tie changes the accidental.
114                    
115                    Maybe check property noTieBreakForceAccidental? */
116                 if (different)
117                   tie_break_reminder = tie_l_arr_[i];
118                 break;
119               }
120
121           /* When do we want accidentals:
122
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)
132             {
133               if (!key_item_p_) 
134                 {
135                   key_item_p_ = new Item (get_property ("Accidentals"));
136                   Local_key_item::set_interface (key_item_p_);
137
138
139                   Staff_symbol_referencer::set_interface (key_item_p_);
140                          
141                   announce_grob (key_item_p_, 0);
142                 }
143
144               
145               bool extra_natural =
146                 sign (p) * (p - a) == 1
147                 && abs (p) == 2;
148
149               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
150                                          to_boolean (note_l->get_mus_property ("cautionary")),
151                                          extra_natural,
152                                          tie_break_reminder);
153               Side_position_interface::add_support (key_item_p_,support_l);
154             }
155           
156           /*
157             We should not record the accidental if it is the first
158             note and it is tied from the previous measure.
159
160             Checking whether it is tied also works mostly, but will it
161             always do the correct thing?
162
163            */
164           bool forget = to_boolean (get_property ("forgetAccidentals"));
165           if (tie_changes)
166             {
167               /*
168                 Remember an alteration that is different both from
169                 that of the tied note and of the key signature.
170
171                */
172               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
173                                                              gh_int2scm (n)),
174                                           SCM_BOOL_T); 
175
176             }
177           else if (!forget)
178             {
179               /*
180                 not really really correct if there are more than one
181                 noteheads with the same notename.
182                */
183               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
184                                                              gh_int2scm (n)),
185                                           gh_int2scm (a)); 
186
187             }
188         }
189
190
191   
192   
193       daddy_trans_l_->set_property ("localKeySignature",  localsig);
194     }
195   
196   if (key_item_p_ && grace_align_l_)
197     {
198       Side_position_interface::add_support (grace_align_l_,key_item_p_);
199       grace_align_l_ =0;
200     }
201
202   if (key_item_p_)
203     {
204       /*
205         Hmm. Which one has to be on the left?
206
207         On which left, code or paper?
208
209  (Arpeggios are engraved left of accidentals, of course.)
210        */
211       for (int i=0;  i < arpeggios_.size ();  i++)
212         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
213
214       arpeggios_.clear ();
215     }
216 }
217
218 void
219 Local_key_engraver::finalize ()
220 {
221   // TODO: if grace ? signal accidentals to Local_key_engraver the 
222 }
223
224 void
225 Local_key_engraver::stop_translation_timestep ()
226 {
227   if (key_item_p_)
228     {
229       for (int i=0; i < support_l_arr_.size (); i++)
230         Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
231
232       typeset_grob (key_item_p_);
233       key_item_p_ =0;
234     }
235
236   grace_align_l_ = 0;
237   mel_l_arr_.clear ();
238   arpeggios_.clear ();
239   tie_l_arr_.clear ();
240   support_l_arr_.clear ();
241   forced_l_arr_.clear ();       
242 }
243
244 void
245 Local_key_engraver::acknowledge_grob (Grob_info info)
246 {
247   SCM wg= get_property ("weAreGraceContext");
248   
249   bool selfgr = gh_boolean_p (wg) &&gh_scm2bool (wg);
250   bool he_gr = to_boolean (info.elem_l_->get_grob_property ("grace"));
251
252   Item * item = dynamic_cast<Item*> (info.elem_l_);  
253   if (he_gr && !selfgr && item && Grace_align_item::has_interface (item))
254     {
255       grace_align_l_ = item;
256     }
257   if (he_gr != selfgr)
258     return;
259   
260   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
261
262   if (note_l && Rhythmic_head::has_interface (info.elem_l_))
263     {
264       mel_l_arr_.push (note_l);
265       support_l_arr_.push (info.elem_l_);
266     }
267   else if (Tie::has_interface (info.elem_l_))
268     {
269       tie_l_arr_.push (info.elem_l_);
270     }
271   else if (Arpeggio::has_interface (info.elem_l_))
272     {
273       arpeggios_.push (info.elem_l_); 
274     }
275   
276 }
277
278 /*
279   ugh. repeated deep_copy generates lots of garbage.
280  */
281 void
282 Local_key_engraver::process_music ()
283 {
284   SCM smp = get_property ("measurePosition");
285   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
286
287   SCM sig = get_property ("keySignature");
288
289   /*
290     Detect key sig changes. If we haven't found any, check if at start
291     of measure, and set localKeySignature anyhow.  */
292   if (last_keysig_ != sig) 
293     {
294       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
295       last_keysig_ = sig;
296     }
297   else if (!mp)
298     {
299       if (!to_boolean (get_property ("noResetKey")))
300         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
301     }
302 }
303
304
305
306 ADD_THIS_TRANSLATOR (Local_key_engraver);
307