2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
14 #include "input-smob.hh"
16 #include "group-interface.hh"
18 #include "paper-score.hh"
30 #include "ly-smobs.icc"
33 Grob::clone (int count) const
35 return new Grob (*this, count);
40 - remove dynamic_cast<Spanner,Item> and put this code into respective
44 #define INFINITY_MSG "Infinity or NaN encountered"
46 Grob::Grob (SCM basicprops,
47 Object_key const* key)
50 /* FIXME: default should be no callback. */
55 immutable_property_alist_ = basicprops;
56 mutable_property_alist_ = SCM_EOL;
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. */
63 SCM meta = get_property ("meta");
64 if (scm_is_pair (meta))
66 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
68 /* Switch off interface checks for the moment. */
69 bool itc = internal_type_checking_global_b;
70 internal_type_checking_global_b = false;
71 internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
72 internal_type_checking_global_b = itc;
77 - destill this into a function, so we can re-init the immutable
78 properties with a new BASICPROPS value after
79 creation. Convenient eg. when using \override with
82 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
83 char const*xnames[] = {"X-extent", "Y-extent"};
84 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
86 for (int a = X_AXIS; a <= Y_AXIS; a++)
88 SCM l = get_property (onames[a]);
90 if (scm_ilength (l) >=0)
92 dim_cache_[a].offset_callbacks_ = l;
93 dim_cache_[a].offsets_left_ = scm_ilength (l);
96 programming_error ("[XY]-offset-callbacks must be a list");
98 SCM cb = get_property (enames[a]);
99 SCM xt = get_property (xnames[a]);
101 /* Should change default to empty? */
102 if (is_number_pair (xt))
104 else if (cb != SCM_BOOL_F
105 && !ly_c_procedure_p (cb) && !scm_is_pair (cb)
106 && ly_c_procedure_p (get_property ("print-function")))
107 cb = stencil_extent_proc;
109 dim_cache_[a].dimension_ = cb;
113 Grob::Grob (Grob const &s, int copy_index)
114 : dim_cache_ (s.dim_cache_)
116 key_ = new Copied_key (s.key_, copy_index);
117 original_ = (Grob*) &s;
120 immutable_property_alist_ = s.immutable_property_alist_;
121 mutable_property_alist_ = SCM_EOL;
123 /* No properties are copied. That is the job of
124 handle_broken_dependencies. */
129 scm_gc_unprotect_object (key_->self_scm ());
136 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
138 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
140 Grob *s = unsmob_grob (element_smob);
141 Axis a = (Axis) scm_to_int (scm_axis);
143 Stencil *m = s->get_stencil ();
147 return ly_interval2scm (e);
152 robust_relative_extent (Grob*me, Grob*refp, Axis a)
154 Interval ext = me->extent (refp, a);
157 ext.add_point (me->relative_coordinate (refp, a));
164 Grob::get_layout () const
166 return pscore_ ? pscore_->layout_ : 0;
170 /* Recursively track all dependencies of this Grob. The status_ field
171 is used as a mark-field. It is marked with BUSY during execution
172 of this function, and marked with FINAL when finished.
174 FUNCPTR is the function to call to update this element. */
176 Grob::calculate_dependencies (int final, int busy, SCM funcname)
178 if (status_ >= final)
183 programming_error ("Element is busy, come back later");
189 for (SCM d = get_property ("dependencies"); scm_is_pair (d);
191 unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname);
193 SCM proc = internal_get_property (funcname);
194 if (ly_c_procedure_p (proc))
195 scm_call_1 (proc, this->self_scm ());
201 Grob::get_stencil () const
206 SCM stil = get_property ("stencil");
207 if (unsmob_stencil (stil))
208 return unsmob_stencil (stil);
210 stil = get_uncached_stencil ();
214 Grob *me = (Grob*) this;
215 me->set_property ("stencil", stil);
218 return unsmob_stencil (stil);
222 Grob::get_uncached_stencil () const
224 SCM proc = get_property ("print-function");
227 if (ly_c_procedure_p (proc))
228 stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
230 if (Stencil *m = unsmob_stencil (stil))
232 if (to_boolean (get_property ("transparent")))
233 stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
236 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm(),
238 stil = Stencil (m->extent_box (),expr). smobbed_copy ();
249 Grob::do_break_processing ()
254 Grob::get_system () const
260 Grob::add_dependency (Grob *e)
263 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),
266 programming_error ("Null dependency added");
270 Grob::handle_broken_dependencies ()
272 Spanner *sp = dynamic_cast<Spanner*> (this);
277 /* THIS, SP is the original spanner. We use a special function
278 because some Spanners have enormously long lists in their
279 properties, and a special function fixes FOO */
280 for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
281 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
283 System *system = get_system ();
286 && system && common_refpoint (system, X_AXIS)
287 && common_refpoint (system, Y_AXIS))
288 substitute_mutable_properties (system
289 ? system->self_scm () : SCM_UNDEFINED,
290 mutable_property_alist_);
291 else if (dynamic_cast <System*> (this))
292 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
294 /* THIS element is `invalid'; it has been removed from all
295 dependencies, so let's junk the element itself.
297 Do not do this for System, since that would remove references
298 to the originals of score-grobs, which get then GC'd (a bad
303 /* Note that we still want references to this element to be
304 rearranged, and not silently thrown away, so we keep pointers like
305 {broken_into_{drul, array}, original}
313 mutable_property_alist_ = SCM_EOL;
314 immutable_property_alist_ = SCM_EOL;
316 set_extent (SCM_EOL, Y_AXIS);
317 set_extent (SCM_EOL, X_AXIS);
319 for (int a = X_AXIS; a <= Y_AXIS; a++)
321 dim_cache_[a].offset_callbacks_ = SCM_EOL;
322 dim_cache_[a].offsets_left_ = 0;
327 Grob::handle_prebroken_dependencies ()
329 /* Don't do this in the derived method, since we want to keep access to
330 mutable_property_alist_ centralized. */
333 Item *it = dynamic_cast<Item*> (this);
334 substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
335 original_->mutable_property_alist_);
340 Grob::find_broken_piece (System *) const
345 /* Translate in one direction. */
347 Grob::translate_axis (Real y, Axis a)
349 if (isinf (y) || isnan (y))
350 programming_error (_ (INFINITY_MSG));
352 dim_cache_[a].offset_ += y;
356 /* Find the offset relative to D. If D equals THIS, then it is 0.
357 Otherwise, it recursively defd as
359 OFFSET_ + PARENT_L_->relative_coordinate (D) */
361 Grob::relative_coordinate (Grob const *refp, Axis a) const
366 /* We catch PARENT_L_ == nil case with this, but we crash if we did
367 not ask for the absolute coordinate (ie. REFP == nil.) */
368 if (refp == dim_cache_[a].parent_)
369 return get_offset (a);
371 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
374 /* Invoke callbacks to get offset relative to parent. */
376 Grob::get_offset (Axis a) const
378 Grob *me = (Grob*) this;
379 while (dim_cache_[a].offsets_left_)
381 int l = --me->dim_cache_[a].offsets_left_;
382 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
383 SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
385 Real r = scm_to_double (retval);
386 if (isinf (r) || isnan (r))
388 programming_error (INFINITY_MSG);
391 me->dim_cache_[a].offset_ += r;
393 return dim_cache_[a].offset_;
397 Grob::is_empty (Axis a) const
399 return ! (scm_is_pair (dim_cache_[a].dimension_)
400 || ly_c_procedure_p (dim_cache_[a].dimension_));
404 Grob::extent (Grob *refp, Axis a) const
406 Real x = relative_coordinate (refp, a);
408 Dimension_cache *d = (Dimension_cache *) &dim_cache_[a];
410 if (scm_is_pair (d->dimension_))
412 else if (ly_c_procedure_p (d->dimension_))
413 /* FIXME: add doco on types, and should typecheck maybe? */
414 d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a));
418 if (!scm_is_pair (d->dimension_))
421 ext = ly_scm2interval (d->dimension_);
423 SCM extra = get_property (a == X_AXIS
428 if (scm_is_pair (extra))
430 ext[BIGGER] += scm_to_double (scm_cdr (extra));
431 ext[SMALLER] += scm_to_double (scm_car (extra));
434 extra = get_property (a == X_AXIS
436 : "minimum-Y-extent");
437 if (scm_is_pair (extra))
438 ext.unite (Interval (scm_to_double (scm_car (extra)),
439 scm_to_double (scm_cdr (extra))));
446 /* Find the group-element which has both #this# and #s# */
448 Grob::common_refpoint (Grob const *s, Axis a) const
450 /* I don't like the quadratic aspect of this code, but I see no
451 other way. The largest chain of parents might be 10 high or so,
452 so it shouldn't be a real issue. */
453 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
454 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
462 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
464 for (; scm_is_pair (elist); elist = scm_cdr (elist))
465 if (Grob *s = unsmob_grob (scm_car (elist)))
468 common = common->common_refpoint (s, a);
477 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
479 for (int i = arr.size (); i--; )
480 if (Grob *s = arr[i])
483 common = common->common_refpoint (s, a);
494 SCM meta = get_property ("meta");
495 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
496 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
497 return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
501 Grob::add_offset_callback (SCM cb, Axis a)
503 if (!has_offset_callback (cb, a))
505 dim_cache_[a].offset_callbacks_
506 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
507 dim_cache_[a].offsets_left_ ++;
512 Grob::has_extent_callback (SCM cb, Axis a)const
514 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
518 Grob::has_offset_callback (SCM cb, Axis a)const
520 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
524 Grob::set_extent (SCM dc, Axis a)
526 dim_cache_[a].dimension_ = dc;
530 Grob::set_parent (Grob *g, Axis a)
532 dim_cache_[a].parent_ = g;
535 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
537 Grob::fixup_refpoint (SCM smob)
539 Grob *me = unsmob_grob (smob);
540 for (int a = X_AXIS; a < NO_AXES; a ++)
543 Grob *parent = me->get_parent (ax);
548 if (parent->get_system () != me->get_system () && me->get_system ())
550 Grob *newparent = parent->find_broken_piece (me->get_system ());
551 me->set_parent (newparent, ax);
554 if (Item *i = dynamic_cast<Item*> (me))
556 Item *parenti = dynamic_cast<Item*> (parent);
560 Direction my_dir = i->break_status_dir () ;
561 if (my_dir!= parenti->break_status_dir ())
563 Item *newparent = parenti->find_prebroken_piece (my_dir);
564 me->set_parent (newparent, ax);
573 Grob::warning (String s)const
575 SCM cause = self_scm ();
576 while (Grob *g = unsmob_grob (cause))
577 cause = g->get_property ("cause");
579 if (Music *m = unsmob_music (cause))
580 m->origin ()->warning (s);
586 Grob::programming_error (String s) const
588 s = "Programming error: " + s;
593 /****************************************************
595 ****************************************************/
597 IMPLEMENT_SMOBS (Grob);
598 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
601 Grob::mark_smob (SCM ses)
603 Grob *s = (Grob*) SCM_CELL_WORD_1 (ses);
604 scm_gc_mark (s->immutable_property_alist_);
606 for (int a = 0 ; a < 2; a++)
608 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
609 scm_gc_mark (s->dim_cache_[a].dimension_);
611 /* Do not mark the parents. The pointers in the mutable
612 property list form two tree like structures (one for X
613 relations, one for Y relations). Marking these can be done
614 in limited stack space. If we add the parents, we will jump
615 between X and Y in an erratic manner, leading to much more
616 recursion depth (and core dumps if we link to pthreads). */
620 scm_gc_mark (s->original_->self_scm ());
622 s->do_derived_mark ();
623 return s->mutable_property_alist_;
627 Grob::print_smob (SCM s, SCM port, scm_print_state *)
629 Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
631 scm_puts ("#<Grob ", port);
632 scm_puts ((char *) sc->name ().to_str0 (), port);
634 /* Do not print properties, that is too much hassle. */
635 scm_puts (" >", port);
640 Grob::do_derived_mark () const
646 Grob::discretionary_processing ()
653 Grob::internal_has_interface (SCM k)
655 SCM ifs = get_property ("interfaces");
657 return scm_c_memq (k, ifs) != SCM_BOOL_F;
660 /** Return Array of Grobs in SCM list LST */
662 ly_scm2grobs (SCM lst)
664 Link_array<Grob> arr;
666 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
669 arr.push (unsmob_grob (e));
677 Grob::get_key () const
682 /** Return SCM list of Grob array A */
684 ly_grobs2scm (Link_array<Grob> a)
687 for (int i = a.size (); i; i--)
688 s = scm_cons (a[i-1]->self_scm (), s);
694 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
696 ADD_INTERFACE (Grob, "grob-interface",
697 "A grob represents a piece of music notation\n"
699 "All grobs have an X and Y-position on the page. These X and Y positions\n"
700 "are stored in a relative format, so they can easily be combined by\n"
701 "stacking them, hanging one grob to the side of another, and coupling\n"
702 "them into a grouping objects.\n"
704 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
705 "is stored relative to that reference point. For example the X-reference\n"
706 "point of a staccato dot usually is the note head that it applies\n"
707 "to. When the note head is moved, the staccato dot moves along\n"
710 "A grob is often associated with a symbol, but some grobs do not print\n"
711 "any symbols. They take care of grouping objects. For example, there is a\n"
712 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
713 "is also an abstract grob: it only moves around chords, but doesn't print\n"
716 "Grobs have a properties: Scheme variables, that can be read and set. "
717 "They have two types. Immutable variables "
718 "define the default style and behavior. They are shared between many objects. "
719 "They can be changed using @code{\\override} and @code{\\revert}. "
721 "Mutable properties are variables that are specific to one grob. Typically, "
722 "lists of other objects, or results from computations are stored in"
723 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
724 "sets a mutable property. "
727 "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
728 "Y-extent-callback print-function extra-offset spacing-procedure "
729 "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
730 "meta layer before-line-breaking-callback "
731 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
732 "minimum-Y-extent transparent tweak-count tweak-rank"