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. */
83 interfaces_ = SCM_EOL;
84 immutable_property_alist_ = basicprops;
85 mutable_property_alist_ = SCM_EOL;
86 object_alist_ = SCM_EOL;
88 /* We do smobify_self () as the first step. Since the object lives
89 on the heap, none of its SCM variables are protected from
90 GC. After smobify_self (), they are. */
94 We always get a new key object for a new grob.
97 ((Object_key *)key_)->unprotect ();
99 SCM meta = get_property ("meta");
100 if (scm_is_pair (meta))
101 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
105 - destill this into a function, so we can re-init the immutable
106 properties with a new BASICPROPS value after
107 creation. Convenient eg. when using \override with
110 SCM off_callbacks[] = {
111 get_property ("X-offset-callbacks"),
112 get_property ("Y-offset-callbacks")
115 for (int a = X_AXIS; a <= Y_AXIS; a++)
117 SCM l = off_callbacks[a];
119 if (scm_ilength (l) >= 0)
121 dim_cache_[a].offset_callbacks_ = l;
122 dim_cache_[a].offsets_left_ = scm_ilength (l);
125 programming_error ("[XY]-offset-callbacks must be a list");
130 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
131 set_property ("X-extent", Grob::stencil_width_proc);
132 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
133 set_property ("Y-extent", Grob::stencil_height_proc);
136 Grob::Grob (Grob const &s, int copy_index)
137 : dim_cache_ (s.dim_cache_)
139 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
140 original_ = (Grob *) & s;
143 immutable_property_alist_ = s.immutable_property_alist_;
144 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
145 interfaces_ = s.interfaces_;
146 object_alist_ = SCM_EOL;
152 ((Object_key *)key_)->unprotect ();
160 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
162 Grob::stencil_height (SCM element_smob)
164 Grob *me = unsmob_grob (element_smob);
165 return stencil_extent (me, Y_AXIS);
168 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
170 Grob::stencil_width (SCM element_smob)
172 Grob *me = unsmob_grob (element_smob);
173 return stencil_extent (me, X_AXIS);
177 Grob::stencil_extent (Grob *me, Axis a)
179 Stencil *m = me->get_stencil ();
183 return ly_interval2scm (e);
187 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
189 Interval ext = me->extent (refpoint, a);
191 ext.add_point (me->relative_coordinate (refpoint, a));
197 Grob::get_layout () const
199 return pscore_ ? pscore_->layout () : 0;
203 Grob::get_stencil () const
208 SCM stil = get_property ("stencil");
209 return unsmob_stencil (stil);
213 Grob::get_print_stencil () const
215 SCM stil = get_property ("stencil");
218 if (Stencil *m = unsmob_stencil (stil))
221 if (to_boolean (get_property ("transparent")))
222 retval = Stencil (m->extent_box (), SCM_EOL);
225 SCM expr = m->expr ();
226 if (point_and_click_global)
227 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
230 retval = Stencil (m->extent_box (), expr);
233 /* color support... see interpret_stencil_expression () for more... */
234 SCM color = get_property ("color");
235 if (color != SCM_EOL)
237 m = unsmob_stencil (stil);
238 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
242 retval = Stencil (m->extent_box (), expr);
254 Grob::do_break_processing ()
259 Grob::get_system () const
266 Grob::handle_broken_dependencies ()
268 Spanner *sp = dynamic_cast<Spanner *> (this);
273 /* THIS, SP is the original spanner. We use a special function
274 because some Spanners have enormously long lists in their
275 properties, and a special function fixes FOO */
277 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
278 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
280 System *system = get_system ();
284 && common_refpoint (system, X_AXIS)
285 && common_refpoint (system, Y_AXIS))
286 substitute_object_links (system->self_scm (), object_alist_);
287 else if (dynamic_cast<System *> (this))
288 substitute_object_links (SCM_UNDEFINED, object_alist_);
290 /* THIS element is `invalid'; it has been removed from all
291 dependencies, so let's junk the element itself.
293 Do not do this for System, since that would remove references
294 to the originals of score-grobs, which get then GC'd (a bad
299 /* Note that we still want references to this element to be
300 rearranged, and not silently thrown away, so we keep pointers like
301 {broken_into_{drul, array}, original}
309 mutable_property_alist_ = SCM_EOL;
310 object_alist_ = SCM_EOL;
311 immutable_property_alist_ = SCM_EOL;
312 interfaces_ = SCM_EOL;
314 for (int a = X_AXIS; a <= Y_AXIS; a++)
316 dim_cache_[a].offset_callbacks_ = SCM_EOL;
317 dim_cache_[a].offsets_left_ = 0;
322 Grob::handle_prebroken_dependencies ()
324 /* Don't do this in the derived method, since we want to keep access to
325 object_alist_ centralized. */
328 Item *it = dynamic_cast<Item *> (this);
329 substitute_object_links (scm_from_int (it->break_status_dir ()),
330 original_->object_alist_);
335 Grob::find_broken_piece (System *) const
340 /* Translate in one direction. */
342 Grob::translate_axis (Real y, Axis a)
344 if (isinf (y) || isnan (y))
345 programming_error (_ (INFINITY_MSG));
347 dim_cache_[a].offset_ += y;
350 /* Find the offset relative to D. If D equals THIS, then it is 0.
351 Otherwise, it recursively defd as
353 OFFSET_ + PARENT_L_->relative_coordinate (D) */
355 Grob::relative_coordinate (Grob const *refp, Axis a) const
360 /* We catch PARENT_L_ == nil case with this, but we crash if we did
361 not ask for the absolute coordinate (ie. REFP == nil.) */
362 if (refp == dim_cache_[a].parent_)
363 return get_offset (a);
365 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
368 /* Invoke callbacks to get offset relative to parent. */
370 Grob::get_offset (Axis a) const
372 Grob *me = (Grob *) this;
373 while (dim_cache_[a].offsets_left_)
375 int l = --me->dim_cache_[a].offsets_left_;
376 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_from_int (l));
377 SCM retval = scm_call_2 (cb, self_scm (), scm_from_int (a));
379 Real r = scm_to_double (retval);
380 if (isinf (r) || isnan (r))
382 programming_error (INFINITY_MSG);
385 me->dim_cache_[a].offset_ += r;
387 return dim_cache_[a].offset_;
392 Grob::is_empty (Axis a) const
394 return !(scm_is_pair (dim_cache_[a].dimension_)
395 || ly_is_procedure (dim_cache_[a].dimension_callback_));
400 Grob::flush_extent_cache (Axis axis)
402 if (dim_cache_[axis].extent_)
405 Ugh, this is not accurate; will flush property, causing
406 callback to be called if.
408 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
409 delete dim_cache_[axis].extent_;
410 dim_cache_[axis].extent_ = 0;
411 if (get_parent (axis))
412 get_parent (axis)->flush_extent_cache (axis);
418 Grob::extent (Grob *refp, Axis a) const
420 Real offset = relative_coordinate (refp, a);
422 if (dim_cache_[a].extent_)
424 real_ext = *dim_cache_[a].extent_;
430 ? ly_symbol2scm ("minimum-X-extent")
431 : ly_symbol2scm ("minimum-Y-extent");
435 ? ly_symbol2scm ("X-extent")
436 : ly_symbol2scm ("Y-extent");
438 SCM min_ext = internal_get_property (min_ext_sym);
439 SCM ext = internal_get_property (ext_sym);
441 if (is_number_pair (min_ext))
442 real_ext.unite (ly_scm2interval (min_ext));
443 if (is_number_pair (ext))
444 real_ext.unite (ly_scm2interval (ext));
446 ((Grob*)this)->del_property (ext_sym);
447 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
450 real_ext.translate (offset);
455 /* Find the group-element which has both #this# and #s# */
457 Grob::common_refpoint (Grob const *s, Axis a) const
459 /* I don't like the quadratic aspect of this code, but I see no
460 other way. The largest chain of parents might be 10 high or so,
461 so it shouldn't be a real issue. */
462 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
463 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
471 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
473 for (; scm_is_pair (elist); elist = scm_cdr (elist))
474 if (Grob *s = unsmob_grob (scm_car (elist)))
477 common = common->common_refpoint (s, a);
486 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
488 for (int i = arr.size (); i--;)
489 if (Grob *s = arr[i])
492 common = common->common_refpoint (s, a);
503 SCM meta = get_property ("meta");
504 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
505 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
506 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
510 Grob::add_offset_callback (SCM cb, Axis a)
512 if (!has_offset_callback (cb, a))
514 dim_cache_[a].offset_callbacks_
515 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
516 dim_cache_[a].offsets_left_++;
521 Grob::has_offset_callback (SCM cb, Axis a) const
523 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
527 Grob::set_parent (Grob *g, Axis a)
529 dim_cache_[a].parent_ = g;
533 Grob::fixup_refpoint ()
535 for (int a = X_AXIS; a < NO_AXES; a++)
538 Grob *parent = get_parent (ax);
543 if (parent->get_system () != get_system () && get_system ())
545 Grob *newparent = parent->find_broken_piece (get_system ());
546 set_parent (newparent, ax);
549 if (Item *i = dynamic_cast<Item *> (this))
551 Item *parenti = dynamic_cast<Item *> (parent);
555 Direction my_dir = i->break_status_dir ();
556 if (my_dir != parenti->break_status_dir ())
558 Item *newparent = parenti->find_prebroken_piece (my_dir);
559 set_parent (newparent, ax);
567 Grob::warning (String s) const
569 SCM cause = self_scm ();
570 while (Grob *g = unsmob_grob (cause))
571 cause = g->get_property ("cause");
573 if (Music *m = unsmob_music (cause))
574 m->origin ()->warning (s);
580 Grob::programming_error (String s) const
582 SCM cause = self_scm ();
583 while (Grob *g = unsmob_grob (cause))
584 cause = g->get_property ("cause");
586 s = _f ("programming error: %s", s);
588 if (Music *m = unsmob_music (cause))
589 m->origin ()->message (s);
594 Grob::discretionary_processing ()
599 Grob::internal_has_interface (SCM k)
601 return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
605 Grob::get_parent (Axis a) const
607 return dim_cache_[a].parent_;
610 /** Return Array of Grobs in SCM list LST */
612 ly_scm2grobs (SCM lst)
614 Link_array<Grob> arr;
616 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
619 arr.push (unsmob_grob (e));
627 Grob::get_key () const
632 /** Return SCM list of Grob array A */
634 ly_grobs2scm (Link_array<Grob> a)
637 for (int i = a.size (); i; i--)
638 s = scm_cons (a[i - 1]->self_scm (), s);
643 ADD_INTERFACE (Grob, "grob-interface",
644 "A grob represents a piece of music notation\n"
646 "All grobs have an X and Y-position on the page. These X and Y positions\n"
647 "are stored in a relative format, so they can easily be combined by\n"
648 "stacking them, hanging one grob to the side of another, and coupling\n"
649 "them into a grouping objects.\n"
651 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
652 "is stored relative to that reference point. For example the X-reference\n"
653 "point of a staccato dot usually is the note head that it applies\n"
654 "to. When the note head is moved, the staccato dot moves along\n"
657 "A grob is often associated with a symbol, but some grobs do not print\n"
658 "any symbols. They take care of grouping objects. For example, there is a\n"
659 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
660 "is also an abstract grob: it only moves around chords, but doesn't print\n"
663 "Grobs have a properties: Scheme variables, that can be read and set. "
664 "They have two types. Immutable variables "
665 "define the default style and behavior. They are shared between many objects. "
666 "They can be changed using @code{\\override} and @code{\\revert}. "
668 "Mutable properties are variables that are specific to one grob. Typically, "
669 "lists of other objects, or results from computations are stored in"
670 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
671 "sets a mutable property. "
673 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
674 "are dummies that are not user-serviceable. "
680 "X-offset-callbacks "
682 "Y-offset-callbacks "
683 "after-line-breaking "
684 "axis-group-parent-X "
685 "axis-group-parent-Y "
686 "before-line-breaking "