2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
13 #include "align-interface.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"
32 return new Grob (*this);
35 Grob::Grob (SCM basicprops)
38 /* FIXME: default should be no callback. */
42 interfaces_ = SCM_EOL;
43 immutable_property_alist_ = basicprops;
44 mutable_property_alist_ = SCM_EOL;
45 object_alist_ = SCM_EOL;
47 /* We do smobify_self () as the first step. Since the object lives
48 on the heap, none of its SCM variables are protected from
49 GC. After smobify_self (), they are. */
52 SCM meta = get_property ("meta");
53 if (scm_is_pair (meta))
55 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
57 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
58 if (scm_is_pair (object_cbs))
60 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
61 set_object (scm_caar (s), scm_cdar (s));
65 if (get_property_data ("X-extent") == SCM_EOL)
66 set_property ("X-extent", Grob::stencil_width_proc);
67 if (get_property_data ("Y-extent") == SCM_EOL)
68 set_property ("Y-extent", Grob::stencil_height_proc);
71 Grob::Grob (Grob const &s)
72 : dim_cache_ (s.dim_cache_)
74 original_ = (Grob *) & s;
77 immutable_property_alist_ = s.immutable_property_alist_;
78 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
79 interfaces_ = s.interfaces_;
80 object_alist_ = SCM_EOL;
90 /****************************************************************
92 ****************************************************************/
95 Grob::get_stencil () const
100 SCM stil = get_property ("stencil");
101 return unsmob_stencil (stil);
105 Grob::get_print_stencil () const
107 SCM stil = get_property ("stencil");
110 if (Stencil *m = unsmob_stencil (stil))
113 if (to_boolean (get_property ("transparent")))
114 retval = Stencil (m->extent_box (), SCM_EOL);
117 SCM expr = m->expr ();
118 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
121 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_degrees (angle, Offset (x, y));
134 /* color support... see interpret_stencil_expression () for more... */
135 SCM color = get_property ("color");
136 if (scm_is_pair (color))
138 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
142 retval = Stencil (retval.extent_box (), expr);
150 /****************************************************************
152 ****************************************************************/
154 Grob::do_break_processing ()
159 Grob::discretionary_processing ()
164 Grob::get_system () const
171 Grob::handle_broken_dependencies ()
173 Spanner *sp = dynamic_cast<Spanner *> (this);
174 if (original () && sp)
178 /* THIS, SP is the original spanner. We use a special function
179 because some Spanners have enormously long lists in their
180 properties, and a special function fixes FOO */
182 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
183 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
185 System *system = get_system ();
189 && common_refpoint (system, X_AXIS)
190 && common_refpoint (system, Y_AXIS))
191 substitute_object_links (system->self_scm (), object_alist_);
192 else if (dynamic_cast<System *> (this))
193 substitute_object_links (SCM_UNDEFINED, object_alist_);
195 /* THIS element is `invalid'; it has been removed from all
196 dependencies, so let's junk the element itself.
198 Do not do this for System, since that would remove references
199 to the originals of score-grobs, which get then GC'd (a bad
204 /* Note that we still want references to this element to be
205 rearranged, and not silently thrown away, so we keep pointers like
206 {broken_into_{drul, array}, original}
214 for (int a = X_AXIS; a < NO_AXES; a++)
215 dim_cache_[a].clear ();
217 mutable_property_alist_ = SCM_EOL;
218 object_alist_ = SCM_EOL;
219 immutable_property_alist_ = SCM_EOL;
220 interfaces_ = SCM_EOL;
224 Grob::handle_prebroken_dependencies ()
226 /* Don't do this in the derived method, since we want to keep access to
227 object_alist_ centralized. */
230 Item *it = dynamic_cast<Item *> (this);
231 substitute_object_links (scm_from_int (it->break_status_dir ()),
232 original ()->object_alist_);
237 Grob::find_broken_piece (System *) const
242 /****************************************************************
244 ****************************************************************/
247 Grob::translate_axis (Real y, Axis a)
249 if (isinf (y) || isnan (y))
251 programming_error (_ ("Infinity or NaN encountered"));
255 if (!dim_cache_[a].offset_)
256 dim_cache_[a].offset_ = new Real (y);
258 *dim_cache_[a].offset_ += y;
261 /* Find the offset relative to D. If D equals THIS, then it is 0.
262 Otherwise, it recursively defd as
264 OFFSET_ + PARENT_L_->relative_coordinate (D) */
266 Grob::relative_coordinate (Grob const *refp, Axis a) const
271 /* We catch PARENT_L_ == nil case with this, but we crash if we did
272 not ask for the absolute coordinate (ie. REFP == nil.) */
273 Real off = get_offset (a);
274 if (refp == dim_cache_[a].parent_)
277 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
283 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
290 if (dim_cache_[Y_AXIS].offset_)
291 off = *dim_cache_[Y_AXIS].offset_;
294 SCM proc = get_property_data ("Y-offset");
296 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
297 off = robust_scm2double (call_pure_function (proc,
298 scm_list_1 (self_scm ()),
301 delete dim_cache_[Y_AXIS].offset_;
302 dim_cache_[Y_AXIS].offset_ = 0;
305 /* we simulate positioning-done if we are the child of a VerticalAlignment,
306 but only if we don't have a cached offset. If we do have a cached offset,
307 it probably means that the Alignment was fixed and it has already been
310 if (Grob *p = get_parent (Y_AXIS))
313 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
314 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
316 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
321 /* Invoke callbacks to get offset relative to parent. */
323 Grob::get_offset (Axis a) const
325 if (dim_cache_[a].offset_)
326 return *dim_cache_[a].offset_;
328 Grob *me = (Grob *) this;
330 SCM sym = axis_offset_symbol (a);
331 me->dim_cache_[a].offset_ = new Real (0.0);
334 UGH: can't fold next 2 statements together. Apparently GCC thinks
335 dim_cache_[a].offset_ is unaliased.
337 Real off = robust_scm2double (internal_get_property (sym), 0.0);
338 if (me->dim_cache_[a].offset_)
340 *me->dim_cache_[a].offset_ += off;
341 me->del_property (sym);
342 return *me->dim_cache_[a].offset_;
349 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
351 if (pure && a != Y_AXIS)
352 programming_error ("tried to get pure X-offset");
353 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
354 : relative_coordinate (refp, a);
357 /****************************************************************
359 ****************************************************************/
362 Grob::flush_extent_cache (Axis axis)
364 if (dim_cache_[axis].extent_)
367 Ugh, this is not accurate; will flush property, causing
368 callback to be called if.
370 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
371 delete dim_cache_[axis].extent_;
372 dim_cache_[axis].extent_ = 0;
373 if (get_parent (axis))
374 get_parent (axis)->flush_extent_cache (axis);
380 Grob::extent (Grob *refp, Axis a) const
382 Real offset = relative_coordinate (refp, a);
384 if (dim_cache_[a].extent_)
386 real_ext = *dim_cache_[a].extent_;
391 Order is significant: ?-extent may trigger suicide.
395 ? ly_symbol2scm ("X-extent")
396 : ly_symbol2scm ("Y-extent");
398 SCM ext = internal_get_property (ext_sym);
399 if (is_number_pair (ext))
400 real_ext.unite (ly_scm2interval (ext));
404 ? ly_symbol2scm ("minimum-X-extent")
405 : ly_symbol2scm ("minimum-Y-extent");
406 SCM min_ext = internal_get_property (min_ext_sym);
407 if (is_number_pair (min_ext))
408 real_ext.unite (ly_scm2interval (min_ext));
410 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
413 real_ext.translate (offset);
419 Grob::pure_height (Grob *refp, int start, int end)
421 SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
422 SCM iv_scm = call_pure_function (proc,
423 scm_list_1 (self_scm ()),
425 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
426 Real offset = pure_relative_y_coordinate (refp, start, end);
428 SCM min_ext = get_property ("minimum-Y-extent");
430 /* we don't add minimum-Y-extent if the extent is empty. This solves
431 a problem with Hara-kiri spanners. They would request_suicide and
432 return empty extents, but we would force them here to be large. */
433 if (!iv.is_empty () && is_number_pair (min_ext))
434 iv.unite (ly_scm2interval (min_ext));
437 iv.translate (offset);
442 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
444 if (pure && a != Y_AXIS)
445 programming_error ("tried to get pure width");
446 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
450 Grob::spanned_rank_interval () const
452 return Interval_t<int> (-1, 0);
455 /****************************************************************
457 ****************************************************************/
459 /* Find the group-element which has both #this# and #s# */
461 Grob::common_refpoint (Grob const *s, Axis a) const
463 /* I don't like the quadratic aspect of this code, but I see no
464 other way. The largest chain of parents might be 10 high or so,
465 so it shouldn't be a real issue. */
466 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
467 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
475 Grob::set_parent (Grob *g, Axis a)
477 dim_cache_[a].parent_ = g;
481 Grob::get_parent (Axis a) const
483 return dim_cache_[a].parent_;
488 Grob::fixup_refpoint ()
490 for (int a = X_AXIS; a < NO_AXES; a++)
493 Grob *parent = get_parent (ax);
498 if (parent->get_system () != get_system () && get_system ())
500 Grob *newparent = parent->find_broken_piece (get_system ());
501 set_parent (newparent, ax);
504 if (Item *i = dynamic_cast<Item *> (this))
506 Item *parenti = dynamic_cast<Item *> (parent);
510 Direction my_dir = i->break_status_dir ();
511 if (my_dir != parenti->break_status_dir ())
513 Item *newparent = parenti->find_prebroken_piece (my_dir);
514 set_parent (newparent, ax);
522 /****************************************************************
524 ****************************************************************/
526 Grob::warning (string s) const
528 SCM cause = self_scm ();
529 while (Grob *g = unsmob_grob (cause))
530 cause = g->get_property ("cause");
532 /* ES TODO: cause can't be Music*/
533 if (Music *m = unsmob_music (cause))
534 m->origin ()->warning (s);
535 else if (Stream_event *ev = unsmob_stream_event (cause))
536 ev->origin ()->warning (s);
545 SCM meta = get_property ("meta");
546 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
547 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
548 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
552 Grob::programming_error (string s) const
554 SCM cause = self_scm ();
555 while (Grob *g = unsmob_grob (cause))
556 cause = g->get_property ("cause");
558 s = _f ("programming error: %s", s);
560 /* ES TODO: cause can't be Music*/
561 if (Music *m = unsmob_music (cause))
562 m->origin ()->message (s);
563 else if (Stream_event *ev = unsmob_stream_event (cause))
564 ev->origin ()->warning (s);
571 "A grob represents a piece of music notation\n"
573 "All grobs have an X and Y-position on the page. These X and Y positions\n"
574 "are stored in a relative format, so they can easily be combined by\n"
575 "stacking them, hanging one grob to the side of another, and coupling\n"
576 "them into a grouping objects.\n"
578 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
579 "is stored relative to that reference point. For example the X-reference\n"
580 "point of a staccato dot usually is the note head that it applies\n"
581 "to. When the note head is moved, the staccato dot moves along\n"
584 "A grob is often associated with a symbol, but some grobs do not print\n"
585 "any symbols. They take care of grouping objects. For example, there is a\n"
586 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
587 "is also an abstract grob: it only moves around chords, but doesn't print\n"
590 "Grobs have a properties: Scheme variables, that can be read and set. "
591 "They have two types. Immutable variables "
592 "define the default style and behavior. They are shared between many objects. "
593 "They can be changed using @code{\\override} and @code{\\revert}. "
595 "Mutable properties are variables that are specific to one grob. Typically, "
596 "lists of other objects, or results from computations are stored in"
597 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
598 "sets a mutable property. "
600 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
601 "are dummies that are not user-serviceable. "
610 "after-line-breaking "
612 "axis-group-parent-X "
613 "axis-group-parent-Y "
614 "before-line-breaking "
626 "outside-staff-horizontal-padding "
627 "outside-staff-padding "
628 "outside-staff-priority "
636 /****************************************************************
638 ****************************************************************/
641 grob_stencil_extent (Grob *me, Axis a)
643 Stencil *m = me->get_stencil ();
647 return ly_interval2scm (e);
651 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
653 Grob::stencil_height (SCM smob)
655 Grob *me = unsmob_grob (smob);
656 return grob_stencil_extent (me, Y_AXIS);
659 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
661 Grob::y_parent_positioning (SCM smob)
663 Grob *me = unsmob_grob (smob);
664 Grob *par = me->get_parent (Y_AXIS);
666 (void) par->get_property ("positioning-done");
668 return scm_from_double (0.0);
672 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
674 Grob::x_parent_positioning (SCM smob)
676 Grob *me = unsmob_grob (smob);
678 Grob *par = me->get_parent (X_AXIS);
680 (void) par->get_property ("positioning-done");
682 return scm_from_double (0.0);
685 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
687 Grob::stencil_width (SCM smob)
689 Grob *me = unsmob_grob (smob);
690 return grob_stencil_extent (me, X_AXIS);
696 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
698 for (; scm_is_pair (elist); elist = scm_cdr (elist))
699 if (Grob *s = unsmob_grob (scm_car (elist)))
702 common = common->common_refpoint (s, a);
711 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
713 for (vsize i = arr.size (); i--;)
714 if (Grob *s = arr[i])
717 common = common->common_refpoint (s, a);
726 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
728 Interval ext = me->extent (refpoint, a);
730 ext.add_point (me->relative_coordinate (refpoint, a));