]> git.donarmstrong.com Git - lilypond.git/blob - lily/local-key-engraver.cc
patch::: 1.3.133.jcn1
[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_reminder = 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                 /* Enable accidentals for broken tie
108
109                    We only want an accidental on a broken tie,
110                    if the tie changes the accidental.
111                    
112                    Maybe check property noTieBreakForceAccidental? */
113                 if (different)
114                   tie_break_reminder = tie_l_arr_[i];
115                 break;
116               }
117
118           /* When do we want accidentals:
119
120              1. when property force-accidental is set, and not
121              tie_changes
122              2. when different and not tie-changes
123              3. maybe when at end of a tie: we must later see if
124              we're after a line break */
125           if (((to_boolean (note_l->get_mus_property ("force-accidental"))
126                 || different)
127                && !tie_changes)
128               || tie_break_reminder)
129             {
130               if (!key_item_p_) 
131                 {
132                   key_item_p_ = new Item(get_property ("Accidentals"));
133                   Local_key_item::set_interface (key_item_p_);
134
135
136                   Staff_symbol_referencer::set_interface (key_item_p_);
137                          
138                   announce_grob (key_item_p_, 0);
139                 }
140
141               
142               bool extra_natural =
143                 sign (p) * (p - a) == 1
144                 && abs(p) == 2;
145
146               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
147                                          to_boolean (note_l->get_mus_property ("cautionary")),
148                                          extra_natural,
149                                          tie_break_reminder);
150               Side_position::add_support (key_item_p_,support_l);
151             }
152           
153           /*
154             We should not record the accidental if it is the first
155             note and it is tied from the previous measure.
156
157             Checking whether it is tied also works mostly, but will it
158             always do the correct thing?
159
160            */
161           bool forget = to_boolean (get_property ("forgetAccidentals"));
162           if (tie_changes)
163             {
164               /*
165                 Remember an alteration that is different both from
166                 that of the tied note and of the key signature.
167
168                */
169               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
170                                                              gh_int2scm (n)),
171                                           SCM_BOOL_T); 
172
173             }
174           else if (!forget)
175             {
176               /*
177                 not really really correct if there are more than one
178                 noteheads with the same notename.
179                */
180               localsig = scm_assoc_set_x (localsig, gh_cons (gh_int2scm (o),
181                                                              gh_int2scm (n)),
182                                           gh_int2scm (a)); 
183
184             }
185         }
186
187
188   
189   
190       daddy_trans_l_->set_property ("localKeySignature",  localsig);
191     }
192   
193   if (key_item_p_ && grace_align_l_)
194     {
195       Side_position::add_support (grace_align_l_,key_item_p_);
196       grace_align_l_ =0;
197     }
198
199   if (key_item_p_)
200     {
201       /*
202         Hmm. Which one has to be on the left?
203
204         On which left, code or paper?
205
206         (Arpeggios are engraved left of accidentals, of course.)
207        */
208       for (int i=0;  i < arpeggios_.size ();  i++)
209         Side_position::add_support (arpeggios_[i], key_item_p_);
210
211       arpeggios_.clear ();
212     }
213 }
214
215 void
216 Local_key_engraver::finalize ()
217 {
218   // TODO: if grace ? signal accidentals to Local_key_engraver the 
219 }
220
221 void
222 Local_key_engraver::stop_translation_timestep()
223 {
224   if (key_item_p_)
225     {
226       for (int i=0; i < support_l_arr_.size(); i++)
227         Side_position::add_support (key_item_p_,support_l_arr_[i]);
228
229       typeset_grob (key_item_p_);
230       key_item_p_ =0;
231     }
232
233   grace_align_l_ = 0;
234   mel_l_arr_.clear();
235   arpeggios_.clear ();
236   tie_l_arr_.clear ();
237   support_l_arr_.clear();
238   forced_l_arr_.clear();        
239 }
240
241 void
242 Local_key_engraver::acknowledge_grob (Grob_info info)
243 {
244   SCM wg= get_property ("weAreGraceContext");
245   
246   bool selfgr = gh_boolean_p (wg) &&gh_scm2bool (wg);
247   bool he_gr = to_boolean (info.elem_l_->get_grob_property ("grace"));
248
249   Item * item = dynamic_cast<Item*> (info.elem_l_);  
250   if (he_gr && !selfgr && item && Grace_align_item::has_interface (item))
251     {
252       grace_align_l_ = item;
253     }
254   if (he_gr != selfgr)
255     return;
256   
257   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
258
259   if (note_l && Rhythmic_head::has_interface (info.elem_l_))
260     {
261       mel_l_arr_.push (note_l);
262       support_l_arr_.push (info.elem_l_);
263     }
264   else if (Tie::has_interface (info.elem_l_))
265     {
266       tie_l_arr_.push (info.elem_l_);
267     }
268   else if (Arpeggio::has_interface (info.elem_l_))
269     {
270       arpeggios_.push (info.elem_l_); 
271     }
272   
273 }
274
275 /*
276   ugh. repeated deep_copy generates lots of garbage.
277  */
278 void
279 Local_key_engraver::process_music()
280 {
281   SCM smp = get_property ("measurePosition");
282   Moment mp =  (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
283
284   SCM sig = get_property ("keySignature");
285
286   /*
287     Detect key sig changes. If we haven't found any, check if at start
288     of measure, and set localKeySignature anyhow.  */
289   if (last_keysig_ != sig) 
290     {
291       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
292       last_keysig_ = sig;
293     }
294   else if (!mp)
295     {
296       if (!to_boolean (get_property ("noResetKey")))
297         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
298     }
299 }
300
301
302
303 ADD_THIS_TRANSLATOR(Local_key_engraver);
304