2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
24 #include "align-interface.hh"
25 #include "axis-group-interface.hh"
27 #include "international.hh"
32 #include "output-def.hh"
33 #include "pointer-group-interface.hh"
34 #include "program-option.hh"
36 #include "stream-event.hh"
40 #include "ly-smobs.icc"
45 return new Grob (*this);
48 Grob::Grob (SCM basicprops)
51 /* FIXME: default should be no callback. */
55 interfaces_ = SCM_EOL;
56 immutable_property_alist_ = basicprops;
57 mutable_property_alist_ = SCM_EOL;
58 object_alist_ = SCM_EOL;
60 /* We do smobify_self () as the first step. Since the object lives
61 on the heap, none of its SCM variables are protected from
62 GC. After smobify_self (), they are. */
65 SCM meta = get_property ("meta");
66 if (scm_is_pair (meta))
68 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
70 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
71 if (scm_is_pair (object_cbs))
73 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
74 set_object (scm_caar (s), scm_cdar (s));
78 if (get_property_data ("X-extent") == SCM_EOL)
79 set_property ("X-extent", Grob::stencil_width_proc);
80 if (get_property_data ("Y-extent") == SCM_EOL)
81 set_property ("Y-extent", Grob::stencil_height_proc);
84 Grob::Grob (Grob const &s)
85 : dim_cache_ (s.dim_cache_)
87 original_ = (Grob *) & s;
90 immutable_property_alist_ = s.immutable_property_alist_;
91 mutable_property_alist_ = SCM_EOL;
92 interfaces_ = s.interfaces_;
93 object_alist_ = SCM_EOL;
99 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
106 /****************************************************************
108 ****************************************************************/
111 Grob::get_stencil () const
116 SCM stil = get_property ("stencil");
117 return unsmob_stencil (stil);
121 Grob::get_print_stencil () const
123 SCM stil = get_property ("stencil");
126 if (Stencil *m = unsmob_stencil (stil))
129 bool transparent = to_boolean (get_property ("transparent"));
132 retval = Stencil (m->extent_box (), SCM_EOL);
135 SCM expr = m->expr ();
136 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
139 retval = Stencil (m->extent_box (), expr);
142 SCM rot = get_property ("rotation");
143 if (scm_is_pair (rot))
145 Real angle = scm_to_double (scm_car (rot));
146 Real x = scm_to_double (scm_cadr (rot));
147 Real y = scm_to_double (scm_caddr (rot));
149 retval.rotate_degrees (angle, Offset (x, y));
152 /* color support... see interpret_stencil_expression () for more... */
153 SCM color = get_property ("color");
154 if (scm_is_pair (color))
156 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
160 retval = Stencil (retval.extent_box (), expr);
163 /* process whiteout */
164 /* a grob has to be visible, otherwise the whiteout property has no effect */
165 if (!transparent && to_boolean (get_property ("whiteout")))
167 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
168 /* to add a round-filled-box stencil to the stencil list */
170 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
171 retval.smobbed_copy ()));
178 /****************************************************************
180 ****************************************************************/
182 Grob::do_break_processing ()
187 Grob::discretionary_processing ()
192 Grob::get_system () const
197 /* This version of get_system is more reliable than this->get_system ()
198 before line-breaking has been done, at which point there is only
199 one system in the whole score and we can find it just by following
202 Grob::get_system (Grob *me)
204 Grob *p = me->get_parent (X_AXIS);
205 return p ? get_system (p) : dynamic_cast<System *>(me);
209 Grob::handle_broken_dependencies ()
211 Spanner *sp = dynamic_cast<Spanner *> (this);
212 if (original () && sp)
216 /* THIS, SP is the original spanner. We use a special function
217 because some Spanners have enormously long lists in their
218 properties, and a special function fixes FOO */
220 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
221 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
223 System *system = get_system ();
227 && common_refpoint (system, X_AXIS)
228 && common_refpoint (system, Y_AXIS))
229 substitute_object_links (system->self_scm (), object_alist_);
230 else if (dynamic_cast<System *> (this))
231 substitute_object_links (SCM_UNDEFINED, object_alist_);
233 /* THIS element is `invalid'; it has been removed from all
234 dependencies, so let's junk the element itself.
236 Do not do this for System, since that would remove references
237 to the originals of score-grobs, which get then GC'd (a bad
242 /* Note that we still want references to this element to be
243 rearranged, and not silently thrown away, so we keep pointers like
244 {broken_into_{drul, array}, original}
252 for (int a = X_AXIS; a < NO_AXES; a++)
253 dim_cache_[a].clear ();
255 mutable_property_alist_ = SCM_EOL;
256 object_alist_ = SCM_EOL;
257 immutable_property_alist_ = SCM_EOL;
258 interfaces_ = SCM_EOL;
262 Grob::handle_prebroken_dependencies ()
264 /* Don't do this in the derived method, since we want to keep access to
265 object_alist_ centralized. */
268 Item *it = dynamic_cast<Item *> (this);
269 substitute_object_links (scm_from_int (it->break_status_dir ()),
270 original ()->object_alist_);
275 Grob::find_broken_piece (System *) const
280 /****************************************************************
282 ****************************************************************/
285 Grob::translate_axis (Real y, Axis a)
287 if (isinf (y) || isnan (y))
289 programming_error ("Infinity or NaN encountered");
293 if (!dim_cache_[a].offset_)
294 dim_cache_[a].offset_ = new Real (y);
296 *dim_cache_[a].offset_ += y;
299 /* Find the offset relative to D. If D equals THIS, then it is 0.
300 Otherwise, it recursively defd as
302 OFFSET_ + PARENT_L_->relative_coordinate (D) */
304 Grob::relative_coordinate (Grob const *refp, Axis a) const
306 /* eaa - hmmm, should we do a programming_error() here? */
307 if ((this == NULL) || (refp == this))
310 /* We catch PARENT_L_ == nil case with this, but we crash if we did
311 not ask for the absolute coordinate (ie. REFP == nil.) */
312 Real off = get_offset (a);
313 if (refp == dim_cache_[a].parent_)
316 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
322 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
329 if (dim_cache_[Y_AXIS].offset_)
331 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
332 programming_error ("cyclic chain in pure-Y-offset callbacks");
334 off = *dim_cache_[Y_AXIS].offset_;
338 SCM proc = get_property_data ("Y-offset");
340 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
341 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
342 off = robust_scm2double (call_pure_function (proc,
343 scm_list_1 (self_scm ()),
346 del_property ("pure-Y-offset-in-progress");
347 delete dim_cache_[Y_AXIS].offset_;
348 dim_cache_[Y_AXIS].offset_ = 0;
351 /* we simulate positioning-done if we are the child of a VerticalAlignment,
352 but only if we don't have a cached offset. If we do have a cached offset,
353 it probably means that the Alignment was fixed and it has already been
356 if (Grob *p = get_parent (Y_AXIS))
359 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
360 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
362 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
367 /* Invoke callbacks to get offset relative to parent. */
369 Grob::get_offset (Axis a) const
371 if (dim_cache_[a].offset_)
372 return *dim_cache_[a].offset_;
374 Grob *me = (Grob *) this;
376 SCM sym = axis_offset_symbol (a);
377 me->dim_cache_[a].offset_ = new Real (0.0);
380 UGH: can't fold next 2 statements together. Apparently GCC thinks
381 dim_cache_[a].offset_ is unaliased.
383 Real off = robust_scm2double (internal_get_property (sym), 0.0);
384 if (me->dim_cache_[a].offset_)
386 *me->dim_cache_[a].offset_ += off;
387 me->del_property (sym);
388 return *me->dim_cache_[a].offset_;
395 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
397 if (pure && a != Y_AXIS)
398 programming_error ("tried to get pure X-offset");
399 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
400 : relative_coordinate (refp, a);
403 /****************************************************************
405 ****************************************************************/
408 Grob::flush_extent_cache (Axis axis)
410 if (dim_cache_[axis].extent_)
413 Ugh, this is not accurate; will flush property, causing
414 callback to be called if.
416 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
417 delete dim_cache_[axis].extent_;
418 dim_cache_[axis].extent_ = 0;
419 if (get_parent (axis))
420 get_parent (axis)->flush_extent_cache (axis);
425 Grob::extent (Grob *refp, Axis a) const
427 Real offset = relative_coordinate (refp, a);
429 if (dim_cache_[a].extent_)
431 real_ext = *dim_cache_[a].extent_;
436 Order is significant: ?-extent may trigger suicide.
440 ? ly_symbol2scm ("X-extent")
441 : ly_symbol2scm ("Y-extent");
443 SCM ext = internal_get_property (ext_sym);
444 if (is_number_pair (ext))
445 real_ext.unite (ly_scm2interval (ext));
449 ? ly_symbol2scm ("minimum-X-extent")
450 : ly_symbol2scm ("minimum-Y-extent");
451 SCM min_ext = internal_get_property (min_ext_sym);
452 if (is_number_pair (min_ext))
453 real_ext.unite (ly_scm2interval (min_ext));
455 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
458 real_ext.translate (offset);
464 Grob::pure_height (Grob *refp, int start, int end)
466 SCM iv_scm = get_pure_property ("Y-extent", start, end);
467 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
468 Real offset = pure_relative_y_coordinate (refp, start, end);
470 SCM min_ext = get_property ("minimum-Y-extent");
472 /* we don't add minimum-Y-extent if the extent is empty. This solves
473 a problem with Hara-kiri spanners. They would request_suicide and
474 return empty extents, but we would force them here to be large. */
475 if (!iv.is_empty () && is_number_pair (min_ext))
476 iv.unite (ly_scm2interval (min_ext));
479 iv.translate (offset);
484 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
486 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
490 Grob::spanned_rank_interval () const
492 return Interval_t<int> (-1, 0);
496 Grob::pure_is_visible (int /* start */, int /* end */) const
501 /* Sort grobs according to their starting column. */
503 Grob::less (Grob *g1, Grob *g2)
505 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
508 /****************************************************************
510 ****************************************************************/
512 /* Find the group-element which has both #this# and #s# */
514 Grob::common_refpoint (Grob const *s, Axis a) const
517 /* Catching the trivial cases is likely costlier than just running
518 through: one can't avoid going to the respective chain ends
519 anyway. We might save the second run through when the chain ends
520 differ, but keeping track of the ends makes the loop more costly.
527 for (c = this; c; ++balance)
528 c = c->dim_cache_[a].parent_;
530 for (d = s; d; --balance)
531 d = d->dim_cache_[a].parent_;
533 /* Cut down ancestry to same size */
535 for (c = this; balance > 0; --balance)
536 c = c->dim_cache_[a].parent_;
538 for (d = s; balance < 0; ++balance)
539 d = d->dim_cache_[a].parent_;
541 /* Now find point where our lineages converge */
544 c = c->dim_cache_[a].parent_;
545 d = d->dim_cache_[a].parent_;
552 Grob::set_parent (Grob *g, Axis a)
554 dim_cache_[a].parent_ = g;
558 Grob::get_parent (Axis a) const
560 return dim_cache_[a].parent_;
564 Grob::fixup_refpoint ()
566 for (int a = X_AXIS; a < NO_AXES; a++)
569 Grob *parent = get_parent (ax);
574 if (parent->get_system () != get_system () && get_system ())
576 Grob *newparent = parent->find_broken_piece (get_system ());
577 set_parent (newparent, ax);
580 if (Item *i = dynamic_cast<Item *> (this))
582 Item *parenti = dynamic_cast<Item *> (parent);
586 Direction my_dir = i->break_status_dir ();
587 if (my_dir != parenti->break_status_dir ())
589 Item *newparent = parenti->find_prebroken_piece (my_dir);
590 set_parent (newparent, ax);
597 /****************************************************************
599 ****************************************************************/
602 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
606 if (Align_interface::has_interface (g))
607 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
608 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
613 Grob::get_root_vertical_alignment (Grob *g)
615 return get_maybe_root_vertical_alignment (g, 0);
619 Grob::get_vertical_axis_group (Grob *g)
623 if (Axis_group_interface::has_interface (g)
624 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
626 return get_vertical_axis_group (g->get_parent (Y_AXIS));
631 Grob::get_vertical_axis_group_index (Grob *g)
633 Grob *val = get_root_vertical_alignment (g);
636 Grob *vax = get_vertical_axis_group (g);
637 extract_grob_set (val, "elements", elts);
638 for (vsize i = 0; i < elts.size (); i++)
641 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
646 Grob::vertical_less (Grob *g1, Grob *g2)
648 return internal_vertical_less (g1, g2, false);
652 Grob::pure_vertical_less (Grob *g1, Grob *g2)
654 return internal_vertical_less (g1, g2, true);
658 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
660 Grob *vag = get_root_vertical_alignment (g1);
665 g1->programming_error ("grob does not belong to a VerticalAlignment?");
668 Grob *ag1 = get_vertical_axis_group (g1);
669 Grob *ag2 = get_vertical_axis_group (g2);
671 extract_grob_set (vag, "elements", elts);
673 if (ag1 == ag2 && !pure)
675 Grob *common = g1->common_refpoint (g2, Y_AXIS);
676 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
679 for (vsize i = 0; i < elts.size (); i++)
687 g1->programming_error ("could not place this grob in its axis group");
691 /****************************************************************
693 ****************************************************************/
695 Grob::programming_error (string s) const
697 SCM cause = self_scm ();
698 while (Grob *g = unsmob_grob (cause))
699 cause = g->get_property ("cause");
701 /* ES TODO: cause can't be Music*/
702 if (Music *m = unsmob_music (cause))
703 m->origin ()->programming_error (s);
704 else if (Stream_event *ev = unsmob_stream_event (cause))
705 ev->origin ()->programming_error (s);
707 ::programming_error (s);
711 Grob::warning (string s) const
713 SCM cause = self_scm ();
714 while (Grob *g = unsmob_grob (cause))
715 cause = g->get_property ("cause");
717 /* ES TODO: cause can't be Music*/
718 if (Music *m = unsmob_music (cause))
719 m->origin ()->warning (s);
720 else if (Stream_event *ev = unsmob_stream_event (cause))
721 ev->origin ()->warning (s);
729 SCM meta = get_property ("meta");
730 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
731 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
732 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
736 "A grob represents a piece of music notation.\n"
738 "All grobs have an X and Y@tie{}position on the page. These"
739 " X and Y@tie{}positions are stored in a relative format, thus"
740 " they can easily be combined by stacking them, hanging one"
741 " grob to the side of another, or coupling them into grouping"
744 "Each grob has a reference point (a.k.a.@: parent): The"
745 " position of a grob is stored relative to that reference"
746 " point. For example, the X@tie{}reference point of a staccato"
747 " dot usually is the note head that it applies to. When the"
748 " note head is moved, the staccato dot moves along"
751 "A grob is often associated with a symbol, but some grobs do"
752 " not print any symbols. They take care of grouping objects."
753 " For example, there is a separate grob that stacks staves"
754 " vertically. The @ref{NoteCollision} object is also an"
755 " abstract grob: It only moves around chords, but doesn't print"
758 "Grobs have properties (Scheme variables) that can be read and"
759 " set. Two types of them exist: immutable and mutable."
760 " Immutable variables define the default style and behavior."
761 " They are shared between many objects. They can be changed"
762 " using @code{\\override} and @code{\\revert}. Mutable"
763 " properties are variables that are specific to one grob."
764 " Typically, lists of other objects, or results from"
765 " computations are stored in mutable properties. In"
766 " particular, every call to @code{ly:grob-set-property!}"
767 " (or its C++ equivalent) sets a mutable property.\n"
769 "The properties @code{after-line-breaking} and"
770 " @code{before-line-breaking} are dummies that are not"
771 " user-serviceable.",
778 "after-line-breaking "
780 "axis-group-parent-X "
781 "axis-group-parent-Y "
782 "before-line-breaking "
795 "outside-staff-horizontal-padding "
796 "outside-staff-padding "
797 "outside-staff-priority "
798 "pure-Y-offset-in-progress "
807 /****************************************************************
809 ****************************************************************/
812 grob_stencil_extent (Grob *me, Axis a)
814 Stencil *m = me->get_stencil ();
818 return ly_interval2scm (e);
821 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
823 Grob::stencil_height (SCM smob)
825 Grob *me = unsmob_grob (smob);
826 return grob_stencil_extent (me, Y_AXIS);
829 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
831 Grob::y_parent_positioning (SCM smob)
833 Grob *me = unsmob_grob (smob);
834 Grob *par = me->get_parent (Y_AXIS);
836 (void) par->get_property ("positioning-done");
838 return scm_from_double (0.0);
841 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
843 Grob::x_parent_positioning (SCM smob)
845 Grob *me = unsmob_grob (smob);
847 Grob *par = me->get_parent (X_AXIS);
849 (void) par->get_property ("positioning-done");
851 return scm_from_double (0.0);
854 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
856 Grob::stencil_width (SCM smob)
858 Grob *me = unsmob_grob (smob);
859 return grob_stencil_extent (me, X_AXIS);
863 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
865 for (; scm_is_pair (elist); elist = scm_cdr (elist))
866 if (Grob *s = unsmob_grob (scm_car (elist)))
869 common = common->common_refpoint (s, a);
878 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
880 for (vsize i = 0; i < arr.size (); i++)
882 common = common->common_refpoint (arr[i], a);
890 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
892 Interval ext = me->extent (refpoint, a);
894 ext.add_point (me->relative_coordinate (refpoint, a));
899 // Checks whether there is a vertical alignment in the chain of
900 // parents between this and commony.
902 Grob::check_cross_staff (Grob *commony)
904 if (Align_interface::has_interface (commony))
907 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
908 if (Align_interface::has_interface (g))