Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 #ifndef SPANNER_ENGRAVER_HH | |
2 #define SPANNER_ENGRAVER_HH | |
3 | |
4 #include "context.hh" | |
5 #include "direction.hh" | |
6 #include "engraver.hh" | |
7 #include "engraver-group.hh" | |
8 #include "std-string.hh" | |
9 #include "std-vector.hh" | |
10 #include "stream-event.hh" | |
11 #include "translator-dispatch-list.hh" | |
12 #include "translator-group.hh" | |
13 #include <utility> | |
14 | |
15 // Context property spannerEngravers is an alist: | |
dak
2016/10/21 13:22:53
Ok, here is the principal problem with this module
| |
16 // ( #(engraver-class-symbol share-context spanner-id) . engraver-list ) | |
dak
2016/10/21 13:22:53
That sounds like something better maintained in in
| |
17 // It is used to keep track of the spanner engraver instances that may exist | |
18 // within a voice. | |
19 | |
20 // Context property sharedSpanners is an alist: | |
21 // (engraver-class-symbol . spanner-id) -> spanner-list | |
22 // spanner-list: (spanner spanner etc) | |
23 // If spanner-list has multiple elements, the spanner-id is associated with | |
24 // multiple spanners. This is needed for, e.g., double slurs | |
25 // Any spanners in the context property may cross voices within that context. | |
26 // The current voice a spanner belongs to is stored in the spanner property | |
27 // current-engraver. | |
28 | |
29 class Spanner_engraver : public Engraver | |
30 { | |
31 public: | |
32 Spanner_engraver (); | |
33 | |
34 private: | |
35 // This instance will only listen/acknowledge for this share context and spann er id | |
36 Context *filter_share_; | |
37 SCM filter_id_; | |
38 // The initial engraver in each voice is responsible for creating other instan ces | |
39 bool is_manager_; | |
40 | |
41 protected: | |
42 virtual void initialize (); | |
43 | |
44 // Filtered listen/acknowledge callbacks are first passed through these functi ons | |
45 template <class T, void (T::*callback) (Grob_info)> | |
46 void spanner_acknowledge (Grob_info info); | |
47 | |
48 template <class T, void (T::*callback) (Stream_event *)> | |
49 void spanner_listen (Stream_event *ev); | |
50 | |
51 template <class T, void (T::*callback) (Stream_event *)> | |
52 void spanner_single_listen (Stream_event *ev); | |
53 | |
54 // Call a function on a particular instance (with some share context and spann er id) | |
55 template <class T, class ParameterType> | |
56 void call_spanner_filtered (SCM share_context, SCM spanner_id, | |
57 void (T::*callback) (ParameterType), ParameterType argument); | |
58 | |
59 // Create a new instance for this share context and spanner id | |
60 // If an instance already exists for that combination, only create another one | |
61 // if multiple is true | |
62 template <class T> | |
63 T *create_instance (SCM share_context, SCM spanner_id, bool multiple = true); | |
64 | |
65 protected: | |
66 // When a spanner changes voices, this needs to be set to NULL in the | |
67 // engraver originally containing the spanner (see take_spanner) | |
68 Spanner *current_spanner_; | |
69 | |
70 #define make_multi_spanner(x, cause, share, id) \ | |
71 internal_make_multi_spanner (ly_symbol2scm (x), cause, share, id, \ | |
72 __FILE__, __LINE__, __FUNCTION__) | |
73 Spanner *internal_make_multi_spanner (SCM x, SCM cause, SCM share, SCM id, | |
74 char const *file, int line, char const * fun); | |
75 | |
76 // Get the spanner with some share context and id, and move it to this context | |
77 Spanner_engraver *take_spanner (SCM share_context, SCM id); | |
78 | |
79 // If cause is SCM_EOL, don't announce | |
80 void end_spanner (Spanner *span, SCM cause); | |
81 | |
82 protected: | |
83 Context *get_share_context (SCM s); | |
84 | |
85 // Get the spanner(s) in a context with an id | |
86 // If spanner-list has more than one spanner, the first function warns | |
87 // and returns the first spanner | |
88 Spanner *get_shared_spanner (Context *share, SCM spanner_id); | |
89 vector<Spanner *> get_shared_spanners (Context *share, SCM spanner_id); | |
90 | |
91 // Delete spanner(s) from share's sharedSpanners property | |
92 void delete_shared_spanner (Context *share, SCM spanner_id); | |
93 | |
94 // Add spanner to share's sharedSpanners property | |
95 void add_shared_spanner (Context *share, SCM spanner_id, Spanner *span); | |
96 | |
97 private: | |
98 inline SCM key (SCM spanner_id) | |
99 { return scm_cons (ly_symbol2scm (class_name ()), spanner_id); } | |
100 }; | |
101 | |
102 template <class T, void (T::*callback) (Grob_info)> | |
dak
2016/10/21 13:22:53
I'm not fond of large, comparatively generic templ
| |
103 void | |
104 Spanner_engraver::spanner_acknowledge (Grob_info info) | |
105 { | |
106 Grob *grob = info.grob (); | |
107 SCM id = grob->get_property ("spanner-id"); | |
108 SCM share_context = grob->get_property ("spanner-share-context"); | |
109 Context *share = get_share_context (share_context); | |
110 | |
111 if (is_manager_) | |
dak
2016/10/21 13:22:53
Ok, this is_manager_ concept seems like a bad fit
dak
2016/10/21 14:38:05
Sorry that's a bit confused as Spanner_engraver is
| |
112 { | |
113 T *instance = create_instance<T> (share_context, id, false); | |
114 if (instance) | |
115 { | |
116 (instance->*callback) (info); | |
117 return; | |
118 } | |
119 } | |
120 | |
121 if (ly_is_equal (id, filter_id_) && ly_is_equal (share->self_scm (), filter_sh are_->self_scm ())) | |
dak
2016/10/21 13:22:53
Yes, this is ugly: basically having the managing c
| |
122 (static_cast<T *> (this)->*callback) (info); | |
123 } | |
124 | |
125 template <class T, void (T::*callback) (Stream_event *)> | |
126 void | |
127 Spanner_engraver::spanner_listen (Stream_event *ev) | |
128 { | |
129 SCM id = ev->get_property ("spanner-id"); | |
130 SCM share_context = ev->get_property ("spanner-share-context"); | |
131 Context *share = get_share_context (share_context); | |
132 | |
133 if (is_manager_) | |
134 { | |
135 T *instance = create_instance<T> (share_context, id, false); | |
136 if (instance) | |
137 { | |
138 (instance->*callback) (ev); | |
139 return; | |
140 } | |
141 } | |
142 | |
143 if (ly_is_equal (id, filter_id_) && ly_is_equal (share->self_scm (), filter_sh are_->self_scm ())) | |
144 (static_cast<T *> (this)->*callback) (ev); | |
145 } | |
146 | |
147 template <class T, void (T::*callback) (Stream_event *)> | |
148 void | |
149 Spanner_engraver::spanner_single_listen (Stream_event *ev) | |
150 { | |
151 if (is_manager_) | |
152 (static_cast<T *> (this)->*callback) (ev); | |
153 } | |
154 | |
155 template <class T, class ParameterType> | |
156 void | |
157 Spanner_engraver::call_spanner_filtered (SCM share_context, SCM spanner_id, | |
158 void (T::*callback) (ParameterType), Pa rameterType argument) | |
159 { | |
160 T *instance = create_instance<T> (share_context, spanner_id, false); | |
161 if (instance) | |
162 { | |
163 (instance->*callback) (argument); | |
164 return; | |
165 } | |
166 | |
167 SCM key = scm_vector (scm_list_3 (ly_symbol2scm (class_name ()), | |
168 get_share_context (share_context)->self_scm (), | |
169 spanner_id)); | |
170 SCM instances = scm_assoc_ref (context ()->get_property ("spannerEngravers"), key); | |
171 while (scm_is_pair (instances)) | |
172 { | |
173 T *instance = unsmob<T> (scm_car (instances)); | |
174 (instance->*callback) (argument); | |
175 instances = scm_cdr (instances); | |
176 } | |
177 } | |
178 | |
179 template <class T> | |
180 T * | |
181 Spanner_engraver::create_instance (SCM share_context, SCM id, bool multiple) | |
182 { | |
183 Context *share = get_share_context (share_context); | |
184 SCM key = scm_vector (scm_list_3 (ly_symbol2scm (class_name ()), share->self_s cm (), id)); | |
185 SCM spanner_engravers = context ()->get_property ("spannerEngravers"); | |
186 SCM instances = scm_assoc_ref (spanner_engravers, key); | |
187 if (!multiple && scm_is_pair (instances)) | |
188 return NULL; | |
189 | |
190 T *instance = new T; | |
191 instance->filter_share_ = share; | |
192 instance->filter_id_ = id; | |
193 instance->is_manager_ = false; | |
194 instance->initialize (); | |
195 instance->unprotect (); | |
196 | |
197 instance->daddy_context_ = daddy_context_; | |
198 instance->connect_to_context (daddy_context_); | |
199 | |
200 SCM instance_scm = instance->self_scm (); | |
201 | |
202 // Add to Translator_group::precomputed_method_bindings | |
203 Translator_group *group = get_daddy_translator (); | |
204 group->simple_trans_list_ = scm_cons (instance_scm, group->simple_trans_list_) ; | |
205 SCM ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT]; | |
206 fetch_precomputable_methods (ptrs); | |
207 for (vsize i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++) | |
208 { | |
209 if (!SCM_UNBNDP (ptrs[i])) | |
210 group->precomputed_method_bindings_[i].push_back (Method_instance (ptrs[ i], instance)); | |
211 } | |
212 | |
213 // It might be faster to just clear Engraver_group::acknowledge_hash_table_dru l_ | |
214 // instead of checking to add a Method_instance for each grob | |
215 Engraver_group *egroup = static_cast<Engraver_group *> (group); | |
216 egroup->acknowledge_hash_table_drul_[START] = scm_c_make_hash_table (61); | |
217 egroup->acknowledge_hash_table_drul_[STOP] = scm_c_make_hash_table (61); | |
218 | |
219 instances = scm_is_pair (instances) | |
220 ? scm_cons (instance_scm, instances) | |
221 : scm_list_1 (instance_scm); | |
222 context ()->set_property ("spannerEngravers", scm_assoc_set_x (spanner_engrave rs, key, instances)); | |
223 | |
224 return instance; | |
225 } | |
226 | |
227 // Based on definitions in translator.icc | |
228 // Filtered callbacks are sent to each child instance, but first redirected | |
229 // through spanner_acknowledge/spanner_listen. If the id and share context | |
230 // match, the callback is then called on the child instance. | |
231 // TODO add option to filter out grobs from sibling engraver instances | |
232 // TODO need to handle listeners/acknowledgers with inheritance | |
233 // TODO should take_spanner be automatically called for filtered listener/acknow ledgers? | |
234 #define ADD_FILTERED_ACKNOWLEDGER_FULL(CLASS, NAME, GROB, DIRECTION) \ | |
235 add_acknowledger \ | |
236 (Callback2_wrapper::make_smob \ | |
237 <trampoline<Spanner_engraver, \ | |
238 &Spanner_engraver::spanner_acknowledge<CLASS, &CLASS::acknowledge _ ## NAME> > > (), \ | |
239 #GROB, acknowledge_static_array_drul_[DIRECTION]) | |
240 | |
241 #define ADD_FILTERED_ACKNOWLEDGER(CLASS, NAME) \ | |
242 ADD_FILTERED_ACKNOWLEDGER_FULL (CLASS, NAME, NAME, START) | |
243 | |
244 #define ADD_FILTERED_ACKNOWLEDGER_FOR(CLASS, NAME, GROB) \ | |
245 ADD_FILTERED_ACKNOWLEDGER_FULL (CLASS, NAME, GROB, START) | |
246 | |
247 #define ADD_FILTERED_END_ACKNOWLEDGER(CLASS, NAME) \ | |
248 ADD_FILTERED_ACKNOWLEDGER_FULL (CLASS, end_ ## NAME, NAME, STOP) | |
249 | |
250 #define ADD_FILTERED_END_ACKNOWLEDGER_FOR(CLASS, NAME, GROB) \ | |
251 ADD_FILTERED_ACKNOWLEDGER_FULL (CLASS, NAME, GROB, STOP) | |
252 | |
253 #define ADD_FILTERED_LISTENER_FOR(CLASS, NAME, EVENT) \ | |
254 listener_list_ = scm_acons \ | |
255 (event_class_symbol (#EVENT), \ | |
256 Callback_wrapper::make_smob \ | |
257 <trampoline<Spanner_engraver, \ | |
258 &Spanner_engraver::spanner_listen<CLASS, &CLASS::listen_ ## NAM E> > > (), \ | |
259 listener_list_) | |
260 | |
261 #define ADD_FILTERED_LISTENER(CLASS, NAME) \ | |
262 ADD_FILTERED_LISTENER_FOR (CLASS, NAME, NAME) | |
263 | |
264 #define ADD_SINGLE_LISTENER(CLASS, NAME) \ | |
265 listener_list_ = scm_acons \ | |
266 (event_class_symbol (#NAME), \ | |
267 Callback_wrapper::make_smob \ | |
268 <trampoline<Spanner_engraver, \ | |
269 &Spanner_engraver::spanner_single_listen<CLASS, &CLASS::listen_ ## NAME> > > (), \ | |
270 listener_list_) | |
271 | |
272 #endif // SPANNER_ENGRAVER_HH | |
OLD | NEW |