2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
15 #include "input-smob.hh"
17 #include "group-interface.hh"
19 #include "paper-score.hh"
29 #include "ly-smobs.icc"
32 Grob::clone (int count) const
34 return new Grob (*this, count);
39 - remove dynamic_cast<Spanner, Item> and put this code into respective
43 #define INFINITY_MSG "Infinity or NaN encountered"
45 Grob::Grob (SCM basicprops,
46 Object_key const* key)
49 /* FIXME: default should be no callback. */
54 immutable_property_alist_ = basicprops;
55 mutable_property_alist_ = SCM_EOL;
57 /* We do smobify_self () as the first step. Since the object lives
58 on the heap, none of its SCM variables are protected from
59 GC. After smobify_self (), they are. */
63 We always get a new key object for a new grob.
65 scm_gc_unprotect_object (key_->self_scm ());
66 SCM meta = get_property ("meta");
67 if (scm_is_pair (meta))
69 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
71 /* Switch off interface checks for the moment. */
72 bool itc = do_internal_type_checking_global;
73 do_internal_type_checking_global = false;
74 internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
75 do_internal_type_checking_global = itc;
80 - destill this into a function, so we can re-init the immutable
81 properties with a new BASICPROPS value after
82 creation. Convenient eg. when using \override with
85 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
86 char const*xnames[] = {"X-extent", "Y-extent"};
87 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
89 for (int a = X_AXIS; a <= Y_AXIS; a++)
91 SCM l = get_property (onames[a]);
93 if (scm_ilength (l) >= 0)
95 dim_cache_[a].offset_callbacks_ = l;
96 dim_cache_[a].offsets_left_ = scm_ilength (l);
99 programming_error ("[XY]-offset-callbacks must be a list");
101 SCM cb = get_property (enames[a]);
102 if (cb == SCM_BOOL_F)
104 dim_cache_[a].dimension_ = SCM_BOOL_F;
107 SCM xt = get_property (xnames[a]);
108 if (is_number_pair (xt))
110 dim_cache_[a].dimension_ = xt;
112 else if (ly_c_procedure_p (cb))
114 dim_cache_[a].dimension_callback_ = cb;
116 else if (cb == SCM_EOL
117 && ly_c_procedure_p (get_property ("print-function")))
118 dim_cache_[a].dimension_callback_ = stencil_extent_proc;
122 Grob::Grob (Grob const &s, int copy_index)
123 : dim_cache_ (s.dim_cache_)
125 key_ = new Copied_key (s.key_, copy_index);
126 original_ = (Grob*) &s;
129 immutable_property_alist_ = s.immutable_property_alist_;
130 mutable_property_alist_ = SCM_EOL;
132 /* No properties are copied. That is the job of
133 handle_broken_dependencies. */
138 scm_gc_unprotect_object (key_->self_scm ());
145 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
147 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
149 Grob *s = unsmob_grob (element_smob);
150 Axis a = (Axis) scm_to_int (scm_axis);
152 Stencil *m = s->get_stencil ();
156 return ly_interval2scm (e);
161 robust_relative_extent (Grob*me, Grob*refp, Axis a)
163 Interval ext = me->extent (refp, a);
166 ext.add_point (me->relative_coordinate (refp, a));
173 Grob::get_layout () const
175 return pscore_ ? pscore_->layout_ : 0;
179 /* Recursively track all dependencies of this Grob. The status_ field
180 is used as a mark-field. It is marked with BUSY during execution
181 of this function, and marked with FINAL when finished.
183 FUNCPTR is the function to call to update this element. */
185 Grob::calculate_dependencies (int final, int busy, SCM funcname)
187 if (status_ >= final)
192 programming_error ("Element is busy, come back later");
198 for (SCM d = get_property ("dependencies"); scm_is_pair (d);
200 unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname);
202 SCM proc = internal_get_property (funcname);
203 if (ly_c_procedure_p (proc))
204 scm_call_1 (proc, this->self_scm ());
210 Grob::get_stencil () const
215 SCM stil = get_property ("stencil");
216 if (unsmob_stencil (stil))
217 return unsmob_stencil (stil);
219 stil = get_uncached_stencil ();
223 Grob *me = (Grob*) this;
224 me->set_property ("stencil", stil);
227 return unsmob_stencil (stil);
231 Grob::get_uncached_stencil () const
233 SCM proc = get_property ("print-function");
236 if (ly_c_procedure_p (proc))
237 stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
239 if (Stencil *m = unsmob_stencil (stil))
241 if (to_boolean (get_property ("transparent")))
242 stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
245 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm(),
247 stil = Stencil (m->extent_box (), expr). smobbed_copy ();
258 Grob::do_break_processing ()
263 Grob::get_system () const
269 Grob::add_dependency (Grob *e)
272 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),
275 programming_error ("Null dependency added");
279 Grob::handle_broken_dependencies ()
281 Spanner *sp = dynamic_cast<Spanner*> (this);
286 /* THIS, SP is the original spanner. We use a special function
287 because some Spanners have enormously long lists in their
288 properties, and a special function fixes FOO */
289 for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
290 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
292 System *system = get_system ();
295 && system && common_refpoint (system, X_AXIS)
296 && common_refpoint (system, Y_AXIS))
297 substitute_mutable_properties (system
298 ? system->self_scm () : SCM_UNDEFINED,
299 mutable_property_alist_);
300 else if (dynamic_cast <System*> (this))
301 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
303 /* THIS element is `invalid'; it has been removed from all
304 dependencies, so let's junk the element itself.
306 Do not do this for System, since that would remove references
307 to the originals of score-grobs, which get then GC'd (a bad
312 /* Note that we still want references to this element to be
313 rearranged, and not silently thrown away, so we keep pointers like
314 {broken_into_{drul, array}, original}
322 mutable_property_alist_ = SCM_EOL;
323 immutable_property_alist_ = SCM_EOL;
325 set_extent (SCM_EOL, Y_AXIS);
326 set_extent (SCM_EOL, X_AXIS);
328 set_extent_callback (SCM_EOL, Y_AXIS);
329 set_extent_callback (SCM_EOL, X_AXIS);
331 for (int a = X_AXIS; a <= Y_AXIS; a++)
333 dim_cache_[a].offset_callbacks_ = SCM_EOL;
334 dim_cache_[a].offsets_left_ = 0;
339 Grob::handle_prebroken_dependencies ()
341 /* Don't do this in the derived method, since we want to keep access to
342 mutable_property_alist_ centralized. */
345 Item *it = dynamic_cast<Item*> (this);
346 substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
347 original_->mutable_property_alist_);
352 Grob::find_broken_piece (System *) const
357 /* Translate in one direction. */
359 Grob::translate_axis (Real y, Axis a)
361 if (isinf (y) || isnan (y))
362 programming_error (_ (INFINITY_MSG));
364 dim_cache_[a].offset_ += y;
368 /* Find the offset relative to D. If D equals THIS, then it is 0.
369 Otherwise, it recursively defd as
371 OFFSET_ + PARENT_L_->relative_coordinate (D) */
373 Grob::relative_coordinate (Grob const *refp, Axis a) const
378 /* We catch PARENT_L_ == nil case with this, but we crash if we did
379 not ask for the absolute coordinate (ie. REFP == nil.) */
380 if (refp == dim_cache_[a].parent_)
381 return get_offset (a);
383 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
386 /* Invoke callbacks to get offset relative to parent. */
388 Grob::get_offset (Axis a) const
390 Grob *me = (Grob*) this;
391 while (dim_cache_[a].offsets_left_)
393 int l = --me->dim_cache_[a].offsets_left_;
394 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
395 SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
397 Real r = scm_to_double (retval);
398 if (isinf (r) || isnan (r))
400 programming_error (INFINITY_MSG);
403 me->dim_cache_[a].offset_ += r;
405 return dim_cache_[a].offset_;
409 Grob::is_empty (Axis a) const
411 return !(scm_is_pair (dim_cache_[a].dimension_)
412 || ly_c_procedure_p (dim_cache_[a].dimension_callback_));
416 Grob::flush_extent_cache (Axis axis)
418 Dimension_cache * d = &dim_cache_[axis];
419 if (ly_c_procedure_p (d->dimension_callback_)
420 && scm_is_pair (d->dimension_))
422 d->dimension_ = SCM_EOL;
425 if (get_parent (axis))
426 get_parent(axis)->flush_extent_cache (axis);
431 Grob::extent (Grob *refp, Axis a) const
433 Real x = relative_coordinate (refp, a);
435 Dimension_cache *d = (Dimension_cache *) &dim_cache_[a];
438 SCM dimpair = d->dimension_;
439 if (scm_is_pair (dimpair))
441 else if (ly_c_procedure_p (d->dimension_callback_)
442 && d->dimension_ == SCM_EOL)
443 d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_int2num (a));
447 if (!scm_is_pair (d->dimension_))
450 ext = ly_scm2interval (d->dimension_);
452 SCM extra = get_property (a == X_AXIS
457 if (scm_is_pair (extra))
459 ext[BIGGER] += scm_to_double (scm_cdr (extra));
460 ext[SMALLER] += scm_to_double (scm_car (extra));
463 extra = get_property (a == X_AXIS
465 : "minimum-Y-extent");
466 if (scm_is_pair (extra))
467 ext.unite (Interval (scm_to_double (scm_car (extra)),
468 scm_to_double (scm_cdr (extra))));
475 /* Find the group-element which has both #this# and #s# */
477 Grob::common_refpoint (Grob const *s, Axis a) const
479 /* I don't like the quadratic aspect of this code, but I see no
480 other way. The largest chain of parents might be 10 high or so,
481 so it shouldn't be a real issue. */
482 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
483 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
491 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
493 for (; scm_is_pair (elist); elist = scm_cdr (elist))
494 if (Grob *s = unsmob_grob (scm_car (elist)))
497 common = common->common_refpoint (s, a);
506 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
508 for (int i = arr.size (); i--; )
509 if (Grob *s = arr[i])
512 common = common->common_refpoint (s, a);
523 SCM meta = get_property ("meta");
524 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
525 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
526 return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
530 Grob::add_offset_callback (SCM cb, Axis a)
532 if (!has_offset_callback (cb, a))
534 dim_cache_[a].offset_callbacks_
535 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
536 dim_cache_[a].offsets_left_ ++;
541 Grob::has_extent_callback (SCM cb, Axis a) const
543 return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
547 Grob::has_offset_callback (SCM cb, Axis a)const
549 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
553 Grob::set_extent (SCM dc, Axis a)
555 dim_cache_[a].dimension_ = dc;
559 Grob::set_extent_callback (SCM dc, Axis a)
561 dim_cache_[a].dimension_callback_ = dc;
565 Grob::set_parent (Grob *g, Axis a)
567 dim_cache_[a].parent_ = g;
570 MAKE_SCHEME_CALLBACK (Grob, fixup_refpoint, 1);
572 Grob::fixup_refpoint (SCM smob)
574 Grob *me = unsmob_grob (smob);
575 for (int a = X_AXIS; a < NO_AXES; a ++)
578 Grob *parent = me->get_parent (ax);
583 if (parent->get_system () != me->get_system () && me->get_system ())
585 Grob *newparent = parent->find_broken_piece (me->get_system ());
586 me->set_parent (newparent, ax);
589 if (Item *i = dynamic_cast<Item*> (me))
591 Item *parenti = dynamic_cast<Item*> (parent);
595 Direction my_dir = i->break_status_dir () ;
596 if (my_dir!= parenti->break_status_dir ())
598 Item *newparent = parenti->find_prebroken_piece (my_dir);
599 me->set_parent (newparent, ax);
608 Grob::warning (String s)const
610 SCM cause = self_scm ();
611 while (Grob *g = unsmob_grob (cause))
612 cause = g->get_property ("cause");
614 if (Music *m = unsmob_music (cause))
615 m->origin ()->warning (s);
621 Grob::programming_error (String s) const
623 s = "Programming error: " + s;
628 /****************************************************
630 ****************************************************/
632 IMPLEMENT_SMOBS (Grob);
633 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
636 Grob::mark_smob (SCM ses)
638 Grob *s = (Grob*) SCM_CELL_WORD_1 (ses);
639 scm_gc_mark (s->immutable_property_alist_);
640 scm_gc_mark (s->key_->self_scm ());
641 for (int a = 0 ; a < 2; a++)
643 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
644 scm_gc_mark (s->dim_cache_[a].dimension_);
645 scm_gc_mark (s->dim_cache_[a].dimension_callback_);
647 /* Do not mark the parents. The pointers in the mutable
648 property list form two tree like structures (one for X
649 relations, one for Y relations). Marking these can be done
650 in limited stack space. If we add the parents, we will jump
651 between X and Y in an erratic manner, leading to much more
652 recursion depth (and core dumps if we link to pthreads). */
656 scm_gc_mark (s->original_->self_scm ());
658 s->do_derived_mark ();
659 return s->mutable_property_alist_;
663 Grob::print_smob (SCM s, SCM port, scm_print_state *)
665 Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
667 scm_puts ("#<Grob ", port);
668 scm_puts ((char *) sc->name ().to_str0 (), port);
670 /* Do not print properties, that is too much hassle. */
671 scm_puts (" >", port);
676 Grob::do_derived_mark () const
682 Grob::discretionary_processing ()
689 Grob::internal_has_interface (SCM k)
691 SCM ifs = get_property ("interfaces");
693 return scm_c_memq (k, ifs) != SCM_BOOL_F;
697 Grob::get_parent (Axis a) const
699 return dim_cache_[a].parent_;
703 /** Return Array of Grobs in SCM list LST */
705 ly_scm2grobs (SCM lst)
707 Link_array<Grob> arr;
709 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
712 arr.push (unsmob_grob (e));
720 Grob::get_key () const
725 /** Return SCM list of Grob array A */
727 ly_grobs2scm (Link_array<Grob> a)
730 for (int i = a.size (); i; i--)
731 s = scm_cons (a[i-1]->self_scm (), s);
737 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
739 ADD_INTERFACE (Grob, "grob-interface",
740 "A grob represents a piece of music notation\n"
742 "All grobs have an X and Y-position on the page. These X and Y positions\n"
743 "are stored in a relative format, so they can easily be combined by\n"
744 "stacking them, hanging one grob to the side of another, and coupling\n"
745 "them into a grouping objects.\n"
747 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
748 "is stored relative to that reference point. For example the X-reference\n"
749 "point of a staccato dot usually is the note head that it applies\n"
750 "to. When the note head is moved, the staccato dot moves along\n"
753 "A grob is often associated with a symbol, but some grobs do not print\n"
754 "any symbols. They take care of grouping objects. For example, there is a\n"
755 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
756 "is also an abstract grob: it only moves around chords, but doesn't print\n"
759 "Grobs have a properties: Scheme variables, that can be read and set. "
760 "They have two types. Immutable variables "
761 "define the default style and behavior. They are shared between many objects. "
762 "They can be changed using @code{\\override} and @code{\\revert}. "
764 "Mutable properties are variables that are specific to one grob. Typically, "
765 "lists of other objects, or results from computations are stored in"
766 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
767 "sets a mutable property. "
770 "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
771 "Y-extent-callback print-function extra-offset spacing-procedure "
772 "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
773 "meta layer before-line-breaking-callback "
774 "axis-group-parent-X "
775 "axis-group-parent-Y "
776 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
777 "minimum-Y-extent transparent tweak-count tweak-rank"