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"
34 - remove dynamic_cast<Spanner,Item> and put this code into respective
38 #define INFINITY_MSG "Infinity or NaN encountered"
40 Grob::Grob (SCM basicprops)
42 /* FIXME: default should be no callback. */
47 immutable_property_alist_ = basicprops;
48 mutable_property_alist_ = SCM_EOL;
50 /* We do smobify_self () as the first step. Since the object lives
51 on the heap, none of its SCM variables are protected from
52 GC. After smobify_self (), they are. */
55 SCM meta = get_property ("meta");
56 if (scm_is_pair (meta))
58 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
60 /* Switch off interface checks for the moment. */
61 bool itc = internal_type_checking_global_b;
62 internal_type_checking_global_b = false;
63 internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
64 internal_type_checking_global_b = itc;
69 - destill this into a function, so we can re-init the immutable
70 properties with a new BASICPROPS value after
71 creation. Convenient eg. when using \override with
74 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
75 char const*xnames[] = {"X-extent", "Y-extent"};
76 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
78 for (int a = X_AXIS; a <= Y_AXIS; a++)
80 SCM l = get_property (onames[a]);
82 if (scm_ilength (l) >=0)
84 dim_cache_[a].offset_callbacks_ = l;
85 dim_cache_[a].offsets_left_ = scm_ilength (l);
88 programming_error ("[XY]-offset-callbacks must be a list");
90 SCM cb = get_property (enames[a]);
91 SCM xt = get_property (xnames[a]);
93 /* Should change default to empty? */
94 if (is_number_pair (xt))
96 else if (cb != SCM_BOOL_F
97 && !ly_c_procedure_p (cb) && !scm_is_pair (cb)
98 && ly_c_procedure_p (get_property ("print-function")))
99 cb = stencil_extent_proc;
101 dim_cache_[a].dimension_ = cb;
105 Grob::Grob (Grob const &s)
106 : dim_cache_ (s.dim_cache_)
108 original_ = (Grob*) &s;
111 immutable_property_alist_ = s.immutable_property_alist_;
112 mutable_property_alist_ = SCM_EOL;
114 /* No properties are copied. That is the job of
115 handle_broken_dependencies. */
126 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
128 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
130 Grob *s = unsmob_grob (element_smob);
131 Axis a = (Axis) scm_to_int (scm_axis);
133 Stencil *m = s->get_stencil ();
137 return ly_interval2scm (e);
142 robust_relative_extent (Grob*me, Grob*refp, Axis a)
144 Interval ext = me->extent (refp, a);
147 ext.add_point (me->relative_coordinate (refp, a));
154 Grob::get_layout () const
156 return pscore_ ? pscore_->layout_ : 0;
160 /* Recursively track all dependencies of this Grob. The status_ field
161 is used as a mark-field. It is marked with BUSY during execution
162 of this function, and marked with FINAL when finished.
164 FUNCPTR is the function to call to update this element. */
166 Grob::calculate_dependencies (int final, int busy, SCM funcname)
168 if (status_ >= final)
173 programming_error ("Element is busy, come back later");
179 for (SCM d = get_property ("dependencies"); scm_is_pair (d);
181 unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname);
183 SCM proc = internal_get_property (funcname);
184 if (ly_c_procedure_p (proc))
185 scm_call_1 (proc, this->self_scm ());
191 Grob::get_stencil () const
196 SCM stil = get_property ("stencil");
197 if (unsmob_stencil (stil))
198 return unsmob_stencil (stil);
200 stil = get_uncached_stencil ();
204 Grob *me = (Grob*) this;
205 me->set_property ("stencil", stil);
208 return unsmob_stencil (stil);
212 Grob::get_uncached_stencil () const
214 SCM proc = get_property ("print-function");
217 if (ly_c_procedure_p (proc))
218 stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
220 if (Stencil *m = unsmob_stencil (stil))
222 if (to_boolean (get_property ("transparent")))
223 stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
226 SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm(),
228 stil = Stencil (m->extent_box (),expr). smobbed_copy ();
240 Grob::do_break_processing ()
245 Grob::get_system () const
251 Grob::add_dependency (Grob *e)
254 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),
257 programming_error ("Null dependency added");
261 Grob::handle_broken_dependencies ()
263 Spanner *sp = dynamic_cast<Spanner*> (this);
268 /* THIS, SP is the original spanner. We use a special function
269 because some Spanners have enormously long lists in their
270 properties, and a special function fixes FOO */
271 for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
272 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
274 System *system = get_system ();
277 && system && common_refpoint (system, X_AXIS)
278 && common_refpoint (system, Y_AXIS))
279 substitute_mutable_properties (system
280 ? system->self_scm () : SCM_UNDEFINED,
281 mutable_property_alist_);
282 else if (dynamic_cast <System*> (this))
283 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
285 /* THIS element is `invalid'; it has been removed from all
286 dependencies, so let's junk the element itself.
288 Do not do this for System, since that would remove references
289 to the originals of score-grobs, which get then GC'd (a bad
294 /* Note that we still want references to this element to be
295 rearranged, and not silently thrown away, so we keep pointers like
296 {broken_into_{drul, array}, original}
304 mutable_property_alist_ = SCM_EOL;
305 immutable_property_alist_ = SCM_EOL;
307 set_extent (SCM_EOL, Y_AXIS);
308 set_extent (SCM_EOL, X_AXIS);
310 for (int a = X_AXIS; a <= Y_AXIS; a++)
312 dim_cache_[a].offset_callbacks_ = SCM_EOL;
313 dim_cache_[a].offsets_left_ = 0;
318 Grob::handle_prebroken_dependencies ()
320 /* Don't do this in the derived method, since we want to keep access to
321 mutable_property_alist_ centralized. */
324 Item *it = dynamic_cast<Item*> (this);
325 substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
326 original_->mutable_property_alist_);
331 Grob::find_broken_piece (System *) const
336 /* Translate in one direction. */
338 Grob::translate_axis (Real y, Axis a)
340 if (isinf (y) || isnan (y))
341 programming_error (_ (INFINITY_MSG));
343 dim_cache_[a].offset_ += y;
347 /* Find the offset relative to D. If D equals THIS, then it is 0.
348 Otherwise, it recursively defd as
350 OFFSET_ + PARENT_L_->relative_coordinate (D) */
352 Grob::relative_coordinate (Grob const *refp, Axis a) const
357 /* We catch PARENT_L_ == nil case with this, but we crash if we did
358 not ask for the absolute coordinate (ie. REFP == nil.) */
359 if (refp == dim_cache_[a].parent_)
360 return get_offset (a);
362 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
365 /* Invoke callbacks to get offset relative to parent. */
367 Grob::get_offset (Axis a) const
369 Grob *me = (Grob*) this;
370 while (dim_cache_[a].offsets_left_)
372 int l = --me->dim_cache_[a].offsets_left_;
373 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
374 SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
376 Real r = scm_to_double (retval);
377 if (isinf (r) || isnan (r))
379 programming_error (INFINITY_MSG);
382 me->dim_cache_[a].offset_ += r;
384 return dim_cache_[a].offset_;
388 Grob::is_empty (Axis a) const
390 return ! (scm_is_pair (dim_cache_[a].dimension_)
391 || ly_c_procedure_p (dim_cache_[a].dimension_));
395 Grob::extent (Grob *refp, Axis a) const
397 Real x = relative_coordinate (refp, a);
399 Dimension_cache *d = (Dimension_cache *) &dim_cache_[a];
401 if (scm_is_pair (d->dimension_))
403 else if (ly_c_procedure_p (d->dimension_))
404 /* FIXME: add doco on types, and should typecheck maybe? */
405 d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a));
409 if (!scm_is_pair (d->dimension_))
412 ext = ly_scm2interval (d->dimension_);
414 SCM extra = get_property (a == X_AXIS
419 if (scm_is_pair (extra))
421 ext[BIGGER] += scm_to_double (scm_cdr (extra));
422 ext[SMALLER] += scm_to_double (scm_car (extra));
425 extra = get_property (a == X_AXIS
427 : "minimum-Y-extent");
428 if (scm_is_pair (extra))
429 ext.unite (Interval (scm_to_double (scm_car (extra)),
430 scm_to_double (scm_cdr (extra))));
437 /* Find the group-element which has both #this# and #s# */
439 Grob::common_refpoint (Grob const *s, Axis a) const
441 /* I don't like the quadratic aspect of this code, but I see no
442 other way. The largest chain of parents might be 10 high or so,
443 so it shouldn't be a real issue. */
444 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
445 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
453 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
455 for (; scm_is_pair (elist); elist = scm_cdr (elist))
456 if (Grob *s = unsmob_grob (scm_car (elist)))
459 common = common->common_refpoint (s, a);
468 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
470 for (int i = arr.size (); i--; )
471 if (Grob *s = arr[i])
474 common = common->common_refpoint (s, a);
485 SCM meta = get_property ("meta");
486 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
487 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
488 return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
492 Grob::add_offset_callback (SCM cb, Axis a)
494 if (!has_offset_callback (cb, a))
496 dim_cache_[a].offset_callbacks_
497 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
498 dim_cache_[a].offsets_left_ ++;
503 Grob::has_extent_callback (SCM cb, Axis a)const
505 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
509 Grob::has_offset_callback (SCM cb, Axis a)const
511 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
515 Grob::set_extent (SCM dc, Axis a)
517 dim_cache_[a].dimension_ = dc;
521 Grob::set_parent (Grob *g, Axis a)
523 dim_cache_[a].parent_ = g;
526 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
528 Grob::fixup_refpoint (SCM smob)
530 Grob *me = unsmob_grob (smob);
531 for (int a = X_AXIS; a < NO_AXES; a ++)
534 Grob *parent = me->get_parent (ax);
539 if (parent->get_system () != me->get_system () && me->get_system ())
541 Grob *newparent = parent->find_broken_piece (me->get_system ());
542 me->set_parent (newparent, ax);
545 if (Item *i = dynamic_cast<Item*> (me))
547 Item *parenti = dynamic_cast<Item*> (parent);
551 Direction my_dir = i->break_status_dir () ;
552 if (my_dir!= parenti->break_status_dir ())
554 Item *newparent = parenti->find_prebroken_piece (my_dir);
555 me->set_parent (newparent, ax);
564 Grob::warning (String s)const
566 SCM cause = self_scm ();
567 while (Grob *g = unsmob_grob (cause))
568 cause = g->get_property ("cause");
570 if (Music *m = unsmob_music (cause))
571 m->origin ()->warning (s);
577 Grob::programming_error (String s) const
579 s = "Programming error: " + s;
584 /****************************************************
586 ****************************************************/
588 IMPLEMENT_SMOBS (Grob);
589 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
592 Grob::mark_smob (SCM ses)
594 Grob *s = (Grob*) SCM_CELL_WORD_1 (ses);
595 scm_gc_mark (s->immutable_property_alist_);
597 for (int a = 0 ; a < 2; a++)
599 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
600 scm_gc_mark (s->dim_cache_[a].dimension_);
602 /* Do not mark the parents. The pointers in the mutable
603 property list form two tree like structures (one for X
604 relations, one for Y relations). Marking these can be done
605 in limited stack space. If we add the parents, we will jump
606 between X and Y in an erratic manner, leading to much more
607 recursion depth (and core dumps if we link to pthreads). */
611 scm_gc_mark (s->original_->self_scm ());
613 s->do_derived_mark ();
614 return s->mutable_property_alist_;
618 Grob::print_smob (SCM s, SCM port, scm_print_state *)
620 Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
622 scm_puts ("#<Grob ", port);
623 scm_puts ((char *) sc->name ().to_str0 (), port);
625 /* Do not print properties, that is too much hassle. */
626 scm_puts (" >", port);
631 Grob::do_derived_mark () const
637 Grob::discretionary_processing ()
642 Grob::internal_has_interface (SCM k)
644 SCM ifs = get_property ("interfaces");
646 return scm_c_memq (k, ifs) != SCM_BOOL_F;
649 /** Return Array of Grobs in SCM list LST */
651 ly_scm2grobs (SCM lst)
653 Link_array<Grob> arr;
655 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
658 arr.push (unsmob_grob (e));
665 /** Return SCM list of Grob array A */
667 ly_grobs2scm (Link_array<Grob> a)
670 for (int i = a.size (); i; i--)
671 s = scm_cons (a[i-1]->self_scm (), s);
677 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
679 ADD_INTERFACE (Grob, "grob-interface",
680 "A grob represents a piece of music notation\n"
682 "All grobs have an X and Y-position on the page. These X and Y positions\n"
683 "are stored in a relative format, so they can easily be combined by\n"
684 "stacking them, hanging one grob to the side of another, and coupling\n"
685 "them into a grouping objects.\n"
687 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
688 "is stored relative to that reference point. For example the X-reference\n"
689 "point of a staccato dot usually is the note head that it applies\n"
690 "to. When the note head is moved, the staccato dot moves along\n"
693 "A grob is often associated with a symbol, but some grobs do not print\n"
694 "any symbols. They take care of grouping objects. For example, there is a\n"
695 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
696 "is also an abstract grob: it only moves around chords, but doesn't print\n"
699 "Grobs have a properties: Scheme variables, that can be read and set. "
700 "They have two types. Immutable variables "
701 "define the default style and behavior. They are shared between many objects. "
702 "They can be changed using @code{\\override} and @code{\\revert}. "
704 "Mutable properties are variables that are specific to one grob. Typically, "
705 "lists of other objects, or results from computations are stored in"
706 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
707 "sets a mutable property. "
710 "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
711 "Y-extent-callback print-function extra-offset spacing-procedure "
712 "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
713 "meta layer before-line-breaking-callback "
714 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
715 // FIXME: page-penalty?
716 "minimum-Y-extent page-penalty transparent "