]> git.donarmstrong.com Git - lilypond.git/blob - lily/translator-group.cc
Restrict spread of symbols in church rests
[lilypond.git] / lily / translator-group.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 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, SCM method)
40 {
41   for (SCM p = list; scm_is_pair (p); p = scm_cdr (p))
42     scm_call_1 (method, scm_car (p));
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 (Translator_group, 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 (Translator_group, 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 (unsmob<Performer> (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 void
149 Translator_group::create_child_translator (SCM sev)
150 {
151   Stream_event *ev = unsmob<Stream_event> (sev);
152   // get from AnnounceNewContext
153   SCM cs = ev->get_property ("context");
154   Context *new_context = unsmob<Context> (cs);
155   Context_def *def = unsmob<Context_def> (new_context->get_definition ());
156   SCM ops = new_context->get_definition_mods ();
157
158   SCM trans_names = def->get_translator_names (ops);
159
160   Translator_group *g = get_translator_group (def->get_translator_group_type ());
161   SCM trans_list = SCM_EOL;
162
163   for (SCM s = trans_names; scm_is_pair (s); s = scm_cdr (s))
164     {
165       SCM definition = scm_car (s);
166       bool is_scheme = false;
167
168       Translator *type = 0;
169       if (ly_is_symbol (definition))
170         type = get_translator (definition);
171       else if (ly_is_pair (definition))
172         {
173           is_scheme = true;
174         }
175       else if (ly_is_procedure (definition))
176         {
177           // `definition' is a procedure, which takes the context as
178           // an argument and evaluates to an a-list scheme engraver
179           // definition.
180           definition = scm_call_1 (definition, cs);
181           is_scheme = true;
182         }
183
184       if (!is_scheme && !type)
185         warning (_f ("cannot find: `%s'", ly_symbol2string (scm_car (s)).c_str ()));
186       else
187         {
188           Translator *instance = is_scheme ? new Scheme_engraver (definition)
189             : type->clone ();
190
191           SCM str = instance->self_scm ();
192
193           if (instance->must_be_last ())
194             {
195               SCM cons = scm_cons (str, SCM_EOL);
196               if (scm_is_pair (trans_list))
197                 scm_set_cdr_x (scm_last_pair (trans_list), cons);
198               else
199                 trans_list = cons;
200             }
201           else
202             trans_list = scm_cons (str, trans_list);
203
204           instance->daddy_context_ = new_context;
205           instance->unprotect ();
206         }
207     }
208
209   /* Filter unwanted translator types. Required to make
210      \with { \consists "..." } work. */
211   if (dynamic_cast<Engraver_group *> (g))
212     g->simple_trans_list_ = filter_performers (trans_list);
213   else if (dynamic_cast<Performer_group *> (g))
214     g->simple_trans_list_ = filter_engravers (trans_list);
215
216   // TODO: scrap Context::implementation
217   new_context->implementation_ = g;
218
219   g->connect_to_context (new_context);
220   g->unprotect ();
221
222   recurse_over_translators
223     (new_context,
224      Callback0_wrapper::make_smob<Translator, &Translator::initialize> (),
225      Callback0_wrapper::make_smob<Translator_group, &Translator_group::initialize> (),
226      DOWN);
227 }
228
229 SCM
230 Translator_group::get_simple_trans_list ()
231 {
232   return simple_trans_list_;
233 }
234
235 void
236 precomputed_recurse_over_translators (Context *c, Translator_precompute_index idx, Direction dir)
237 {
238   Translator_group *tg
239     = dynamic_cast<Translator_group *> (c->implementation ());
240
241   if (tg && dir == DOWN)
242     {
243       tg->precomputed_translator_foreach (idx);
244     }
245
246   for (SCM s = c->children_contexts (); scm_is_pair (s);
247        s = scm_cdr (s))
248     precomputed_recurse_over_translators (unsmob<Context> (scm_car (s)), idx, dir);
249
250   if (tg && dir == UP)
251     {
252       tg->precomputed_translator_foreach (idx);
253     }
254 }
255
256 void
257 recurse_over_translators (Context *c, SCM ptr,
258                           SCM tg_ptr, Direction dir)
259 {
260   Translator_group *tg = c->implementation ();
261   SCM tg_scm = tg ? tg->self_scm () : SCM_UNDEFINED;
262
263   if (tg && dir == DOWN)
264     {
265       scm_call_1 (tg_ptr, tg_scm);
266       translator_each (tg->get_simple_trans_list (), ptr);
267     }
268
269   for (SCM s = c->children_contexts (); scm_is_pair (s);
270        s = scm_cdr (s))
271     recurse_over_translators (unsmob<Context> (scm_car (s)), ptr, tg_ptr, dir);
272
273   if (tg && dir == UP)
274     {
275       translator_each (tg->get_simple_trans_list (),
276                        ptr);
277
278       scm_call_1 (tg_ptr, tg_scm);
279     }
280 }
281
282 Translator_group::Translator_group ()
283 {
284   simple_trans_list_ = SCM_EOL;
285   protected_events_ = SCM_EOL;
286   context_ = 0;
287   smobify_self ();
288 }
289
290 void
291 Translator_group::derived_mark () const
292 {
293 }
294
295 void
296 Translator_group::precompute_method_bindings ()
297 {
298   for (SCM s = simple_trans_list_; scm_is_pair (s); s = scm_cdr (s))
299     {
300       Translator *tr = unsmob<Translator> (scm_car (s));
301       SCM ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
302       tr->fetch_precomputable_methods (ptrs);
303
304       assert (tr);
305       for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
306         {
307           if (!SCM_UNBNDP (ptrs[i]))
308             precomputed_method_bindings_[i].push_back (Method_instance (ptrs[i], tr));
309         }
310     }
311
312 }
313
314 void
315 Translator_group::precomputed_translator_foreach (Translator_precompute_index idx)
316 {
317   vector<Method_instance> &bindings (precomputed_method_bindings_[idx]);
318   for (vsize i = 0; i < bindings.size (); i++)
319     bindings[i]();
320 }
321
322 Translator_group::~Translator_group ()
323 {
324 }
325
326
327 const char * const Translator_group::type_p_name_ = "ly:translator-group?";
328
329 int
330 Translator_group::print_smob (SCM port, scm_print_state *) const
331 {
332   scm_puts ("#<Translator_group ", port);
333   scm_puts (class_name (), port);
334   scm_display (simple_trans_list_, port);
335   scm_puts (" >", port);
336   return 1;
337 }
338
339 SCM
340 Translator_group::mark_smob () const
341 {
342   derived_mark ();
343   scm_gc_mark (protected_events_);
344   return simple_trans_list_;
345 }