]> git.donarmstrong.com Git - lilypond.git/blob - lily/context.cc
* lily/context.cc (internal_send_stream_event): thinko fix.
[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                      0);
267
268   return new_context;
269 }
270
271 /*
272   Default child context as a SCM string, or something else if there is
273   none.
274 */
275 SCM
276 Context::default_child_context_name () const
277 {
278   return scm_is_pair (accepts_list_)
279     ? scm_car (accepts_list_)
280     : SCM_EOL;
281 }
282
283 bool
284 Context::is_bottom_context () const
285 {
286   return !scm_is_symbol (default_child_context_name ());
287 }
288
289 Context *
290 Context::get_default_interpreter ()
291 {
292   if (!is_bottom_context ())
293     {
294       SCM nm = default_child_context_name ();
295       SCM st = find_context_def (get_output_def (), nm);
296
297       string name = ly_symbol2string (nm);
298       Context_def *t = unsmob_context_def (st);
299       if (!t)
300         {
301           warning (_f ("can't find or create: `%s'", name.c_str ()));
302           t = unsmob_context_def (this->definition_);
303         }
304
305       Context *tg = create_context (t, "", SCM_EOL);
306       return tg->get_default_interpreter ();
307     }
308   return this;
309 }
310
311 /*
312   PROPERTIES
313 */
314 Context *
315 Context::where_defined (SCM sym, SCM *value) const
316 {
317 #ifndef NDEBUG
318   if (profile_property_accesses)
319     note_property_access (&context_property_lookup_table, sym);
320 #endif
321
322   if (properties_dict ()->try_retrieve (sym, value))
323     return (Context *)this;
324
325   return (daddy_context_) ? daddy_context_->where_defined (sym, value) : 0;
326 }
327
328 /*
329   return SCM_EOL when not found.
330 */
331 SCM
332 Context::internal_get_property (SCM sym) const
333 {
334 #ifndef NDEBUG
335   if (profile_property_accesses)
336     note_property_access (&context_property_lookup_table, sym);
337 #endif
338
339   SCM val = SCM_EOL;
340   if (properties_dict ()->try_retrieve (sym, &val))
341     return val;
342
343   if (daddy_context_)
344     return daddy_context_->internal_get_property (sym);
345
346   return val;
347 }
348
349 void
350 Context::internal_send_stream_event (SCM type, SCM props[])
351 {
352   Stream_event *e = new Stream_event (this, type);
353   for (int i = 0; props[i]; i += 2)
354     {
355       e->internal_set_property (props[i], props[i+1]);
356     }
357   event_source_->broadcast (e);
358   e->unprotect ();
359 }
360
361 bool
362 Context::is_alias (SCM sym) const
363 {
364   if (sym == ly_symbol2scm ("Bottom")
365       && !scm_is_pair (accepts_list_))
366     return true;
367   if (sym == unsmob_context_def (definition_)->get_context_name ())
368     return true;
369
370   return scm_c_memq (sym, aliases_) != SCM_BOOL_F;
371 }
372
373 void
374 Context::add_alias (SCM sym)
375 {
376   aliases_ = scm_cons (sym, aliases_);
377 }
378
379 void
380 Context::internal_set_property (SCM sym, SCM val)
381 {
382 #ifndef NDEBUG
383   if (do_internal_type_checking_global)
384     assert (type_check_assignment (sym, val, ly_symbol2scm ("translation-type?")));
385 #endif
386
387   properties_dict ()->set (sym, val);
388 }
389
390 /*
391   TODO: look up to check whether we have inherited var?
392 */
393 void
394 Context::unset_property (SCM sym)
395 {
396   properties_dict ()->remove (sym);
397 }
398
399 /**
400    Remove a context from the hierarchy.
401 */
402 Context *
403 Context::remove_context (Context *trans)
404 {
405   assert (trans);
406
407   context_list_ = scm_delq_x (trans->self_scm (), context_list_);
408   trans->daddy_context_ = 0;
409   return trans;
410 }
411
412
413 /*
414   ID == "" means accept any ID.
415 */
416 Context *
417 find_context_below (Context *where,
418                     SCM type, string id)
419 {
420   if (where->is_alias (type))
421     {
422       if (id == "" || where->id_string () == id)
423         return where;
424     }
425
426   Context *found = 0;
427   for (SCM s = where->children_contexts ();
428        !found && scm_is_pair (s); s = scm_cdr (s))
429     {
430       Context *tr = unsmob_context (scm_car (s));
431
432       found = find_context_below (tr, type, id);
433     }
434
435   return found;
436 }
437
438 Context *
439 find_context_below (Context *where,
440                     int unique)
441 {
442   if (where->get_unique () == unique)
443     return where;
444
445   Context *found = 0;
446   for (SCM s = where->children_contexts ();
447        !found && scm_is_pair (s); s = scm_cdr (s))
448     {
449       Context *tr = unsmob_context (scm_car (s));
450
451       found = find_context_below (tr, unique);
452     }
453
454   return found;
455 }
456
457 SCM
458 Context::properties_as_alist () const
459 {
460   return properties_dict ()->to_alist ();
461 }
462
463 SCM
464 Context::context_name_symbol () const
465 {
466   Context_def *td = unsmob_context_def (definition_);
467   return td->get_context_name ();
468 }
469
470 string
471 Context::context_name () const
472 {
473   return ly_symbol2string (context_name_symbol ());
474 }
475
476 Score_context *
477 Context::get_score_context () const
478 {
479   if (Score_context *sc = dynamic_cast<Score_context *> ((Context *) this))
480     return sc;
481   else if (daddy_context_)
482     return daddy_context_->get_score_context ();
483   else
484     return 0;
485 }
486
487 Output_def *
488 Context::get_output_def () const
489 {
490   return daddy_context_ ? daddy_context_->get_output_def () : 0;
491 }
492
493 Context::~Context ()
494 {
495 }
496
497 Moment
498 Context::now_mom () const
499 {
500   Context const *p = this;
501   while (p->daddy_context_)
502     p = p->daddy_context_;
503
504   return p->now_mom ();
505 }
506
507 int
508 Context::print_smob (SCM s, SCM port, scm_print_state *)
509 {
510   Context *sc = (Context *) SCM_CELL_WORD_1 (s);
511
512   scm_puts ("#<", port);
513   scm_puts (sc->class_name (), port);
514   if (Context_def *d = unsmob_context_def (sc->definition_))
515     {
516       scm_puts (" ", port);
517       scm_display (d->get_context_name (), port);
518     }
519
520   if (!sc->id_string_.empty ())
521     {
522       scm_puts ("=", port);
523       scm_puts (sc->id_string_.c_str (), port);
524     }
525
526   scm_puts (" ", port);
527
528   scm_display (sc->context_list_, port);
529   scm_puts (" >", port);
530
531   return 1;
532 }
533
534 Object_key const *
535 Context::get_grob_key (string name)
536 {
537   return key_manager_.get_grob_key (now_mom (), name);
538 }
539
540 Object_key const *
541 Context::get_context_key (string name, string id)
542 {
543   return key_manager_.get_context_key (now_mom (), name, id);
544 }
545
546 SCM
547 Context::mark_smob (SCM sm)
548 {
549   Context *me = (Context *) SCM_CELL_WORD_1 (sm);
550   me->key_manager_.gc_mark();
551
552   scm_gc_mark (me->context_list_);
553   scm_gc_mark (me->aliases_);
554   scm_gc_mark (me->definition_);
555   scm_gc_mark (me->definition_mods_);
556   scm_gc_mark (me->properties_scm_);
557   scm_gc_mark (me->accepts_list_);
558   if (me->implementation_)
559     scm_gc_mark (me->implementation_->self_scm ());
560   if (me->event_source_) scm_gc_mark (me->event_source_->self_scm ());
561   if (me->events_below_) scm_gc_mark (me->events_below_->self_scm ());
562
563   return me->properties_scm_;
564 }
565
566 IMPLEMENT_SMOBS (Context);
567 IMPLEMENT_DEFAULT_EQUAL_P (Context);
568 IMPLEMENT_TYPE_P (Context, "ly:context?");
569
570 bool
571 Context::try_music (Music *m)
572 {
573   Translator_group *t = implementation ();
574   if (!t)
575     return false;
576
577   bool b = t->try_music (m);
578   if (!b && daddy_context_)
579     b = daddy_context_->try_music (m);
580
581   return b;
582 }
583
584 Global_context *
585 Context::get_global_context () const
586 {
587   if (dynamic_cast<Global_context *> ((Context *) this))
588     return dynamic_cast<Global_context *> ((Context *) this);
589
590   if (daddy_context_)
591     return daddy_context_->get_global_context ();
592
593   programming_error ("no Global context");
594   return 0;
595 }
596
597 Context *
598 Context::get_parent_context () const
599 {
600   return daddy_context_;
601 }
602
603 void
604 Context::clear_key_disambiguations ()
605 {
606   if (!use_object_keys)
607     return;
608
609   key_manager_.clear ();
610   for (SCM s = context_list_; scm_is_pair (s); s = scm_cdr (s))
611     unsmob_context (scm_car (s))->clear_key_disambiguations ();
612 }
613
614 /*
615   Ugh. Where to put this?
616 */
617 Rational
618 measure_length (Context const *context)
619 {
620   SCM l = context->get_property ("measureLength");
621   Rational length (1);
622   if (unsmob_moment (l))
623     length = unsmob_moment (l)->main_part_;
624   return length;
625 }
626
627 Moment
628 measure_position (Context const *context)
629 {
630   SCM sm = context->get_property ("measurePosition");
631
632   Moment m = 0;
633   if (unsmob_moment (sm))
634     {
635       m = *unsmob_moment (sm);
636
637       if (m.main_part_ < Rational (0))
638         {
639           Rational length (measure_length (context));
640           while (m.main_part_ < Rational (0))
641             m.main_part_ += length;
642         }
643     }
644
645   return m;
646 }
647
648
649 void
650 set_context_property_on_children (Context *trans, SCM sym, SCM val)
651 {
652   trans->internal_set_property (sym, ly_deep_copy (val));
653   for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
654     {
655       Context *trg = unsmob_context (scm_car (p));
656       set_context_property_on_children (trg, sym, ly_deep_copy (val));
657     }
658 }