]> git.donarmstrong.com Git - lilypond.git/blob - lily/local-key-engraver.cc
patch::: 1.3.132.jcn3
[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 */
28
29
30 struct Local_key_engraver : Engraver {
31   Item *key_item_p_;
32 protected:
33   VIRTUAL_COPY_CONS(Translator);
34   virtual void process_music();
35   virtual void acknowledge_grob (Grob_info);
36   virtual void stop_translation_timestep();
37   virtual void initialize ();
38   virtual void create_grobs ();
39   virtual void finalize ();
40 public:
41
42   // todo -> property
43   SCM last_keysig_;
44
45   /*
46     Urgh. Since the accidentals depend on lots of variables, we have to
47     store all information before we can really create the accidentals.
48    */
49   Link_array<Grob> arpeggios_;
50   
51   Link_array<Note_req> mel_l_arr_;
52   Link_array<Grob> support_l_arr_;
53   Link_array<Item> forced_l_arr_;
54   Link_array<Grob> tie_l_arr_;
55   Local_key_engraver();
56
57   Item * grace_align_l_;
58 };
59
60 Local_key_engraver::Local_key_engraver()
61 {
62   key_item_p_ =0;
63   grace_align_l_ =0;
64   last_keysig_ = SCM_EOL;
65 }
66
67 void
68 Local_key_engraver::initialize ()
69 {
70   last_keysig_ = get_property ("keySignature");
71   daddy_trans_l_->set_property ("localKeySignature",  last_keysig_);  
72 }
73
74 void
75 Local_key_engraver::create_grobs ()
76 {
77   if (!key_item_p_ && mel_l_arr_.size()) 
78     {
79       SCM localsig = get_property ("localKeySignature");
80   
81       for (int i=0; i  < mel_l_arr_.size(); i++) 
82         {
83           Grob * support_l = support_l_arr_[i];
84           Note_req * note_l = mel_l_arr_[i];
85
86           int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
87           int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
88           int a = unsmob_pitch (note_l->get_mus_property ("pitch"))->alteration_i_;
89           
90           /* see if there's a tie that "changes" the accidental */
91           /* works because if there's a tie, the note to the left
92              is of the same pitch as the actual note */
93
94           SCM prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), localsig);
95           if (prev == SCM_BOOL_F)
96             prev = scm_assoc (gh_int2scm (n), localsig);
97           SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm(0) : gh_cdr (prev);
98           bool different = !gh_equal_p(prev_acc , gh_int2scm(a));
99           int p = gh_number_p(prev_acc) ? gh_scm2int(prev_acc) : 0;
100
101           Grob *tie_break_cautionary = 0;
102           bool tie_changes = false;
103           for (int i=0; i < tie_l_arr_.size (); i++)
104             if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
105               {
106                 tie_changes = different;
107 #if 0
108                 // don't do this, yet.  the accidentals can't be
109                 // deleted, yet.
110                 tie_break_cautionary = tie_l_arr_[i];
111 #endif
112                 break;
113               }
114
115           /* When do we want accidentals:
116
117              1. when property force-accidental is set, and not
118              tie_changes
119              2. when different and not tie-changes
120              3. maybe when at end of a tie: we must later see if
121              we're after a line break */
122           if (((to_boolean (note_l->get_mus_property ("force-accidental"))
123                 || different)
124                && !tie_changes)
125               || tie_break_cautionary)
126             {
127               if (!key_item_p_) 
128                 {
129                   key_item_p_ = new Item(get_property ("Accidentals"));
130                   Local_key_item::set_interface (key_item_p_);
131
132
133                   Staff_symbol_referencer::set_interface (key_item_p_);
134                          
135                   announce_grob (key_item_p_, 0);
136                 }
137
138               
139               bool extra_natural =
140                 sign (p) * (p - a) == 1
141                 && abs(p) == 2;
142
143               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
144                                          to_boolean (note_l->get_mus_property ("cautionary")),
145                                          extra_natural,
146                                          tie_break_cautionary);
147               Side_position::add_support (key_item_p_,support_l);
148             }
149           
150           /*
151             We should not record the accidental if it is the first
152             note and it is tied from the previous measure.
153
154             Checking whether it is tied also works mostly, but will it
155             always do the correct thing?
156
157            */
158           bool forget = to_boolean (get_property ("forgetAccidentals"));
159           if (tie_changes)
160             {
161               /*
162                 Remember an alteration that is different both from
163                 that of the tied note and of the key signature.
164
165                */
166               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
167                                                              gh_int2scm (n)),
168                                           SCM_BOOL_T); 
169
170             }
171           else if (!forget)
172             {
173               /*
174                 not really really correct if there are more than one
175                 noteheads with the same notename.
176                */
177               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
178                                                              gh_int2scm (n)),
179                                           gh_int2scm (a)); 
180
181             }
182         }
183
184
185   
186   
187       daddy_trans_l_->set_property ("localKeySignature",  localsig);
188     }
189   
190   if (key_item_p_ && grace_align_l_)
191     {
192       Side_position::add_support (grace_align_l_,key_item_p_);
193       grace_align_l_ =0;
194     }
195
196   if (key_item_p_)
197     {
198       /*
199         Hmm. Which one has to be on the left?
200
201         On which left, code or paper?
202
203         (Arpeggios are engraved left of accidentals, of course.)
204        */
205       for (int i=0;  i < arpeggios_.size ();  i++)
206         Side_position::add_support (arpeggios_[i], key_item_p_);
207
208       arpeggios_.clear ();
209     }
210 }
211
212 void
213 Local_key_engraver::finalize ()
214 {
215   // TODO: if grace ? signal accidentals to Local_key_engraver the 
216 }
217
218 void
219 Local_key_engraver::stop_translation_timestep()
220 {
221   if (key_item_p_)
222     {
223       for (int i=0; i < support_l_arr_.size(); i++)
224         Side_position::add_support (key_item_p_,support_l_arr_[i]);
225
226       typeset_grob (key_item_p_);
227       key_item_p_ =0;
228     }
229
230   grace_align_l_ = 0;
231   mel_l_arr_.clear();
232   arpeggios_.clear ();
233   tie_l_arr_.clear ();
234   support_l_arr_.clear();
235   forced_l_arr_.clear();        
236 }
237
238 void
239 Local_key_engraver::acknowledge_grob (Grob_info info)
240 {
241   SCM wg= get_property ("weAreGraceContext");
242   
243   bool selfgr = gh_boolean_p (wg) &&gh_scm2bool (wg);
244   bool he_gr = to_boolean (info.elem_l_->get_grob_property ("grace"));
245
246   Item * item = dynamic_cast<Item*> (info.elem_l_);  
247   if (he_gr && !selfgr && item && Grace_align_item::has_interface (item))
248     {
249       grace_align_l_ = item;
250     }
251   if (he_gr != selfgr)
252     return;
253   
254   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
255
256   if (note_l && Rhythmic_head::has_interface (info.elem_l_))
257     {
258       mel_l_arr_.push (note_l);
259       support_l_arr_.push (info.elem_l_);
260     }
261   else if (Tie::has_interface (info.elem_l_))
262     {
263       tie_l_arr_.push (info.elem_l_);
264     }
265   else if (Arpeggio::has_interface (info.elem_l_))
266     {
267       arpeggios_.push (info.elem_l_); 
268     }
269   
270 }
271
272 /*
273   ugh. repeated deep_copy generates lots of garbage.
274  */
275 void
276 Local_key_engraver::process_music()
277 {
278   SCM smp = get_property ("measurePosition");
279   Moment mp =  (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
280
281   SCM sig = get_property ("keySignature");
282
283   /*
284     Detect key sig changes. If we haven't found any, check if at start
285     of measure, and set localKeySignature anyhow.  */
286   if (last_keysig_ != sig) 
287     {
288       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
289       last_keysig_ = sig;
290     }
291   else if (!mp)
292     {
293       if (!to_boolean (get_property ("noResetKey")))
294         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
295     }
296 }
297
298
299
300 ADD_THIS_TRANSLATOR(Local_key_engraver);
301