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 = other_axis ((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 (get_property ("print-function")))
150 dim_cache_[a].dimension_callback_ = stencil_extent_proc;
155 Grob::Grob (Grob const &s, int copy_index)
156 : dim_cache_ (s.dim_cache_)
158 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
159 original_ = (Grob *) & s;
162 immutable_property_alist_ = s.immutable_property_alist_;
163 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
164 interfaces_ = s.interfaces_;
165 property_callbacks_ = s.property_callbacks_;
166 object_alist_ = SCM_EOL;
172 ((Object_key *)key_)->unprotect ();
179 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
181 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
183 Grob *s = unsmob_grob (element_smob);
184 Axis a = (Axis) scm_to_int (scm_axis);
186 Stencil *m = s->get_stencil ();
190 return ly_interval2scm (e);
194 robust_relative_extent (Grob *me, Grob *refp, Axis a)
196 Interval ext = me->extent (refp, a);
198 ext.add_point (me->relative_coordinate (refp, a));
204 Grob::get_layout () const
206 return pscore_ ? pscore_->layout () : 0;
210 Grob::get_stencil () const
215 SCM stil = get_property ("stencil");
216 if (unsmob_stencil (stil))
217 return unsmob_stencil (stil);
219 stil = get_uncached_stencil ();
222 Grob *me = (Grob *) this;
223 me->set_property ("stencil", stil);
226 return unsmob_stencil (stil);
230 Grob::get_uncached_stencil () const
232 SCM proc = get_property ("print-function");
235 if (ly_is_procedure (proc))
236 stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
238 if (Stencil *m = unsmob_stencil (stil))
240 if (to_boolean (get_property ("transparent")))
241 stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
244 SCM expr = m->expr ();
245 if (point_and_click_global)
246 expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm (), expr);
248 stil = Stencil (m->extent_box (), expr).smobbed_copy ();
251 /* color support... see interpret_stencil_expression () for more... */
252 SCM color = get_property ("color");
253 if (color != SCM_EOL)
255 m = unsmob_stencil (stil);
256 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
260 stil = Stencil (m->extent_box (), expr).smobbed_copy ();
271 Grob::do_break_processing ()
276 Grob::get_system () const
283 Grob::handle_broken_dependencies ()
285 Spanner *sp = dynamic_cast<Spanner *> (this);
290 /* THIS, SP is the original spanner. We use a special function
291 because some Spanners have enormously long lists in their
292 properties, and a special function fixes FOO */
294 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
295 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
297 System *system = get_system ();
301 && common_refpoint (system, X_AXIS)
302 && common_refpoint (system, Y_AXIS))
303 substitute_object_links (system->self_scm (), object_alist_);
304 else if (dynamic_cast<System *> (this))
305 substitute_object_links (SCM_UNDEFINED, object_alist_);
307 /* THIS element is `invalid'; it has been removed from all
308 dependencies, so let's junk the element itself.
310 Do not do this for System, since that would remove references
311 to the originals of score-grobs, which get then GC'd (a bad
316 /* Note that we still want references to this element to be
317 rearranged, and not silently thrown away, so we keep pointers like
318 {broken_into_{drul, array}, original}
326 mutable_property_alist_ = SCM_EOL;
327 object_alist_ = SCM_EOL;
328 property_callbacks_ = SCM_EOL;
329 immutable_property_alist_ = SCM_EOL;
330 interfaces_ = SCM_EOL;
332 set_extent (SCM_EOL, Y_AXIS);
333 set_extent (SCM_EOL, X_AXIS);
335 set_extent_callback (SCM_EOL, Y_AXIS);
336 set_extent_callback (SCM_EOL, X_AXIS);
338 for (int a = X_AXIS; a <= Y_AXIS; a++)
340 dim_cache_[a].offset_callbacks_ = SCM_EOL;
341 dim_cache_[a].offsets_left_ = 0;
346 Grob::handle_prebroken_dependencies ()
348 /* Don't do this in the derived method, since we want to keep access to
349 object_alist_ centralized. */
352 Item *it = dynamic_cast<Item *> (this);
353 substitute_object_links (scm_from_int (it->break_status_dir ()),
354 original_->object_alist_);
359 Grob::find_broken_piece (System *) const
364 /* Translate in one direction. */
366 Grob::translate_axis (Real y, Axis a)
368 if (isinf (y) || isnan (y))
369 programming_error (_ (INFINITY_MSG));
371 dim_cache_[a].offset_ += y;
374 /* Find the offset relative to D. If D equals THIS, then it is 0.
375 Otherwise, it recursively defd as
377 OFFSET_ + PARENT_L_->relative_coordinate (D) */
379 Grob::relative_coordinate (Grob const *refp, Axis a) const
384 /* We catch PARENT_L_ == nil case with this, but we crash if we did
385 not ask for the absolute coordinate (ie. REFP == nil.) */
386 if (refp == dim_cache_[a].parent_)
387 return get_offset (a);
389 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
392 /* Invoke callbacks to get offset relative to parent. */
394 Grob::get_offset (Axis a) const
396 Grob *me = (Grob *) this;
397 while (dim_cache_[a].offsets_left_)
399 int l = --me->dim_cache_[a].offsets_left_;
400 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_from_int (l));
401 SCM retval = scm_call_2 (cb, self_scm (), scm_from_int (a));
403 Real r = scm_to_double (retval);
404 if (isinf (r) || isnan (r))
406 programming_error (INFINITY_MSG);
409 me->dim_cache_[a].offset_ += r;
411 return dim_cache_[a].offset_;
415 Grob::is_empty (Axis a) const
417 return !(scm_is_pair (dim_cache_[a].dimension_)
418 || ly_is_procedure (dim_cache_[a].dimension_callback_));
422 Grob::flush_extent_cache (Axis axis)
424 Dimension_cache *d = &dim_cache_[axis];
425 if (ly_is_procedure (d->dimension_callback_)
426 && scm_is_pair (d->dimension_))
428 d->dimension_ = SCM_EOL;
430 if (get_parent (axis))
431 get_parent (axis)->flush_extent_cache (axis);
436 Grob::extent (Grob *refp, Axis a) const
438 Real x = relative_coordinate (refp, a);
440 Dimension_cache *d = (Dimension_cache *) & dim_cache_[a];
443 SCM dimpair = d->dimension_;
444 if (scm_is_pair (dimpair))
446 else if (ly_is_procedure (d->dimension_callback_)
447 && d->dimension_ == SCM_EOL)
448 d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_from_int (a));
452 if (!scm_is_pair (d->dimension_))
455 ext = ly_scm2interval (d->dimension_);
457 SCM extra = (a == X_AXIS)
458 ? get_property ("extra-X-extent")
459 : get_property ("extra-Y-extent");
462 if (scm_is_pair (extra))
464 ext[BIGGER] += scm_to_double (scm_cdr (extra));
465 ext[SMALLER] += scm_to_double (scm_car (extra));
468 extra = (a == X_AXIS)
469 ? get_property ("minimum-X-extent")
470 : get_property ("minimum-Y-extent");
472 if (scm_is_pair (extra))
473 ext.unite (Interval (scm_to_double (scm_car (extra)),
474 scm_to_double (scm_cdr (extra))));
481 /* Find the group-element which has both #this# and #s# */
483 Grob::common_refpoint (Grob const *s, Axis a) const
485 /* I don't like the quadratic aspect of this code, but I see no
486 other way. The largest chain of parents might be 10 high or so,
487 so it shouldn't be a real issue. */
488 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
489 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
497 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
499 for (; scm_is_pair (elist); elist = scm_cdr (elist))
500 if (Grob *s = unsmob_grob (scm_car (elist)))
503 common = common->common_refpoint (s, a);
512 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
514 for (int i = arr.size (); i--;)
515 if (Grob *s = arr[i])
518 common = common->common_refpoint (s, a);
529 SCM meta = get_property ("meta");
530 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
531 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
532 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
536 Grob::add_offset_callback (SCM cb, Axis a)
538 if (!has_offset_callback (cb, a))
540 dim_cache_[a].offset_callbacks_
541 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
542 dim_cache_[a].offsets_left_++;
547 Grob::has_extent_callback (SCM cb, Axis a) const
549 return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
553 Grob::has_offset_callback (SCM cb, Axis a) const
555 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
559 Grob::set_extent (SCM dc, Axis a)
561 dim_cache_[a].dimension_ = dc;
565 Grob::set_extent_callback (SCM dc, Axis a)
567 dim_cache_[a].dimension_callback_ = dc;
571 Grob::set_parent (Grob *g, Axis a)
573 dim_cache_[a].parent_ = g;
577 Grob::fixup_refpoint ()
579 for (int a = X_AXIS; a < NO_AXES; a++)
582 Grob *parent = get_parent (ax);
587 if (parent->get_system () != get_system () && get_system ())
589 Grob *newparent = parent->find_broken_piece (get_system ());
590 set_parent (newparent, ax);
593 if (Item *i = dynamic_cast<Item *> (this))
595 Item *parenti = dynamic_cast<Item *> (parent);
599 Direction my_dir = i->break_status_dir ();
600 if (my_dir != parenti->break_status_dir ())
602 Item *newparent = parenti->find_prebroken_piece (my_dir);
603 set_parent (newparent, ax);
611 Grob::warning (String s) const
613 SCM cause = self_scm ();
614 while (Grob *g = unsmob_grob (cause))
615 cause = g->get_property ("cause");
617 if (Music *m = unsmob_music (cause))
618 m->origin ()->warning (s);
624 Grob::programming_error (String s) const
626 SCM cause = self_scm ();
627 while (Grob *g = unsmob_grob (cause))
628 cause = g->get_property ("cause");
630 s = _f ("programming error: %s", s);
632 if (Music *m = unsmob_music (cause))
633 m->origin ()->message (s);
638 Grob::discretionary_processing ()
643 Grob::internal_has_interface (SCM k)
645 return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
649 Grob::get_parent (Axis a) const
651 return dim_cache_[a].parent_;
654 /** Return Array of Grobs in SCM list LST */
656 ly_scm2grobs (SCM lst)
658 Link_array<Grob> arr;
660 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
663 arr.push (unsmob_grob (e));
671 Grob::get_key () const
676 /** Return SCM list of Grob array A */
678 ly_grobs2scm (Link_array<Grob> a)
681 for (int i = a.size (); i; i--)
682 s = scm_cons (a[i - 1]->self_scm (), s);
687 ADD_INTERFACE (Grob, "grob-interface",
688 "A grob represents a piece of music notation\n"
690 "All grobs have an X and Y-position on the page. These X and Y positions\n"
691 "are stored in a relative format, so they can easily be combined by\n"
692 "stacking them, hanging one grob to the side of another, and coupling\n"
693 "them into a grouping objects.\n"
695 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
696 "is stored relative to that reference point. For example the X-reference\n"
697 "point of a staccato dot usually is the note head that it applies\n"
698 "to. When the note head is moved, the staccato dot moves along\n"
701 "A grob is often associated with a symbol, but some grobs do not print\n"
702 "any symbols. They take care of grouping objects. For example, there is a\n"
703 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
704 "is also an abstract grob: it only moves around chords, but doesn't print\n"
707 "Grobs have a properties: Scheme variables, that can be read and set. "
708 "They have two types. Immutable variables "
709 "define the default style and behavior. They are shared between many objects. "
710 "They can be changed using @code{\\override} and @code{\\revert}. "
712 "Mutable properties are variables that are specific to one grob. Typically, "
713 "lists of other objects, or results from computations are stored in"
714 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
715 "sets a mutable property. ",
718 "The properties @code{after-line-breaking} and @code{before-line-breaking} are unused dummies. "
723 "X-offset-callbacks "
726 "Y-offset-callbacks "
727 "after-line-breaking "
728 "axis-group-parent-X "
729 "axis-group-parent-Y "
730 "before-line-breaking "