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