2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 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);
82 if (get_property_data ("vertical-skylines") == SCM_EOL)
83 set_property ("vertical-skylines", Grob::simple_vertical_skylines_from_stencil_proc);
84 if (get_property_data ("horizontal-skylines") == SCM_EOL)
85 set_property ("horizontal-skylines", Grob::simple_horizontal_skylines_from_stencil_proc);
88 Grob::Grob (Grob const &s)
89 : dim_cache_ (s.dim_cache_)
91 original_ = (Grob *) & s;
94 immutable_property_alist_ = s.immutable_property_alist_;
95 mutable_property_alist_ = SCM_EOL;
96 interfaces_ = s.interfaces_;
97 object_alist_ = SCM_EOL;
103 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
110 /****************************************************************
112 ****************************************************************/
115 Grob::get_stencil () const
120 SCM stil = get_property ("stencil");
121 return unsmob_stencil (stil);
125 Grob::get_print_stencil () const
127 SCM stil = get_property ("stencil");
130 if (Stencil *m = unsmob_stencil (stil))
133 bool transparent = to_boolean (get_property ("transparent"));
136 retval = Stencil (m->extent_box (), SCM_EOL);
139 SCM expr = m->expr ();
140 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
143 retval = Stencil (m->extent_box (), expr);
146 SCM rot = get_property ("rotation");
147 if (scm_is_pair (rot))
149 Real angle = scm_to_double (scm_car (rot));
150 Real x = scm_to_double (scm_cadr (rot));
151 Real y = scm_to_double (scm_caddr (rot));
153 retval.rotate_degrees (angle, Offset (x, y));
156 /* color support... see interpret_stencil_expression () for more... */
157 SCM color = get_property ("color");
158 if (scm_is_pair (color))
160 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
164 retval = Stencil (retval.extent_box (), expr);
167 /* process whiteout */
168 /* a grob has to be visible, otherwise the whiteout property has no effect */
169 if (!transparent && to_boolean (get_property ("whiteout")))
171 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
172 /* to add a round-filled-box stencil to the stencil list */
174 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
175 retval.smobbed_copy ()));
178 SCM id = get_property ("id");
179 if (scm_is_string (id))
181 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
185 retval = Stencil (retval.extent_box (), expr);
193 /****************************************************************
195 ****************************************************************/
197 Grob::do_break_processing ()
202 Grob::discretionary_processing ()
207 Grob::get_system () const
212 /* This version of get_system is more reliable than this->get_system ()
213 before line-breaking has been done, at which point there is only
214 one system in the whole score and we can find it just by following
217 Grob::get_system (Grob *me)
219 Grob *p = me->get_parent (X_AXIS);
220 return p ? get_system (p) : dynamic_cast<System *>(me);
224 Grob::handle_broken_dependencies ()
226 Spanner *sp = dynamic_cast<Spanner *> (this);
227 if (original () && sp)
231 /* THIS, SP is the original spanner. We use a special function
232 because some Spanners have enormously long lists in their
233 properties, and a special function fixes FOO */
235 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
236 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
238 System *system = get_system ();
242 && common_refpoint (system, X_AXIS)
243 && common_refpoint (system, Y_AXIS))
244 substitute_object_links (system->self_scm (), object_alist_);
245 else if (dynamic_cast<System *> (this))
246 substitute_object_links (SCM_UNDEFINED, object_alist_);
248 /* THIS element is `invalid'; it has been removed from all
249 dependencies, so let's junk the element itself.
251 Do not do this for System, since that would remove references
252 to the originals of score-grobs, which get then GC'd (a bad
257 /* Note that we still want references to this element to be
258 rearranged, and not silently thrown away, so we keep pointers like
259 {broken_into_{drul, array}, original}
267 for (int a = X_AXIS; a < NO_AXES; a++)
268 dim_cache_[a].clear ();
270 mutable_property_alist_ = SCM_EOL;
271 object_alist_ = SCM_EOL;
272 immutable_property_alist_ = SCM_EOL;
273 interfaces_ = SCM_EOL;
277 Grob::handle_prebroken_dependencies ()
279 /* Don't do this in the derived method, since we want to keep access to
280 object_alist_ centralized. */
283 Item *it = dynamic_cast<Item *> (this);
284 substitute_object_links (scm_from_int (it->break_status_dir ()),
285 original ()->object_alist_);
290 Grob::find_broken_piece (System *) const
295 /****************************************************************
297 ****************************************************************/
300 Grob::translate_axis (Real y, Axis a)
302 if (isinf (y) || isnan (y))
304 programming_error ("Infinity or NaN encountered");
308 if (!dim_cache_[a].offset_)
309 dim_cache_[a].offset_ = new Real (y);
311 *dim_cache_[a].offset_ += y;
314 /* Find the offset relative to D. If D equals THIS, then it is 0.
315 Otherwise, it recursively defd as
317 OFFSET_ + PARENT_L_->relative_coordinate (D) */
319 Grob::relative_coordinate (Grob const *refp, Axis a) const
321 /* eaa - hmmm, should we do a programming_error() here? */
322 if ((this == NULL) || (refp == this))
325 /* We catch PARENT_L_ == nil case with this, but we crash if we did
326 not ask for the absolute coordinate (ie. REFP == nil.) */
327 Real off = get_offset (a);
328 if (refp == dim_cache_[a].parent_)
331 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
337 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
344 if (dim_cache_[Y_AXIS].offset_)
346 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
347 programming_error ("cyclic chain in pure-Y-offset callbacks");
349 off = *dim_cache_[Y_AXIS].offset_;
353 SCM proc = get_property_data ("Y-offset");
355 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
356 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
357 off = robust_scm2double (call_pure_function (proc,
358 scm_list_1 (self_scm ()),
361 del_property ("pure-Y-offset-in-progress");
362 delete dim_cache_[Y_AXIS].offset_;
363 dim_cache_[Y_AXIS].offset_ = 0;
366 /* we simulate positioning-done if we are the child of a VerticalAlignment,
367 but only if we don't have a cached offset. If we do have a cached offset,
368 it probably means that the Alignment was fixed and it has already been
371 if (Grob *p = get_parent (Y_AXIS))
374 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
375 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
377 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
382 /* Invoke callbacks to get offset relative to parent. */
384 Grob::get_offset (Axis a) const
386 if (dim_cache_[a].offset_)
387 return *dim_cache_[a].offset_;
389 Grob *me = (Grob *) this;
391 SCM sym = axis_offset_symbol (a);
392 me->dim_cache_[a].offset_ = new Real (0.0);
395 UGH: can't fold next 2 statements together. Apparently GCC thinks
396 dim_cache_[a].offset_ is unaliased.
398 Real off = robust_scm2double (internal_get_property (sym), 0.0);
399 if (me->dim_cache_[a].offset_)
401 *me->dim_cache_[a].offset_ += off;
402 me->del_property (sym);
403 return *me->dim_cache_[a].offset_;
410 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
412 if (pure && a != Y_AXIS)
413 programming_error ("tried to get pure X-offset");
414 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
415 : relative_coordinate (refp, a);
418 /****************************************************************
420 ****************************************************************/
423 Grob::flush_extent_cache (Axis axis)
425 if (dim_cache_[axis].extent_)
428 Ugh, this is not accurate; will flush property, causing
429 callback to be called if.
431 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
432 delete dim_cache_[axis].extent_;
433 dim_cache_[axis].extent_ = 0;
434 if (get_parent (axis))
435 get_parent (axis)->flush_extent_cache (axis);
440 Grob::extent (Grob *refp, Axis a) const
442 Real offset = relative_coordinate (refp, a);
444 if (dim_cache_[a].extent_)
446 real_ext = *dim_cache_[a].extent_;
451 Order is significant: ?-extent may trigger suicide.
455 ? ly_symbol2scm ("X-extent")
456 : ly_symbol2scm ("Y-extent");
458 SCM ext = internal_get_property (ext_sym);
459 if (is_number_pair (ext))
460 real_ext.unite (ly_scm2interval (ext));
464 ? ly_symbol2scm ("minimum-X-extent")
465 : ly_symbol2scm ("minimum-Y-extent");
466 SCM min_ext = internal_get_property (min_ext_sym);
467 if (is_number_pair (min_ext))
468 real_ext.unite (ly_scm2interval (min_ext));
470 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
473 // We never want nan, so we avoid shifting infinite values.
474 for (LEFT_and_RIGHT (d))
475 if (!isinf (real_ext[d]))
476 real_ext[d] += offset;
482 Grob::pure_height (Grob *refp, int start, int end)
484 SCM iv_scm = get_pure_property ("Y-extent", start, end);
485 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
486 Real offset = pure_relative_y_coordinate (refp, start, end);
488 SCM min_ext = get_property ("minimum-Y-extent");
490 /* we don't add minimum-Y-extent if the extent is empty. This solves
491 a problem with Hara-kiri spanners. They would request_suicide and
492 return empty extents, but we would force them here to be large. */
493 if (!iv.is_empty () && is_number_pair (min_ext))
494 iv.unite (ly_scm2interval (min_ext));
497 iv.translate (offset);
502 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
504 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
508 Grob::spanned_rank_interval () const
510 return Interval_t<int> (-1, 0);
514 Grob::pure_is_visible (int /* start */, int /* end */) const
519 /* Sort grobs according to their starting column. */
521 Grob::less (Grob *g1, Grob *g2)
523 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
526 /****************************************************************
528 ****************************************************************/
530 /* Find the group-element which has both #this# and #s# */
532 Grob::common_refpoint (Grob const *s, Axis a) const
535 /* Catching the trivial cases is likely costlier than just running
536 through: one can't avoid going to the respective chain ends
537 anyway. We might save the second run through when the chain ends
538 differ, but keeping track of the ends makes the loop more costly.
545 for (c = this; c; ++balance)
546 c = c->dim_cache_[a].parent_;
548 for (d = s; d; --balance)
549 d = d->dim_cache_[a].parent_;
551 /* Cut down ancestry to same size */
553 for (c = this; balance > 0; --balance)
554 c = c->dim_cache_[a].parent_;
556 for (d = s; balance < 0; ++balance)
557 d = d->dim_cache_[a].parent_;
559 /* Now find point where our lineages converge */
562 c = c->dim_cache_[a].parent_;
563 d = d->dim_cache_[a].parent_;
570 Grob::set_parent (Grob *g, Axis a)
572 dim_cache_[a].parent_ = g;
576 Grob::get_parent (Axis a) const
578 return dim_cache_[a].parent_;
582 Grob::fixup_refpoint ()
584 for (int a = X_AXIS; a < NO_AXES; a++)
587 Grob *parent = get_parent (ax);
592 if (parent->get_system () != get_system () && get_system ())
594 Grob *newparent = parent->find_broken_piece (get_system ());
595 set_parent (newparent, ax);
598 if (Item *i = dynamic_cast<Item *> (this))
600 Item *parenti = dynamic_cast<Item *> (parent);
604 Direction my_dir = i->break_status_dir ();
605 if (my_dir != parenti->break_status_dir ())
607 Item *newparent = parenti->find_prebroken_piece (my_dir);
608 set_parent (newparent, ax);
615 /****************************************************************
617 ****************************************************************/
620 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
624 if (Align_interface::has_interface (g))
625 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
626 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
631 Grob::get_root_vertical_alignment (Grob *g)
633 return get_maybe_root_vertical_alignment (g, 0);
637 Grob::get_vertical_axis_group (Grob *g)
641 if (!g->get_parent (Y_AXIS))
643 if (Axis_group_interface::has_interface (g)
644 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
646 return get_vertical_axis_group (g->get_parent (Y_AXIS));
651 Grob::get_vertical_axis_group_index (Grob *g)
653 Grob *val = get_root_vertical_alignment (g);
656 Grob *vax = get_vertical_axis_group (g);
657 extract_grob_set (val, "elements", elts);
658 for (vsize i = 0; i < elts.size (); i++)
661 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
666 Grob::vertical_less (Grob *g1, Grob *g2)
668 return internal_vertical_less (g1, g2, false);
672 Grob::pure_vertical_less (Grob *g1, Grob *g2)
674 return internal_vertical_less (g1, g2, true);
678 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
680 Grob *vag = get_root_vertical_alignment (g1);
683 g1->programming_error ("grob does not belong to a VerticalAlignment?");
687 Grob *ag1 = get_vertical_axis_group (g1);
688 Grob *ag2 = get_vertical_axis_group (g2);
690 extract_grob_set (vag, "elements", elts);
692 if (ag1 == ag2 && !pure)
694 Grob *common = g1->common_refpoint (g2, Y_AXIS);
695 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
698 for (vsize i = 0; i < elts.size (); i++)
706 g1->programming_error ("could not place this grob in its axis group");
710 /****************************************************************
712 ****************************************************************/
714 Grob::programming_error (string s) const
716 SCM cause = self_scm ();
717 while (Grob *g = unsmob_grob (cause))
718 cause = g->get_property ("cause");
720 /* ES TODO: cause can't be Music*/
721 if (Music *m = unsmob_music (cause))
722 m->origin ()->programming_error (s);
723 else if (Stream_event *ev = unsmob_stream_event (cause))
724 ev->origin ()->programming_error (s);
726 ::programming_error (s);
730 Grob::warning (string s) const
732 SCM cause = self_scm ();
733 while (Grob *g = unsmob_grob (cause))
734 cause = g->get_property ("cause");
736 /* ES TODO: cause can't be Music*/
737 if (Music *m = unsmob_music (cause))
738 m->origin ()->warning (s);
739 else if (Stream_event *ev = unsmob_stream_event (cause))
740 ev->origin ()->warning (s);
748 SCM meta = get_property ("meta");
749 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
750 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
751 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
755 "A grob represents a piece of music notation.\n"
757 "All grobs have an X and Y@tie{}position on the page. These"
758 " X and Y@tie{}positions are stored in a relative format, thus"
759 " they can easily be combined by stacking them, hanging one"
760 " grob to the side of another, or coupling them into grouping"
763 "Each grob has a reference point (a.k.a.@: parent): The"
764 " position of a grob is stored relative to that reference"
765 " point. For example, the X@tie{}reference point of a staccato"
766 " dot usually is the note head that it applies to. When the"
767 " note head is moved, the staccato dot moves along"
770 "A grob is often associated with a symbol, but some grobs do"
771 " not print any symbols. They take care of grouping objects."
772 " For example, there is a separate grob that stacks staves"
773 " vertically. The @ref{NoteCollision} object is also an"
774 " abstract grob: It only moves around chords, but doesn't print"
777 "Grobs have properties (Scheme variables) that can be read and"
778 " set. Two types of them exist: immutable and mutable."
779 " Immutable variables define the default style and behavior."
780 " They are shared between many objects. They can be changed"
781 " using @code{\\override} and @code{\\revert}. Mutable"
782 " properties are variables that are specific to one grob."
783 " Typically, lists of other objects, or results from"
784 " computations are stored in mutable properties. In"
785 " particular, every call to @code{ly:grob-set-property!}"
786 " (or its C++ equivalent) sets a mutable property.\n"
788 "The properties @code{after-line-breaking} and"
789 " @code{before-line-breaking} are dummies that are not"
790 " user-serviceable.",
797 "after-line-breaking "
799 "axis-group-parent-X "
800 "axis-group-parent-Y "
801 "before-line-breaking "
811 "horizontal-skylines "
817 "outside-staff-horizontal-padding "
818 "outside-staff-padding "
819 "outside-staff-priority "
820 "pure-Y-offset-in-progress "
822 "skyline-horizontal-padding "
831 /****************************************************************
833 ****************************************************************/
836 grob_stencil_extent (Grob *me, Axis a)
838 Stencil *m = me->get_stencil ();
842 return ly_interval2scm (e);
845 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
847 Grob::stencil_height (SCM smob)
849 Grob *me = unsmob_grob (smob);
850 return grob_stencil_extent (me, Y_AXIS);
853 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
855 Grob::y_parent_positioning (SCM smob)
857 Grob *me = unsmob_grob (smob);
858 Grob *par = me->get_parent (Y_AXIS);
860 (void) par->get_property ("positioning-done");
862 return scm_from_double (0.0);
865 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
867 Grob::x_parent_positioning (SCM smob)
869 Grob *me = unsmob_grob (smob);
871 Grob *par = me->get_parent (X_AXIS);
873 (void) par->get_property ("positioning-done");
875 return scm_from_double (0.0);
878 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
880 Grob::stencil_width (SCM smob)
882 Grob *me = unsmob_grob (smob);
883 return grob_stencil_extent (me, X_AXIS);
887 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
889 for (; scm_is_pair (elist); elist = scm_cdr (elist))
890 if (Grob *s = unsmob_grob (scm_car (elist)))
893 common = common->common_refpoint (s, a);
902 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
904 for (vsize i = 0; i < arr.size (); i++)
906 common = common->common_refpoint (arr[i], a);
914 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
916 Interval ext = me->extent (refpoint, a);
918 ext.add_point (me->relative_coordinate (refpoint, a));
923 // Checks whether there is a vertical alignment in the chain of
924 // parents between this and commony.
926 Grob::check_cross_staff (Grob *commony)
928 if (Align_interface::has_interface (commony))
931 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
932 if (Align_interface::has_interface (g))