]> git.donarmstrong.com Git - lilypond.git/blob - lily/translator-group.cc
* lily/ various: Introduce stream events of types Prepare,
[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 ("MusicEvent"));
52   c->event_source ()->add_listener (GET_LISTENER (create_child_translator),
53                                     ly_symbol2scm ("AnnounceNewContext"));
54 }
55
56 void
57 Translator_group::disconnect_from_context ()
58 {
59   context_->event_source ()->remove_listener (GET_LISTENER (eat_event),
60                                               ly_symbol2scm ("MusicEvent"));
61   context_->event_source ()->remove_listener (GET_LISTENER (create_child_translator),
62                                               ly_symbol2scm ("AnnounceNewContext"));
63   context_ = 0;
64 }
65
66 void
67 Translator_group::finalize ()
68 {
69 }
70
71 bool
72 translator_accepts_any_of (Translator *tr, SCM ifaces)
73 {
74   SCM ack_ifs = scm_assoc (ly_symbol2scm ("events-accepted"),
75                            tr->translator_description ());
76   ack_ifs = scm_cdr (ack_ifs);
77   for (SCM s = ifaces; scm_is_pair (s); s = scm_cdr (s))
78     if (scm_c_memq (scm_car (s), ack_ifs) != SCM_BOOL_F)
79       return true;
80   return false;
81 }
82
83 SCM
84 find_accept_translators (SCM gravlist, SCM ifaces)
85 {
86   SCM l = SCM_EOL;
87   for (SCM s = gravlist; scm_is_pair (s); s = scm_cdr (s))
88     {
89       Translator *tr = unsmob_translator (scm_car (s));
90       if (translator_accepts_any_of (tr, ifaces))
91         l = scm_cons (tr->self_scm (), l);
92     }
93   l = scm_reverse_x (l, SCM_EOL);
94
95   return l;
96 }
97
98 SCM
99 filter_performers (SCM ell)
100 {
101   SCM *tail = &ell;
102   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
103     {
104       if (dynamic_cast<Performer *> (unsmob_translator (scm_car (*tail))))
105         *tail = scm_cdr (*tail);
106       else
107         tail = SCM_CDRLOC (*tail);
108     }
109   return ell;
110 }
111
112 SCM
113 filter_engravers (SCM ell)
114 {
115   SCM *tail = &ell;
116   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
117     {
118       if (dynamic_cast<Engraver *> (unsmob_translator (scm_car (*tail))))
119         *tail = scm_cdr (*tail);
120       else
121         tail = SCM_CDRLOC (*tail);
122     }
123   return ell;
124 }
125
126 /*
127   Create a new translator for a newly created child context. Triggered
128   by AnnounceNewContext events.
129  */
130 IMPLEMENT_LISTENER (Translator_group, create_child_translator);
131 void
132 Translator_group::create_child_translator (SCM sev)
133 {
134   Stream_event *ev = unsmob_stream_event (sev);
135   // get from AnnounceNewContext
136   SCM cs = ev->get_property ("context");
137   Context *new_context = unsmob_context (cs);
138   Context_def *def = unsmob_context_def (new_context->get_definition ());
139   SCM ops = new_context->get_definition_mods ();
140   
141   SCM trans_names = def->get_translator_names (ops);
142
143   Translator_group *g = get_translator_group (def->get_translator_group_type ());
144   SCM trans_list = SCM_EOL;
145
146   for (SCM s = trans_names; scm_is_pair (s); s = scm_cdr (s))
147     {
148       Translator *type = get_translator (scm_car (s));
149       if (!type)
150         warning (_f ("can't find: `%s'", ly_symbol2string (scm_car (s)).c_str ()));
151       else
152         {
153           Translator *tr = type->clone ();
154           SCM str = tr->self_scm ();
155
156           if (tr->must_be_last ())
157             {
158               SCM cons = scm_cons (str, SCM_EOL);
159               if (scm_is_pair (trans_list))
160                 scm_set_cdr_x (scm_last_pair (trans_list), cons);
161               else
162                 trans_list = cons;
163             }
164           else
165             trans_list = scm_cons (str, trans_list);
166
167           tr->daddy_context_ = new_context;
168           tr->unprotect ();
169         }
170     }
171
172   g->simple_trans_list_ = trans_list;
173
174   /* Filter unwanted translator types. Required to make
175      \with {\consists "..."} work. */
176   if (dynamic_cast<Engraver_group *> (g))
177     g->simple_trans_list_ = filter_performers (g->simple_trans_list_);
178   else if (dynamic_cast<Performer_group *> (g))
179     g->simple_trans_list_ = filter_engravers (g->simple_trans_list_);
180
181   // TODO: scrap Context::implementation
182   new_context->implementation_ = g;
183
184   g->connect_to_context (new_context);
185   g->unprotect ();
186
187   recurse_over_translators (new_context,
188                             &Translator::initialize,
189                             &Translator_group::initialize,
190                             DOWN);
191 }
192
193 IMPLEMENT_LISTENER (Translator_group, eat_event);
194 void
195 Translator_group::eat_event (SCM sev)
196 {
197   Stream_event *ev = unsmob_stream_event (sev);
198   SCM sm = ev->get_property ("music");
199   Music *m = unsmob_music (sm);
200   try_music (m);
201 }
202
203 bool
204 Translator_group::try_music (Music *m)
205 {
206   SCM name = scm_sloppy_assq (ly_symbol2scm ("name"),
207                               m->get_property_alist (false));
208
209   if (!scm_is_pair (name))
210     return false;
211
212   name = scm_cdr (name);
213   SCM accept_list = scm_hashq_ref (accept_hash_table_, name, SCM_UNDEFINED);
214   if (accept_list == SCM_BOOL_F)
215     {
216       accept_list = find_accept_translators (get_simple_trans_list (),
217                                              m->get_property ("types"));
218       scm_hashq_set_x (accept_hash_table_, name, accept_list);
219     }
220
221   for (SCM p = accept_list; scm_is_pair (p); p = scm_cdr (p))
222     {
223       Translator *t = unsmob_translator (scm_car (p));
224       if (t && t->try_music (m))
225         return true;
226     }
227     
228   // We couldn't swallow the event in this context. Try parent.
229   Context *p = context ()->get_parent_context ();
230   // Global context's translator group is a dummy, so don't try it.
231   if (p->get_parent_context())
232     // ES todo: Make Translators listeners directly instead.
233     return p->implementation ()->try_music (m);
234   else
235     // We have tried all possible contexts. Give up.
236     m->origin ()->warning (_f ("junking event: `%s'", m->name ()));
237   return false;
238 }
239
240 SCM
241 Translator_group::get_simple_trans_list ()
242 {
243   return simple_trans_list_;
244 }
245
246 void
247 precomputed_recurse_over_translators (Context *c, Translator_precompute_index idx, Direction dir)
248 {
249   Translator_group *tg
250     = dynamic_cast<Translator_group *> (c->implementation ());
251
252   if (dir == DOWN)
253     {
254       tg->precomputed_translator_foreach (idx);
255       tg->call_precomputed_self_method (idx);
256     }
257
258   for (SCM s = c->children_contexts (); scm_is_pair (s);
259        s = scm_cdr (s))
260     precomputed_recurse_over_translators (unsmob_context (scm_car (s)), idx, dir);
261
262   if (dir == UP)
263     {
264       tg->precomputed_translator_foreach (idx);
265       tg->call_precomputed_self_method (idx);
266     }
267 }
268
269 void
270 recurse_over_translators (Context *c, Translator_method ptr, Translator_group_method tg_ptr, Direction dir)
271 {
272   Translator_group *tg
273     = dynamic_cast<Translator_group *> (c->implementation ());
274
275   if (dir == DOWN)
276     {
277       (tg->*tg_ptr) ();
278       translator_each (tg->get_simple_trans_list (), ptr);
279     }
280
281   for (SCM s = c->children_contexts (); scm_is_pair (s);
282        s = scm_cdr (s))
283     recurse_over_translators (unsmob_context (scm_car (s)), ptr, tg_ptr, dir);
284
285   if (dir == UP)
286     {
287       translator_each (tg->get_simple_trans_list (),
288                        ptr);
289
290       (tg->*tg_ptr) ();
291     }
292 }
293
294 Translator_group::Translator_group ()
295 {
296   simple_trans_list_ = SCM_EOL;
297   accept_hash_table_ = SCM_EOL;
298   context_ = 0;
299   smobify_self ();
300
301   accept_hash_table_ = scm_c_make_hash_table (19);
302 }
303
304 void
305 Translator_group::derived_mark () const
306 {
307 }
308
309 void
310 Translator_group::precompute_method_bindings ()
311 {
312   for (SCM s = simple_trans_list_; scm_is_pair (s); s = scm_cdr (s))
313     {
314       Translator *tr = unsmob_translator (scm_car (s));
315       Translator_void_method_ptr ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
316       tr->fetch_precomputable_methods (ptrs);
317
318       assert (tr);
319       for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
320         {
321           if (ptrs[i])
322             precomputed_method_bindings_[i].push_back (Translator_method_binding (tr, ptrs[i]));
323         }
324     }
325
326   fetch_precomputable_methods (precomputed_self_method_bindings_);
327 }
328
329 void
330 Translator_group::precomputed_translator_foreach (Translator_precompute_index idx)
331 {
332   vector<Translator_method_binding> &bindings (precomputed_method_bindings_[idx]);
333   for (vsize i = 0; i < bindings.size (); i++)
334     bindings[i].invoke ();
335 }
336
337 void
338 Translator_group::fetch_precomputable_methods (Translator_group_void_method ptrs[])
339 {
340   for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
341     ptrs[i] = 0;
342 }
343
344 void
345 Translator_group::call_precomputed_self_method (Translator_precompute_index idx)
346 {
347   if (precomputed_self_method_bindings_[idx])
348     (*precomputed_self_method_bindings_[idx]) (this);
349 }
350
351 Translator_group::~Translator_group ()
352 {
353 }
354
355 #include "ly-smobs.icc"
356
357 IMPLEMENT_SMOBS (Translator_group);
358 IMPLEMENT_DEFAULT_EQUAL_P (Translator_group);
359 IMPLEMENT_TYPE_P (Translator_group, "ly:translator-group?");
360
361 int
362 Translator_group::print_smob (SCM s, SCM port, scm_print_state *)
363 {
364   Translator_group *me = (Translator_group *) SCM_CELL_WORD_1 (s);
365   scm_puts ("#<Translator_group ", port);
366   scm_puts (me->class_name (), port);
367   scm_display (me->simple_trans_list_, port);
368   scm_puts (" >", port);
369   return 1;
370 }
371
372 SCM
373 Translator_group::mark_smob (SCM smob)
374 {
375   Translator_group *me = (Translator_group *)SCM_CELL_WORD_1 (smob);
376
377   me->derived_mark ();
378   scm_gc_mark (me->accept_hash_table_);
379   return me->simple_trans_list_;
380 }