]> git.donarmstrong.com Git - lilypond.git/blob - lily/translator-group.cc
ab79d1347696c3d8ea18ae9cc1871ecd0bbe964f
[lilypond.git] / lily / translator-group.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>,
5                  Erik Sandberg <mandolaerik@gmail.com>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "translator-group.hh"
22
23 #include "context-def.hh"
24 #include "context.hh"
25 #include "dispatcher.hh"
26 #include "engraver.hh"
27 #include "engraver-group.hh"
28 #include "international.hh"
29 #include "main.hh"
30 #include "music.hh"
31 #include "output-def.hh"
32 #include "performer.hh"
33 #include "performer-group.hh"
34 #include "scheme-engraver.hh"
35 #include "scm-hash.hh"
36 #include "warn.hh"
37
38 void
39 translator_each (SCM list, Translator_method method)
40 {
41   for (SCM p = list; scm_is_pair (p); p = scm_cdr (p))
42     (unsmob_translator (scm_car (p))->*method) ();
43 }
44
45 void
46 Translator_group::initialize ()
47 {
48   precompute_method_bindings ();
49 }
50
51 void
52 Translator_group::connect_to_context (Context *c)
53 {
54   if (context_)
55     {
56       programming_error ("translator group is already connected to context "
57                          + context_->context_name ());
58     }
59
60   context_ = c;
61   c->event_source ()->add_listener (GET_LISTENER (create_child_translator),
62                                     ly_symbol2scm ("AnnounceNewContext"));
63   for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
64     {
65       Translator *tr = unsmob_translator (scm_car (tr_list));
66       tr->connect_to_context (c);
67     }
68 }
69
70 void
71 Translator_group::disconnect_from_context ()
72 {
73   for (SCM tr_list = simple_trans_list_; scm_is_pair (tr_list); tr_list = scm_cdr (tr_list))
74     {
75       Translator *tr = unsmob_translator (scm_car (tr_list));
76       tr->disconnect_from_context (context_);
77     }
78   context_->event_source ()->remove_listener (GET_LISTENER (create_child_translator),
79                                               ly_symbol2scm ("AnnounceNewContext"));
80   context_ = 0;
81   protected_events_ = SCM_EOL;
82 }
83
84 void
85 Translator_group::finalize ()
86 {
87 }
88
89 /*
90   Both filter_performers and filter_engravers used to use a direct dynamic_cast
91   on the unsmobbed translator to be filtered, i.e.,
92
93   if (dynamic_cast<Performer *> (unsmob_translator (scm_car (*tail))))
94
95   but this caused mysterious optimisation issues in several GUB builds.  See
96   issue #818 for the background to this change.
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 (unsmob_performer (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 (unsmob_engraver (scm_car (*tail)))
119         *tail = scm_cdr (*tail);
120       else
121         tail = SCM_CDRLOC (*tail);
122     }
123   return ell;
124 }
125
126 /*
127   Protects the parameter from being garbage collected. The object is
128   protected until the next disconnect_from_context call.
129
130   Whenever a child translator hears an event, the event is added to
131   this list. This eliminates the need for derived_mark methods in most
132   translators; all incoming events are instead protected by the
133   translator group.
134
135   TODO: Should the list also be flushed at the beginning of each new
136   moment?
137  */
138 void
139 Translator_group::protect_event (SCM ev)
140 {
141   protected_events_ = scm_cons (ev, protected_events_);
142 }
143
144 /*
145   Create a new translator for a newly created child context. Triggered
146   by AnnounceNewContext events.
147  */
148 IMPLEMENT_LISTENER (Translator_group, create_child_translator);
149 void
150 Translator_group::create_child_translator (SCM sev)
151 {
152   Stream_event *ev = unsmob_stream_event (sev);
153   // get from AnnounceNewContext
154   SCM cs = ev->get_property ("context");
155   Context *new_context = unsmob_context (cs);
156   Context_def *def = unsmob_context_def (new_context->get_definition ());
157   SCM ops = new_context->get_definition_mods ();
158
159   SCM trans_names = def->get_translator_names (ops);
160
161   Translator_group *g = get_translator_group (def->get_translator_group_type ());
162   SCM trans_list = SCM_EOL;
163
164   for (SCM s = trans_names; scm_is_pair (s); s = scm_cdr (s))
165     {
166       SCM definition = scm_car (s);
167       bool is_scheme = false;
168
169       Translator *type = 0;
170       if (ly_is_symbol (definition))
171         type = get_translator (definition);
172       else if (ly_is_pair (definition))
173         {
174           type = get_translator (ly_symbol2scm ("Scheme_engraver"));
175           is_scheme = true;
176         }
177       else if (ly_is_procedure (definition))
178         {
179           // `definition' is a procedure, which takes the context as
180           // an argument and evaluates to an a-list scheme engraver
181           // definition.
182           definition = scm_call_1 (definition, cs);
183           type = get_translator (ly_symbol2scm ("Scheme_engraver"));
184           is_scheme = true;
185         }
186
187       if (!type)
188         warning (_f ("cannot find: `%s'", ly_symbol2string (scm_car (s)).c_str ()));
189       else
190         {
191           Translator *instance = type->clone ();
192           if (is_scheme)
193             dynamic_cast<Scheme_engraver *> (instance)->init_from_scheme (definition);
194
195           SCM str = instance->self_scm ();
196
197           if (instance->must_be_last ())
198             {
199               SCM cons = scm_cons (str, SCM_EOL);
200               if (scm_is_pair (trans_list))
201                 scm_set_cdr_x (scm_last_pair (trans_list), cons);
202               else
203                 trans_list = cons;
204             }
205           else
206             trans_list = scm_cons (str, trans_list);
207
208           instance->daddy_context_ = new_context;
209           instance->unprotect ();
210         }
211     }
212
213   /* Filter unwanted translator types. Required to make
214      \with { \consists "..." } work. */
215   if (dynamic_cast<Engraver_group *> (g))
216     g->simple_trans_list_ = filter_performers (trans_list);
217   else if (dynamic_cast<Performer_group *> (g))
218     g->simple_trans_list_ = filter_engravers (trans_list);
219
220   // TODO: scrap Context::implementation
221   new_context->implementation_ = g;
222
223   g->connect_to_context (new_context);
224   g->unprotect ();
225
226   recurse_over_translators (new_context,
227                             &Translator::initialize,
228                             &Translator_group::initialize,
229                             DOWN);
230 }
231
232 SCM
233 Translator_group::get_simple_trans_list ()
234 {
235   return simple_trans_list_;
236 }
237
238 void
239 precomputed_recurse_over_translators (Context *c, Translator_precompute_index idx, Direction dir)
240 {
241   Translator_group *tg
242     = dynamic_cast<Translator_group *> (c->implementation ());
243
244   if (tg && dir == DOWN)
245     {
246       tg->precomputed_translator_foreach (idx);
247       tg->call_precomputed_self_method (idx);
248     }
249
250   for (SCM s = c->children_contexts (); scm_is_pair (s);
251        s = scm_cdr (s))
252     precomputed_recurse_over_translators (unsmob_context (scm_car (s)), idx, dir);
253
254   if (tg && dir == UP)
255     {
256       tg->precomputed_translator_foreach (idx);
257       tg->call_precomputed_self_method (idx);
258     }
259 }
260
261 void
262 recurse_over_translators (Context *c, Translator_method ptr,
263                           Translator_group_method tg_ptr, Direction dir)
264 {
265   Translator_group *tg
266     = dynamic_cast<Translator_group *> (c->implementation ());
267
268   if (tg && dir == DOWN)
269     {
270       (tg->*tg_ptr) ();
271       translator_each (tg->get_simple_trans_list (), ptr);
272     }
273
274   for (SCM s = c->children_contexts (); scm_is_pair (s);
275        s = scm_cdr (s))
276     recurse_over_translators (unsmob_context (scm_car (s)), ptr, tg_ptr, dir);
277
278   if (tg && dir == UP)
279     {
280       translator_each (tg->get_simple_trans_list (),
281                        ptr);
282
283       (tg->*tg_ptr) ();
284     }
285 }
286
287 Translator_group::Translator_group ()
288 {
289   simple_trans_list_ = SCM_EOL;
290   protected_events_ = SCM_EOL;
291   context_ = 0;
292   smobify_self ();
293 }
294
295 void
296 Translator_group::derived_mark () const
297 {
298 }
299
300 void
301 Translator_group::precompute_method_bindings ()
302 {
303   for (SCM s = simple_trans_list_; scm_is_pair (s); s = scm_cdr (s))
304     {
305       Translator *tr = unsmob_translator (scm_car (s));
306       Translator_void_method_ptr ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
307       tr->fetch_precomputable_methods (ptrs);
308
309       assert (tr);
310       for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
311         {
312           if (ptrs[i])
313             precomputed_method_bindings_[i].push_back (Translator_method_binding (tr, ptrs[i]));
314         }
315     }
316
317   fetch_precomputable_methods (precomputed_self_method_bindings_);
318 }
319
320 void
321 Translator_group::precomputed_translator_foreach (Translator_precompute_index idx)
322 {
323   vector<Translator_method_binding> &bindings (precomputed_method_bindings_[idx]);
324   for (vsize i = 0; i < bindings.size (); i++)
325     bindings[i].invoke ();
326 }
327
328 void
329 Translator_group::fetch_precomputable_methods (Translator_group_void_method ptrs[])
330 {
331   for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
332     ptrs[i] = 0;
333 }
334
335 void
336 Translator_group::call_precomputed_self_method (Translator_precompute_index idx)
337 {
338   if (precomputed_self_method_bindings_[idx])
339     (*precomputed_self_method_bindings_[idx]) (this);
340 }
341
342 Translator_group::~Translator_group ()
343 {
344 }
345
346 #include "ly-smobs.icc"
347
348 IMPLEMENT_SMOBS (Translator_group);
349 IMPLEMENT_DEFAULT_EQUAL_P (Translator_group);
350 IMPLEMENT_TYPE_P (Translator_group, "ly:translator-group?");
351
352 int
353 Translator_group::print_smob (SCM s, SCM port, scm_print_state *)
354 {
355   Translator_group *me = (Translator_group *) SCM_CELL_WORD_1 (s);
356   scm_puts ("#<Translator_group ", port);
357   scm_puts (me->class_name (), port);
358   scm_display (me->simple_trans_list_, port);
359   scm_puts (" >", port);
360   return 1;
361 }
362
363 SCM
364 Translator_group::mark_smob (SCM smob)
365 {
366   Translator_group *me = (Translator_group *)SCM_CELL_WORD_1 (smob);
367
368   me->derived_mark ();
369   scm_gc_mark (me->protected_events_);
370   return me->simple_trans_list_;
371 }