]> git.donarmstrong.com Git - lilypond.git/blob - lily/translator-group.cc
Added new system for how translators listen to events.
[lilypond.git] / lily / translator-group.cc
1 /*
2   translator-group.cc -- implement Translator_group
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>,
7                  Erik Sandberg <mandolaerik@gmail.com>
8 */
9
10 #include "translator-group.hh"
11
12 #include "context-def.hh"
13 #include "context.hh"
14 #include "dispatcher.hh"
15 #include "engraver-group.hh"
16 #include "international.hh"
17 #include "main.hh"
18 #include "music.hh"
19 #include "output-def.hh"
20 #include "performer-group.hh"
21 #include "scm-hash.hh"
22 #include "stream-event.hh"
23 #include "warn.hh"
24
25 Translator_group *
26 Translator_group::get_daddy_translator () const
27 {
28   return context ()->get_parent_context ()->implementation ();
29 }
30
31 void
32 translator_each (SCM list, Translator_method method)
33 {
34   for (SCM p = list; scm_is_pair (p); p = scm_cdr (p))
35     (unsmob_translator (scm_car (p))->*method) ();
36 }
37
38 void
39 Translator_group::initialize ()
40 {
41   precompute_method_bindings ();
42 }
43
44 void
45 Translator_group::connect_to_context (Context *c)
46 {
47   if (context_)
48     programming_error ("translator group is already connected to a context");
49   context_ = c;
50   c->event_source ()->add_listener (GET_LISTENER (eat_event),
51                                     ly_symbol2scm ("OldMusicEvent"));
52   c->event_source ()->add_listener (GET_LISTENER (create_child_translator),
53                                     ly_symbol2scm ("AnnounceNewContext"));
54   for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
55     {
56       Translator *tr = unsmob_translator (scm_car (tr_list));
57       tr->connect_to_context (c);
58     }
59 }
60
61 void
62 Translator_group::disconnect_from_context ()
63 {
64   for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
65     {
66       Translator *tr = unsmob_translator (scm_car (tr_list));
67       tr->disconnect_from_context (context_);
68     }
69   context_->event_source ()->remove_listener (GET_LISTENER (eat_event),
70                                               ly_symbol2scm ("OldMusicEvent"));
71   context_->event_source ()->remove_listener (GET_LISTENER (create_child_translator),
72                                               ly_symbol2scm ("AnnounceNewContext"));
73   context_ = 0;
74   protected_events_ = SCM_EOL;
75 }
76
77 void
78 Translator_group::finalize ()
79 {
80 }
81
82 bool
83 translator_accepts_any_of (Translator *tr, SCM ifaces)
84 {
85   SCM ack_ifs = scm_assoc (ly_symbol2scm ("events-accepted"),
86                            tr->translator_description ());
87   ack_ifs = scm_cdr (ack_ifs);
88   for (SCM s = ifaces; scm_is_pair (s); s = scm_cdr (s))
89     if (scm_c_memq (scm_car (s), ack_ifs) != SCM_BOOL_F)
90       return true;
91   return false;
92 }
93
94 SCM
95 find_accept_translators (SCM gravlist, SCM ifaces)
96 {
97   SCM l = SCM_EOL;
98   for (SCM s = gravlist; scm_is_pair (s); s = scm_cdr (s))
99     {
100       Translator *tr = unsmob_translator (scm_car (s));
101       if (translator_accepts_any_of (tr, ifaces))
102         l = scm_cons (tr->self_scm (), l);
103     }
104   l = scm_reverse_x (l, SCM_EOL);
105
106   return l;
107 }
108
109 SCM
110 filter_performers (SCM ell)
111 {
112   SCM *tail = &ell;
113   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
114     {
115       if (dynamic_cast<Performer *> (unsmob_translator (scm_car (*tail))))
116         *tail = scm_cdr (*tail);
117       else
118         tail = SCM_CDRLOC (*tail);
119     }
120   return ell;
121 }
122
123 SCM
124 filter_engravers (SCM ell)
125 {
126   SCM *tail = &ell;
127   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
128     {
129       if (dynamic_cast<Engraver *> (unsmob_translator (scm_car (*tail))))
130         *tail = scm_cdr (*tail);
131       else
132         tail = SCM_CDRLOC (*tail);
133     }
134   return ell;
135 }
136
137 /* 
138   Protects the parameter from being garbage collected. The object is
139   protected until the next disconnect_from_context call.
140
141   Whenever a child translator hears an event, the event is added to
142   this list. This eliminates the need for derived_mark methods in most
143   translators; all incoming events are instead protected by the
144   translator group.
145  
146   TODO: Should the list also be flushed at the beginning of each new
147   moment?
148  */
149 void
150 Translator_group::protect_event (SCM ev)
151 {
152   protected_events_ = scm_cons (ev, protected_events_);
153 }
154
155 /*
156   Create a new translator for a newly created child context. Triggered
157   by AnnounceNewContext events.
158  */
159 IMPLEMENT_LISTENER (Translator_group, create_child_translator);
160 void
161 Translator_group::create_child_translator (SCM sev)
162 {
163   Stream_event *ev = unsmob_stream_event (sev);
164   // get from AnnounceNewContext
165   SCM cs = ev->get_property ("context");
166   Context *new_context = unsmob_context (cs);
167   Context_def *def = unsmob_context_def (new_context->get_definition ());
168   SCM ops = new_context->get_definition_mods ();
169   
170   SCM trans_names = def->get_translator_names (ops);
171
172   Translator_group *g = get_translator_group (def->get_translator_group_type ());
173   SCM trans_list = SCM_EOL;
174
175   for (SCM s = trans_names; scm_is_pair (s); s = scm_cdr (s))
176     {
177       Translator *type = get_translator (scm_car (s));
178       if (!type)
179         warning (_f ("can't find: `%s'", ly_symbol2string (scm_car (s)).c_str ()));
180       else
181         {
182           Translator *tr = type->clone ();
183           SCM str = tr->self_scm ();
184
185           if (tr->must_be_last ())
186             {
187               SCM cons = scm_cons (str, SCM_EOL);
188               if (scm_is_pair (trans_list))
189                 scm_set_cdr_x (scm_last_pair (trans_list), cons);
190               else
191                 trans_list = cons;
192             }
193           else
194             trans_list = scm_cons (str, trans_list);
195
196           tr->daddy_context_ = new_context;
197           tr->unprotect ();
198         }
199     }
200
201   /* Filter unwanted translator types. Required to make
202      \with {\consists "..."} work. */
203   if (dynamic_cast<Engraver_group *> (g))
204     g->simple_trans_list_ = filter_performers (trans_list);
205   else if (dynamic_cast<Performer_group *> (g))
206     g->simple_trans_list_ = filter_engravers (trans_list);
207
208   // TODO: scrap Context::implementation
209   new_context->implementation_ = g;
210
211   g->connect_to_context (new_context);
212   g->unprotect ();
213
214   recurse_over_translators (new_context,
215                             &Translator::initialize,
216                             &Translator_group::initialize,
217                             DOWN);
218 }
219
220 IMPLEMENT_LISTENER (Translator_group, eat_event);
221 void
222 Translator_group::eat_event (SCM sev)
223 {
224   Stream_event *ev = unsmob_stream_event (sev);
225   SCM sm = ev->get_property ("music");
226   Music *m = unsmob_music (sm);
227   try_music (m);
228 }
229
230 bool
231 Translator_group::try_music (Music *m)
232 {
233   SCM name = scm_sloppy_assq (ly_symbol2scm ("name"),
234                               m->get_property_alist (false));
235
236   if (!scm_is_pair (name))
237     return false;
238
239   name = scm_cdr (name);
240   SCM accept_list = scm_hashq_ref (accept_hash_table_, name, SCM_UNDEFINED);
241   if (accept_list == SCM_BOOL_F)
242     {
243       accept_list = find_accept_translators (get_simple_trans_list (),
244                                              m->get_property ("types"));
245       scm_hashq_set_x (accept_hash_table_, name, accept_list);
246     }
247
248   for (SCM p = accept_list; scm_is_pair (p); p = scm_cdr (p))
249     {
250       Translator *t = unsmob_translator (scm_car (p));
251       if (t && t->try_music (m))
252         return true;
253     }
254     
255   // We couldn't swallow the event in this context. Try parent.
256   Context *p = context ()->get_parent_context ();
257   // Global context's translator group is a dummy, so don't try it.
258   if (p->get_parent_context())
259     // ES todo: Make Translators listeners directly instead.
260     return p->implementation ()->try_music (m);
261   // 'junking event' warning is temporarily disabled during translator cleanup
262   /*
263   else
264     // We have tried all possible contexts. Give up.
265     m->origin ()->warning (_f ("junking event: `%s'", m->name ()));
266   */
267   return false;
268 }
269
270 SCM
271 Translator_group::get_simple_trans_list ()
272 {
273   return simple_trans_list_;
274 }
275
276 void
277 precomputed_recurse_over_translators (Context *c, Translator_precompute_index idx, Direction dir)
278 {
279   Translator_group *tg
280     = dynamic_cast<Translator_group *> (c->implementation ());
281
282   if (dir == DOWN)
283     {
284       tg->precomputed_translator_foreach (idx);
285       tg->call_precomputed_self_method (idx);
286     }
287
288   for (SCM s = c->children_contexts (); scm_is_pair (s);
289        s = scm_cdr (s))
290     precomputed_recurse_over_translators (unsmob_context (scm_car (s)), idx, dir);
291
292   if (dir == UP)
293     {
294       tg->precomputed_translator_foreach (idx);
295       tg->call_precomputed_self_method (idx);
296     }
297 }
298
299 void
300 recurse_over_translators (Context *c, Translator_method ptr, Translator_group_method tg_ptr, Direction dir)
301 {
302   Translator_group *tg
303     = dynamic_cast<Translator_group *> (c->implementation ());
304
305   if (dir == DOWN)
306     {
307       (tg->*tg_ptr) ();
308       translator_each (tg->get_simple_trans_list (), ptr);
309     }
310
311   for (SCM s = c->children_contexts (); scm_is_pair (s);
312        s = scm_cdr (s))
313     recurse_over_translators (unsmob_context (scm_car (s)), ptr, tg_ptr, dir);
314
315   if (dir == UP)
316     {
317       translator_each (tg->get_simple_trans_list (),
318                        ptr);
319
320       (tg->*tg_ptr) ();
321     }
322 }
323
324 Translator_group::Translator_group ()
325 {
326   simple_trans_list_ = SCM_EOL;
327   accept_hash_table_ = SCM_EOL;
328   protected_events_ = SCM_EOL;
329   context_ = 0;
330   smobify_self ();
331
332   accept_hash_table_ = scm_c_make_hash_table (19);
333 }
334
335 void
336 Translator_group::derived_mark () const
337 {
338 }
339
340 void
341 Translator_group::precompute_method_bindings ()
342 {
343   for (SCM s = simple_trans_list_; scm_is_pair (s); s = scm_cdr (s))
344     {
345       Translator *tr = unsmob_translator (scm_car (s));
346       Translator_void_method_ptr ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
347       tr->fetch_precomputable_methods (ptrs);
348
349       assert (tr);
350       for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
351         {
352           if (ptrs[i])
353             precomputed_method_bindings_[i].push_back (Translator_method_binding (tr, ptrs[i]));
354         }
355     }
356
357   fetch_precomputable_methods (precomputed_self_method_bindings_);
358 }
359
360 void
361 Translator_group::precomputed_translator_foreach (Translator_precompute_index idx)
362 {
363   vector<Translator_method_binding> &bindings (precomputed_method_bindings_[idx]);
364   for (vsize i = 0; i < bindings.size (); i++)
365     bindings[i].invoke ();
366 }
367
368 void
369 Translator_group::fetch_precomputable_methods (Translator_group_void_method ptrs[])
370 {
371   for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
372     ptrs[i] = 0;
373 }
374
375 void
376 Translator_group::call_precomputed_self_method (Translator_precompute_index idx)
377 {
378   if (precomputed_self_method_bindings_[idx])
379     (*precomputed_self_method_bindings_[idx]) (this);
380 }
381
382 Translator_group::~Translator_group ()
383 {
384 }
385
386 #include "ly-smobs.icc"
387
388 IMPLEMENT_SMOBS (Translator_group);
389 IMPLEMENT_DEFAULT_EQUAL_P (Translator_group);
390 IMPLEMENT_TYPE_P (Translator_group, "ly:translator-group?");
391
392 int
393 Translator_group::print_smob (SCM s, SCM port, scm_print_state *)
394 {
395   Translator_group *me = (Translator_group *) SCM_CELL_WORD_1 (s);
396   scm_puts ("#<Translator_group ", port);
397   scm_puts (me->class_name (), port);
398   scm_display (me->simple_trans_list_, port);
399   scm_puts (" >", port);
400   return 1;
401 }
402
403 SCM
404 Translator_group::mark_smob (SCM smob)
405 {
406   Translator_group *me = (Translator_group *)SCM_CELL_WORD_1 (smob);
407
408   me->derived_mark ();
409   scm_gc_mark (me->accept_hash_table_);
410   scm_gc_mark (me->protected_events_);
411   return me->simple_trans_list_;
412 }