2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
13 #include "align-interface.hh"
14 #include "input-smob.hh"
15 #include "international.hh"
20 #include "output-def.hh"
21 #include "pointer-group-interface.hh"
23 #include "stream-event.hh"
27 #include "ly-smobs.icc"
30 Grob::clone (int count) const
32 return new Grob (*this, count);
35 Grob::Grob (SCM basicprops,
36 Object_key const *key)
39 /* FIXME: default should be no callback. */
43 interfaces_ = SCM_EOL;
44 immutable_property_alist_ = basicprops;
45 mutable_property_alist_ = SCM_EOL;
46 object_alist_ = SCM_EOL;
48 /* We do smobify_self () as the first step. Since the object lives
49 on the heap, none of its SCM variables are protected from
50 GC. After smobify_self (), they are. */
54 We always get a new key object for a new grob.
57 ((Object_key *)key_)->unprotect ();
59 SCM meta = get_property ("meta");
60 if (scm_is_pair (meta))
61 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
63 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
64 set_property ("X-extent", Grob::stencil_width_proc);
65 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
66 set_property ("Y-extent", Grob::stencil_height_proc);
69 Grob::Grob (Grob const &s, int copy_index)
70 : dim_cache_ (s.dim_cache_)
72 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
73 original_ = (Grob *) & s;
76 immutable_property_alist_ = s.immutable_property_alist_;
77 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
78 interfaces_ = s.interfaces_;
79 object_alist_ = SCM_EOL;
85 ((Object_key *)key_)->unprotect ();
91 /****************************************************************
93 ****************************************************************/
96 Grob::get_stencil () const
101 SCM stil = get_property ("stencil");
102 return unsmob_stencil (stil);
106 Grob::get_print_stencil () const
108 SCM stil = get_property ("stencil");
111 if (Stencil *m = unsmob_stencil (stil))
114 if (to_boolean (get_property ("transparent")))
115 retval = Stencil (m->extent_box (), SCM_EOL);
118 SCM expr = m->expr ();
119 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
122 retval = Stencil (m->extent_box (), expr);
124 SCM rot = get_property ("rotation");
125 if (scm_is_pair (rot))
127 Real angle = scm_to_double (scm_car (rot));
128 Real x = scm_to_double (scm_cadr (rot));
129 Real y = scm_to_double (scm_caddr (rot));
131 retval.rotate (angle, Offset (x, y));
134 /* color support... see interpret_stencil_expression () for more... */
135 SCM color = get_property ("color");
136 if (color != SCM_EOL)
138 m = unsmob_stencil (stil);
139 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
143 retval = Stencil (m->extent_box (), expr);
151 /****************************************************************
153 ****************************************************************/
155 Grob::do_break_processing ()
160 Grob::discretionary_processing ()
165 Grob::get_system () const
172 Grob::handle_broken_dependencies ()
174 Spanner *sp = dynamic_cast<Spanner *> (this);
175 if (original () && sp)
179 /* THIS, SP is the original spanner. We use a special function
180 because some Spanners have enormously long lists in their
181 properties, and a special function fixes FOO */
183 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
184 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
186 System *system = get_system ();
190 && common_refpoint (system, X_AXIS)
191 && common_refpoint (system, Y_AXIS))
192 substitute_object_links (system->self_scm (), object_alist_);
193 else if (dynamic_cast<System *> (this))
194 substitute_object_links (SCM_UNDEFINED, object_alist_);
196 /* THIS element is `invalid'; it has been removed from all
197 dependencies, so let's junk the element itself.
199 Do not do this for System, since that would remove references
200 to the originals of score-grobs, which get then GC'd (a bad
205 /* Note that we still want references to this element to be
206 rearranged, and not silently thrown away, so we keep pointers like
207 {broken_into_{drul, array}, original}
215 for (int a = X_AXIS; a < NO_AXES; a++)
216 dim_cache_[a].clear ();
218 mutable_property_alist_ = SCM_EOL;
219 object_alist_ = SCM_EOL;
220 immutable_property_alist_ = SCM_EOL;
221 interfaces_ = SCM_EOL;
225 Grob::handle_prebroken_dependencies ()
227 /* Don't do this in the derived method, since we want to keep access to
228 object_alist_ centralized. */
231 Item *it = dynamic_cast<Item *> (this);
232 substitute_object_links (scm_from_int (it->break_status_dir ()),
233 original ()->object_alist_);
238 Grob::find_broken_piece (System *) const
243 /****************************************************************
245 ****************************************************************/
248 Grob::translate_axis (Real y, Axis a)
250 if (isinf (y) || isnan (y))
252 programming_error (_ ("Infinity or NaN encountered"));
256 if (!dim_cache_[a].offset_)
257 dim_cache_[a].offset_ = new Real (y);
259 *dim_cache_[a].offset_ += y;
262 /* Find the offset relative to D. If D equals THIS, then it is 0.
263 Otherwise, it recursively defd as
265 OFFSET_ + PARENT_L_->relative_coordinate (D) */
267 Grob::relative_coordinate (Grob const *refp, Axis a) const
272 /* We catch PARENT_L_ == nil case with this, but we crash if we did
273 not ask for the absolute coordinate (ie. REFP == nil.) */
274 Real off = get_offset (a);
275 if (refp == dim_cache_[a].parent_)
278 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
284 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
289 SCM pure_off = ly_lily_module_constant ("pure-Y-offset");
292 if (dim_cache_[Y_AXIS].offset_)
293 off = *dim_cache_[Y_AXIS].offset_;
294 else if (ly_is_procedure (pure_off))
296 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
297 off = scm_to_double (scm_apply_3 (pure_off, self_scm (),
298 scm_from_int (start), scm_from_int (end),
300 delete dim_cache_[Y_AXIS].offset_;
301 dim_cache_[Y_AXIS].offset_ = 0;
304 /* we simulate positioning-done if we are the child of a VerticalAlignment */
305 Grob *p = get_parent (Y_AXIS);
307 if (Align_interface::has_interface (p))
308 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
311 + dim_cache_[Y_AXIS].parent_->pure_relative_y_coordinate (refp, start, end);
314 /* Invoke callbacks to get offset relative to parent. */
316 Grob::get_offset (Axis a) const
318 if (dim_cache_[a].offset_)
319 return *dim_cache_[a].offset_;
321 Grob *me = (Grob *) this;
323 SCM sym = axis_offset_symbol (a);
324 me->dim_cache_[a].offset_ = new Real (0.0);
327 UGH: can't fold next 2 statements together. Apparently GCC thinks
328 dim_cache_[a].offset_ is unaliased.
330 Real off = robust_scm2double (internal_get_property (sym), 0.0);
331 if (me->dim_cache_[a].offset_)
333 *me->dim_cache_[a].offset_ += off;
334 me->del_property (sym);
335 return *me->dim_cache_[a].offset_;
342 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
344 if (pure && a != Y_AXIS)
345 programming_error ("tried to get pure X-offset");
346 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
347 : relative_coordinate (refp, a);
350 /****************************************************************
352 ****************************************************************/
355 Grob::flush_extent_cache (Axis axis)
357 if (dim_cache_[axis].extent_)
360 Ugh, this is not accurate; will flush property, causing
361 callback to be called if.
363 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
364 delete dim_cache_[axis].extent_;
365 dim_cache_[axis].extent_ = 0;
366 if (get_parent (axis))
367 get_parent (axis)->flush_extent_cache (axis);
373 Grob::extent (Grob *refp, Axis a) const
375 Real offset = relative_coordinate (refp, a);
377 if (dim_cache_[a].extent_)
379 real_ext = *dim_cache_[a].extent_;
384 Order is significant: ?-extent may trigger suicide.
388 ? ly_symbol2scm ("X-extent")
389 : ly_symbol2scm ("Y-extent");
391 SCM ext = internal_get_property (ext_sym);
392 if (is_number_pair (ext))
393 real_ext.unite (ly_scm2interval (ext));
397 ? ly_symbol2scm ("minimum-X-extent")
398 : ly_symbol2scm ("minimum-Y-extent");
399 SCM min_ext = internal_get_property (min_ext_sym);
400 if (is_number_pair (min_ext))
401 real_ext.unite (ly_scm2interval (min_ext));
402 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
405 real_ext.translate (offset);
411 Grob::pure_height (Grob *refp, int start, int end)
413 SCM pure_height = ly_lily_module_constant ("pure-Y-extent");
416 if (ly_is_procedure (pure_height))
417 iv = ly_scm2interval (scm_apply_3 (pure_height, self_scm (),
418 scm_from_int (start), scm_from_int (end),
420 Real offset = pure_relative_y_coordinate (refp, start, end);
422 iv.translate (offset);
427 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
429 if (pure && a != Y_AXIS)
430 programming_error ("tried to get pure width");
431 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
435 Grob::spanned_rank_iv ()
437 return Interval_t<int> (INT_MIN, INT_MAX);
440 /****************************************************************
442 ****************************************************************/
444 /* Find the group-element which has both #this# and #s# */
446 Grob::common_refpoint (Grob const *s, Axis a) const
448 /* I don't like the quadratic aspect of this code, but I see no
449 other way. The largest chain of parents might be 10 high or so,
450 so it shouldn't be a real issue. */
451 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
452 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
460 Grob::set_parent (Grob *g, Axis a)
462 dim_cache_[a].parent_ = g;
466 Grob::get_parent (Axis a) const
468 return dim_cache_[a].parent_;
473 Grob::fixup_refpoint ()
475 for (int a = X_AXIS; a < NO_AXES; a++)
478 Grob *parent = get_parent (ax);
483 if (parent->get_system () != get_system () && get_system ())
485 Grob *newparent = parent->find_broken_piece (get_system ());
486 set_parent (newparent, ax);
489 if (Item *i = dynamic_cast<Item *> (this))
491 Item *parenti = dynamic_cast<Item *> (parent);
495 Direction my_dir = i->break_status_dir ();
496 if (my_dir != parenti->break_status_dir ())
498 Item *newparent = parenti->find_prebroken_piece (my_dir);
499 set_parent (newparent, ax);
507 /****************************************************************
509 ****************************************************************/
511 Grob::warning (string s) const
513 SCM cause = self_scm ();
514 while (Grob *g = unsmob_grob (cause))
515 cause = g->get_property ("cause");
517 /* ES TODO: cause can't be Music*/
518 if (Music *m = unsmob_music (cause))
519 m->origin ()->warning (s);
520 else if (Stream_event *ev = unsmob_stream_event (cause))
521 ev->origin ()->warning (s);
530 SCM meta = get_property ("meta");
531 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
532 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
533 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
537 Grob::programming_error (string s) const
539 SCM cause = self_scm ();
540 while (Grob *g = unsmob_grob (cause))
541 cause = g->get_property ("cause");
543 s = _f ("programming error: %s", s);
545 /* ES TODO: cause can't be Music*/
546 if (Music *m = unsmob_music (cause))
547 m->origin ()->message (s);
548 else if (Stream_event *ev = unsmob_stream_event (cause))
549 ev->origin ()->warning (s);
555 ADD_INTERFACE (Grob, "grob-interface",
556 "A grob represents a piece of music notation\n"
558 "All grobs have an X and Y-position on the page. These X and Y positions\n"
559 "are stored in a relative format, so they can easily be combined by\n"
560 "stacking them, hanging one grob to the side of another, and coupling\n"
561 "them into a grouping objects.\n"
563 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
564 "is stored relative to that reference point. For example the X-reference\n"
565 "point of a staccato dot usually is the note head that it applies\n"
566 "to. When the note head is moved, the staccato dot moves along\n"
569 "A grob is often associated with a symbol, but some grobs do not print\n"
570 "any symbols. They take care of grouping objects. For example, there is a\n"
571 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
572 "is also an abstract grob: it only moves around chords, but doesn't print\n"
575 "Grobs have a properties: Scheme variables, that can be read and set. "
576 "They have two types. Immutable variables "
577 "define the default style and behavior. They are shared between many objects. "
578 "They can be changed using @code{\\override} and @code{\\revert}. "
580 "Mutable properties are variables that are specific to one grob. Typically, "
581 "lists of other objects, or results from computations are stored in"
582 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
583 "sets a mutable property. "
585 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
586 "are dummies that are not user-serviceable. "
595 "after-line-breaking "
596 "axis-group-parent-X "
597 "axis-group-parent-Y "
598 "before-line-breaking "
620 /****************************************************************
622 ****************************************************************/
626 grob_stencil_extent (Grob *me, Axis a)
628 Stencil *m = me->get_stencil ();
632 return ly_interval2scm (e);
636 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
638 Grob::stencil_height (SCM smob)
640 Grob *me = unsmob_grob (smob);
641 return grob_stencil_extent (me, Y_AXIS);
644 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
646 Grob::y_parent_positioning (SCM smob)
648 Grob *me = unsmob_grob (smob);
649 Grob *par = me->get_parent (Y_AXIS);
651 (void) par->get_property ("positioning-done");
653 return scm_from_double (0.0);
657 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
659 Grob::x_parent_positioning (SCM smob)
661 Grob *me = unsmob_grob (smob);
663 Grob *par = me->get_parent (X_AXIS);
665 (void) par->get_property ("positioning-done");
667 return scm_from_double (0.0);
670 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
672 Grob::stencil_width (SCM smob)
674 Grob *me = unsmob_grob (smob);
675 return grob_stencil_extent (me, X_AXIS);
681 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
683 for (; scm_is_pair (elist); elist = scm_cdr (elist))
684 if (Grob *s = unsmob_grob (scm_car (elist)))
687 common = common->common_refpoint (s, a);
696 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
698 for (vsize i = arr.size (); i--;)
699 if (Grob *s = arr[i])
702 common = common->common_refpoint (s, a);
711 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
713 Interval ext = me->extent (refpoint, a);
715 ext.add_point (me->relative_coordinate (refpoint, a));