]> git.donarmstrong.com Git - lilypond.git/blob - lily/context.cc
* The grand 2005-2006 replace.
[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 "program-option.hh"
12 #include "context-def.hh"
13 #include "ly-smobs.icc"
14 #include "main.hh"
15 #include "output-def.hh"
16 #include "scm-hash.hh"
17 #include "score-context.hh"
18 #include "translator-group.hh"
19 #include "warn.hh"
20 #include "lilypond-key.hh"
21 #include "profile.hh"
22
23 bool
24 Context::is_removable () const
25 {
26   return context_list_ == SCM_EOL && ! iterator_count_
27     && !dynamic_cast<Score_context const *> (this);
28 }
29
30 void
31 Context::check_removal ()
32 {
33   for (SCM p = context_list_; scm_is_pair (p); p = scm_cdr (p))
34     {
35       Context *trg = unsmob_context (scm_car (p));
36
37       trg->check_removal ();
38       if (trg->is_removable ())
39         {
40           recurse_over_translators (trg, &Translator::finalize,
41                                     &Translator_group::finalize,
42                                     UP);
43           remove_context (trg);
44         }
45     }
46 }
47
48 Context::Context (Context const &)
49 {
50   assert (false);
51 }
52
53 Scheme_hash_table *
54 Context::properties_dict () const
55 {
56   return Scheme_hash_table::unsmob (properties_scm_);
57 }
58
59 void
60 Context::add_context (Context *t)
61 {
62   SCM ts = t->self_scm ();
63   context_list_ = ly_append2 (context_list_,
64                               scm_cons (ts, SCM_EOL));
65
66   t->daddy_context_ = this;
67   if (!t->init_)
68     {
69       t->init_ = true;
70
71       t->unprotect ();
72       Context_def *td = unsmob_context_def (t->definition_);
73
74       /* This cannot move before add_context (), because \override
75          operations require that we are in the hierarchy.  */
76       td->apply_default_property_operations (t);
77
78       recurse_over_translators (t,
79                                 &Translator::initialize,
80                                 &Translator_group::initialize,
81                                 DOWN);
82     }
83 }
84
85
86 Context::Context (Object_key const *key)
87 {
88   key_ = key;
89   daddy_context_ = 0;
90   init_ = false;
91   aliases_ = SCM_EOL;
92   iterator_count_ = 0;
93   implementation_ = 0;
94   properties_scm_ = SCM_EOL;
95   accepts_list_ = SCM_EOL;
96   context_list_ = SCM_EOL;
97   definition_ = SCM_EOL;
98
99   smobify_self ();
100
101   Scheme_hash_table *tab = new Scheme_hash_table;
102   properties_scm_ = tab->unprotect ();
103
104   /*
105     UGH UGH
106     const correctness.
107   */
108   if (key_)
109     ((Object_key *)key)->unprotect ();
110 }
111
112 /* TODO:  this shares code with find_create_context ().  */
113 Context *
114 Context::create_unique_context (SCM n, SCM operations)
115 {
116   /*
117     Don't create multiple score contexts.
118   */
119   if (dynamic_cast<Global_context *> (this)
120       && dynamic_cast<Global_context *> (this)->get_score_context ())
121     return get_score_context ()->create_unique_context (n, operations);
122
123   /*
124     TODO: use accepts_list_.
125   */
126   Link_array<Context_def> path
127     = unsmob_context_def (definition_)->path_to_acceptable_context (n, get_output_def ());
128
129   if (path.size ())
130     {
131       Context *current = this;
132
133       // start at 1.  The first one (index 0) will be us.
134       for (int i = 0; i < path.size (); i++)
135         {
136           SCM ops = (i == path.size () -1) ? operations : SCM_EOL;
137
138           current = current->create_context (path[i],
139                                              "\\new",
140                                              ops);
141         }
142
143       return current;
144     }
145
146   /*
147     Don't go up to Global_context, because global goes down to
148     Score_context
149   */
150   Context *ret = 0;
151   if (daddy_context_ && !dynamic_cast<Global_context *> (daddy_context_))
152     ret = daddy_context_->create_unique_context (n, operations);
153   else
154     {
155       warning (_f ("can't find or create new `%s'",
156                    ly_symbol2string (n).to_str0 ()));
157       ret = 0;
158     }
159   return ret;
160 }
161
162 Context *
163 Context::find_create_context (SCM n, String id, SCM operations)
164 {
165   /*
166     Don't create multiple score contexts.
167   */
168   if (dynamic_cast<Global_context *> (this)
169       && dynamic_cast<Global_context *> (this)->get_score_context ())
170     return get_score_context ()->find_create_context (n, id, operations);
171
172   if (Context *existing = find_context_below (this, n, id))
173     return existing;
174
175   if (n == ly_symbol2scm ("Bottom"))
176     {
177       Context *tg = get_default_interpreter ();
178       return tg;
179     }
180
181   /*
182     TODO: use accepts_list_.
183   */
184   Link_array<Context_def> path
185     = unsmob_context_def (definition_)->path_to_acceptable_context (n, get_output_def ());
186
187   if (path.size ())
188     {
189       Context *current = this;
190
191       // start at 1.  The first one (index 0) will be us.
192       for (int i = 0; i < path.size (); i++)
193         {
194           SCM ops = (i == path.size () -1) ? operations : SCM_EOL;
195
196           String this_id = "";
197           if (i == path.size () -1)
198             this_id = id;
199
200           current = current->create_context (path[i],
201                                              this_id,
202                                              ops);
203         }
204
205       return current;
206     }
207
208   /*
209     Don't go up to Global_context, because global goes down to
210     Score_context
211   */
212   Context *ret = 0;
213   if (daddy_context_ && !dynamic_cast<Global_context *> (daddy_context_))
214     ret = daddy_context_->find_create_context (n, id, operations);
215   else
216     {
217       warning (_f ("can't find or create `%s' called `%s'",
218                    ly_symbol2string (n).to_str0 (), id));
219       ret = 0;
220     }
221   return ret;
222 }
223
224 Context *
225 Context::create_context (Context_def *cdef,
226                          String id,
227                          SCM ops)
228 {
229   String type = ly_symbol2string (cdef->get_context_name ());
230   Object_key const *key = get_context_key (type, id);
231   Context *new_context
232     = cdef->instantiate (ops, key);
233
234   new_context->id_string_ = id;
235   add_context (new_context);
236   apply_property_operations (new_context, ops);
237
238   return new_context;
239 }
240
241 Object_key const *
242 Context::get_context_key (String type, String id)
243 {
244   if (!use_object_keys)
245     return 0;
246
247   String now_key = type + "@" + id;
248
249   int disambiguation_count = 0;
250   if (context_counts_.find (now_key) != context_counts_.end ())
251     disambiguation_count = context_counts_[now_key];
252
253   context_counts_[now_key] = disambiguation_count + 1;
254
255   return new Lilypond_context_key (key (),
256                                    now_mom (),
257                                    type, id,
258                                    disambiguation_count);
259 }
260
261 Object_key const *
262 Context::get_grob_key (String name)
263 {
264   if (!use_object_keys)
265     return 0;
266
267   int disambiguation_count = 0;
268   if (grob_counts_.find (name) != grob_counts_.end ())
269     disambiguation_count = grob_counts_[name];
270   grob_counts_[name] = disambiguation_count + 1;
271
272   Object_key *k = new Lilypond_grob_key (key (),
273                                          now_mom (),
274                                          name,
275                                          disambiguation_count);
276
277   return k;
278 }
279
280 /*
281   Default child context as a SCM string, or something else if there is
282   none.
283 */
284 SCM
285 Context::default_child_context_name () const
286 {
287   return scm_is_pair (accepts_list_)
288     ? scm_car (accepts_list_)
289     : SCM_EOL;
290 }
291
292 bool
293 Context::is_bottom_context () const
294 {
295   return !scm_is_symbol (default_child_context_name ());
296 }
297
298 Context *
299 Context::get_default_interpreter ()
300 {
301   if (!is_bottom_context ())
302     {
303       SCM nm = default_child_context_name ();
304       SCM st = find_context_def (get_output_def (), nm);
305
306       String name = ly_symbol2string (nm);
307       Context_def *t = unsmob_context_def (st);
308       if (!t)
309         {
310           warning (_f ("can't find or create: `%s'", name.to_str0 ()));
311           t = unsmob_context_def (this->definition_);
312         }
313
314       Context *tg = create_context (t, "", SCM_EOL);
315       if (!tg->is_bottom_context ())
316         return tg->get_default_interpreter ();
317       else
318         return tg;
319     }
320   return this;
321 }
322
323 /*
324   PROPERTIES
325 */
326 Context *
327 Context::where_defined (SCM sym, SCM *value) const
328 {
329 #ifndef NDEBUG
330   if (profile_property_accesses)
331     note_property_access (&context_property_lookup_table, sym);
332 #endif
333
334   if (properties_dict ()->try_retrieve (sym, value))
335     return (Context *)this;
336
337   return (daddy_context_) ? daddy_context_->where_defined (sym, value) : 0;
338 }
339
340 /*
341   return SCM_EOL when not found.
342 */
343 SCM
344 Context::internal_get_property (SCM sym) const
345 {
346 #ifndef NDEBUG
347   if (profile_property_accesses)
348     note_property_access (&context_property_lookup_table, sym);
349 #endif
350
351   SCM val = SCM_EOL;
352   if (properties_dict ()->try_retrieve (sym, &val))
353     return val;
354
355   if (daddy_context_)
356     return daddy_context_->internal_get_property (sym);
357
358   return val;
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   ID == "" means accept any ID.
414 */
415 Context *
416 find_context_below (Context *where,
417                     SCM type, String id)
418 {
419   if (where->is_alias (type))
420     {
421       if (id == "" || where->id_string () == id)
422         return where;
423     }
424
425   Context *found = 0;
426   for (SCM s = where->children_contexts ();
427        !found && scm_is_pair (s); s = scm_cdr (s))
428     {
429       Context *tr = unsmob_context (scm_car (s));
430
431       found = find_context_below (tr, type, id);
432     }
433
434   return found;
435 }
436
437 SCM
438 Context::properties_as_alist () const
439 {
440   return properties_dict ()->to_alist ();
441 }
442
443 SCM
444 Context::context_name_symbol () const
445 {
446   Context_def *td = unsmob_context_def (definition_);
447   return td->get_context_name ();
448 }
449
450 String
451 Context::context_name () const
452 {
453   return ly_symbol2string (context_name_symbol ());
454 }
455
456 Score_context *
457 Context::get_score_context () const
458 {
459   if (Score_context *sc = dynamic_cast<Score_context *> ((Context *) this))
460     return sc;
461   else if (daddy_context_)
462     return daddy_context_->get_score_context ();
463   else
464     return 0;
465 }
466
467 Output_def *
468 Context::get_output_def () const
469 {
470   return daddy_context_ ? daddy_context_->get_output_def () : 0;
471 }
472
473 Context::~Context ()
474 {
475 }
476
477 Moment
478 Context::now_mom () const
479 {
480   Context const *p = this;
481   while (p->daddy_context_)
482     p = p->daddy_context_;
483
484   return p->now_mom ();
485 }
486
487 int
488 Context::print_smob (SCM s, SCM port, scm_print_state *)
489 {
490   Context *sc = (Context *) SCM_CELL_WORD_1 (s);
491
492   scm_puts ("#<", port);
493   scm_puts (sc->class_name (), port);
494   if (Context_def *d = unsmob_context_def (sc->definition_))
495     {
496       scm_puts (" ", port);
497       scm_display (d->get_context_name (), port);
498     }
499
500   if (!sc->id_string_.is_empty ())
501     {
502       scm_puts ("=", port);
503       scm_puts (sc->id_string_.to_str0 (), port);
504     }
505
506   scm_puts (" ", port);
507
508   scm_display (sc->context_list_, port);
509   scm_puts (" >", port);
510
511   return 1;
512 }
513
514 SCM
515 Context::mark_smob (SCM sm)
516 {
517   Context *me = (Context *) SCM_CELL_WORD_1 (sm);
518   if (me->key_)
519     scm_gc_mark (me->key_->self_scm ());
520
521   scm_gc_mark (me->context_list_);
522   scm_gc_mark (me->aliases_);
523   scm_gc_mark (me->definition_);
524   scm_gc_mark (me->properties_scm_);
525   scm_gc_mark (me->accepts_list_);
526   if (me->implementation_)
527     scm_gc_mark (me->implementation_->self_scm ());
528
529   return me->properties_scm_;
530 }
531
532 IMPLEMENT_SMOBS (Context);
533 IMPLEMENT_DEFAULT_EQUAL_P (Context);
534 IMPLEMENT_TYPE_P (Context, "ly:context?");
535
536 bool
537 Context::try_music (Music *m)
538 {
539   Translator_group *t = implementation ();
540   if (!t)
541     return false;
542
543   bool b = t->try_music (m);
544   if (!b && daddy_context_)
545     b = daddy_context_->try_music (m);
546
547   return b;
548 }
549
550 Global_context *
551 Context::get_global_context () const
552 {
553   if (dynamic_cast<Global_context *> ((Context *) this))
554     return dynamic_cast<Global_context *> ((Context *) this);
555
556   if (daddy_context_)
557     return daddy_context_->get_global_context ();
558
559   programming_error ("no Global context");
560   return 0;
561 }
562
563 Context *
564 Context::get_parent_context () const
565 {
566   return daddy_context_;
567 }
568
569 void
570 Context::clear_key_disambiguations ()
571 {
572   if (!use_object_keys)
573     return;
574
575   grob_counts_.clear ();
576   context_counts_.clear ();
577   for (SCM s = context_list_; scm_is_pair (s); s = scm_cdr (s))
578     unsmob_context (scm_car (s))->clear_key_disambiguations ();
579 }
580
581 /*
582   Ugh. Where to put this?
583 */
584 Rational
585 measure_length (Context const *context)
586 {
587   SCM l = context->get_property ("measureLength");
588   Rational length (1);
589   if (unsmob_moment (l))
590     length = unsmob_moment (l)->main_part_;
591   return length;
592 }
593
594 Moment
595 measure_position (Context const *context)
596 {
597   SCM sm = context->get_property ("measurePosition");
598
599   Moment m = 0;
600   if (unsmob_moment (sm))
601     {
602       m = *unsmob_moment (sm);
603
604       if (m.main_part_ < Rational (0))
605         {
606           Rational length (measure_length (context));
607           while (m.main_part_ < Rational (0))
608             m.main_part_ += length;
609         }
610     }
611
612   return m;
613 }
614
615
616 void
617 set_context_property_on_children (Context *trans, SCM sym, SCM val)
618 {
619   trans->internal_set_property (sym, ly_deep_copy (val));
620   for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
621     {
622       Context *trg = unsmob_context (scm_car (p));
623       set_context_property_on_children (trg, sym, ly_deep_copy (val));
624     }
625 }