]> git.donarmstrong.com Git - lilypond.git/blob - lily/context-def.cc
Fix some bugs in the dynamic engraver and PostScript backend
[lilypond.git] / lily / context-def.cc
1 /*
2   translator-def.cc -- implement Context_def
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2000--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 /* TODO: should junk this class an replace by
10    a single list of context modifications?  */
11
12 #include "context-def.hh"
13
14 #include "engraver-group.hh"
15 #include "engraver.hh"
16 #include "international.hh"
17 #include "output-def.hh"
18 #include "performer-group.hh"
19 #include "performer.hh"
20 #include "score-context.hh"
21 #include "translator-group.hh"
22 #include "warn.hh"
23
24 Context_def::Context_def ()
25 {
26   context_aliases_ = SCM_EOL;
27   translator_group_type_ = SCM_EOL;
28   accept_mods_ = SCM_EOL;
29   translator_mods_ = SCM_EOL;
30   property_ops_ = SCM_EOL;
31   context_name_ = SCM_EOL;
32   default_child_ = SCM_EOL;
33   description_ = SCM_EOL;
34
35   smobify_self ();
36
37   context_name_ = ly_symbol2scm ("");
38 }
39
40 Context_def::Context_def (Context_def const &s)
41   : Input (s)
42 {
43   context_aliases_ = SCM_EOL;
44   translator_group_type_ = SCM_EOL;
45   accept_mods_ = SCM_EOL;
46   translator_mods_ = SCM_EOL;
47   property_ops_ = SCM_EOL;
48   context_name_ = SCM_EOL;
49   description_ = SCM_EOL;
50   default_child_ = SCM_EOL;
51
52   smobify_self ();
53   description_ = s.description_;
54
55   default_child_ = s.default_child_;
56   accept_mods_ = s.accept_mods_;
57   property_ops_ = s.property_ops_;
58   translator_mods_ = s.translator_mods_;
59   context_aliases_ = s.context_aliases_;
60   translator_group_type_ = s.translator_group_type_;
61   context_name_ = s.context_name_;
62 }
63
64 Context_def::~Context_def ()
65 {
66 }
67
68 #include "ly-smobs.icc"
69 IMPLEMENT_SMOBS (Context_def);
70 IMPLEMENT_DEFAULT_EQUAL_P (Context_def);
71
72 int
73 Context_def::print_smob (SCM smob, SCM port, scm_print_state*)
74 {
75   Context_def *me = (Context_def *) SCM_CELL_WORD_1 (smob);
76
77   scm_puts ("#<Context_def ", port);
78   scm_display (me->context_name_, port);
79   scm_puts (">", port);
80   return 1;
81 }
82
83 SCM
84 Context_def::mark_smob (SCM smob)
85 {
86   Context_def *me = (Context_def *) SCM_CELL_WORD_1 (smob);
87
88   scm_gc_mark (me->description_);
89   scm_gc_mark (me->context_aliases_);
90   scm_gc_mark (me->accept_mods_);
91   scm_gc_mark (me->translator_mods_);
92   scm_gc_mark (me->property_ops_);
93   scm_gc_mark (me->translator_group_type_);
94   scm_gc_mark (me->default_child_);
95
96   return me->context_name_;
97 }
98
99 void
100 Context_def::add_context_mod (SCM mod)
101 {
102   SCM tag = scm_car (mod);
103   if (ly_symbol2scm ("description") == tag)
104     {
105       description_ = scm_cadr (mod);
106       return;
107     }
108
109   /*
110     other modifiers take symbols as argument.
111   */
112   SCM sym = scm_cadr (mod);
113   if (scm_is_string (sym))
114     sym = scm_string_to_symbol (sym);
115
116   if (ly_symbol2scm ("default-child") == tag)
117     default_child_ = sym;
118   else if (ly_symbol2scm ("consists") == tag
119            || ly_symbol2scm ("consists-end") == tag
120            || ly_symbol2scm ("remove") == tag)
121     {
122       if (!get_translator (sym))
123         error (_f ("program has no such type: `%s'",
124                    ly_symbol2string (sym).c_str ()));
125       else
126         translator_mods_ = scm_cons (scm_list_2 (tag, sym), translator_mods_);
127     }
128   else if (ly_symbol2scm ("accepts") == tag
129            || ly_symbol2scm ("denies") == tag)
130     accept_mods_ = scm_cons (scm_list_2 (tag, sym), accept_mods_);
131   else if (ly_symbol2scm ("pop") == tag
132            || ly_symbol2scm ("push") == tag
133            || ly_symbol2scm ("assign") == tag
134            || ly_symbol2scm ("unset") == tag)
135     property_ops_ = scm_cons (mod, property_ops_);
136   else if (ly_symbol2scm ("alias") == tag)
137     context_aliases_ = scm_cons (sym, context_aliases_);
138   else if (ly_symbol2scm ("translator-type") == tag)
139     translator_group_type_ = sym;
140   else if (ly_symbol2scm ("context-name") == tag)
141     context_name_ = sym;
142   else
143     programming_error ("unknown context mod tag");
144 }
145
146 SCM
147 Context_def::get_context_name () const
148 {
149   return context_name_;
150 }
151
152 SCM
153 Context_def::get_accepted (SCM user_mod) const
154 {
155   SCM mods = scm_reverse_x (scm_list_copy (accept_mods_), user_mod);
156   SCM acc = SCM_EOL;
157   for (SCM s = mods; scm_is_pair (s); s = scm_cdr (s))
158     {
159       SCM tag = scm_caar (s);
160       SCM sym = scm_cadar (s);
161       if (tag == ly_symbol2scm ("accepts"))
162         acc = scm_cons (sym, acc);
163       else if (tag == ly_symbol2scm ("denies"))
164         acc = scm_delete_x (sym, acc);
165     }
166
167   SCM def = get_default_child (user_mod);
168   if (scm_is_symbol (def))
169     {
170       if (scm_memq (def, acc))
171         acc = scm_delete_x (def, acc);
172       acc = scm_cons (def, acc);
173     }
174
175   return acc;
176 }
177
178 SCM
179 Context_def::get_default_child (SCM user_mod) const
180 {
181   SCM name = default_child_;
182   for (SCM s = user_mod; scm_is_pair (s); s = scm_cdr (s))
183     {
184       SCM entry = scm_car (s);
185       if (scm_car (entry) == ly_symbol2scm ("default-child"))
186         {
187           name = scm_cadr (entry);
188           break;
189         }
190     }
191
192   return name;
193 }
194
195 vector<Context_def*>
196 Context_def::path_to_acceptable_context (SCM type_sym, Output_def *odef) const
197 {
198   assert (scm_is_symbol (type_sym));
199
200   SCM accepted = get_accepted (SCM_EOL);
201
202   vector<Context_def*> accepteds;
203   for (SCM s = accepted; scm_is_pair (s); s = scm_cdr (s))
204     if (Context_def *t = unsmob_context_def (find_context_def (odef,
205                                                                scm_car (s))))
206       accepteds.push_back (t);
207
208   vector<Context_def*> best_result;
209   for (vsize i = 0; i < accepteds.size (); i++)
210     {
211       /* do not check aliases, because \context Staff should not
212          create RhythmicStaff. */
213       if (ly_is_equal (accepteds[i]->get_context_name (), type_sym))
214         {
215           best_result.push_back (accepteds[i]);
216           return best_result;
217         }
218     }
219
220   vsize best_depth = INT_MAX;
221   for (vsize i = 0; i < accepteds.size (); i++)
222     {
223       Context_def *g = accepteds[i];
224
225       vector<Context_def*> result
226         = g->path_to_acceptable_context (type_sym, odef);
227       if (result.size () && result.size () < best_depth)
228         {
229           best_depth = result.size ();
230           result.insert (result.begin (), g);
231           best_result = result;
232         }
233     }
234
235   return best_result;
236 }
237
238 SCM
239 Context_def::get_translator_names (SCM user_mod) const
240 {
241   SCM l1 = SCM_EOL;
242
243   SCM mods = scm_reverse_x (scm_list_copy (translator_mods_), user_mod);
244
245   for (SCM s = mods; scm_is_pair (s); s = scm_cdr (s))
246     {
247       SCM tag = scm_caar (s);
248       SCM arg = scm_cadar (s);
249
250       if (scm_is_string (arg))
251         arg = scm_string_to_symbol (arg);
252
253       if (ly_symbol2scm ("consists") == tag)
254         l1 = scm_cons (arg, l1);
255       else if (ly_symbol2scm ("remove") == tag)
256         l1 = scm_delete_x (arg, l1);
257     }
258
259   return l1;
260 }
261
262 SCM
263 filter_performers (SCM ell)
264 {
265   SCM *tail = &ell;
266   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
267     {
268       if (dynamic_cast<Performer *> (unsmob_translator (scm_car (*tail))))
269         *tail = scm_cdr (*tail);
270       else
271         tail = SCM_CDRLOC (*tail);
272     }
273   return ell;
274 }
275
276 SCM
277 filter_engravers (SCM ell)
278 {
279   SCM *tail = &ell;
280   for (SCM p = ell; scm_is_pair (p); p = scm_cdr (p))
281     {
282       if (dynamic_cast<Engraver *> (unsmob_translator (scm_car (*tail))))
283         *tail = scm_cdr (*tail);
284       else
285         tail = SCM_CDRLOC (*tail);
286     }
287   return ell;
288 }
289
290 Context *
291 Context_def::instantiate (SCM ops, Object_key const *key)
292 {
293   Context *context = 0;
294
295   if (context_name_ == ly_symbol2scm ("Score"))
296     context = new Score_context (key);
297   else
298     context = new Context (key);
299
300   context->definition_ = self_scm ();
301   context->definition_mods_ = ops;
302
303   SCM trans_names = get_translator_names (ops);
304
305   Translator_group *g = get_translator_group (translator_group_type_);
306   SCM trans_list = SCM_EOL;
307
308   for (SCM s = trans_names; scm_is_pair (s); s = scm_cdr (s))
309     {
310       Translator *t = get_translator (scm_car (s));
311       if (!t)
312         warning (_f ("can't find: `%s'", ly_symbol2string (scm_car (s)).c_str ()));
313       else
314         {
315           Translator *tr = t->clone ();
316           SCM str = tr->self_scm ();
317
318           if (tr->must_be_last ())
319             {
320               SCM cons = scm_cons (str, SCM_EOL);
321               if (scm_is_pair (trans_list))
322                 scm_set_cdr_x (scm_last_pair (trans_list), cons);
323               else
324                 trans_list = cons;
325             }
326           else
327             trans_list = scm_cons (str, trans_list);
328
329           tr->daddy_context_ = context;
330           tr->unprotect ();
331         }
332     }
333
334   /*
335     Ugh,  todo: should just make a private
336     copy of Context_def with the user mods.
337   */
338
339   g->simple_trans_list_ = trans_list;
340
341   context->implementation_ = g;
342   if (dynamic_cast<Engraver_group *> (g))
343     g->simple_trans_list_ = filter_performers (g->simple_trans_list_);
344   else if (dynamic_cast<Performer_group *> (g))
345     g->simple_trans_list_ = filter_engravers (g->simple_trans_list_);
346
347   context->aliases_ = context_aliases_;
348   g->connect_to_context (context);
349   g->unprotect ();
350
351   context->accepts_list_ = get_accepted (ops);
352
353   return context;
354 }
355
356 SCM
357 Context_def::clone_scm () const
358 {
359   Context_def *t = new Context_def (*this);
360   return t->unprotect ();
361 }
362
363 SCM
364 Context_def::make_scm ()
365 {
366   Context_def *t = new Context_def;
367   return t->unprotect ();
368 }
369
370 void
371 Context_def::apply_default_property_operations (Context *tg)
372 {
373   apply_property_operations (tg, property_ops_);
374 }
375
376 SCM
377 Context_def::to_alist () const
378 {
379   SCM ell = SCM_EOL;
380
381   ell = scm_cons (scm_cons (ly_symbol2scm ("consists"),
382                             get_translator_names (SCM_EOL)), ell);
383   ell = scm_cons (scm_cons (ly_symbol2scm ("description"), description_), ell);
384   ell = scm_cons (scm_cons (ly_symbol2scm ("aliases"), context_aliases_), ell);
385   ell = scm_cons (scm_cons (ly_symbol2scm ("accepts"), get_accepted (SCM_EOL)),
386                   ell);
387   ell = scm_cons (scm_cons (ly_symbol2scm ("property-ops"), property_ops_),
388                   ell);
389   ell = scm_cons (scm_cons (ly_symbol2scm ("context-name"), context_name_),
390                   ell);
391
392   if (scm_is_symbol (translator_group_type_))
393     ell = scm_cons (scm_cons (ly_symbol2scm ("group-type"),
394                               translator_group_type_), ell);
395   return ell;
396 }
397