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 SCM min_ext = get_property ("minimum-Y-extent");
423 if (is_number_pair (min_ext))
424 iv.unite (ly_scm2interval (min_ext));
426 iv.translate (offset);
431 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
433 if (pure && a != Y_AXIS)
434 programming_error ("tried to get pure width");
435 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
439 Grob::spanned_rank_iv ()
441 return Interval_t<int> (-1, 0);
444 /****************************************************************
446 ****************************************************************/
448 /* Find the group-element which has both #this# and #s# */
450 Grob::common_refpoint (Grob const *s, Axis a) const
452 /* I don't like the quadratic aspect of this code, but I see no
453 other way. The largest chain of parents might be 10 high or so,
454 so it shouldn't be a real issue. */
455 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
456 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
464 Grob::set_parent (Grob *g, Axis a)
466 dim_cache_[a].parent_ = g;
470 Grob::get_parent (Axis a) const
472 return dim_cache_[a].parent_;
477 Grob::fixup_refpoint ()
479 for (int a = X_AXIS; a < NO_AXES; a++)
482 Grob *parent = get_parent (ax);
487 if (parent->get_system () != get_system () && get_system ())
489 Grob *newparent = parent->find_broken_piece (get_system ());
490 set_parent (newparent, ax);
493 if (Item *i = dynamic_cast<Item *> (this))
495 Item *parenti = dynamic_cast<Item *> (parent);
499 Direction my_dir = i->break_status_dir ();
500 if (my_dir != parenti->break_status_dir ())
502 Item *newparent = parenti->find_prebroken_piece (my_dir);
503 set_parent (newparent, ax);
511 /****************************************************************
513 ****************************************************************/
515 Grob::warning (string s) const
517 SCM cause = self_scm ();
518 while (Grob *g = unsmob_grob (cause))
519 cause = g->get_property ("cause");
521 /* ES TODO: cause can't be Music*/
522 if (Music *m = unsmob_music (cause))
523 m->origin ()->warning (s);
524 else if (Stream_event *ev = unsmob_stream_event (cause))
525 ev->origin ()->warning (s);
534 SCM meta = get_property ("meta");
535 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
536 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
537 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
541 Grob::programming_error (string s) const
543 SCM cause = self_scm ();
544 while (Grob *g = unsmob_grob (cause))
545 cause = g->get_property ("cause");
547 s = _f ("programming error: %s", s);
549 /* ES TODO: cause can't be Music*/
550 if (Music *m = unsmob_music (cause))
551 m->origin ()->message (s);
552 else if (Stream_event *ev = unsmob_stream_event (cause))
553 ev->origin ()->warning (s);
559 ADD_INTERFACE (Grob, "grob-interface",
560 "A grob represents a piece of music notation\n"
562 "All grobs have an X and Y-position on the page. These X and Y positions\n"
563 "are stored in a relative format, so they can easily be combined by\n"
564 "stacking them, hanging one grob to the side of another, and coupling\n"
565 "them into a grouping objects.\n"
567 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
568 "is stored relative to that reference point. For example the X-reference\n"
569 "point of a staccato dot usually is the note head that it applies\n"
570 "to. When the note head is moved, the staccato dot moves along\n"
573 "A grob is often associated with a symbol, but some grobs do not print\n"
574 "any symbols. They take care of grouping objects. For example, there is a\n"
575 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
576 "is also an abstract grob: it only moves around chords, but doesn't print\n"
579 "Grobs have a properties: Scheme variables, that can be read and set. "
580 "They have two types. Immutable variables "
581 "define the default style and behavior. They are shared between many objects. "
582 "They can be changed using @code{\\override} and @code{\\revert}. "
584 "Mutable properties are variables that are specific to one grob. Typically, "
585 "lists of other objects, or results from computations are stored in"
586 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
587 "sets a mutable property. "
589 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
590 "are dummies that are not user-serviceable. "
599 "after-line-breaking "
600 "axis-group-parent-X "
601 "axis-group-parent-Y "
602 "before-line-breaking "
624 /****************************************************************
626 ****************************************************************/
630 grob_stencil_extent (Grob *me, Axis a)
632 Stencil *m = me->get_stencil ();
636 return ly_interval2scm (e);
640 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
642 Grob::stencil_height (SCM smob)
644 Grob *me = unsmob_grob (smob);
645 return grob_stencil_extent (me, Y_AXIS);
648 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
650 Grob::y_parent_positioning (SCM smob)
652 Grob *me = unsmob_grob (smob);
653 Grob *par = me->get_parent (Y_AXIS);
655 (void) par->get_property ("positioning-done");
657 return scm_from_double (0.0);
661 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
663 Grob::x_parent_positioning (SCM smob)
665 Grob *me = unsmob_grob (smob);
667 Grob *par = me->get_parent (X_AXIS);
669 (void) par->get_property ("positioning-done");
671 return scm_from_double (0.0);
674 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
676 Grob::stencil_width (SCM smob)
678 Grob *me = unsmob_grob (smob);
679 return grob_stencil_extent (me, X_AXIS);
685 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
687 for (; scm_is_pair (elist); elist = scm_cdr (elist))
688 if (Grob *s = unsmob_grob (scm_car (elist)))
691 common = common->common_refpoint (s, a);
700 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
702 for (vsize i = arr.size (); i--;)
703 if (Grob *s = arr[i])
706 common = common->common_refpoint (s, a);
715 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
717 Interval ext = me->extent (refpoint, a);
719 ext.add_point (me->relative_coordinate (refpoint, a));