]> git.donarmstrong.com Git - lilypond.git/blob - lily/context.cc
Fix some bugs in the dynamic engraver and PostScript backend
[lilypond.git] / lily / context.cc
1 /*
2   context.cc -- implement Context
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2004--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "context.hh"
10
11 #include "context-def.hh"
12 #include "dispatcher.hh"
13 #include "international.hh"
14 #include "ly-smobs.icc"
15 #include "main.hh"
16 #include "output-def.hh"
17 #include "profile.hh"
18 #include "program-option.hh"
19 #include "scm-hash.hh"
20 #include "score-context.hh"
21 #include "translator-group.hh"
22 #include "warn.hh"
23
24 bool
25 Context::is_removable () const
26 {
27   return context_list_ == SCM_EOL && ! iterator_count_
28     && !dynamic_cast<Global_context const *> (daddy_context_);
29 }
30
31 void
32 Context::check_removal ()
33 {
34   for (SCM p = context_list_; scm_is_pair (p); p = scm_cdr (p))
35     {
36       Context *trg = unsmob_context (scm_car (p));
37
38       trg->check_removal ();
39       if (trg->is_removable ())
40         {
41           recurse_over_translators (trg, &Translator::finalize,
42                                     &Translator_group::finalize,
43                                     UP);
44           remove_context (trg);
45         }
46     }
47 }
48
49 Context::Context (Context const &src)
50   : key_manager_ (src.key_manager_)
51 {
52   assert (false);
53 }
54
55 Scheme_hash_table *
56 Context::properties_dict () const
57 {
58   return Scheme_hash_table::unsmob (properties_scm_);
59 }
60
61 void
62 Context::add_context (Context *t)
63 {
64   SCM ts = t->self_scm ();
65   context_list_ = ly_append2 (context_list_,
66                               scm_cons (ts, SCM_EOL));
67
68   t->daddy_context_ = this;
69   if (!t->init_)
70     {
71       t->init_ = true;
72
73       t->unprotect ();
74       Context_def *td = unsmob_context_def (t->definition_);
75
76       /* This cannot move before add_context (), because \override
77          operations require that we are in the hierarchy.  */
78       td->apply_default_property_operations (t);
79
80       recurse_over_translators (t,
81                                 &Translator::initialize,
82                                 &Translator_group::initialize,
83                                 DOWN);
84     }
85 }
86
87
88 Context::Context (Object_key const *key)
89   : key_manager_ (key)
90 {
91   daddy_context_ = 0;
92   init_ = false;
93   aliases_ = SCM_EOL;
94   iterator_count_ = 0;
95   implementation_ = 0;
96   properties_scm_ = SCM_EOL;
97   accepts_list_ = SCM_EOL;
98   context_list_ = SCM_EOL;
99   definition_ = SCM_EOL;
100   definition_mods_ = SCM_EOL;
101   unique_ = -1;
102   event_source_ = 0;
103   events_below_ = 0;
104
105   smobify_self ();
106
107   Scheme_hash_table *tab = new Scheme_hash_table;
108   properties_scm_ = tab->unprotect ();
109   event_source_ = new Dispatcher ();
110   event_source_->unprotect ();
111   events_below_ = new Dispatcher ();
112   events_below_->unprotect ();
113
114   /*
115     UGH UGH
116     const correctness.
117   */
118   key_manager_.unprotect();
119 }
120
121 /* TODO:  this shares code with find_create_context ().  */
122 Context *
123 Context::create_unique_context (SCM name, string id, SCM operations)
124 {
125   /*
126     Don't create multiple score contexts.
127   */
128   Global_context *gthis = dynamic_cast<Global_context *> (this);
129   if (gthis && gthis->get_score_context ())
130     return gthis->get_score_context ()->create_unique_context (name, id, operations);
131
132   /*
133     TODO: use accepts_list_.
134   */
135   vector<Context_def*> path
136     = unsmob_context_def (definition_)->path_to_acceptable_context (name, get_output_def ());
137
138   if (path.size ())
139     {
140       Context *current = this;
141
142       // start at 1.  The first one (index 0) will be us.
143       for (vsize i = 0; i < path.size (); i++)
144         {
145           SCM ops = SCM_EOL;
146           string id_str = "\\new";
147           if (i == path.size () - 1)
148             {
149               ops = operations;
150               id_str = id;
151             }
152           current = current->create_context (path[i],
153                                              id_str,
154                                              ops);
155         }
156
157       return current;
158     }
159
160   /*
161     Don't go up to Global_context, because global goes down to
162     Score_context
163   */
164   Context *ret = 0;
165   if (daddy_context_ && !dynamic_cast<Global_context *> (daddy_context_))
166     ret = daddy_context_->create_unique_context (name, id, operations);
167   else
168     {
169       warning (_f ("can't find or create new `%s'",
170                    ly_symbol2string (name).c_str ()));
171       ret = 0;
172     }
173   return ret;
174 }
175
176 Context *
177 Context::find_create_context (SCM n, string id, SCM operations)
178 {
179   /*
180     Don't create multiple score contexts.
181   */
182   Global_context *gthis = dynamic_cast<Global_context *> (this);
183   if (gthis && gthis->get_score_context ())
184     return gthis->get_score_context ()->find_create_context (n, id, operations);
185
186   if (Context *existing = find_context_below (this, n, id))
187     return existing;
188
189   if (n == ly_symbol2scm ("Bottom"))
190     {
191       Context *tg = get_default_interpreter ();
192       return tg;
193     }
194
195   /*
196     TODO: use accepts_list_.
197   */
198   vector<Context_def*> path
199     = unsmob_context_def (definition_)->path_to_acceptable_context (n, get_output_def ());
200
201   if (path.size ())
202     {
203       Context *current = this;
204
205       // start at 1.  The first one (index 0) will be us.
206       for (vsize i = 0; i < path.size (); i++)
207         {
208           SCM ops = (i == path.size () -1) ? operations : SCM_EOL;
209
210           string this_id = "";
211           if (i == path.size () -1)
212             this_id = id;
213
214           current = current->create_context (path[i],
215                                              this_id,
216                                              ops);
217         }
218
219       return current;
220     }
221
222   /*
223     Don't go up to Global_context, because global goes down to
224     Score_context
225   */
226   Context *ret = 0;
227   if (daddy_context_ && !dynamic_cast<Global_context *> (daddy_context_))
228     ret = daddy_context_->find_create_context (n, id, operations);
229   else
230     {
231       warning (_f ("can't find or create `%s' called `%s'",
232                    ly_symbol2string (n).c_str (), id));
233       ret = 0;
234     }
235   return ret;
236 }
237
238 Context *
239 Context::create_context (Context_def *cdef,
240                          string id,
241                          SCM ops)
242 {
243   int unique = get_global_context()->new_unique();
244
245   // TODO: The following should be carried out by a listener.
246   string type = ly_symbol2string (cdef->get_context_name ());
247   Object_key const *key = key_manager_.get_context_key (now_mom(), type, id);
248   Context *new_context
249     = cdef->instantiate (ops, key);
250
251   new_context->id_string_ = id;
252   new_context->unique_ = unique;
253   
254   new_context->events_below_->register_as_listener (new_context->event_source_);
255   
256   add_context (new_context);
257   apply_property_operations (new_context, ops);
258   events_below_->register_as_listener (new_context->events_below_);
259
260   // TODO: The above operations should be performed by a listener to the following event.
261   send_stream_event (this, "CreateContext",
262                      ly_symbol2scm ("unique"), scm_int2num (unique),
263                      ly_symbol2scm ("ops"), ops,
264                      ly_symbol2scm ("type"), cdef->get_context_name (),
265                      ly_symbol2scm ("id"), scm_makfrom0str (id.c_str ()));
266
267   return new_context;
268 }
269
270 /*
271   Default child context as a SCM string, or something else if there is
272   none.
273 */
274 SCM
275 Context::default_child_context_name () const
276 {
277   return scm_is_pair (accepts_list_)
278     ? scm_car (accepts_list_)
279     : SCM_EOL;
280 }
281
282 bool
283 Context::is_bottom_context () const
284 {
285   return !scm_is_symbol (default_child_context_name ());
286 }
287
288 Context *
289 Context::get_default_interpreter ()
290 {
291   if (!is_bottom_context ())
292     {
293       SCM nm = default_child_context_name ();
294       SCM st = find_context_def (get_output_def (), nm);
295
296       string name = ly_symbol2string (nm);
297       Context_def *t = unsmob_context_def (st);
298       if (!t)
299         {
300           warning (_f ("can't find or create: `%s'", name.c_str ()));
301           t = unsmob_context_def (this->definition_);
302         }
303
304       Context *tg = create_context (t, "", SCM_EOL);
305       return tg->get_default_interpreter ();
306     }
307   return this;
308 }
309
310 /*
311   PROPERTIES
312 */
313 Context *
314 Context::where_defined (SCM sym, SCM *value) const
315 {
316 #ifndef NDEBUG
317   if (profile_property_accesses)
318     note_property_access (&context_property_lookup_table, sym);
319 #endif
320
321   if (properties_dict ()->try_retrieve (sym, value))
322     return (Context *)this;
323
324   return (daddy_context_) ? daddy_context_->where_defined (sym, value) : 0;
325 }
326
327 /*
328   return SCM_EOL when not found.
329 */
330 SCM
331 Context::internal_get_property (SCM sym) const
332 {
333 #ifndef NDEBUG
334   if (profile_property_accesses)
335     note_property_access (&context_property_lookup_table, sym);
336 #endif
337
338   SCM val = SCM_EOL;
339   if (properties_dict ()->try_retrieve (sym, &val))
340     return val;
341
342   if (daddy_context_)
343     return daddy_context_->internal_get_property (sym);
344
345   return val;
346 }
347
348 /*
349 Called by the send_stream_event macro. props is a 0-terminated array of
350 properties and corresponding values, interleaved. This method should not
351 be called from any other place than the send_stream_event macro.
352 */
353 void
354 Context::internal_send_stream_event (SCM type, SCM props[])
355 {
356   Stream_event *e = new Stream_event (this, type);
357   for (int i = 0; props[i]; i += 2)
358     {
359       e->internal_set_property (props[i], props[i+1]);
360     }
361   event_source_->broadcast (e);
362   e->unprotect ();
363 }
364
365 bool
366 Context::is_alias (SCM sym) const
367 {
368   if (sym == ly_symbol2scm ("Bottom")
369       && !scm_is_pair (accepts_list_))
370     return true;
371   if (sym == unsmob_context_def (definition_)->get_context_name ())
372     return true;
373
374   return scm_c_memq (sym, aliases_) != SCM_BOOL_F;
375 }
376
377 void
378 Context::add_alias (SCM sym)
379 {
380   aliases_ = scm_cons (sym, aliases_);
381 }
382
383 void
384 Context::internal_set_property (SCM sym, SCM val)
385 {
386 #ifndef NDEBUG
387   if (do_internal_type_checking_global)
388     assert (type_check_assignment (sym, val, ly_symbol2scm ("translation-type?")));
389 #endif
390
391   properties_dict ()->set (sym, val);
392 }
393
394 /*
395   TODO: look up to check whether we have inherited var?
396 */
397 void
398 Context::unset_property (SCM sym)
399 {
400   properties_dict ()->remove (sym);
401 }
402
403 /**
404    Remove a context from the hierarchy.
405 */
406 Context *
407 Context::remove_context (Context *trans)
408 {
409   assert (trans);
410
411   context_list_ = scm_delq_x (trans->self_scm (), context_list_);
412   trans->daddy_context_ = 0;
413   return trans;
414 }
415
416
417 /*
418   ID == "" means accept any ID.
419 */
420 Context *
421 find_context_below (Context *where,
422                     SCM type, string id)
423 {
424   if (where->is_alias (type))
425     {
426       if (id == "" || where->id_string () == id)
427         return where;
428     }
429
430   Context *found = 0;
431   for (SCM s = where->children_contexts ();
432        !found && scm_is_pair (s); s = scm_cdr (s))
433     {
434       Context *tr = unsmob_context (scm_car (s));
435
436       found = find_context_below (tr, type, id);
437     }
438
439   return found;
440 }
441
442 Context *
443 find_context_below (Context *where,
444                     int unique)
445 {
446   if (where->get_unique () == unique)
447     return where;
448
449   Context *found = 0;
450   for (SCM s = where->children_contexts ();
451        !found && scm_is_pair (s); s = scm_cdr (s))
452     {
453       Context *tr = unsmob_context (scm_car (s));
454
455       found = find_context_below (tr, unique);
456     }
457
458   return found;
459 }
460
461 SCM
462 Context::properties_as_alist () const
463 {
464   return properties_dict ()->to_alist ();
465 }
466
467 SCM
468 Context::context_name_symbol () const
469 {
470   Context_def *td = unsmob_context_def (definition_);
471   return td->get_context_name ();
472 }
473
474 string
475 Context::context_name () const
476 {
477   return ly_symbol2string (context_name_symbol ());
478 }
479
480 Score_context *
481 Context::get_score_context () const
482 {
483   if (Score_context *sc = dynamic_cast<Score_context *> ((Context *) this))
484     return sc;
485   else if (daddy_context_)
486     return daddy_context_->get_score_context ();
487   else
488     return 0;
489 }
490
491 Output_def *
492 Context::get_output_def () const
493 {
494   return daddy_context_ ? daddy_context_->get_output_def () : 0;
495 }
496
497 Context::~Context ()
498 {
499 }
500
501 Moment
502 Context::now_mom () const
503 {
504   Context const *p = this;
505   while (p->daddy_context_)
506     p = p->daddy_context_;
507
508   return p->now_mom ();
509 }
510
511 int
512 Context::print_smob (SCM s, SCM port, scm_print_state *)
513 {
514   Context *sc = (Context *) SCM_CELL_WORD_1 (s);
515
516   scm_puts ("#<", port);
517   scm_puts (sc->class_name (), port);
518   if (Context_def *d = unsmob_context_def (sc->definition_))
519     {
520       scm_puts (" ", port);
521       scm_display (d->get_context_name (), port);
522     }
523
524   if (!sc->id_string_.empty ())
525     {
526       scm_puts ("=", port);
527       scm_puts (sc->id_string_.c_str (), port);
528     }
529
530   scm_puts (" ", port);
531
532   scm_display (sc->context_list_, port);
533   scm_puts (" >", port);
534
535   return 1;
536 }
537
538 Object_key const *
539 Context::get_grob_key (string name)
540 {
541   return key_manager_.get_grob_key (now_mom (), name);
542 }
543
544 Object_key const *
545 Context::get_context_key (string name, string id)
546 {
547   return key_manager_.get_context_key (now_mom (), name, id);
548 }
549
550 SCM
551 Context::mark_smob (SCM sm)
552 {
553   Context *me = (Context *) SCM_CELL_WORD_1 (sm);
554   me->key_manager_.gc_mark();
555
556   scm_gc_mark (me->context_list_);
557   scm_gc_mark (me->aliases_);
558   scm_gc_mark (me->definition_);
559   scm_gc_mark (me->definition_mods_);
560   scm_gc_mark (me->properties_scm_);
561   scm_gc_mark (me->accepts_list_);
562   if (me->implementation_)
563     scm_gc_mark (me->implementation_->self_scm ());
564   if (me->event_source_) scm_gc_mark (me->event_source_->self_scm ());
565   if (me->events_below_) scm_gc_mark (me->events_below_->self_scm ());
566
567   return me->properties_scm_;
568 }
569
570 IMPLEMENT_SMOBS (Context);
571 IMPLEMENT_DEFAULT_EQUAL_P (Context);
572 IMPLEMENT_TYPE_P (Context, "ly:context?");
573
574 bool
575 Context::try_music (Music *m)
576 {
577   Translator_group *t = implementation ();
578   if (!t)
579     return false;
580
581   bool b = t->try_music (m);
582   if (!b && daddy_context_)
583     b = daddy_context_->try_music (m);
584
585   return b;
586 }
587
588 Global_context *
589 Context::get_global_context () const
590 {
591   if (dynamic_cast<Global_context *> ((Context *) this))
592     return dynamic_cast<Global_context *> ((Context *) this);
593
594   if (daddy_context_)
595     return daddy_context_->get_global_context ();
596
597   programming_error ("no Global context");
598   return 0;
599 }
600
601 Context *
602 Context::get_parent_context () const
603 {
604   return daddy_context_;
605 }
606
607 void
608 Context::clear_key_disambiguations ()
609 {
610   if (!use_object_keys)
611     return;
612
613   key_manager_.clear ();
614   for (SCM s = context_list_; scm_is_pair (s); s = scm_cdr (s))
615     unsmob_context (scm_car (s))->clear_key_disambiguations ();
616 }
617
618 /*
619   Ugh. Where to put this?
620 */
621 Rational
622 measure_length (Context const *context)
623 {
624   SCM l = context->get_property ("measureLength");
625   Rational length (1);
626   if (unsmob_moment (l))
627     length = unsmob_moment (l)->main_part_;
628   return length;
629 }
630
631 Moment
632 measure_position (Context const *context)
633 {
634   SCM sm = context->get_property ("measurePosition");
635
636   Moment m = 0;
637   if (unsmob_moment (sm))
638     {
639       m = *unsmob_moment (sm);
640
641       if (m.main_part_ < Rational (0))
642         {
643           Rational length (measure_length (context));
644           while (m.main_part_ < Rational (0))
645             m.main_part_ += length;
646         }
647     }
648
649   return m;
650 }
651
652
653 void
654 set_context_property_on_children (Context *trans, SCM sym, SCM val)
655 {
656   trans->internal_set_property (sym, ly_deep_copy (val));
657   for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
658     {
659       Context *trg = unsmob_context (scm_car (p));
660       set_context_property_on_children (trg, sym, ly_deep_copy (val));
661     }
662 }
663
664 bool
665 melisma_busy (Context *tr)
666 {
667   SCM melisma_properties = tr->get_property ("melismaBusyProperties");
668   bool busy = false;
669
670   for (; scm_is_pair (melisma_properties);
671        melisma_properties = scm_cdr (melisma_properties))
672     busy = busy || to_boolean (tr->internal_get_property (scm_car (melisma_properties)));
673
674   return busy;
675 }