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>
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. */
62 SCM meta = get_property ("meta");
63 if (scm_is_pair (meta))
65 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
67 /* Switch off interface checks for the moment. */
68 bool itc = internal_type_checking_global_b;
69 internal_type_checking_global_b = false;
70 internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
71 internal_type_checking_global_b = itc;
76 - destill this into a function, so we can re-init the immutable
77 properties with a new BASICPROPS value after
78 creation. Convenient eg. when using \override with
81 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
82 char const*xnames[] = {"X-extent", "Y-extent"};
83 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
85 for (int a = X_AXIS; a <= Y_AXIS; a++)
87 SCM l = get_property (onames[a]);
89 if (scm_ilength (l) >=0)
91 dim_cache_[a].offset_callbacks_ = l;
92 dim_cache_[a].offsets_left_ = scm_ilength (l);
95 programming_error ("[XY]-offset-callbacks must be a list");
97 SCM cb = get_property (enames[a]);
98 SCM xt = get_property (xnames[a]);
100 /* Should change default to empty? */
101 if (is_number_pair (xt))
103 else if (cb != SCM_BOOL_F
104 && !ly_c_procedure_p (cb) && !scm_is_pair (cb)
105 && ly_c_procedure_p (get_property ("print-function")))
106 cb = stencil_extent_proc;
108 dim_cache_[a].dimension_ = cb;
112 Grob::Grob (Grob const &s, int copy_index)
113 : dim_cache_ (s.dim_cache_)
115 key_ = new Copied_key (s.key_, copy_index);
116 original_ = (Grob*) &s;
119 immutable_property_alist_ = s.immutable_property_alist_;
120 mutable_property_alist_ = SCM_EOL;
122 /* No properties are copied. That is the job of
123 handle_broken_dependencies. */
128 scm_gc_unprotect_object (key_->self_scm ());
135 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
137 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
139 Grob *s = unsmob_grob (element_smob);
140 Axis a = (Axis) scm_to_int (scm_axis);
142 Stencil *m = s->get_stencil ();
146 return ly_interval2scm (e);
151 robust_relative_extent (Grob*me, Grob*refp, Axis a)
153 Interval ext = me->extent (refp, a);
156 ext.add_point (me->relative_coordinate (refp, a));
163 Grob::get_layout () const
165 return pscore_ ? pscore_->layout_ : 0;
169 /* Recursively track all dependencies of this Grob. The status_ field
170 is used as a mark-field. It is marked with BUSY during execution
171 of this function, and marked with FINAL when finished.
173 FUNCPTR is the function to call to update this element. */
175 Grob::calculate_dependencies (int final, int busy, SCM funcname)
177 if (status_ >= final)
182 programming_error ("Element is busy, come back later");
188 for (SCM d = get_property ("dependencies"); scm_is_pair (d);
190 unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname);
192 SCM proc = internal_get_property (funcname);
193 if (ly_c_procedure_p (proc))
194 scm_call_1 (proc, this->self_scm ());
200 Grob::get_stencil () const
205 SCM stil = get_property ("stencil");
206 if (unsmob_stencil (stil))
207 return unsmob_stencil (stil);
209 stil = get_uncached_stencil ();
213 Grob *me = (Grob*) this;
214 me->set_property ("stencil", stil);
217 return unsmob_stencil (stil);
221 Grob::get_uncached_stencil () const
223 SCM proc = get_property ("print-function");
226 if (ly_c_procedure_p (proc))
227 stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
229 if (Stencil *m = unsmob_stencil (stil))
231 if (to_boolean (get_property ("transparent")))
232 stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
235 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm(),
237 stil = Stencil (m->extent_box (),expr). smobbed_copy ();
248 Grob::do_break_processing ()
253 Grob::get_system () const
259 Grob::add_dependency (Grob *e)
262 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),
265 programming_error ("Null dependency added");
269 Grob::handle_broken_dependencies ()
271 Spanner *sp = dynamic_cast<Spanner*> (this);
276 /* THIS, SP is the original spanner. We use a special function
277 because some Spanners have enormously long lists in their
278 properties, and a special function fixes FOO */
279 for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
280 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
282 System *system = get_system ();
285 && system && common_refpoint (system, X_AXIS)
286 && common_refpoint (system, Y_AXIS))
287 substitute_mutable_properties (system
288 ? system->self_scm () : SCM_UNDEFINED,
289 mutable_property_alist_);
290 else if (dynamic_cast <System*> (this))
291 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
293 /* THIS element is `invalid'; it has been removed from all
294 dependencies, so let's junk the element itself.
296 Do not do this for System, since that would remove references
297 to the originals of score-grobs, which get then GC'd (a bad
302 /* Note that we still want references to this element to be
303 rearranged, and not silently thrown away, so we keep pointers like
304 {broken_into_{drul, array}, original}
312 mutable_property_alist_ = SCM_EOL;
313 immutable_property_alist_ = SCM_EOL;
315 set_extent (SCM_EOL, Y_AXIS);
316 set_extent (SCM_EOL, X_AXIS);
318 for (int a = X_AXIS; a <= Y_AXIS; a++)
320 dim_cache_[a].offset_callbacks_ = SCM_EOL;
321 dim_cache_[a].offsets_left_ = 0;
326 Grob::handle_prebroken_dependencies ()
328 /* Don't do this in the derived method, since we want to keep access to
329 mutable_property_alist_ centralized. */
332 Item *it = dynamic_cast<Item*> (this);
333 substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
334 original_->mutable_property_alist_);
339 Grob::find_broken_piece (System *) const
344 /* Translate in one direction. */
346 Grob::translate_axis (Real y, Axis a)
348 if (isinf (y) || isnan (y))
349 programming_error (_ (INFINITY_MSG));
351 dim_cache_[a].offset_ += y;
355 /* Find the offset relative to D. If D equals THIS, then it is 0.
356 Otherwise, it recursively defd as
358 OFFSET_ + PARENT_L_->relative_coordinate (D) */
360 Grob::relative_coordinate (Grob const *refp, Axis a) const
365 /* We catch PARENT_L_ == nil case with this, but we crash if we did
366 not ask for the absolute coordinate (ie. REFP == nil.) */
367 if (refp == dim_cache_[a].parent_)
368 return get_offset (a);
370 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
373 /* Invoke callbacks to get offset relative to parent. */
375 Grob::get_offset (Axis a) const
377 Grob *me = (Grob*) this;
378 while (dim_cache_[a].offsets_left_)
380 int l = --me->dim_cache_[a].offsets_left_;
381 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
382 SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
384 Real r = scm_to_double (retval);
385 if (isinf (r) || isnan (r))
387 programming_error (INFINITY_MSG);
390 me->dim_cache_[a].offset_ += r;
392 return dim_cache_[a].offset_;
396 Grob::is_empty (Axis a) const
398 return ! (scm_is_pair (dim_cache_[a].dimension_)
399 || ly_c_procedure_p (dim_cache_[a].dimension_));
403 Grob::extent (Grob *refp, Axis a) const
405 Real x = relative_coordinate (refp, a);
407 Dimension_cache *d = (Dimension_cache *) &dim_cache_[a];
409 if (scm_is_pair (d->dimension_))
411 else if (ly_c_procedure_p (d->dimension_))
412 /* FIXME: add doco on types, and should typecheck maybe? */
413 d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a));
417 if (!scm_is_pair (d->dimension_))
420 ext = ly_scm2interval (d->dimension_);
422 SCM extra = get_property (a == X_AXIS
427 if (scm_is_pair (extra))
429 ext[BIGGER] += scm_to_double (scm_cdr (extra));
430 ext[SMALLER] += scm_to_double (scm_car (extra));
433 extra = get_property (a == X_AXIS
435 : "minimum-Y-extent");
436 if (scm_is_pair (extra))
437 ext.unite (Interval (scm_to_double (scm_car (extra)),
438 scm_to_double (scm_cdr (extra))));
445 /* Find the group-element which has both #this# and #s# */
447 Grob::common_refpoint (Grob const *s, Axis a) const
449 /* I don't like the quadratic aspect of this code, but I see no
450 other way. The largest chain of parents might be 10 high or so,
451 so it shouldn't be a real issue. */
452 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
453 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
461 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
463 for (; scm_is_pair (elist); elist = scm_cdr (elist))
464 if (Grob *s = unsmob_grob (scm_car (elist)))
467 common = common->common_refpoint (s, a);
476 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
478 for (int i = arr.size (); i--; )
479 if (Grob *s = arr[i])
482 common = common->common_refpoint (s, a);
493 SCM meta = get_property ("meta");
494 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
495 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
496 return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
500 Grob::add_offset_callback (SCM cb, Axis a)
502 if (!has_offset_callback (cb, a))
504 dim_cache_[a].offset_callbacks_
505 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
506 dim_cache_[a].offsets_left_ ++;
511 Grob::has_extent_callback (SCM cb, Axis a)const
513 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
517 Grob::has_offset_callback (SCM cb, Axis a)const
519 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
523 Grob::set_extent (SCM dc, Axis a)
525 dim_cache_[a].dimension_ = dc;
529 Grob::set_parent (Grob *g, Axis a)
531 dim_cache_[a].parent_ = g;
534 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
536 Grob::fixup_refpoint (SCM smob)
538 Grob *me = unsmob_grob (smob);
539 for (int a = X_AXIS; a < NO_AXES; a ++)
542 Grob *parent = me->get_parent (ax);
547 if (parent->get_system () != me->get_system () && me->get_system ())
549 Grob *newparent = parent->find_broken_piece (me->get_system ());
550 me->set_parent (newparent, ax);
553 if (Item *i = dynamic_cast<Item*> (me))
555 Item *parenti = dynamic_cast<Item*> (parent);
559 Direction my_dir = i->break_status_dir () ;
560 if (my_dir!= parenti->break_status_dir ())
562 Item *newparent = parenti->find_prebroken_piece (my_dir);
563 me->set_parent (newparent, ax);
572 Grob::warning (String s)const
574 SCM cause = self_scm ();
575 while (Grob *g = unsmob_grob (cause))
576 cause = g->get_property ("cause");
578 if (Music *m = unsmob_music (cause))
579 m->origin ()->warning (s);
585 Grob::programming_error (String s) const
587 s = "Programming error: " + s;
592 /****************************************************
594 ****************************************************/
596 IMPLEMENT_SMOBS (Grob);
597 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
600 Grob::mark_smob (SCM ses)
602 Grob *s = (Grob*) SCM_CELL_WORD_1 (ses);
603 scm_gc_mark (s->immutable_property_alist_);
604 scm_gc_mark (s->key_->self_scm ());
605 for (int a = 0 ; a < 2; a++)
607 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
608 scm_gc_mark (s->dim_cache_[a].dimension_);
610 /* Do not mark the parents. The pointers in the mutable
611 property list form two tree like structures (one for X
612 relations, one for Y relations). Marking these can be done
613 in limited stack space. If we add the parents, we will jump
614 between X and Y in an erratic manner, leading to much more
615 recursion depth (and core dumps if we link to pthreads). */
619 scm_gc_mark (s->original_->self_scm ());
621 s->do_derived_mark ();
622 return s->mutable_property_alist_;
626 Grob::print_smob (SCM s, SCM port, scm_print_state *)
628 Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
630 scm_puts ("#<Grob ", port);
631 scm_puts ((char *) sc->name ().to_str0 (), port);
633 /* Do not print properties, that is too much hassle. */
634 scm_puts (" >", port);
639 Grob::do_derived_mark () const
645 Grob::discretionary_processing ()
652 Grob::internal_has_interface (SCM k)
654 SCM ifs = get_property ("interfaces");
656 return scm_c_memq (k, ifs) != SCM_BOOL_F;
659 /** Return Array of Grobs in SCM list LST */
661 ly_scm2grobs (SCM lst)
663 Link_array<Grob> arr;
665 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
668 arr.push (unsmob_grob (e));
676 Grob::get_key () const
681 /** Return SCM list of Grob array A */
683 ly_grobs2scm (Link_array<Grob> a)
686 for (int i = a.size (); i; i--)
687 s = scm_cons (a[i-1]->self_scm (), s);
693 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
695 ADD_INTERFACE (Grob, "grob-interface",
696 "A grob represents a piece of music notation\n"
698 "All grobs have an X and Y-position on the page. These X and Y positions\n"
699 "are stored in a relative format, so they can easily be combined by\n"
700 "stacking them, hanging one grob to the side of another, and coupling\n"
701 "them into a grouping objects.\n"
703 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
704 "is stored relative to that reference point. For example the X-reference\n"
705 "point of a staccato dot usually is the note head that it applies\n"
706 "to. When the note head is moved, the staccato dot moves along\n"
709 "A grob is often associated with a symbol, but some grobs do not print\n"
710 "any symbols. They take care of grouping objects. For example, there is a\n"
711 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
712 "is also an abstract grob: it only moves around chords, but doesn't print\n"
715 "Grobs have a properties: Scheme variables, that can be read and set. "
716 "They have two types. Immutable variables "
717 "define the default style and behavior. They are shared between many objects. "
718 "They can be changed using @code{\\override} and @code{\\revert}. "
720 "Mutable properties are variables that are specific to one grob. Typically, "
721 "lists of other objects, or results from computations are stored in"
722 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
723 "sets a mutable property. "
726 "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
727 "Y-extent-callback print-function extra-offset spacing-procedure "
728 "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
729 "meta layer before-line-breaking-callback "
730 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
731 "minimum-Y-extent transparent tweak-count tweak-rank"