]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
(music-descriptions): remove
[lilypond.git] / lily / grob.cc
1 /*
2   grob.cc -- implement Grob
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "grob.hh"
10
11 #include <cstring>
12 #include <math.h>
13
14 #include "main.hh"
15 #include "input-smob.hh"
16 #include "warn.hh"
17 #include "group-interface.hh"
18 #include "misc.hh"
19 #include "paper-score.hh"
20 #include "stencil.hh"
21 #include "warn.hh"
22 #include "system.hh"
23 #include "item.hh"
24 #include "stencil.hh"
25 #include "misc.hh"
26 #include "music.hh"
27 #include "item.hh"
28 #include "paper-score.hh"
29 #include "ly-smobs.icc"
30 #include "output-def.hh"
31
32 Grob *
33 Grob::clone (int count) const
34 {
35   return new Grob (*this, count);
36 }
37
38 /* TODO:
39
40 - remove dynamic_cast<Spanner, Item> and put this code into respective
41 subclass.  */
42
43 #define HASH_SIZE 3
44 #define INFINITY_MSG "Infinity or NaN encountered"
45
46 Grob::Grob (SCM basicprops,
47             Object_key const *key)
48 {
49   key_ = key;
50   /* FIXME: default should be no callback.  */
51   self_scm_ = SCM_EOL;
52   pscore_ = 0;
53   status_ = 0;
54   original_ = 0;
55   immutable_property_alist_ = basicprops;
56   mutable_property_alist_ = SCM_EOL;
57
58   /* We do smobify_self () as the first step.  Since the object lives
59      on the heap, none of its SCM variables are protected from
60      GC. After smobify_self (), they are.  */
61   smobify_self ();
62
63   /*
64     We always get a new key object for a new grob.
65   */
66   if (key_)
67     scm_gc_unprotect_object (key_->self_scm ());
68   SCM meta = get_property ("meta");
69   if (scm_is_pair (meta))
70     {
71       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
72
73       /* Switch off interface checks for the moment.  */
74       bool itc = do_internal_type_checking_global;
75       do_internal_type_checking_global = false;
76       internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
77       do_internal_type_checking_global = itc;
78     }
79
80   /* TODO:
81
82   - destill this into a function, so we can re-init the immutable
83   properties with a new BASICPROPS value after
84   creation. Convenient eg. when using \override with
85   StaffSymbol.  */
86
87   char const *onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
88   char const *xnames[] = {"X-extent", "Y-extent"};
89   char const *enames[] = {"X-extent-callback", "Y-extent-callback"};
90
91   for (int a = X_AXIS; a <= Y_AXIS; a++)
92     {
93       SCM l = get_property (onames[a]);
94
95       if (scm_ilength (l) >= 0)
96         {
97           dim_cache_[a].offset_callbacks_ = l;
98           dim_cache_[a].offsets_left_ = scm_ilength (l);
99         }
100       else
101         programming_error ("[XY]-offset-callbacks must be a list");
102
103       SCM cb = get_property (enames[a]);
104       if (cb == SCM_BOOL_F)
105         {
106           dim_cache_[a].dimension_ = SCM_BOOL_F;
107         }
108
109       SCM xt = get_property (xnames[a]);
110       if (is_number_pair (xt))
111         {
112           dim_cache_[a].dimension_ = xt;
113         }
114       else if (ly_is_procedure (cb))
115         {
116           dim_cache_[a].dimension_callback_ = cb;
117         }
118       else if (cb == SCM_EOL
119                && ly_is_procedure (get_property ("print-function")))
120         dim_cache_[a].dimension_callback_ = stencil_extent_proc;
121     }
122 }
123
124 Grob::Grob (Grob const &s, int copy_index)
125   : dim_cache_ (s.dim_cache_)
126 {
127   key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
128   original_ = (Grob *) & s;
129   self_scm_ = SCM_EOL;
130
131   immutable_property_alist_ = s.immutable_property_alist_;
132   mutable_property_alist_ = SCM_EOL;
133
134   /* No properties are copied.  That is the job of
135      handle_broken_dependencies.  */
136   status_ = s.status_;
137   pscore_ = 0;
138
139   smobify_self ();
140   if (key_)
141     scm_gc_unprotect_object (key_->self_scm ());
142 }
143
144 Grob::~Grob ()
145 {
146 }
147
148 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
149 SCM
150 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
151 {
152   Grob *s = unsmob_grob (element_smob);
153   Axis a = (Axis) scm_to_int (scm_axis);
154
155   Stencil *m = s->get_stencil ();
156   Interval e;
157   if (m)
158     e = m->extent (a);
159   return ly_interval2scm (e);
160 }
161
162 Interval
163 robust_relative_extent (Grob *me, Grob *refp, Axis a)
164 {
165   Interval ext = me->extent (refp, a);
166   if (ext.is_empty ())
167     {
168       ext.add_point (me->relative_coordinate (refp, a));
169     }
170
171   return ext;
172 }
173
174 Output_def *
175 Grob::get_layout () const
176 {
177   return pscore_ ? pscore_->layout () : 0;
178 }
179
180 /* Recursively track all dependencies of this Grob.  The status_ field
181    is used as a mark-field.  It is marked with BUSY during execution
182    of this function, and marked with FINAL when finished.
183
184    FUNCPTR is the function to call to update this element.  */
185 void
186 Grob::calculate_dependencies (int final, int busy, SCM funcname)
187 {
188   if (status_ >= final)
189     return;
190
191   if (status_ == busy)
192     {
193       programming_error ("element is busy, come back later");
194       return;
195     }
196
197   status_ = busy;
198
199   for (SCM d = get_property ("dependencies"); scm_is_pair (d);
200        d = scm_cdr (d))
201     unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname);
202
203   SCM proc = internal_get_property (funcname);
204   if (ly_is_procedure (proc))
205     scm_call_1 (proc, this->self_scm ());
206
207   status_ = final;
208 }
209
210 Stencil *
211 Grob::get_stencil () const
212 {
213   if (!is_live ())
214     return 0;
215
216   SCM stil = get_property ("stencil");
217   if (unsmob_stencil (stil))
218     return unsmob_stencil (stil);
219
220   stil = get_uncached_stencil ();
221   if (is_live ())
222     {
223       Grob *me = (Grob *) this;
224       me->set_property ("stencil", stil);
225     }
226
227   return unsmob_stencil (stil);
228 }
229
230 SCM
231 Grob::get_uncached_stencil () const
232 {
233   SCM proc = get_property ("print-function");
234
235   SCM stil = SCM_EOL;
236   if (ly_is_procedure (proc))
237     stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
238
239   if (Stencil *m = unsmob_stencil (stil))
240     {
241       if (to_boolean (get_property ("transparent")))
242         stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
243       else
244         {
245           SCM expr = m->expr ();
246           if (point_and_click_global)
247             expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm (), expr);
248           
249           stil = Stencil (m->extent_box (), expr).smobbed_copy ();
250         }
251
252       /* color support... see interpret_stencil_expression () for more... */
253       SCM color = get_property ("color");
254       if (color != SCM_EOL)
255         {
256           m = unsmob_stencil (stil);
257           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
258                                  color,
259                                  m->expr ());
260
261           stil = Stencil (m->extent_box (), expr).smobbed_copy ();
262         }
263     }
264
265   return stil;
266 }
267
268 /*
269   VIRTUAL STUBS
270 */
271 void
272 Grob::do_break_processing ()
273 {
274 }
275
276 System *
277 Grob::get_system () const
278 {
279   return 0;
280 }
281
282 void
283 Grob::add_dependency (Grob *e)
284 {
285   if (e)
286     Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"), e);
287   else
288     programming_error ("null dependency added");
289 }
290
291 void
292 Grob::handle_broken_dependencies ()
293 {
294   Spanner *sp = dynamic_cast<Spanner *> (this);
295   if (original_ && sp)
296     return;
297
298   if (sp)
299     /* THIS, SP is the original spanner.  We use a special function
300        because some Spanners have enormously long lists in their
301        properties, and a special function fixes FOO  */
302     for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
303       sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
304
305   System *system = get_system ();
306
307   if (is_live ()
308       && system && common_refpoint (system, X_AXIS)
309       && common_refpoint (system, Y_AXIS))
310     substitute_mutable_properties (system
311                                    ? system->self_scm () : SCM_UNDEFINED,
312                                    mutable_property_alist_);
313   else if (dynamic_cast<System *> (this))
314     substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
315   else
316     /* THIS element is `invalid'; it has been removed from all
317        dependencies, so let's junk the element itself.
318
319        Do not do this for System, since that would remove references
320        to the originals of score-grobs, which get then GC'd (a bad
321        thing).  */
322     suicide ();
323 }
324
325 /* Note that we still want references to this element to be
326    rearranged, and not silently thrown away, so we keep pointers like
327    {broken_into_{drul, array}, original}
328 */
329 void
330 Grob::suicide ()
331 {
332   if (!is_live ())
333     return;
334
335   mutable_property_alist_ = SCM_EOL;
336   immutable_property_alist_ = SCM_EOL;
337
338   set_extent (SCM_EOL, Y_AXIS);
339   set_extent (SCM_EOL, X_AXIS);
340
341   set_extent_callback (SCM_EOL, Y_AXIS);
342   set_extent_callback (SCM_EOL, X_AXIS);
343
344   for (int a = X_AXIS; a <= Y_AXIS; a++)
345     {
346       dim_cache_[a].offset_callbacks_ = SCM_EOL;
347       dim_cache_[a].offsets_left_ = 0;
348     }
349 }
350
351 void
352 Grob::handle_prebroken_dependencies ()
353 {
354   /* Don't do this in the derived method, since we want to keep access to
355      mutable_property_alist_ centralized.  */
356   if (original_)
357     {
358       Item *it = dynamic_cast<Item *> (this);
359       substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
360                                      original_->mutable_property_alist_);
361     }
362 }
363
364 Grob *
365 Grob::find_broken_piece (System *) const
366 {
367   return 0;
368 }
369
370 /* Translate in one direction.  */
371 void
372 Grob::translate_axis (Real y, Axis a)
373 {
374   if (isinf (y) || isnan (y))
375     programming_error (_ (INFINITY_MSG));
376   else
377     dim_cache_[a].offset_ += y;
378 }
379
380 /* Find the offset relative to D.  If D equals THIS, then it is 0.
381    Otherwise, it recursively defd as
382
383    OFFSET_ + PARENT_L_->relative_coordinate (D) */
384 Real
385 Grob::relative_coordinate (Grob const *refp, Axis a) const
386 {
387   if (refp == this)
388     return 0.0;
389
390   /* We catch PARENT_L_ == nil case with this, but we crash if we did
391      not ask for the absolute coordinate (ie. REFP == nil.)  */
392   if (refp == dim_cache_[a].parent_)
393     return get_offset (a);
394
395   return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
396 }
397
398 /* Invoke callbacks to get offset relative to parent.  */
399 Real
400 Grob::get_offset (Axis a) const
401 {
402   Grob *me = (Grob *) this;
403   while (dim_cache_[a].offsets_left_)
404     {
405       int l = --me->dim_cache_[a].offsets_left_;
406       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
407       SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
408
409       Real r = scm_to_double (retval);
410       if (isinf (r) || isnan (r))
411         {
412           programming_error (INFINITY_MSG);
413           r = 0.0;
414         }
415       me->dim_cache_[a].offset_ += r;
416     }
417   return dim_cache_[a].offset_;
418 }
419
420 bool
421 Grob::is_empty (Axis a) const
422 {
423   return !(scm_is_pair (dim_cache_[a].dimension_)
424            || ly_is_procedure (dim_cache_[a].dimension_callback_));
425 }
426
427 void
428 Grob::flush_extent_cache (Axis axis)
429 {
430   Dimension_cache *d = &dim_cache_[axis];
431   if (ly_is_procedure (d->dimension_callback_)
432       && scm_is_pair (d->dimension_))
433     {
434       d->dimension_ = SCM_EOL;
435
436       if (get_parent (axis))
437         get_parent (axis)->flush_extent_cache (axis);
438     }
439 }
440
441 Interval
442 Grob::extent (Grob *refp, Axis a) const
443 {
444   Real x = relative_coordinate (refp, a);
445
446   Dimension_cache *d = (Dimension_cache *) & dim_cache_[a];
447   Interval ext;
448
449   SCM dimpair = d->dimension_;
450   if (scm_is_pair (dimpair))
451     ;
452   else if (ly_is_procedure (d->dimension_callback_)
453            && d->dimension_ == SCM_EOL)
454     d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_int2num (a));
455   else
456     return ext;
457
458   if (!scm_is_pair (d->dimension_))
459     return ext;
460
461   ext = ly_scm2interval (d->dimension_);
462
463   SCM extra = get_property (a == X_AXIS
464                             ? "extra-X-extent"
465                             : "extra-Y-extent");
466
467   /* Signs ?  */
468   if (scm_is_pair (extra))
469     {
470       ext[BIGGER] += scm_to_double (scm_cdr (extra));
471       ext[SMALLER] += scm_to_double (scm_car (extra));
472     }
473
474   extra = get_property (a == X_AXIS
475                         ? "minimum-X-extent"
476                         : "minimum-Y-extent");
477   if (scm_is_pair (extra))
478     ext.unite (Interval (scm_to_double (scm_car (extra)),
479                          scm_to_double (scm_cdr (extra))));
480
481   ext.translate (x);
482
483   return ext;
484 }
485
486 /* Find the group-element which has both #this# and #s#  */
487 Grob *
488 Grob::common_refpoint (Grob const *s, Axis a) const
489 {
490   /* I don't like the quadratic aspect of this code, but I see no
491      other way.  The largest chain of parents might be 10 high or so,
492      so it shouldn't be a real issue.  */
493   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
494     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
495       if (d == c)
496         return (Grob *) d;
497
498   return 0;
499 }
500
501 Grob *
502 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
503 {
504   for (; scm_is_pair (elist); elist = scm_cdr (elist))
505     if (Grob *s = unsmob_grob (scm_car (elist)))
506       {
507         if (common)
508           common = common->common_refpoint (s, a);
509         else
510           common = s;
511       }
512
513   return common;
514 }
515
516 Grob *
517 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
518 {
519   for (int i = arr.size (); i--;)
520     if (Grob *s = arr[i])
521       {
522         if (common)
523           common = common->common_refpoint (s, a);
524         else
525           common = s;
526       }
527
528   return common;
529 }
530
531 String
532 Grob::name () const
533 {
534   SCM meta = get_property ("meta");
535   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
536   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
537   return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
538 }
539
540 void
541 Grob::add_offset_callback (SCM cb, Axis a)
542 {
543   if (!has_offset_callback (cb, a))
544     {
545       dim_cache_[a].offset_callbacks_
546         = scm_cons (cb, dim_cache_[a].offset_callbacks_);
547       dim_cache_[a].offsets_left_++;
548     }
549 }
550
551 bool
552 Grob::has_extent_callback (SCM cb, Axis a) const
553 {
554   return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
555 }
556
557 bool
558 Grob::has_offset_callback (SCM cb, Axis a) const
559 {
560   return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
561 }
562
563 void
564 Grob::set_extent (SCM dc, Axis a)
565 {
566   dim_cache_[a].dimension_ = dc;
567 }
568
569 void
570 Grob::set_extent_callback (SCM dc, Axis a)
571 {
572   dim_cache_[a].dimension_callback_ = dc;
573 }
574
575 void
576 Grob::set_parent (Grob *g, Axis a)
577 {
578   dim_cache_[a].parent_ = g;
579 }
580
581 MAKE_SCHEME_CALLBACK (Grob, fixup_refpoint, 1);
582 SCM
583 Grob::fixup_refpoint (SCM smob)
584 {
585   Grob *me = unsmob_grob (smob);
586   for (int a = X_AXIS; a < NO_AXES; a++)
587     {
588       Axis ax = (Axis)a;
589       Grob *parent = me->get_parent (ax);
590
591       if (!parent)
592         continue;
593
594       if (parent->get_system () != me->get_system () && me->get_system ())
595         {
596           Grob *newparent = parent->find_broken_piece (me->get_system ());
597           me->set_parent (newparent, ax);
598         }
599
600       if (Item *i = dynamic_cast<Item *> (me))
601         {
602           Item *parenti = dynamic_cast<Item *> (parent);
603
604           if (parenti && i)
605             {
606               Direction my_dir = i->break_status_dir ();
607               if (my_dir != parenti->break_status_dir ())
608                 {
609                   Item *newparent = parenti->find_prebroken_piece (my_dir);
610                   me->set_parent (newparent, ax);
611                 }
612             }
613         }
614     }
615   return smob;
616 }
617
618 void
619 Grob::warning (String s) const
620 {
621   SCM cause = self_scm ();
622   while (Grob *g = unsmob_grob (cause))
623     cause = g->get_property ("cause");
624
625   if (Music *m = unsmob_music (cause))
626     m->origin ()->warning (s);
627   else
628     ::warning (s);
629 }
630
631 void
632 Grob::programming_error (String s) const
633 {
634   s = _f ("programming error: %s", s);
635   message (s);
636 }
637
638 /****************************************************
639   SMOB funcs
640 ****************************************************/
641
642 IMPLEMENT_SMOBS (Grob);
643 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
644
645 SCM
646 Grob::mark_smob (SCM ses)
647 {
648   Grob *s = (Grob *) SCM_CELL_WORD_1 (ses);
649   scm_gc_mark (s->immutable_property_alist_);
650
651   if (s->key_)
652     scm_gc_mark (s->key_->self_scm ());
653   for (int a = 0; a < 2; a++)
654     {
655       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
656       scm_gc_mark (s->dim_cache_[a].dimension_);
657       scm_gc_mark (s->dim_cache_[a].dimension_callback_);
658
659       /* Do not mark the parents.  The pointers in the mutable
660          property list form two tree like structures (one for X
661          relations, one for Y relations).  Marking these can be done
662          in limited stack space.  If we add the parents, we will jump
663          between X and Y in an erratic manner, leading to much more
664          recursion depth (and core dumps if we link to pthreads).  */
665     }
666
667   if (s->original_)
668     scm_gc_mark (s->original_->self_scm ());
669
670   if (s->pscore_)
671     scm_gc_mark (s->pscore_->self_scm ());
672
673   s->do_derived_mark ();
674   return s->mutable_property_alist_;
675 }
676
677 int
678 Grob::print_smob (SCM s, SCM port, scm_print_state *)
679 {
680   Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
681
682   scm_puts ("#<Grob ", port);
683   scm_puts ((char *) sc->name ().to_str0 (), port);
684
685   /* Do not print properties, that is too much hassle.  */
686   scm_puts (" >", port);
687   return 1;
688 }
689
690 SCM
691 Grob::do_derived_mark () const
692 {
693   return SCM_EOL;
694 }
695
696 void
697 Grob::discretionary_processing ()
698 {
699 }
700
701 bool
702 Grob::internal_has_interface (SCM k)
703 {
704   SCM ifs = get_property ("interfaces");
705
706   return scm_c_memq (k, ifs) != SCM_BOOL_F;
707 }
708
709 Grob *
710 Grob::get_parent (Axis a) const
711 {
712   return dim_cache_[a].parent_;
713 }
714
715 /** Return Array of Grobs in SCM list LST */
716 Link_array<Grob>
717 ly_scm2grobs (SCM lst)
718 {
719   Link_array<Grob> arr;
720
721   for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
722     {
723       SCM e = scm_car (s);
724       arr.push (unsmob_grob (e));
725     }
726
727   arr.reverse ();
728   return arr;
729 }
730
731 Object_key const *
732 Grob::get_key () const
733 {
734   return key_;
735 }
736
737 /** Return SCM list of Grob array A */
738 SCM
739 ly_grobs2scm (Link_array<Grob> a)
740 {
741   SCM s = SCM_EOL;
742   for (int i = a.size (); i; i--)
743     s = scm_cons (a[i - 1]->self_scm (), s);
744
745   return s;
746 }
747
748 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
749
750 ADD_INTERFACE (Grob, "grob-interface",
751                "A grob represents a piece of music notation\n"
752                "\n"
753                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
754                "are stored in a relative format, so they can easily be combined by\n"
755                "stacking them, hanging one grob to the side of another, and coupling\n"
756                "them into a grouping objects.\n"
757                "\n"
758                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
759                "is stored relative to that reference point. For example the X-reference\n"
760                "point of a staccato dot usually is the note head that it applies\n"
761                "to. When the note head is moved, the staccato dot moves along\n"
762                "automatically.\n"
763                "\n"
764                "A grob is often associated with a symbol, but some grobs do not print\n"
765                "any symbols. They take care of grouping objects. For example, there is a\n"
766                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
767                "is also an abstract grob: it only moves around chords, but doesn't print\n"
768                "anything.\n"
769                "\n"
770                "Grobs have a properties: Scheme variables, that can be read and set. "
771                "They have two types. Immutable variables "
772                "define the default style and behavior.  They are shared between  many objects. "
773                "They can be changed using @code{\\override} and @code{\\revert}. "
774                "\n\n"
775                "Mutable properties are variables that are specific to one grob. Typically, "
776                "lists of other objects, or results from computations are stored in"
777                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
778                "sets a mutable property. ",
779                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
780                "Y-extent-callback print-function extra-offset spacing-procedure "
781                "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
782                "meta layer before-line-breaking-callback "
783                "color "
784                "axis-group-parent-X "
785                "axis-group-parent-Y "
786                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
787                "minimum-Y-extent transparent tweak-count tweak-rank");
788