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>
14 #include "input-smob.hh"
16 #include "pointer-group-interface.hh"
18 #include "paper-score.hh"
27 #include "paper-score.hh"
28 #include "ly-smobs.icc"
29 #include "output-def.hh"
31 MAKE_SCHEME_CALLBACK(Grob, same_axis_parent_positioning, 2);
33 Grob::same_axis_parent_positioning (SCM element_smob, SCM axis)
35 Grob *me = unsmob_grob (element_smob);
36 Axis ax = Axis (scm_to_int (axis));
38 Grob *par = me->get_parent (ax);
40 par->get_property ("positioning-done");
42 return scm_from_double (0.0);
45 MAKE_SCHEME_CALLBACK(Grob,other_axis_parent_positioning, 2);
47 Grob::other_axis_parent_positioning (SCM element_smob, SCM axis)
49 Grob *me = unsmob_grob (element_smob);
50 Axis ax = other_axis ((Axis) scm_to_int (axis));
52 Grob *par = me->get_parent (ax);
54 par->get_property ("positioning-done");
56 return scm_from_double (0.0);
62 Grob::clone (int count) const
64 return new Grob (*this, count);
69 - remove dynamic_cast<Spanner, Item> and put this code into respective
73 #define INFINITY_MSG "Infinity or NaN encountered"
75 Grob::Grob (SCM basicprops,
76 Object_key const *key)
79 /* FIXME: default should be no callback. */
84 interfaces_ = SCM_EOL;
85 immutable_property_alist_ = basicprops;
86 mutable_property_alist_ = SCM_EOL;
87 object_alist_ = SCM_EOL;
88 property_callbacks_ = SCM_EOL;
90 /* We do smobify_self () as the first step. Since the object lives
91 on the heap, none of its SCM variables are protected from
92 GC. After smobify_self (), they are. */
96 We always get a new key object for a new grob.
99 ((Object_key *)key_)->unprotect ();
101 SCM meta = get_property ("meta");
102 if (scm_is_pair (meta))
103 interfaces_ = scm_cdr (scm_assoc (ly_symbol2scm ("interfaces"), meta));
107 - destill this into a function, so we can re-init the immutable
108 properties with a new BASICPROPS value after
109 creation. Convenient eg. when using \override with
112 property_callbacks_ = get_property ("callbacks");
114 SCM off_callbacks[] = {
115 get_property ("X-offset-callbacks"),
116 get_property ("Y-offset-callbacks")
119 get_property ("X-extent"),
120 get_property ("Y-extent")
122 SCM extent_callbacks[] = {
123 get_property ("X-extent-callback"),
124 get_property ("Y-extent-callback")
127 for (int a = X_AXIS; a <= Y_AXIS; a++)
129 SCM l = off_callbacks[a];
131 if (scm_ilength (l) >= 0)
133 dim_cache_[a].offset_callbacks_ = l;
134 dim_cache_[a].offsets_left_ = scm_ilength (l);
137 programming_error ("[XY]-offset-callbacks must be a list");
139 SCM cb = extent_callbacks[a];
140 if (cb == SCM_BOOL_F)
141 dim_cache_[a].dimension_ = SCM_BOOL_F;
144 if (is_number_pair (xt))
145 dim_cache_[a].dimension_ = xt;
146 else if (ly_is_procedure (cb))
147 dim_cache_[a].dimension_callback_ = cb;
148 else if (cb == SCM_EOL
149 && ly_is_procedure (ly_assoc_get (ly_symbol2scm ("stencil"),
150 property_callbacks_, SCM_BOOL_F)))
151 dim_cache_[a].dimension_callback_ = stencil_extent_proc;
156 Grob::Grob (Grob const &s, int copy_index)
157 : dim_cache_ (s.dim_cache_)
159 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
160 original_ = (Grob *) & s;
163 immutable_property_alist_ = s.immutable_property_alist_;
164 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
165 interfaces_ = s.interfaces_;
166 property_callbacks_ = s.property_callbacks_;
167 object_alist_ = SCM_EOL;
173 ((Object_key *)key_)->unprotect ();
180 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
182 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
184 Grob *s = unsmob_grob (element_smob);
185 Axis a = (Axis) scm_to_int (scm_axis);
187 Stencil *m = s->get_stencil ();
191 return ly_interval2scm (e);
195 robust_relative_extent (Grob *me, Grob *refp, Axis a)
197 Interval ext = me->extent (refp, a);
199 ext.add_point (me->relative_coordinate (refp, a));
205 Grob::get_layout () const
207 return pscore_ ? pscore_->layout () : 0;
211 Grob::get_stencil () const
216 SCM stil = get_property ("stencil");
217 return unsmob_stencil (stil);
221 Grob::get_print_stencil () const
223 SCM stil = get_property ("stencil");
226 if (Stencil *m = unsmob_stencil (stil))
229 if (to_boolean (get_property ("transparent")))
230 retval = Stencil (m->extent_box (), SCM_EOL);
233 SCM expr = m->expr ();
234 if (point_and_click_global)
235 expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm (), expr);
237 retval = Stencil (m->extent_box (), expr);
240 /* color support... see interpret_stencil_expression () for more... */
241 SCM color = get_property ("color");
242 if (color != SCM_EOL)
244 m = unsmob_stencil (stil);
245 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
249 retval = Stencil (m->extent_box (), expr);
261 Grob::do_break_processing ()
266 Grob::get_system () const
273 Grob::handle_broken_dependencies ()
275 Spanner *sp = dynamic_cast<Spanner *> (this);
280 /* THIS, SP is the original spanner. We use a special function
281 because some Spanners have enormously long lists in their
282 properties, and a special function fixes FOO */
284 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
285 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
287 System *system = get_system ();
291 && common_refpoint (system, X_AXIS)
292 && common_refpoint (system, Y_AXIS))
293 substitute_object_links (system->self_scm (), object_alist_);
294 else if (dynamic_cast<System *> (this))
295 substitute_object_links (SCM_UNDEFINED, object_alist_);
297 /* THIS element is `invalid'; it has been removed from all
298 dependencies, so let's junk the element itself.
300 Do not do this for System, since that would remove references
301 to the originals of score-grobs, which get then GC'd (a bad
306 /* Note that we still want references to this element to be
307 rearranged, and not silently thrown away, so we keep pointers like
308 {broken_into_{drul, array}, original}
316 mutable_property_alist_ = SCM_EOL;
317 object_alist_ = SCM_EOL;
318 property_callbacks_ = SCM_EOL;
319 immutable_property_alist_ = SCM_EOL;
320 interfaces_ = SCM_EOL;
322 set_extent (SCM_EOL, Y_AXIS);
323 set_extent (SCM_EOL, X_AXIS);
325 set_extent_callback (SCM_EOL, Y_AXIS);
326 set_extent_callback (SCM_EOL, X_AXIS);
328 for (int a = X_AXIS; a <= Y_AXIS; a++)
330 dim_cache_[a].offset_callbacks_ = SCM_EOL;
331 dim_cache_[a].offsets_left_ = 0;
336 Grob::handle_prebroken_dependencies ()
338 /* Don't do this in the derived method, since we want to keep access to
339 object_alist_ centralized. */
342 Item *it = dynamic_cast<Item *> (this);
343 substitute_object_links (scm_from_int (it->break_status_dir ()),
344 original_->object_alist_);
349 Grob::find_broken_piece (System *) const
354 /* Translate in one direction. */
356 Grob::translate_axis (Real y, Axis a)
358 if (isinf (y) || isnan (y))
359 programming_error (_ (INFINITY_MSG));
361 dim_cache_[a].offset_ += y;
364 /* Find the offset relative to D. If D equals THIS, then it is 0.
365 Otherwise, it recursively defd as
367 OFFSET_ + PARENT_L_->relative_coordinate (D) */
369 Grob::relative_coordinate (Grob const *refp, Axis a) const
374 /* We catch PARENT_L_ == nil case with this, but we crash if we did
375 not ask for the absolute coordinate (ie. REFP == nil.) */
376 if (refp == dim_cache_[a].parent_)
377 return get_offset (a);
379 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
382 /* Invoke callbacks to get offset relative to parent. */
384 Grob::get_offset (Axis a) const
386 Grob *me = (Grob *) this;
387 while (dim_cache_[a].offsets_left_)
389 int l = --me->dim_cache_[a].offsets_left_;
390 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_from_int (l));
391 SCM retval = scm_call_2 (cb, self_scm (), scm_from_int (a));
393 Real r = scm_to_double (retval);
394 if (isinf (r) || isnan (r))
396 programming_error (INFINITY_MSG);
399 me->dim_cache_[a].offset_ += r;
401 return dim_cache_[a].offset_;
405 Grob::is_empty (Axis a) const
407 return !(scm_is_pair (dim_cache_[a].dimension_)
408 || ly_is_procedure (dim_cache_[a].dimension_callback_));
412 Grob::flush_extent_cache (Axis axis)
414 Dimension_cache *d = &dim_cache_[axis];
415 if (ly_is_procedure (d->dimension_callback_)
416 && scm_is_pair (d->dimension_))
418 d->dimension_ = SCM_EOL;
420 if (get_parent (axis))
421 get_parent (axis)->flush_extent_cache (axis);
426 Grob::extent (Grob *refp, Axis a) const
428 Real x = relative_coordinate (refp, a);
430 Dimension_cache *d = (Dimension_cache *) & dim_cache_[a];
433 SCM dimpair = d->dimension_;
434 if (scm_is_pair (dimpair))
436 else if (ly_is_procedure (d->dimension_callback_)
437 && d->dimension_ == SCM_EOL)
438 d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_from_int (a));
442 if (!scm_is_pair (d->dimension_))
445 ext = ly_scm2interval (d->dimension_);
447 SCM extra = (a == X_AXIS)
448 ? get_property ("extra-X-extent")
449 : get_property ("extra-Y-extent");
452 if (scm_is_pair (extra))
454 ext[BIGGER] += scm_to_double (scm_cdr (extra));
455 ext[SMALLER] += scm_to_double (scm_car (extra));
458 extra = (a == X_AXIS)
459 ? get_property ("minimum-X-extent")
460 : get_property ("minimum-Y-extent");
462 if (scm_is_pair (extra))
463 ext.unite (Interval (scm_to_double (scm_car (extra)),
464 scm_to_double (scm_cdr (extra))));
471 /* Find the group-element which has both #this# and #s# */
473 Grob::common_refpoint (Grob const *s, Axis a) const
475 /* I don't like the quadratic aspect of this code, but I see no
476 other way. The largest chain of parents might be 10 high or so,
477 so it shouldn't be a real issue. */
478 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
479 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
487 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
489 for (; scm_is_pair (elist); elist = scm_cdr (elist))
490 if (Grob *s = unsmob_grob (scm_car (elist)))
493 common = common->common_refpoint (s, a);
502 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
504 for (int i = arr.size (); i--;)
505 if (Grob *s = arr[i])
508 common = common->common_refpoint (s, a);
519 SCM meta = get_property ("meta");
520 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
521 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
522 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
526 Grob::add_offset_callback (SCM cb, Axis a)
528 if (!has_offset_callback (cb, a))
530 dim_cache_[a].offset_callbacks_
531 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
532 dim_cache_[a].offsets_left_++;
537 Grob::has_extent_callback (SCM cb, Axis a) const
539 return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
543 Grob::has_offset_callback (SCM cb, Axis a) const
545 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
549 Grob::set_extent (SCM dc, Axis a)
551 dim_cache_[a].dimension_ = dc;
555 Grob::set_extent_callback (SCM dc, Axis a)
557 dim_cache_[a].dimension_callback_ = dc;
561 Grob::set_parent (Grob *g, Axis a)
563 dim_cache_[a].parent_ = g;
567 Grob::fixup_refpoint ()
569 for (int a = X_AXIS; a < NO_AXES; a++)
572 Grob *parent = get_parent (ax);
577 if (parent->get_system () != get_system () && get_system ())
579 Grob *newparent = parent->find_broken_piece (get_system ());
580 set_parent (newparent, ax);
583 if (Item *i = dynamic_cast<Item *> (this))
585 Item *parenti = dynamic_cast<Item *> (parent);
589 Direction my_dir = i->break_status_dir ();
590 if (my_dir != parenti->break_status_dir ())
592 Item *newparent = parenti->find_prebroken_piece (my_dir);
593 set_parent (newparent, ax);
601 Grob::warning (String s) const
603 SCM cause = self_scm ();
604 while (Grob *g = unsmob_grob (cause))
605 cause = g->get_property ("cause");
607 if (Music *m = unsmob_music (cause))
608 m->origin ()->warning (s);
614 Grob::programming_error (String s) const
616 SCM cause = self_scm ();
617 while (Grob *g = unsmob_grob (cause))
618 cause = g->get_property ("cause");
620 s = _f ("programming error: %s", s);
622 if (Music *m = unsmob_music (cause))
623 m->origin ()->message (s);
628 Grob::discretionary_processing ()
633 Grob::internal_has_interface (SCM k)
635 return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
639 Grob::get_parent (Axis a) const
641 return dim_cache_[a].parent_;
644 /** Return Array of Grobs in SCM list LST */
646 ly_scm2grobs (SCM lst)
648 Link_array<Grob> arr;
650 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
653 arr.push (unsmob_grob (e));
661 Grob::get_key () const
666 /** Return SCM list of Grob array A */
668 ly_grobs2scm (Link_array<Grob> a)
671 for (int i = a.size (); i; i--)
672 s = scm_cons (a[i - 1]->self_scm (), s);
677 ADD_INTERFACE (Grob, "grob-interface",
678 "A grob represents a piece of music notation\n"
680 "All grobs have an X and Y-position on the page. These X and Y positions\n"
681 "are stored in a relative format, so they can easily be combined by\n"
682 "stacking them, hanging one grob to the side of another, and coupling\n"
683 "them into a grouping objects.\n"
685 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
686 "is stored relative to that reference point. For example the X-reference\n"
687 "point of a staccato dot usually is the note head that it applies\n"
688 "to. When the note head is moved, the staccato dot moves along\n"
691 "A grob is often associated with a symbol, but some grobs do not print\n"
692 "any symbols. They take care of grouping objects. For example, there is a\n"
693 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
694 "is also an abstract grob: it only moves around chords, but doesn't print\n"
697 "Grobs have a properties: Scheme variables, that can be read and set. "
698 "They have two types. Immutable variables "
699 "define the default style and behavior. They are shared between many objects. "
700 "They can be changed using @code{\\override} and @code{\\revert}. "
702 "Mutable properties are variables that are specific to one grob. Typically, "
703 "lists of other objects, or results from computations are stored in"
704 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
705 "sets a mutable property. "
707 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
708 "are dummies that are not user-serviceable. "
715 "X-offset-callbacks "
718 "Y-offset-callbacks "
719 "after-line-breaking "
720 "axis-group-parent-X "
721 "axis-group-parent-Y "
722 "before-line-breaking "