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 Real off = get_offset (a);
363 if (refp == dim_cache_[a].parent_)
366 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
371 /* Invoke callbacks to get offset relative to parent. */
373 Grob::get_offset (Axis a) const
375 Grob *me = (Grob *) this;
376 while (dim_cache_[a].offsets_left_)
378 int l = --me->dim_cache_[a].offsets_left_;
379 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_from_int (l));
380 SCM retval = scm_call_2 (cb, self_scm (), scm_from_int (a));
382 Real r = scm_to_double (retval);
383 if (isinf (r) || isnan (r))
385 programming_error (INFINITY_MSG);
388 me->dim_cache_[a].offset_ += r;
390 return dim_cache_[a].offset_;
395 Grob::flush_extent_cache (Axis axis)
397 if (dim_cache_[axis].extent_)
400 Ugh, this is not accurate; will flush property, causing
401 callback to be called if.
403 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
404 delete dim_cache_[axis].extent_;
405 dim_cache_[axis].extent_ = 0;
406 if (get_parent (axis))
407 get_parent (axis)->flush_extent_cache (axis);
413 Grob::extent (Grob *refp, Axis a) const
415 Real offset = relative_coordinate (refp, a);
417 if (dim_cache_[a].extent_)
419 real_ext = *dim_cache_[a].extent_;
425 ? ly_symbol2scm ("minimum-X-extent")
426 : ly_symbol2scm ("minimum-Y-extent");
430 ? ly_symbol2scm ("X-extent")
431 : ly_symbol2scm ("Y-extent");
433 SCM min_ext = internal_get_property (min_ext_sym);
434 SCM ext = internal_get_property (ext_sym);
436 if (is_number_pair (min_ext))
437 real_ext.unite (ly_scm2interval (min_ext));
438 if (is_number_pair (ext))
439 real_ext.unite (ly_scm2interval (ext));
441 ((Grob*)this)->del_property (ext_sym);
442 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
445 real_ext.translate (offset);
450 /* Find the group-element which has both #this# and #s# */
452 Grob::common_refpoint (Grob const *s, Axis a) const
454 /* I don't like the quadratic aspect of this code, but I see no
455 other way. The largest chain of parents might be 10 high or so,
456 so it shouldn't be a real issue. */
457 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
458 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
466 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
468 for (; scm_is_pair (elist); elist = scm_cdr (elist))
469 if (Grob *s = unsmob_grob (scm_car (elist)))
472 common = common->common_refpoint (s, a);
481 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
483 for (int i = arr.size (); i--;)
484 if (Grob *s = arr[i])
487 common = common->common_refpoint (s, a);
498 SCM meta = get_property ("meta");
499 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
500 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
501 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
505 Grob::add_offset_callback (SCM cb, Axis a)
507 if (!has_offset_callback (cb, a))
509 dim_cache_[a].offset_callbacks_
510 = scm_cons (cb, dim_cache_[a].offset_callbacks_);
511 dim_cache_[a].offsets_left_++;
516 Grob::has_offset_callback (SCM cb, Axis a) const
518 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
522 Grob::set_parent (Grob *g, Axis a)
524 dim_cache_[a].parent_ = g;
528 Grob::fixup_refpoint ()
530 for (int a = X_AXIS; a < NO_AXES; a++)
533 Grob *parent = get_parent (ax);
538 if (parent->get_system () != get_system () && get_system ())
540 Grob *newparent = parent->find_broken_piece (get_system ());
541 set_parent (newparent, ax);
544 if (Item *i = dynamic_cast<Item *> (this))
546 Item *parenti = dynamic_cast<Item *> (parent);
550 Direction my_dir = i->break_status_dir ();
551 if (my_dir != parenti->break_status_dir ())
553 Item *newparent = parenti->find_prebroken_piece (my_dir);
554 set_parent (newparent, ax);
562 Grob::warning (String s) const
564 SCM cause = self_scm ();
565 while (Grob *g = unsmob_grob (cause))
566 cause = g->get_property ("cause");
568 if (Music *m = unsmob_music (cause))
569 m->origin ()->warning (s);
575 Grob::programming_error (String s) const
577 SCM cause = self_scm ();
578 while (Grob *g = unsmob_grob (cause))
579 cause = g->get_property ("cause");
581 s = _f ("programming error: %s", s);
583 if (Music *m = unsmob_music (cause))
584 m->origin ()->message (s);
589 Grob::discretionary_processing ()
594 Grob::internal_has_interface (SCM k)
596 return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
600 Grob::get_parent (Axis a) const
602 return dim_cache_[a].parent_;
605 /** Return Array of Grobs in SCM list LST */
607 ly_scm2grobs (SCM lst)
609 Link_array<Grob> arr;
611 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
614 arr.push (unsmob_grob (e));
622 Grob::get_key () const
627 /** Return SCM list of Grob array A */
629 ly_grobs2scm (Link_array<Grob> a)
632 for (int i = a.size (); i; i--)
633 s = scm_cons (a[i - 1]->self_scm (), s);
638 ADD_INTERFACE (Grob, "grob-interface",
639 "A grob represents a piece of music notation\n"
641 "All grobs have an X and Y-position on the page. These X and Y positions\n"
642 "are stored in a relative format, so they can easily be combined by\n"
643 "stacking them, hanging one grob to the side of another, and coupling\n"
644 "them into a grouping objects.\n"
646 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
647 "is stored relative to that reference point. For example the X-reference\n"
648 "point of a staccato dot usually is the note head that it applies\n"
649 "to. When the note head is moved, the staccato dot moves along\n"
652 "A grob is often associated with a symbol, but some grobs do not print\n"
653 "any symbols. They take care of grouping objects. For example, there is a\n"
654 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
655 "is also an abstract grob: it only moves around chords, but doesn't print\n"
658 "Grobs have a properties: Scheme variables, that can be read and set. "
659 "They have two types. Immutable variables "
660 "define the default style and behavior. They are shared between many objects. "
661 "They can be changed using @code{\\override} and @code{\\revert}. "
663 "Mutable properties are variables that are specific to one grob. Typically, "
664 "lists of other objects, or results from computations are stored in"
665 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
666 "sets a mutable property. "
668 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
669 "are dummies that are not user-serviceable. "
675 "X-offset-callbacks "
677 "Y-offset-callbacks "
678 "after-line-breaking "
679 "axis-group-parent-X "
680 "axis-group-parent-Y "
681 "before-line-breaking "