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/>.
25 #include "align-interface.hh"
26 #include "axis-group-interface.hh"
28 #include "international.hh"
33 #include "output-def.hh"
34 #include "pointer-group-interface.hh"
35 #include "program-option.hh"
37 #include "stream-event.hh"
41 #include "ly-smobs.icc"
46 return new Grob (*this);
49 Grob::Grob (SCM basicprops)
52 /* FIXME: default should be no callback. */
56 interfaces_ = SCM_EOL;
57 immutable_property_alist_ = basicprops;
58 mutable_property_alist_ = SCM_EOL;
59 object_alist_ = SCM_EOL;
61 /* We do smobify_self () as the first step. Since the object lives
62 on the heap, none of its SCM variables are protected from
63 GC. After smobify_self (), they are. */
66 SCM meta = get_property ("meta");
67 if (scm_is_pair (meta))
69 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
71 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
72 if (scm_is_pair (object_cbs))
74 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
75 set_object (scm_caar (s), scm_cdar (s));
79 if (get_property_data ("X-extent") == SCM_EOL)
80 set_property ("X-extent", Grob::stencil_width_proc);
81 if (get_property_data ("Y-extent") == SCM_EOL)
82 set_property ("Y-extent", Grob::stencil_height_proc);
83 if (get_property_data ("vertical-skylines") == SCM_EOL)
84 set_property ("vertical-skylines", Grob::simple_vertical_skylines_from_extents_proc);
85 if (get_property_data ("horizontal-skylines") == SCM_EOL)
86 set_property ("horizontal-skylines", Grob::simple_horizontal_skylines_from_extents_proc);
89 Grob::Grob (Grob const &s)
91 original_ = (Grob *) & s;
94 immutable_property_alist_ = s.immutable_property_alist_;
95 mutable_property_alist_ = SCM_EOL;
97 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
98 dim_cache_ [a] = s.dim_cache_ [a];
100 interfaces_ = s.interfaces_;
101 object_alist_ = SCM_EOL;
107 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
114 /****************************************************************
116 ****************************************************************/
119 Grob::get_stencil () const
124 SCM stil = get_property ("stencil");
125 return unsmob_stencil (stil);
129 Grob::get_print_stencil () const
131 SCM stil = get_property ("stencil");
134 if (Stencil *m = unsmob_stencil (stil))
137 bool transparent = to_boolean (get_property ("transparent"));
140 retval = Stencil (m->extent_box (), SCM_EOL);
143 SCM expr = m->expr ();
144 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
147 retval = Stencil (m->extent_box (), expr);
150 SCM rot = get_property ("rotation");
151 if (scm_is_pair (rot))
153 Real angle = scm_to_double (scm_car (rot));
154 Real x = scm_to_double (scm_cadr (rot));
155 Real y = scm_to_double (scm_caddr (rot));
157 retval.rotate_degrees (angle, Offset (x, y));
160 /* color support... see interpret_stencil_expression () for more... */
161 SCM color = get_property ("color");
162 if (scm_is_pair (color))
164 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
168 retval = Stencil (retval.extent_box (), expr);
171 /* process whiteout */
172 /* a grob has to be visible, otherwise the whiteout property has no effect */
173 if (!transparent && to_boolean (get_property ("whiteout")))
175 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
176 /* to add a round-filled-box stencil to the stencil list */
178 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
179 retval.smobbed_copy ()));
182 SCM id = get_property ("id");
183 if (scm_is_string (id))
185 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
189 retval = Stencil (retval.extent_box (), expr);
197 /****************************************************************
199 ****************************************************************/
201 Grob::do_break_processing ()
206 Grob::discretionary_processing ()
211 Grob::get_system () const
216 /* This version of get_system is more reliable than this->get_system ()
217 before line-breaking has been done, at which point there is only
218 one system in the whole score and we can find it just by following
221 Grob::get_system (Grob *me)
223 Grob *p = me->get_parent (X_AXIS);
224 return p ? get_system (p) : dynamic_cast<System *>(me);
228 Grob::handle_broken_dependencies ()
230 Spanner *sp = dynamic_cast<Spanner *> (this);
231 if (original () && sp)
235 /* THIS, SP is the original spanner. We use a special function
236 because some Spanners have enormously long lists in their
237 properties, and a special function fixes FOO */
239 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
240 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
242 System *system = get_system ();
246 && common_refpoint (system, X_AXIS)
247 && common_refpoint (system, Y_AXIS))
248 substitute_object_links (system->self_scm (), object_alist_);
249 else if (dynamic_cast<System *> (this))
250 substitute_object_links (SCM_UNDEFINED, object_alist_);
252 /* THIS element is `invalid'; it has been removed from all
253 dependencies, so let's junk the element itself.
255 Do not do this for System, since that would remove references
256 to the originals of score-grobs, which get then GC'd (a bad
261 /* Note that we still want references to this element to be
262 rearranged, and not silently thrown away, so we keep pointers like
263 {broken_into_{drul, array}, original}
271 for (int a = X_AXIS; a < NO_AXES; a++)
272 dim_cache_[a].clear ();
274 mutable_property_alist_ = SCM_EOL;
275 object_alist_ = SCM_EOL;
276 immutable_property_alist_ = SCM_EOL;
277 interfaces_ = SCM_EOL;
281 Grob::handle_prebroken_dependencies ()
283 /* Don't do this in the derived method, since we want to keep access to
284 object_alist_ centralized. */
287 Item *it = dynamic_cast<Item *> (this);
288 substitute_object_links (scm_from_int (it->break_status_dir ()),
289 original ()->object_alist_);
294 Grob::find_broken_piece (System *) const
299 /****************************************************************
301 ****************************************************************/
304 Grob::translate_axis (Real y, Axis a)
306 if (isinf (y) || isnan (y))
308 programming_error ("Infinity or NaN encountered");
312 if (!dim_cache_[a].offset_)
313 dim_cache_[a].offset_ = new Real (y);
315 *dim_cache_[a].offset_ += y;
318 /* Find the offset relative to D. If D equals THIS, then it is 0.
319 Otherwise, it recursively defd as
321 OFFSET_ + PARENT_L_->relative_coordinate (D) */
323 Grob::relative_coordinate (Grob const *refp, Axis a) const
325 /* eaa - hmmm, should we do a programming_error() here? */
326 if ((this == NULL) || (refp == this))
329 /* We catch PARENT_L_ == nil case with this, but we crash if we did
330 not ask for the absolute coordinate (ie. REFP == nil.) */
331 Real off = get_offset (a);
332 if (refp == dim_cache_[a].parent_)
335 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
341 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
348 if (dim_cache_[Y_AXIS].offset_)
350 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
351 programming_error ("cyclic chain in pure-Y-offset callbacks");
353 off = *dim_cache_[Y_AXIS].offset_;
357 SCM proc = get_property_data ("Y-offset");
359 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
360 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
361 off = robust_scm2double (call_pure_function (proc,
362 scm_list_1 (self_scm ()),
365 del_property ("pure-Y-offset-in-progress");
366 delete dim_cache_[Y_AXIS].offset_;
367 dim_cache_[Y_AXIS].offset_ = 0;
370 /* we simulate positioning-done if we are the child of a VerticalAlignment,
371 but only if we don't have a cached offset. If we do have a cached offset,
372 it probably means that the Alignment was fixed and it has already been
375 if (Grob *p = get_parent (Y_AXIS))
378 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
379 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
381 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
386 /* Invoke callbacks to get offset relative to parent. */
388 Grob::get_offset (Axis a) const
390 if (dim_cache_[a].offset_)
391 return *dim_cache_[a].offset_;
393 Grob *me = (Grob *) this;
395 SCM sym = axis_offset_symbol (a);
396 me->dim_cache_[a].offset_ = new Real (0.0);
399 UGH: can't fold next 2 statements together. Apparently GCC thinks
400 dim_cache_[a].offset_ is unaliased.
402 Real off = robust_scm2double (internal_get_property (sym), 0.0);
403 if (me->dim_cache_[a].offset_)
405 *me->dim_cache_[a].offset_ += off;
406 me->del_property (sym);
407 return *me->dim_cache_[a].offset_;
414 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
416 if (pure && a != Y_AXIS)
417 programming_error ("tried to get pure X-offset");
418 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
419 : relative_coordinate (refp, a);
422 /****************************************************************
424 ****************************************************************/
427 Grob::flush_extent_cache (Axis axis)
429 if (dim_cache_[axis].extent_)
432 Ugh, this is not accurate; will flush property, causing
433 callback to be called if.
435 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
436 delete dim_cache_[axis].extent_;
437 dim_cache_[axis].extent_ = 0;
438 if (get_parent (axis))
439 get_parent (axis)->flush_extent_cache (axis);
444 Grob::extent (Grob *refp, Axis a) const
446 Real offset = relative_coordinate (refp, a);
448 if (dim_cache_[a].extent_)
450 real_ext = *dim_cache_[a].extent_;
455 Order is significant: ?-extent may trigger suicide.
459 ? ly_symbol2scm ("X-extent")
460 : ly_symbol2scm ("Y-extent");
462 SCM ext = internal_get_property (ext_sym);
463 if (is_number_pair (ext))
464 real_ext.unite (ly_scm2interval (ext));
468 ? ly_symbol2scm ("minimum-X-extent")
469 : ly_symbol2scm ("minimum-Y-extent");
470 SCM min_ext = internal_get_property (min_ext_sym);
471 if (is_number_pair (min_ext))
472 real_ext.unite (ly_scm2interval (min_ext));
474 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
477 // We never want nan, so we avoid shifting infinite values.
479 real_ext.translate(offset);
481 this->warning(_f ("ignored infinite %s-offset",
482 a == X_AXIS ? "X" : "Y"));
488 Grob::pure_height (Grob *refp, int start, int end)
490 SCM iv_scm = get_pure_property ("Y-extent", start, end);
491 // TODO: Why is this Interval (0,0)
492 // Shouldn't it just be an empty interval?
493 // 0,0 puts an arbitrary point at 0,0 which will influence spacing
494 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
495 Real offset = pure_relative_y_coordinate (refp, start, end);
497 SCM min_ext = get_property ("minimum-Y-extent");
499 /* we don't add minimum-Y-extent if the extent is empty. This solves
500 a problem with Hara-kiri spanners. They would request_suicide and
501 return empty extents, but we would force them here to be large. */
502 if (!iv.is_empty () && is_number_pair (min_ext))
503 iv.unite (ly_scm2interval (min_ext));
506 iv.translate (offset);
511 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
513 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
517 Grob::spanned_rank_interval () const
519 return Interval_t<int> (-1, 0);
523 Grob::pure_is_visible (int /* start */, int /* end */) const
528 /* Sort grobs according to their starting column. */
530 Grob::less (Grob *g1, Grob *g2)
532 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
535 /****************************************************************
537 ****************************************************************/
539 /* Find the group-element which has both #this# and #s# */
541 Grob::common_refpoint (Grob const *s, Axis a) const
544 /* Catching the trivial cases is likely costlier than just running
545 through: one can't avoid going to the respective chain ends
546 anyway. We might save the second run through when the chain ends
547 differ, but keeping track of the ends makes the loop more costly.
554 for (c = this; c; ++balance)
555 c = c->dim_cache_[a].parent_;
557 for (d = s; d; --balance)
558 d = d->dim_cache_[a].parent_;
560 /* Cut down ancestry to same size */
562 for (c = this; balance > 0; --balance)
563 c = c->dim_cache_[a].parent_;
565 for (d = s; balance < 0; ++balance)
566 d = d->dim_cache_[a].parent_;
568 /* Now find point where our lineages converge */
571 c = c->dim_cache_[a].parent_;
572 d = d->dim_cache_[a].parent_;
579 Grob::set_parent (Grob *g, Axis a)
581 dim_cache_[a].parent_ = g;
585 Grob::get_parent (Axis a) const
587 return dim_cache_[a].parent_;
591 Grob::fixup_refpoint ()
593 for (int a = X_AXIS; a < NO_AXES; a++)
596 Grob *parent = get_parent (ax);
601 if (parent->get_system () != get_system () && get_system ())
603 Grob *newparent = parent->find_broken_piece (get_system ());
604 set_parent (newparent, ax);
607 if (Item *i = dynamic_cast<Item *> (this))
609 Item *parenti = dynamic_cast<Item *> (parent);
613 Direction my_dir = i->break_status_dir ();
614 if (my_dir != parenti->break_status_dir ())
616 Item *newparent = parenti->find_prebroken_piece (my_dir);
617 set_parent (newparent, ax);
624 /****************************************************************
626 ****************************************************************/
629 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
633 if (Align_interface::has_interface (g))
634 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
635 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
640 Grob::get_root_vertical_alignment (Grob *g)
642 return get_maybe_root_vertical_alignment (g, 0);
646 Grob::get_vertical_axis_group (Grob *g)
650 if (!g->get_parent (Y_AXIS))
652 if (Axis_group_interface::has_interface (g)
653 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
655 return get_vertical_axis_group (g->get_parent (Y_AXIS));
660 Grob::get_vertical_axis_group_index (Grob *g)
662 Grob *val = get_root_vertical_alignment (g);
665 Grob *vax = get_vertical_axis_group (g);
666 extract_grob_set (val, "elements", elts);
667 for (vsize i = 0; i < elts.size (); i++)
670 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
675 Grob::vertical_less (Grob *g1, Grob *g2)
677 return internal_vertical_less (g1, g2, false);
681 Grob::pure_vertical_less (Grob *g1, Grob *g2)
683 return internal_vertical_less (g1, g2, true);
687 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
689 Grob *vag = get_root_vertical_alignment (g1);
692 g1->programming_error ("grob does not belong to a VerticalAlignment?");
696 Grob *ag1 = get_vertical_axis_group (g1);
697 Grob *ag2 = get_vertical_axis_group (g2);
699 extract_grob_set (vag, "elements", elts);
701 if (ag1 == ag2 && !pure)
703 Grob *common = g1->common_refpoint (g2, Y_AXIS);
704 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
707 for (vsize i = 0; i < elts.size (); i++)
715 g1->programming_error ("could not place this grob in its axis group");
719 /****************************************************************
721 ****************************************************************/
723 Grob::programming_error (string s) const
725 SCM cause = self_scm ();
726 while (Grob *g = unsmob_grob (cause))
727 cause = g->get_property ("cause");
729 /* ES TODO: cause can't be Music*/
730 if (Music *m = unsmob_music (cause))
731 m->origin ()->programming_error (s);
732 else if (Stream_event *ev = unsmob_stream_event (cause))
733 ev->origin ()->programming_error (s);
735 ::programming_error (s);
739 Grob::warning (string s) const
741 SCM cause = self_scm ();
742 while (Grob *g = unsmob_grob (cause))
743 cause = g->get_property ("cause");
745 /* ES TODO: cause can't be Music*/
746 if (Music *m = unsmob_music (cause))
747 m->origin ()->warning (s);
748 else if (Stream_event *ev = unsmob_stream_event (cause))
749 ev->origin ()->warning (s);
757 SCM meta = get_property ("meta");
758 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
759 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
760 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
764 "A grob represents a piece of music notation.\n"
766 "All grobs have an X and Y@tie{}position on the page. These"
767 " X and Y@tie{}positions are stored in a relative format, thus"
768 " they can easily be combined by stacking them, hanging one"
769 " grob to the side of another, or coupling them into grouping"
772 "Each grob has a reference point (a.k.a.@: parent): The"
773 " position of a grob is stored relative to that reference"
774 " point. For example, the X@tie{}reference point of a staccato"
775 " dot usually is the note head that it applies to. When the"
776 " note head is moved, the staccato dot moves along"
779 "A grob is often associated with a symbol, but some grobs do"
780 " not print any symbols. They take care of grouping objects."
781 " For example, there is a separate grob that stacks staves"
782 " vertically. The @ref{NoteCollision} object is also an"
783 " abstract grob: It only moves around chords, but doesn't print"
786 "Grobs have properties (Scheme variables) that can be read and"
787 " set. Two types of them exist: immutable and mutable."
788 " Immutable variables define the default style and behavior."
789 " They are shared between many objects. They can be changed"
790 " using @code{\\override} and @code{\\revert}. Mutable"
791 " properties are variables that are specific to one grob."
792 " Typically, lists of other objects, or results from"
793 " computations are stored in mutable properties. In"
794 " particular, every call to @code{ly:grob-set-property!}"
795 " (or its C++ equivalent) sets a mutable property.\n"
797 "The properties @code{after-line-breaking} and"
798 " @code{before-line-breaking} are dummies that are not"
799 " user-serviceable.",
806 "after-line-breaking "
808 "axis-group-parent-X "
809 "axis-group-parent-Y "
810 "before-line-breaking "
818 "horizontal-skylines "
824 "outside-staff-horizontal-padding "
825 "outside-staff-padding "
826 "outside-staff-priority "
827 "pure-Y-offset-in-progress "
829 "skyline-horizontal-padding "
838 /****************************************************************
840 ****************************************************************/
843 grob_stencil_extent (Grob *me, Axis a)
845 Stencil *m = me->get_stencil ();
849 return ly_interval2scm (e);
852 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
854 Grob::stencil_height (SCM smob)
856 Grob *me = unsmob_grob (smob);
857 return grob_stencil_extent (me, Y_AXIS);
860 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
862 Grob::y_parent_positioning (SCM smob)
864 Grob *me = unsmob_grob (smob);
865 Grob *par = me->get_parent (Y_AXIS);
867 (void) par->get_property ("positioning-done");
869 return scm_from_double (0.0);
872 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
874 Grob::x_parent_positioning (SCM smob)
876 Grob *me = unsmob_grob (smob);
878 Grob *par = me->get_parent (X_AXIS);
880 (void) par->get_property ("positioning-done");
882 return scm_from_double (0.0);
885 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
887 Grob::stencil_width (SCM smob)
889 Grob *me = unsmob_grob (smob);
890 return grob_stencil_extent (me, X_AXIS);
894 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
896 for (; scm_is_pair (elist); elist = scm_cdr (elist))
897 if (Grob *s = unsmob_grob (scm_car (elist)))
900 common = common->common_refpoint (s, a);
909 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
911 for (vsize i = 0; i < arr.size (); i++)
913 common = common->common_refpoint (arr[i], a);
921 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
923 set<Grob *>::iterator it;
925 for (it = arr.begin (); it != arr.end (); it++)
927 common = common->common_refpoint (*it, a);
935 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
937 Interval ext = me->extent (refpoint, a);
939 ext.add_point (me->relative_coordinate (refpoint, a));
944 // Checks whether there is a vertical alignment in the chain of
945 // parents between this and commony.
947 Grob::check_cross_staff (Grob *commony)
949 if (Align_interface::has_interface (commony))
952 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
953 if (Align_interface::has_interface (g))