]> git.donarmstrong.com Git - lilypond.git/blob - lily/translator-group.cc
Web-ja: update introduction
[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 trans = scm_car (s);
166
167       if (ly_is_symbol (trans))
168         trans = get_translator_creator (trans);
169       if (ly_is_procedure (trans))
170         trans = scm_call_1 (trans, cs);
171       if (ly_cheap_is_list (trans))
172         trans = (new Scheme_engraver (trans, new_context))->unprotect ();
173       Translator *instance = unsmob<Translator> (trans);
174       if (!instance)
175         {
176           warning (_f ("cannot find: `%s'", ly_scm_write_string (trans).c_str ()));
177           continue;
178         }
179
180       if (instance->must_be_last ())
181         {
182           SCM cons = scm_cons (trans, SCM_EOL);
183           if (scm_is_pair (trans_list))
184             scm_set_cdr_x (scm_last_pair (trans_list), cons);
185           else
186             trans_list = cons;
187         }
188       else
189         trans_list = scm_cons (trans, trans_list);
190
191     }
192
193   /* Filter unwanted translator types. Required to make
194      \with { \consists "..." } work. */
195   if (dynamic_cast<Engraver_group *> (g))
196     g->simple_trans_list_ = filter_performers (trans_list);
197   else if (dynamic_cast<Performer_group *> (g))
198     g->simple_trans_list_ = filter_engravers (trans_list);
199
200   // TODO: scrap Context::implementation
201   new_context->implementation_ = g;
202
203   g->connect_to_context (new_context);
204   g->unprotect ();
205
206   recurse_over_translators
207     (new_context,
208      Callback0_wrapper::make_smob<Translator, &Translator::initialize> (),
209      Callback0_wrapper::make_smob<Translator_group, &Translator_group::initialize> (),
210      DOWN);
211 }
212
213 SCM
214 Translator_group::get_simple_trans_list ()
215 {
216   return simple_trans_list_;
217 }
218
219 void
220 precomputed_recurse_over_translators (Context *c, Translator_precompute_index idx, Direction dir)
221 {
222   Translator_group *tg
223     = dynamic_cast<Translator_group *> (c->implementation ());
224
225   if (tg && dir == DOWN)
226     {
227       tg->precomputed_translator_foreach (idx);
228     }
229
230   for (SCM s = c->children_contexts (); scm_is_pair (s);
231        s = scm_cdr (s))
232     precomputed_recurse_over_translators (unsmob<Context> (scm_car (s)), idx, dir);
233
234   if (tg && dir == UP)
235     {
236       tg->precomputed_translator_foreach (idx);
237     }
238 }
239
240 void
241 recurse_over_translators (Context *c, SCM ptr,
242                           SCM tg_ptr, Direction dir)
243 {
244   Translator_group *tg = c->implementation ();
245   SCM tg_scm = tg ? tg->self_scm () : SCM_UNDEFINED;
246
247   if (tg && dir == DOWN)
248     {
249       scm_call_1 (tg_ptr, tg_scm);
250       translator_each (tg->get_simple_trans_list (), ptr);
251     }
252
253   for (SCM s = c->children_contexts (); scm_is_pair (s);
254        s = scm_cdr (s))
255     recurse_over_translators (unsmob<Context> (scm_car (s)), ptr, tg_ptr, dir);
256
257   if (tg && dir == UP)
258     {
259       translator_each (tg->get_simple_trans_list (),
260                        ptr);
261
262       scm_call_1 (tg_ptr, tg_scm);
263     }
264 }
265
266 Translator_group::Translator_group ()
267 {
268   simple_trans_list_ = SCM_EOL;
269   protected_events_ = SCM_EOL;
270   context_ = 0;
271   smobify_self ();
272 }
273
274 void
275 Translator_group::derived_mark () const
276 {
277 }
278
279 void
280 Translator_group::precompute_method_bindings ()
281 {
282   for (SCM s = simple_trans_list_; scm_is_pair (s); s = scm_cdr (s))
283     {
284       Translator *tr = unsmob<Translator> (scm_car (s));
285       SCM ptrs[TRANSLATOR_METHOD_PRECOMPUTE_COUNT];
286       tr->fetch_precomputable_methods (ptrs);
287
288       assert (tr);
289       for (int i = 0; i < TRANSLATOR_METHOD_PRECOMPUTE_COUNT; i++)
290         {
291           if (!SCM_UNBNDP (ptrs[i]))
292             precomputed_method_bindings_[i].push_back (Method_instance (ptrs[i], tr));
293         }
294     }
295
296 }
297
298 void
299 Translator_group::precomputed_translator_foreach (Translator_precompute_index idx)
300 {
301   vector<Method_instance> &bindings (precomputed_method_bindings_[idx]);
302   for (vsize i = 0; i < bindings.size (); i++)
303     bindings[i]();
304 }
305
306 Translator_group::~Translator_group ()
307 {
308 }
309
310
311 const char * const Translator_group::type_p_name_ = "ly:translator-group?";
312
313 int
314 Translator_group::print_smob (SCM port, scm_print_state *) const
315 {
316   scm_puts ("#<Translator_group ", port);
317   scm_puts (class_name (), port);
318   scm_display (simple_trans_list_, port);
319   scm_puts (" >", port);
320   return 1;
321 }
322
323 SCM
324 Translator_group::mark_smob () const
325 {
326   derived_mark ();
327   scm_gc_mark (protected_events_);
328   return simple_trans_list_;
329 }