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"),
238 retval = Stencil (m->extent_box (), expr);
241 /* color support... see interpret_stencil_expression () for more... */
242 SCM color = get_property ("color");
243 if (color != SCM_EOL)
245 m = unsmob_stencil (stil);
246 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
250 retval = Stencil (m->extent_box (), expr);
262 Grob::do_break_processing ()
267 Grob::get_system () const
274 Grob::handle_broken_dependencies ()
276 Spanner *sp = dynamic_cast<Spanner *> (this);
281 /* THIS, SP is the original spanner. We use a special function
282 because some Spanners have enormously long lists in their
283 properties, and a special function fixes FOO */
285 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
286 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
288 System *system = get_system ();
292 && common_refpoint (system, X_AXIS)
293 && common_refpoint (system, Y_AXIS))
294 substitute_object_links (system->self_scm (), object_alist_);
295 else if (dynamic_cast<System *> (this))
296 substitute_object_links (SCM_UNDEFINED, object_alist_);
298 /* THIS element is `invalid'; it has been removed from all
299 dependencies, so let's junk the element itself.
301 Do not do this for System, since that would remove references
302 to the originals of score-grobs, which get then GC'd (a bad
307 /* Note that we still want references to this element to be
308 rearranged, and not silently thrown away, so we keep pointers like
309 {broken_into_{drul, array}, original}
317 mutable_property_alist_ = SCM_EOL;
318 object_alist_ = SCM_EOL;
319 property_callbacks_ = SCM_EOL;
320 immutable_property_alist_ = SCM_EOL;
321 interfaces_ = SCM_EOL;
323 set_extent (SCM_EOL, Y_AXIS);
324 set_extent (SCM_EOL, X_AXIS);
326 set_extent_callback (SCM_EOL, Y_AXIS);
327 set_extent_callback (SCM_EOL, X_AXIS);
329 for (int a = X_AXIS; a <= Y_AXIS; a++)
331 dim_cache_[a].offset_callbacks_ = SCM_EOL;
332 dim_cache_[a].offsets_left_ = 0;
337 Grob::handle_prebroken_dependencies ()
339 /* Don't do this in the derived method, since we want to keep access to
340 object_alist_ centralized. */
343 Item *it = dynamic_cast<Item *> (this);
344 substitute_object_links (scm_from_int (it->break_status_dir ()),
345 original_->object_alist_);
350 Grob::find_broken_piece (System *) const
355 /* Translate in one direction. */
357 Grob::translate_axis (Real y, Axis a)
359 if (isinf (y) || isnan (y))
360 programming_error (_ (INFINITY_MSG));
362 dim_cache_[a].offset_ += y;
365 /* Find the offset relative to D. If D equals THIS, then it is 0.
366 Otherwise, it recursively defd as
368 OFFSET_ + PARENT_L_->relative_coordinate (D) */
370 Grob::relative_coordinate (Grob const *refp, Axis a) const
375 /* We catch PARENT_L_ == nil case with this, but we crash if we did
376 not ask for the absolute coordinate (ie. REFP == nil.) */
377 if (refp == dim_cache_[a].parent_)
378 return get_offset (a);
380 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
383 /* Invoke callbacks to get offset relative to parent. */
385 Grob::get_offset (Axis a) const
387 Grob *me = (Grob *) this;
388 while (dim_cache_[a].offsets_left_)
390 int l = --me->dim_cache_[a].offsets_left_;
391 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_from_int (l));
392 SCM retval = scm_call_2 (cb, self_scm (), scm_from_int (a));
394 Real r = scm_to_double (retval);
395 if (isinf (r) || isnan (r))
397 programming_error (INFINITY_MSG);
400 me->dim_cache_[a].offset_ += r;
402 return dim_cache_[a].offset_;
406 Grob::is_empty (Axis a) const
408 return !(scm_is_pair (dim_cache_[a].dimension_)
409 || ly_is_procedure (dim_cache_[a].dimension_callback_));
413 Grob::flush_extent_cache (Axis axis)
415 Dimension_cache *d = &dim_cache_[axis];
416 if (ly_is_procedure (d->dimension_callback_)
417 && scm_is_pair (d->dimension_))
419 d->dimension_ = SCM_EOL;
421 if (get_parent (axis))
422 get_parent (axis)->flush_extent_cache (axis);
427 Grob::extent (Grob *refp, Axis a) const
429 Real x = relative_coordinate (refp, a);
431 Dimension_cache *d = (Dimension_cache *) & dim_cache_[a];
434 SCM dimpair = d->dimension_;
435 if (scm_is_pair (dimpair))
437 else if (ly_is_procedure (d->dimension_callback_)
438 && d->dimension_ == SCM_EOL)
439 d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_from_int (a));
443 if (!scm_is_pair (d->dimension_))
446 ext = ly_scm2interval (d->dimension_);
448 SCM extra = (a == X_AXIS)
449 ? get_property ("extra-X-extent")
450 : get_property ("extra-Y-extent");
453 if (scm_is_pair (extra))
455 ext[BIGGER] += scm_to_double (scm_cdr (extra));
456 ext[SMALLER] += scm_to_double (scm_car (extra));
459 extra = (a == X_AXIS)
460 ? get_property ("minimum-X-extent")
461 : get_property ("minimum-Y-extent");
463 if (scm_is_pair (extra))
464 ext.unite (Interval (scm_to_double (scm_car (extra)),
465 scm_to_double (scm_cdr (extra))));
472 /* Find the group-element which has both #this# and #s# */
474 Grob::common_refpoint (Grob const *s, Axis a) const
476 /* I don't like the quadratic aspect of this code, but I see no
477 other way. The largest chain of parents might be 10 high or so,
478 so it shouldn't be a real issue. */
479 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
480 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
488 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
490 for (; scm_is_pair (elist); elist = scm_cdr (elist))
491 if (Grob *s = unsmob_grob (scm_car (elist)))
494 common = common->common_refpoint (s, a);
503 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
505 for (int i = arr.size (); i--;)
506 if (Grob *s = arr[i])
509 common = common->common_refpoint (s, a);
520 SCM meta = get_property ("meta");
521 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
522 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
523 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
527 Grob::add_offset_callback (SCM cb, Axis a)
529 if (!has_offset_callback (cb, a))
531 dim_cache_[a].offset_callbacks_
532 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
533 dim_cache_[a].offsets_left_++;
538 Grob::has_extent_callback (SCM cb, Axis a) const
540 return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
544 Grob::has_offset_callback (SCM cb, Axis a) const
546 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
550 Grob::set_extent (SCM dc, Axis a)
552 dim_cache_[a].dimension_ = dc;
556 Grob::set_extent_callback (SCM dc, Axis a)
558 dim_cache_[a].dimension_callback_ = dc;
562 Grob::set_parent (Grob *g, Axis a)
564 dim_cache_[a].parent_ = g;
568 Grob::fixup_refpoint ()
570 for (int a = X_AXIS; a < NO_AXES; a++)
573 Grob *parent = get_parent (ax);
578 if (parent->get_system () != get_system () && get_system ())
580 Grob *newparent = parent->find_broken_piece (get_system ());
581 set_parent (newparent, ax);
584 if (Item *i = dynamic_cast<Item *> (this))
586 Item *parenti = dynamic_cast<Item *> (parent);
590 Direction my_dir = i->break_status_dir ();
591 if (my_dir != parenti->break_status_dir ())
593 Item *newparent = parenti->find_prebroken_piece (my_dir);
594 set_parent (newparent, ax);
602 Grob::warning (String s) const
604 SCM cause = self_scm ();
605 while (Grob *g = unsmob_grob (cause))
606 cause = g->get_property ("cause");
608 if (Music *m = unsmob_music (cause))
609 m->origin ()->warning (s);
615 Grob::programming_error (String s) const
617 SCM cause = self_scm ();
618 while (Grob *g = unsmob_grob (cause))
619 cause = g->get_property ("cause");
621 s = _f ("programming error: %s", s);
623 if (Music *m = unsmob_music (cause))
624 m->origin ()->message (s);
629 Grob::discretionary_processing ()
634 Grob::internal_has_interface (SCM k)
636 return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
640 Grob::get_parent (Axis a) const
642 return dim_cache_[a].parent_;
645 /** Return Array of Grobs in SCM list LST */
647 ly_scm2grobs (SCM lst)
649 Link_array<Grob> arr;
651 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
654 arr.push (unsmob_grob (e));
662 Grob::get_key () const
667 /** Return SCM list of Grob array A */
669 ly_grobs2scm (Link_array<Grob> a)
672 for (int i = a.size (); i; i--)
673 s = scm_cons (a[i - 1]->self_scm (), s);
678 ADD_INTERFACE (Grob, "grob-interface",
679 "A grob represents a piece of music notation\n"
681 "All grobs have an X and Y-position on the page. These X and Y positions\n"
682 "are stored in a relative format, so they can easily be combined by\n"
683 "stacking them, hanging one grob to the side of another, and coupling\n"
684 "them into a grouping objects.\n"
686 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
687 "is stored relative to that reference point. For example the X-reference\n"
688 "point of a staccato dot usually is the note head that it applies\n"
689 "to. When the note head is moved, the staccato dot moves along\n"
692 "A grob is often associated with a symbol, but some grobs do not print\n"
693 "any symbols. They take care of grouping objects. For example, there is a\n"
694 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
695 "is also an abstract grob: it only moves around chords, but doesn't print\n"
698 "Grobs have a properties: Scheme variables, that can be read and set. "
699 "They have two types. Immutable variables "
700 "define the default style and behavior. They are shared between many objects. "
701 "They can be changed using @code{\\override} and @code{\\revert}. "
703 "Mutable properties are variables that are specific to one grob. Typically, "
704 "lists of other objects, or results from computations are stored in"
705 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
706 "sets a mutable property. "
708 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
709 "are dummies that are not user-serviceable. "
716 "X-offset-callbacks "
719 "Y-offset-callbacks "
720 "after-line-breaking "
721 "axis-group-parent-X "
722 "axis-group-parent-Y "
723 "before-line-breaking "