]> git.donarmstrong.com Git - lilypond.git/blob - lily/context.cc
Merge branch 'master' of ssh+git://git.sv.gnu.org/srv/git/lilypond
[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--2008 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "context.hh"
10
11 #include "context-def.hh"
12 #include "dispatcher.hh"
13 #include "global-context.hh"
14 #include "international.hh"
15 #include "ly-smobs.icc"
16 #include "main.hh"
17 #include "output-def.hh"
18 #include "profile.hh"
19 #include "program-option.hh"
20 #include "scm-hash.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 *ctx = unsmob_context (scm_car (p));
37
38       ctx->check_removal ();
39       if (ctx->is_removable ())
40         {
41           recurse_over_translators (ctx, &Translator::finalize,
42                                     &Translator_group::finalize,
43                                     UP);
44           send_stream_event (ctx, "RemoveContext", 0, 0);
45         }
46     }
47 }
48
49 Context::Context (Context const & /* src */)
50 {
51   assert (false);
52 }
53
54 Scheme_hash_table *
55 Context::properties_dict () const
56 {
57   return Scheme_hash_table::unsmob (properties_scm_);
58 }
59
60 void
61 Context::add_context (Context *child)
62 {
63   context_list_ = ly_append2 (context_list_,
64                               scm_cons (child->self_scm (), SCM_EOL));
65
66   child->daddy_context_ = this;
67   this->events_below_->register_as_listener (child->events_below_);
68 }
69
70
71 Context::Context ()
72 {
73   daddy_context_ = 0;
74   aliases_ = SCM_EOL;
75   iterator_count_ = 0;
76   implementation_ = 0;
77   properties_scm_ = SCM_EOL;
78   accepts_list_ = SCM_EOL;
79   context_list_ = SCM_EOL;
80   definition_ = SCM_EOL;
81   definition_mods_ = SCM_EOL;
82   event_source_ = 0;
83   events_below_ = 0;
84
85   smobify_self ();
86
87   Scheme_hash_table *tab = new Scheme_hash_table;
88   properties_scm_ = tab->unprotect ();
89   event_source_ = new Dispatcher ();
90   event_source_->unprotect ();
91   events_below_ = new Dispatcher ();
92   events_below_->unprotect ();
93 }
94
95 /* TODO:  this shares code with find_create_context ().  */
96 Context *
97 Context::create_unique_context (SCM name, string id, SCM operations)
98 {
99   /*
100     Don't create multiple score contexts.
101   */
102   Global_context *gthis = dynamic_cast<Global_context *> (this);
103   if (gthis && gthis->get_score_context ())
104     return gthis->get_score_context ()->create_unique_context (name, id, operations);
105
106   vector<Context_def*> path = path_to_acceptable_context (name);
107   if (path.size ())
108     {
109       Context *current = this;
110
111       // Iterate through the path and create all of the implicit contexts.
112       for (vsize i = 0; i < path.size (); i++)
113         {
114           SCM ops = SCM_EOL;
115           string id_str = "\\new";
116           if (i == path.size () - 1)
117             {
118               ops = operations;
119               id_str = id;
120             }
121           current = current->create_context (path[i],
122                                              id_str,
123                                              ops);
124         }
125
126       return current;
127     }
128
129   /*
130     Don't go up to Global_context, because global goes down to the
131     Score context
132   */
133   Context *ret = 0;
134   if (daddy_context_ && !dynamic_cast<Global_context *> (daddy_context_))
135     ret = daddy_context_->create_unique_context (name, id, operations);
136   else
137     {
138       warning (_f ("cannot find or create new `%s'",
139                    ly_symbol2string (name).c_str ()));
140       ret = 0;
141     }
142   return ret;
143 }
144
145 Context *
146 Context::find_create_context (SCM n, string id, SCM operations)
147 {
148   /*
149     Don't create multiple score contexts.
150   */
151   Global_context *gthis = dynamic_cast<Global_context *> (this);
152   if (gthis && gthis->get_score_context ())
153     return gthis->get_score_context ()->find_create_context (n, id, operations);
154
155   if (Context *existing = find_context_below (this, n, id))
156     return existing;
157
158   if (n == ly_symbol2scm ("Bottom"))
159     {
160       Context *tg = get_default_interpreter ();
161       return tg;
162     }
163
164   vector<Context_def*> path = path_to_acceptable_context (n);
165
166   if (path.size ())
167     {
168       Context *current = this;
169
170       // start at 1.  The first one (index 0) will be us.
171       for (vsize i = 0; i < path.size (); i++)
172         {
173           SCM ops = (i == path.size () -1) ? operations : SCM_EOL;
174
175           string this_id = "";
176           if (i == path.size () -1)
177             this_id = id;
178
179           current = current->create_context (path[i],
180                                              this_id,
181                                              ops);
182         }
183
184       return current;
185     }
186
187   /*
188     Don't go up to Global_context, because global goes down to the
189     Score context
190   */
191   Context *ret = 0;
192   if (daddy_context_ && !dynamic_cast<Global_context *> (daddy_context_))
193     ret = daddy_context_->find_create_context (n, id, operations);
194   else
195     {
196       warning (_f ("cannot find or create `%s' called `%s'",
197                    ly_symbol2string (n).c_str (), id));
198       ret = 0;
199     }
200   return ret;
201 }
202
203 IMPLEMENT_LISTENER (Context, acknowledge_infant);
204 void
205 Context::acknowledge_infant (SCM sev)
206 {
207   infant_event_ = unsmob_stream_event (sev);
208 }
209
210 IMPLEMENT_LISTENER (Context, set_property_from_event);
211 void
212 Context::set_property_from_event (SCM sev)
213 {
214   Stream_event *ev = unsmob_stream_event (sev);
215   
216   SCM sym = ev->get_property ("symbol");
217   if (scm_is_symbol (sym))
218     {
219       SCM val = ev->get_property ("value");
220       bool ok = true;
221       if (val != SCM_EOL)
222         ok = type_check_assignment (sym, val, ly_symbol2scm ("translation-type?"));
223       if (ok)
224         set_property (sym, val);
225     }
226 }
227
228 IMPLEMENT_LISTENER (Context, unset_property_from_event);
229 void
230 Context::unset_property_from_event (SCM sev)
231 {
232   Stream_event *ev = unsmob_stream_event (sev);
233   
234   SCM sym = ev->get_property ("symbol");
235   type_check_assignment (sym, SCM_EOL, ly_symbol2scm ("translation-type?"));
236   unset_property (sym);
237 }
238
239 /*
240   Creates a new context from a CreateContext event, and sends an
241   AnnounceNewContext event to this context.
242 */
243 IMPLEMENT_LISTENER (Context, create_context_from_event);
244 void
245 Context::create_context_from_event (SCM sev)
246 {
247   Stream_event *ev = unsmob_stream_event (sev);
248   
249   string id = ly_scm2string (ev->get_property ("id"));
250   SCM ops = ev->get_property ("ops");
251   SCM type_scm = ev->get_property ("type");
252   string type = ly_symbol2string (type_scm);
253   
254   vector<Context_def*> path = path_to_acceptable_context (type_scm);
255
256   if (path.size () != 1)
257     {
258       programming_error (_f ("Invalid CreateContext event: Cannot create %s context", type.c_str ()));
259       return;
260     }
261   Context_def *cdef = path[0];
262   
263   Context *new_context = cdef->instantiate (ops);
264
265   new_context->id_string_ = id;
266   
267   /* Register various listeners:
268       - Make the new context hear events that universally affect contexts
269       - connect events_below etc. properly */
270   /* We want to be the first ones to hear our own events. Therefore, wait
271      before registering events_below_ */
272   new_context->event_source ()->
273     add_listener (GET_LISTENER (new_context->create_context_from_event),
274                   ly_symbol2scm ("CreateContext"));
275   new_context->event_source ()->
276     add_listener (GET_LISTENER (new_context->remove_context),
277                   ly_symbol2scm ("RemoveContext"));
278   new_context->event_source ()->
279     add_listener (GET_LISTENER (new_context->change_parent),
280                   ly_symbol2scm ("ChangeParent"));
281   new_context->event_source ()->
282     add_listener (GET_LISTENER (new_context->set_property_from_event),
283                   ly_symbol2scm ("SetProperty"));
284   new_context->event_source ()->
285     add_listener (GET_LISTENER (new_context->unset_property_from_event),
286                   ly_symbol2scm ("UnsetProperty"));
287
288   new_context->events_below_->register_as_listener (new_context->event_source_);
289   this->add_context (new_context);
290
291   new_context->unprotect ();
292
293   Context_def *td = unsmob_context_def (new_context->definition_);
294
295   /* This cannot move before add_context (), because \override
296      operations require that we are in the hierarchy.  */
297   td->apply_default_property_operations (new_context);
298   apply_property_operations (new_context, ops);
299
300   send_stream_event (this, "AnnounceNewContext", 0,
301                      ly_symbol2scm ("context"), new_context->self_scm (),
302                      ly_symbol2scm ("creator"), sev);
303 }
304
305 vector<Context_def*>
306 Context::path_to_acceptable_context (SCM name) const
307 {
308   // The 'accepts elements in definition_mods_ is a list of ('accepts string),
309   // but the Context_def expects to see elements of the form ('accepts symbol).
310   SCM accepts = SCM_EOL;
311   for (SCM s = scm_reverse (definition_mods_); scm_is_pair (s); s = scm_cdr (s))
312     if (scm_caar (s) == ly_symbol2scm ("accepts"))
313       {
314         SCM elt = scm_list_2 (scm_caar (s), scm_string_to_symbol (scm_cadar (s)));
315         accepts = scm_cons (elt, accepts);
316       }
317
318   return unsmob_context_def (definition_)->path_to_acceptable_context (name,
319                                                                        get_output_def (),
320                                                                        accepts);
321                                                                        
322 }
323
324 Context *
325 Context::create_context (Context_def *cdef,
326                          string id,
327                          SCM ops)
328 {
329   infant_event_ = 0;
330   /* TODO: This is fairly misplaced. We can fix this when we have taken out all
331      iterator specific stuff from the Context class */
332   event_source_->
333     add_listener (GET_LISTENER (acknowledge_infant),
334                   ly_symbol2scm ("AnnounceNewContext"));
335   /* The CreateContext creates a new context, and sends an announcement of the
336      new context through another event. That event will be stored in
337      infant_event_ to create a return value. */
338   send_stream_event (this, "CreateContext", 0,
339                      ly_symbol2scm ("ops"), ops,
340                      ly_symbol2scm ("type"), cdef->get_context_name (),
341                      ly_symbol2scm ("id"), ly_string2scm (id));
342   event_source_->
343     remove_listener (GET_LISTENER (acknowledge_infant),
344                      ly_symbol2scm ("AnnounceNewContext"));
345
346   assert (infant_event_);
347   SCM infant_scm = infant_event_->get_property ("context");
348   Context *infant = unsmob_context (infant_scm);
349
350   if (!infant || infant->get_parent_context () != this)
351     {
352       programming_error ("create_context: can't locate newly created context");
353       return 0;
354     }
355
356   return infant;
357 }
358
359 /*
360   Default child context as a SCM string, or something else if there is
361   none.
362 */
363 SCM
364 Context::default_child_context_name () const
365 {
366   return scm_is_pair (accepts_list_)
367     ? scm_car (accepts_list_)
368     : SCM_EOL;
369 }
370
371 bool
372 Context::is_bottom_context () const
373 {
374   return !scm_is_symbol (default_child_context_name ());
375 }
376
377 Context *
378 Context::get_default_interpreter ()
379 {
380   if (!is_bottom_context ())
381     {
382       SCM nm = default_child_context_name ();
383       SCM st = find_context_def (get_output_def (), nm);
384
385       string name = ly_symbol2string (nm);
386       Context_def *t = unsmob_context_def (st);
387       if (!t)
388         {
389           warning (_f ("cannot find or create: `%s'", name.c_str ()));
390           t = unsmob_context_def (this->definition_);
391         }
392
393       Context *tg = create_context (t, "", SCM_EOL);
394       return tg->get_default_interpreter ();
395     }
396   return this;
397 }
398
399 /*
400   PROPERTIES
401 */
402 Context *
403 Context::where_defined (SCM sym, SCM *value) const
404 {
405 #ifndef NDEBUG
406   if (profile_property_accesses)
407     note_property_access (&context_property_lookup_table, sym);
408 #endif
409
410   if (properties_dict ()->try_retrieve (sym, value))
411     return (Context *)this;
412
413   return (daddy_context_) ? daddy_context_->where_defined (sym, value) : 0;
414 }
415
416 /*
417   return SCM_EOL when not found.
418 */
419 SCM
420 Context::internal_get_property (SCM sym) const
421 {
422 #ifndef NDEBUG
423   if (profile_property_accesses)
424     note_property_access (&context_property_lookup_table, sym);
425 #endif
426
427   SCM val = SCM_EOL;
428   if (properties_dict ()->try_retrieve (sym, &val))
429     return val;
430
431   if (daddy_context_)
432     return daddy_context_->internal_get_property (sym);
433
434   return val;
435 }
436
437 /*
438 Called by the send_stream_event macro. props is a 0-terminated array of
439 properties and corresponding values, interleaved. This method should not
440 be called from any other place than the send_stream_event macro.
441 */
442 void
443 Context::internal_send_stream_event (SCM type, Input *origin, SCM props[])
444 {
445   Stream_event *e = new Stream_event (type, origin);
446   for (int i = 0; props[i]; i += 2)
447     {
448       e->set_property (props[i], props[i+1]);
449     }
450   event_source_->broadcast (e);
451   e->unprotect ();
452 }
453
454 bool
455 Context::is_alias (SCM sym) const
456 {
457   if (sym == ly_symbol2scm ("Bottom")
458       && !scm_is_pair (accepts_list_))
459     return true;
460   if (sym == unsmob_context_def (definition_)->get_context_name ())
461     return true;
462
463   return scm_c_memq (sym, aliases_) != SCM_BOOL_F;
464 }
465
466 void
467 Context::add_alias (SCM sym)
468 {
469   aliases_ = scm_cons (sym, aliases_);
470 }
471
472 /* we don't (yet) instrument context properties */
473 void
474 Context::instrumented_set_property (SCM sym, SCM val, const char*, int, const char*)
475 {
476   internal_set_property (sym, val);
477 }
478
479 void
480 Context::internal_set_property (SCM sym, SCM val)
481 {
482   if (do_internal_type_checking_global)
483     assert (type_check_assignment (sym, val, ly_symbol2scm ("translation-type?")));
484
485   properties_dict ()->set (sym, val);
486 }
487
488 /*
489   TODO: look up to check whether we have inherited var?
490 */
491 void
492 Context::unset_property (SCM sym)
493 {
494   properties_dict ()->remove (sym);
495 }
496
497 IMPLEMENT_LISTENER (Context, change_parent);
498 void
499 Context::change_parent (SCM sev)
500 {
501   Stream_event *ev = unsmob_stream_event (sev);
502   Context *to = unsmob_context (ev->get_property ("context"));
503
504   disconnect_from_parent ();
505   to->add_context (this);
506 }
507
508 /*
509   Die. The next GC sweep should take care of the actual death.
510  */
511 IMPLEMENT_LISTENER (Context, remove_context);
512 void
513 Context::remove_context (SCM)
514 {
515   /* ugh, the translator group should listen to RemoveContext events by itself */
516   Translator_group *impl = implementation ();
517   if (impl)
518     impl->disconnect_from_context ();
519   disconnect_from_parent ();
520 }
521
522 void
523 Context::disconnect_from_parent ()
524 {
525   daddy_context_->events_below_->unregister_as_listener (this->events_below_);
526   daddy_context_->context_list_ = scm_delq_x (this->self_scm (), daddy_context_->context_list_);
527   daddy_context_ = 0;
528 }
529
530 /*
531   ID == "" means accept any ID.
532 */
533 Context *
534 find_context_below (Context *where,
535                     SCM type, string id)
536 {
537   if (where->is_alias (type))
538     {
539       if (id == "" || where->id_string () == id)
540         return where;
541     }
542
543   Context *found = 0;
544   for (SCM s = where->children_contexts ();
545        !found && scm_is_pair (s); s = scm_cdr (s))
546     {
547       Context *tr = unsmob_context (scm_car (s));
548
549       found = find_context_below (tr, type, id);
550     }
551
552   return found;
553 }
554
555 SCM
556 Context::properties_as_alist () const
557 {
558   return properties_dict ()->to_alist ();
559 }
560
561 SCM
562 Context::context_name_symbol () const
563 {
564   Context_def *td = unsmob_context_def (definition_);
565   return td->get_context_name ();
566 }
567
568 string
569 Context::context_name () const
570 {
571   return ly_symbol2string (context_name_symbol ());
572 }
573
574 Context *
575 Context::get_score_context () const
576 {
577   if (daddy_context_)
578     return daddy_context_->get_score_context ();
579   else
580     return 0;
581 }
582
583 Output_def *
584 Context::get_output_def () const
585 {
586   return daddy_context_ ? daddy_context_->get_output_def () : 0;
587 }
588
589 Context::~Context ()
590 {
591 }
592
593 Moment
594 Context::now_mom () const
595 {
596   Context const *p = this;
597   while (p->daddy_context_)
598     p = p->daddy_context_;
599
600   return p->now_mom ();
601 }
602
603 int
604 Context::print_smob (SCM s, SCM port, scm_print_state *)
605 {
606   Context *sc = (Context *) SCM_CELL_WORD_1 (s);
607
608   scm_puts ("#<", port);
609   scm_puts (sc->class_name (), port);
610   if (Context_def *d = unsmob_context_def (sc->definition_))
611     {
612       scm_puts (" ", port);
613       scm_display (d->get_context_name (), port);
614     }
615
616   if (!sc->id_string_.empty ())
617     {
618       scm_puts ("=", port);
619       scm_puts (sc->id_string_.c_str (), port);
620     }
621
622   scm_puts (" ", port);
623
624   scm_display (sc->context_list_, port);
625   scm_puts (" >", port);
626
627   return 1;
628 }
629
630 SCM
631 Context::mark_smob (SCM sm)
632 {
633   Context *me = (Context *) SCM_CELL_WORD_1 (sm);
634
635   scm_gc_mark (me->context_list_);
636   scm_gc_mark (me->aliases_);
637   scm_gc_mark (me->definition_);
638   scm_gc_mark (me->definition_mods_);
639   scm_gc_mark (me->properties_scm_);
640   scm_gc_mark (me->accepts_list_);
641
642   if (me->implementation_)
643     scm_gc_mark (me->implementation_->self_scm ());
644
645   if (me->event_source_)
646     scm_gc_mark (me->event_source_->self_scm ());
647
648   if (me->events_below_)
649     scm_gc_mark (me->events_below_->self_scm ());
650
651   return me->properties_scm_;
652 }
653
654 IMPLEMENT_SMOBS (Context);
655 IMPLEMENT_DEFAULT_EQUAL_P (Context);
656 IMPLEMENT_TYPE_P (Context, "ly:context?");
657
658 Global_context *
659 Context::get_global_context () const
660 {
661   if (dynamic_cast<Global_context *> ((Context *) this))
662     return dynamic_cast<Global_context *> ((Context *) this);
663
664   if (daddy_context_)
665     return daddy_context_->get_global_context ();
666
667   programming_error ("no Global context");
668   return 0;
669 }
670
671 Context *
672 Context::get_parent_context () const
673 {
674   return daddy_context_;
675 }
676
677 /*
678   Ugh. Where to put this?
679 */
680 Rational
681 measure_length (Context const *context)
682 {
683   SCM l = context->get_property ("measureLength");
684   Rational length (1);
685   if (unsmob_moment (l))
686     length = unsmob_moment (l)->main_part_;
687   return length;
688 }
689
690 Moment
691 measure_position (Context const *context)
692 {
693   SCM sm = context->get_property ("measurePosition");
694
695   Moment m = 0;
696   if (unsmob_moment (sm))
697     {
698       m = *unsmob_moment (sm);
699
700       if (m.main_part_ < Rational (0))
701         {
702           Rational length (measure_length (context));
703           while (m.main_part_ < Rational (0))
704             m.main_part_ += length;
705         }
706     }
707
708   return m;
709 }
710
711 /* Finds the measure position after a note of length DUR that
712    begins at the current measure position. */
713 Moment
714 measure_position (Context const *context, Duration const *dur)
715 {
716   Moment pos = measure_position (context);
717   Rational dur_length = dur ? dur->get_length () : Rational (0);
718
719   Moment end_pos = pos.grace_part_ < Rational(0)
720     ? Moment(pos.main_part_, pos.grace_part_ + dur_length)
721     : Moment(pos.main_part_ + dur_length, 0);
722
723   return end_pos;
724 }
725
726 int
727 measure_number (Context const *context)
728 {
729   SCM barnum = context->get_property ("internalBarNumber");
730   SCM smp = context->get_property ("measurePosition");
731
732   int bn = robust_scm2int (barnum, 0);
733   Moment mp = robust_scm2moment (smp, Moment (0));
734   if (mp.main_part_ < Rational (0))
735     bn--;
736
737   return bn;
738 }
739
740
741 void
742 set_context_property_on_children (Context *trans, SCM sym, SCM val)
743 {
744   trans->set_property (sym, ly_deep_copy (val));
745   for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
746     {
747       Context *trg = unsmob_context (scm_car (p));
748       set_context_property_on_children (trg, sym, ly_deep_copy (val));
749     }
750 }
751
752 bool
753 melisma_busy (Context *tr)
754 {
755   SCM melisma_properties = tr->get_property ("melismaBusyProperties");
756   bool busy = false;
757
758   for (; !busy && scm_is_pair (melisma_properties);
759        melisma_properties = scm_cdr (melisma_properties))
760     busy = busy || to_boolean (tr->internal_get_property (scm_car (melisma_properties)));
761
762   return busy;
763 }