]> git.donarmstrong.com Git - lilypond.git/blob - lily/key-engraver.cc
use correct left to right ordering in keySignature list
[lilypond.git] / lily / key-engraver.cc
1 /*
2   key-engraver.cc -- implement Key_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "bar-line.hh"
10 #include "clef.hh"
11 #include "context.hh"
12 #include "engraver.hh"
13 #include "item.hh"
14 #include "pitch.hh"
15 #include "protected-scm.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "stream-event.hh"
18
19 #include "translator.icc"
20
21 class Key_engraver : public Engraver
22 {
23   void create_key (bool);
24   void read_event (Stream_event const *r);
25
26   Stream_event *key_event_;
27   Item *item_;
28   Item *cancellation_;
29 public:
30   TRANSLATOR_DECLARATIONS (Key_engraver);
31
32 protected:
33   virtual void initialize ();
34   virtual void finalize ();
35   void stop_translation_timestep ();
36   void process_music ();
37
38   DECLARE_TRANSLATOR_LISTENER (key_change);
39   DECLARE_ACKNOWLEDGER (clef);
40   DECLARE_ACKNOWLEDGER (bar_line);
41 };
42
43 void
44 Key_engraver::finalize ()
45 {
46 }
47
48 Key_engraver::Key_engraver ()
49 {
50   key_event_ = 0;
51   item_ = 0;
52   cancellation_ = 0;
53 }
54
55
56 void
57 Key_engraver::create_key (bool is_default)
58 {
59   if (!item_)
60     {
61       item_ = make_item ("KeySignature",
62                          key_event_ ? key_event_->self_scm () : SCM_EOL);
63
64       item_->set_property ("c0-position",
65                            get_property ("middleCPosition"));
66
67       SCM last = get_property ("lastKeySignature");
68       SCM key = get_property ("keySignature");
69       bool extranatural = to_boolean (get_property ("extraNatural"));
70
71       if ((to_boolean (get_property ("printKeyCancellation"))
72            || key == SCM_EOL)
73           && !scm_is_eq (last, key))
74         {
75           SCM restore = SCM_EOL;
76           SCM *tail = &restore;
77           for (SCM s = last; scm_is_pair (s); s = scm_cdr (s))
78             {
79               SCM new_alter_pair = scm_assoc (scm_caar (s), key);
80               Rational old_alter = robust_scm2rational (scm_cdar (s), 0);
81               if (new_alter_pair == SCM_BOOL_F
82                   || (extranatural
83                       && (ly_scm2rational (scm_cdr (new_alter_pair)) - old_alter)*old_alter
84                           < Rational (0)))
85                 {
86                   *tail = scm_cons (scm_car (s), *tail);
87                   tail = SCM_CDRLOC (*tail);
88                 }
89             }
90
91           if (scm_is_pair (restore))
92             {
93               cancellation_ = make_item ("KeyCancellation",
94                                          key_event_
95                                          ? key_event_->self_scm () : SCM_EOL);
96               
97               cancellation_->set_property ("alteration-alist", scm_reverse (restore));
98               cancellation_->set_property ("c0-position",
99                                            get_property ("middleCPosition"));
100             }
101         }
102
103       item_->set_property ("alteration-alist", scm_reverse (key));
104     }
105
106   if (!is_default)
107     {
108       SCM visibility = get_property ("explicitKeySignatureVisibility");
109       item_->set_property ("break-visibility", visibility);
110     }
111 }
112
113 IMPLEMENT_TRANSLATOR_LISTENER (Key_engraver, key_change);
114 void
115 Key_engraver::listen_key_change (Stream_event *ev)
116 {
117   /* do this only once, just to be on the safe side.  */
118   if (ASSIGN_EVENT_ONCE (key_event_, ev))
119     read_event (key_event_);
120 }
121
122 void
123 Key_engraver::acknowledge_clef (Grob_info /* info */)
124 {
125   SCM c = get_property ("createKeyOnClefChange");
126   if (to_boolean (c))
127     create_key (false);
128 }
129
130 void
131 Key_engraver::acknowledge_bar_line (Grob_info /* info */)
132 {
133   if (scm_is_pair (get_property ("keySignature")))
134     create_key (true);
135 }
136
137 void
138 Key_engraver::process_music ()
139 {
140   if (key_event_
141       || get_property ("lastKeySignature") != get_property ("keySignature"))
142     create_key (false);
143 }
144
145 void
146 Key_engraver::stop_translation_timestep ()
147 {
148   item_ = 0;
149   context ()->set_property ("lastKeySignature", get_property ("keySignature"));
150   cancellation_ = 0;
151   key_event_ = 0;
152 }
153
154 void
155 Key_engraver::read_event (Stream_event const *r)
156 {
157   SCM p = r->get_property ("pitch-alist");
158   if (!scm_is_pair (p))
159     return;
160
161   SCM accs = SCM_EOL;
162
163   SCM alist = scm_list_copy (p);
164   SCM order = get_property ("keyAlterationOrder");
165   for (SCM s = order;
166        scm_is_pair (s) && scm_is_pair (alist); s = scm_cdr (s))
167     {
168       SCM head = scm_member (scm_car (s), alist);
169       
170       if (scm_is_pair (head))
171         {
172           accs = scm_cons (scm_car (head), accs);
173           alist = scm_delete_x (scm_car (head), alist);
174         }
175     }
176
177   if (scm_is_pair (alist))
178     {
179       bool warn = false;
180       for (SCM s = alist; scm_is_pair (s); s = scm_cdr (s))
181         if (ly_scm2rational (scm_cdar (s)))
182           {
183             warn = true;
184             accs = scm_cons (scm_car (s), accs);
185           }
186
187       if (warn)
188         r->origin ()->warning ("No ordering for key signature alterations");      
189     }
190   
191   context ()->set_property ("keySignature", scm_reverse (accs));
192   context ()->set_property ("tonic",
193                             r->get_property ("tonic"));
194 }
195
196 void
197 Key_engraver::initialize ()
198 {
199   context ()->set_property ("keySignature", SCM_EOL);
200   context ()->set_property ("lastKeySignature", SCM_EOL);
201
202   Pitch p (0, 0, 0);
203   context ()->set_property ("tonic", p.smobbed_copy ());
204 }
205
206 ADD_ACKNOWLEDGER (Key_engraver, clef);
207 ADD_ACKNOWLEDGER (Key_engraver, bar_line);
208
209 ADD_TRANSLATOR (Key_engraver,
210                 /* doc */
211                 "Engrave a key signature.",
212
213                 /* create */
214                 "KeyCancellation "
215                 "KeySignature ",
216                 
217                 /* read */
218                 "createKeyOnClefChange "
219                 "explicitKeySignatureVisibility "
220                 "extraNatural "
221                 "keyAlterationOrder "
222                 "keySignature "
223                 "lastKeySignature "
224                 "printKeyCancellation ",
225                 
226                 /* write */
227                 "keySignature "
228                 "lastKeySignature "
229                 "tonic "
230                 );