]> git.donarmstrong.com Git - lilypond.git/blob - lily/key-engraver.cc
Merge branch 'master' of git://git.sv.gnu.org/lilypond
[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--2006 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 SCM
57 make_qt_key (SCM rat_key)
58 {
59   SCM qt_key = SCM_EOL;
60   SCM *tail = &qt_key;
61       
62   for (SCM s = rat_key; scm_is_pair (s); s = scm_cdr (s))
63     {
64       *tail = scm_cons (scm_cons (scm_caar (s),
65                                   scm_from_int (Rational (4)* ly_scm2rational (scm_cdar (s)))),
66                         SCM_EOL);
67       tail =  SCM_CDRLOC (*tail);
68     }
69
70   return qt_key;
71 }
72
73 void
74 Key_engraver::create_key (bool is_default)
75 {
76   if (!item_)
77     {
78       item_ = make_item ("KeySignature",
79                          key_event_ ? key_event_->self_scm () : SCM_EOL);
80
81       item_->set_property ("c0-position",
82                            get_property ("middleCPosition"));
83
84       SCM last = get_property ("lastKeySignature");
85       SCM key = get_property ("keySignature");
86       bool extranatural = to_boolean(get_property("extraNatural"));
87
88       if ((to_boolean (get_property ("printKeyCancellation"))
89            || key == SCM_EOL)
90           && !scm_is_eq (last, key))
91         {
92           SCM restore = SCM_EOL;
93           SCM *tail = &restore;
94           for (SCM s = last; scm_is_pair (s); s = scm_cdr (s))
95             {
96               SCM new_alter_pair = scm_assoc (scm_caar (s), key);
97               Rational old_alter = ly_scm2rational (scm_cdar (s));
98               if (new_alter_pair == SCM_BOOL_F
99                   || extranatural
100                   && (ly_scm2rational (scm_cdr (new_alter_pair)) - old_alter)*old_alter < Rational (0))
101                 {
102                   *tail = scm_cons (scm_car (s), *tail);
103                   tail = SCM_CDRLOC (*tail);
104                 }
105             }
106
107           if (scm_is_pair (restore))
108             {
109               cancellation_ = make_item ("KeyCancellation",
110                                          key_event_
111                                          ? key_event_->self_scm () : SCM_EOL);
112               
113               cancellation_->set_property ("alteration-alist", make_qt_key (restore));
114               cancellation_->set_property ("c0-position",
115                                            get_property ("middleCPosition"));
116             }
117         }
118
119
120       item_->set_property ("alteration-alist", make_qt_key (key));
121     }
122
123   if (!is_default)
124     {
125       SCM visibility = get_property ("explicitKeySignatureVisibility");
126       item_->set_property ("break-visibility", visibility);
127       if (cancellation_)
128         cancellation_->set_property ("break-visibility", visibility);
129     }
130 }
131
132 IMPLEMENT_TRANSLATOR_LISTENER (Key_engraver, key_change);
133 void
134 Key_engraver::listen_key_change (Stream_event *ev)
135 {
136   /* do this only once, just to be on the safe side.  */
137   if (ASSIGN_EVENT_ONCE (key_event_, ev))
138     read_event (key_event_);
139 }
140
141 void
142 Key_engraver::acknowledge_clef (Grob_info info)
143 {
144   (void)info;
145   SCM c = get_property ("createKeyOnClefChange");
146   if (to_boolean (c))
147     create_key (false);
148 }
149
150 void
151 Key_engraver::acknowledge_bar_line (Grob_info info)
152 {
153   (void)info;
154   if (scm_is_pair (get_property ("keySignature")))
155     create_key (true);
156 }
157
158 void
159 Key_engraver::process_music ()
160 {
161   if (key_event_
162       || get_property ("lastKeySignature") != get_property ("keySignature"))
163     create_key (false);
164 }
165
166 void
167 Key_engraver::stop_translation_timestep ()
168 {
169   item_ = 0;
170   context ()->set_property ("lastKeySignature", get_property ("keySignature"));
171   cancellation_ = 0;
172   key_event_ = 0;
173 }
174
175 void
176 Key_engraver::read_event (Stream_event const *r)
177 {
178   SCM p = r->get_property ("pitch-alist");
179   if (!scm_is_pair (p))
180     return;
181
182   SCM accs = SCM_EOL;
183
184   SCM alist = scm_list_copy (p);
185   SCM order = get_property ("keyAlterationOrder");
186   for (SCM s = order;
187        scm_is_pair (s) && scm_is_pair (alist); s = scm_cdr (s))
188     {
189       SCM head = scm_member (scm_car (s), alist);
190       
191       if (scm_is_pair (head))
192         {
193           accs = scm_cons (scm_car (head), accs);
194           alist = scm_delete_x (scm_car (head), alist);
195         }
196     }
197
198   if (scm_is_pair (alist))
199     {
200       bool warn = false;
201       for (SCM s = alist; scm_is_pair (s); s = scm_cdr (s))
202         if (ly_scm2rational (scm_cdar (s)))
203           {
204             warn = true;
205             accs = scm_cons (scm_car (s), accs);
206           }
207
208       if (warn)
209         r->origin ()->warning ("No ordering for key signature alterations");      
210     }
211   
212   context ()->set_property ("keySignature", accs);
213   context ()->set_property ("tonic",
214                             r->get_property ("tonic"));
215 }
216
217 void
218 Key_engraver::initialize ()
219 {
220   context ()->set_property ("keySignature", SCM_EOL);
221   context ()->set_property ("lastKeySignature", SCM_EOL);
222
223   Pitch p (0, 0, 0);
224   context ()->set_property ("tonic", p.smobbed_copy ());
225 }
226
227 ADD_ACKNOWLEDGER (Key_engraver, clef);
228 ADD_ACKNOWLEDGER (Key_engraver, bar_line);
229
230 ADD_TRANSLATOR (Key_engraver,
231                 /* doc */ "",
232                 /* create */ "KeySignature",
233                 
234                 /* read */
235                 "createKeyOnClefChange "
236                 "explicitKeySignatureVisibility "
237                 "extraNatural "
238                 "keyAlterationOrder "
239                 "keySignature "
240                 "keySignature "
241                 "lastKeySignature "
242                 "printKeyCancellation "
243                 ,
244                 
245                 /* write */
246                 "keySignature "
247                 "lastKeySignature "
248                 "tonic ")