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_stencil_proc);
85 if (get_property_data ("horizontal-skylines") == SCM_EOL)
86 set_property ("horizontal-skylines", Grob::simple_horizontal_skylines_from_stencil_proc);
89 Grob::Grob (Grob const &s)
90 : dim_cache_ (s.dim_cache_)
92 original_ = (Grob *) & s;
95 immutable_property_alist_ = s.immutable_property_alist_;
96 mutable_property_alist_ = SCM_EOL;
97 interfaces_ = s.interfaces_;
98 object_alist_ = SCM_EOL;
104 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
111 /****************************************************************
113 ****************************************************************/
116 Grob::get_stencil () const
121 SCM stil = get_property ("stencil");
122 return unsmob_stencil (stil);
126 Grob::get_print_stencil () const
128 SCM stil = get_property ("stencil");
131 if (Stencil *m = unsmob_stencil (stil))
134 bool transparent = to_boolean (get_property ("transparent"));
137 retval = Stencil (m->extent_box (), SCM_EOL);
140 SCM expr = m->expr ();
141 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
144 retval = Stencil (m->extent_box (), expr);
147 SCM rot = get_property ("rotation");
148 if (scm_is_pair (rot))
150 Real angle = scm_to_double (scm_car (rot));
151 Real x = scm_to_double (scm_cadr (rot));
152 Real y = scm_to_double (scm_caddr (rot));
154 retval.rotate_degrees (angle, Offset (x, y));
157 /* color support... see interpret_stencil_expression () for more... */
158 SCM color = get_property ("color");
159 if (scm_is_pair (color))
161 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
165 retval = Stencil (retval.extent_box (), expr);
168 /* process whiteout */
169 /* a grob has to be visible, otherwise the whiteout property has no effect */
170 if (!transparent && to_boolean (get_property ("whiteout")))
172 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
173 /* to add a round-filled-box stencil to the stencil list */
175 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
176 retval.smobbed_copy ()));
179 SCM id = get_property ("id");
180 if (scm_is_string (id))
182 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
186 retval = Stencil (retval.extent_box (), expr);
194 /****************************************************************
196 ****************************************************************/
198 Grob::do_break_processing ()
203 Grob::discretionary_processing ()
208 Grob::get_system () const
213 /* This version of get_system is more reliable than this->get_system ()
214 before line-breaking has been done, at which point there is only
215 one system in the whole score and we can find it just by following
218 Grob::get_system (Grob *me)
220 Grob *p = me->get_parent (X_AXIS);
221 return p ? get_system (p) : dynamic_cast<System *>(me);
225 Grob::handle_broken_dependencies ()
227 Spanner *sp = dynamic_cast<Spanner *> (this);
228 if (original () && sp)
232 /* THIS, SP is the original spanner. We use a special function
233 because some Spanners have enormously long lists in their
234 properties, and a special function fixes FOO */
236 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
237 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
239 System *system = get_system ();
243 && common_refpoint (system, X_AXIS)
244 && common_refpoint (system, Y_AXIS))
245 substitute_object_links (system->self_scm (), object_alist_);
246 else if (dynamic_cast<System *> (this))
247 substitute_object_links (SCM_UNDEFINED, object_alist_);
249 /* THIS element is `invalid'; it has been removed from all
250 dependencies, so let's junk the element itself.
252 Do not do this for System, since that would remove references
253 to the originals of score-grobs, which get then GC'd (a bad
258 /* Note that we still want references to this element to be
259 rearranged, and not silently thrown away, so we keep pointers like
260 {broken_into_{drul, array}, original}
268 for (int a = X_AXIS; a < NO_AXES; a++)
269 dim_cache_[a].clear ();
271 mutable_property_alist_ = SCM_EOL;
272 object_alist_ = SCM_EOL;
273 immutable_property_alist_ = SCM_EOL;
274 interfaces_ = SCM_EOL;
278 Grob::handle_prebroken_dependencies ()
280 /* Don't do this in the derived method, since we want to keep access to
281 object_alist_ centralized. */
284 Item *it = dynamic_cast<Item *> (this);
285 substitute_object_links (scm_from_int (it->break_status_dir ()),
286 original ()->object_alist_);
291 Grob::find_broken_piece (System *) const
296 /****************************************************************
298 ****************************************************************/
301 Grob::translate_axis (Real y, Axis a)
303 if (isinf (y) || isnan (y))
305 programming_error ("Infinity or NaN encountered");
309 if (!dim_cache_[a].offset_)
310 dim_cache_[a].offset_ = new Real (y);
312 *dim_cache_[a].offset_ += y;
315 /* Find the offset relative to D. If D equals THIS, then it is 0.
316 Otherwise, it recursively defd as
318 OFFSET_ + PARENT_L_->relative_coordinate (D) */
320 Grob::relative_coordinate (Grob const *refp, Axis a) const
322 /* eaa - hmmm, should we do a programming_error() here? */
323 if ((this == NULL) || (refp == this))
326 /* We catch PARENT_L_ == nil case with this, but we crash if we did
327 not ask for the absolute coordinate (ie. REFP == nil.) */
328 Real off = get_offset (a);
329 if (refp == dim_cache_[a].parent_)
332 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
338 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
345 if (dim_cache_[Y_AXIS].offset_)
347 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
348 programming_error ("cyclic chain in pure-Y-offset callbacks");
350 off = *dim_cache_[Y_AXIS].offset_;
354 SCM proc = get_property_data ("Y-offset");
356 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
357 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
358 off = robust_scm2double (call_pure_function (proc,
359 scm_list_1 (self_scm ()),
362 del_property ("pure-Y-offset-in-progress");
363 delete dim_cache_[Y_AXIS].offset_;
364 dim_cache_[Y_AXIS].offset_ = 0;
367 /* we simulate positioning-done if we are the child of a VerticalAlignment,
368 but only if we don't have a cached offset. If we do have a cached offset,
369 it probably means that the Alignment was fixed and it has already been
372 if (Grob *p = get_parent (Y_AXIS))
375 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
376 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
378 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
383 /* Invoke callbacks to get offset relative to parent. */
385 Grob::get_offset (Axis a) const
387 if (dim_cache_[a].offset_)
388 return *dim_cache_[a].offset_;
390 Grob *me = (Grob *) this;
392 SCM sym = axis_offset_symbol (a);
393 me->dim_cache_[a].offset_ = new Real (0.0);
396 UGH: can't fold next 2 statements together. Apparently GCC thinks
397 dim_cache_[a].offset_ is unaliased.
399 Real off = robust_scm2double (internal_get_property (sym), 0.0);
400 if (me->dim_cache_[a].offset_)
402 *me->dim_cache_[a].offset_ += off;
403 me->del_property (sym);
404 return *me->dim_cache_[a].offset_;
411 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
413 if (pure && a != Y_AXIS)
414 programming_error ("tried to get pure X-offset");
415 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
416 : relative_coordinate (refp, a);
419 /****************************************************************
421 ****************************************************************/
424 Grob::flush_extent_cache (Axis axis)
426 if (dim_cache_[axis].extent_)
429 Ugh, this is not accurate; will flush property, causing
430 callback to be called if.
432 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
433 delete dim_cache_[axis].extent_;
434 dim_cache_[axis].extent_ = 0;
435 if (get_parent (axis))
436 get_parent (axis)->flush_extent_cache (axis);
441 Grob::extent (Grob *refp, Axis a) const
443 Real offset = relative_coordinate (refp, a);
445 if (dim_cache_[a].extent_)
447 real_ext = *dim_cache_[a].extent_;
452 Order is significant: ?-extent may trigger suicide.
456 ? ly_symbol2scm ("X-extent")
457 : ly_symbol2scm ("Y-extent");
459 SCM ext = internal_get_property (ext_sym);
460 if (is_number_pair (ext))
461 real_ext.unite (ly_scm2interval (ext));
465 ? ly_symbol2scm ("minimum-X-extent")
466 : ly_symbol2scm ("minimum-Y-extent");
467 SCM min_ext = internal_get_property (min_ext_sym);
468 if (is_number_pair (min_ext))
469 real_ext.unite (ly_scm2interval (min_ext));
471 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
474 // We never want nan, so we avoid shifting infinite values.
476 real_ext.translate(offset);
478 this->warning(_f ("ignored infinite %s-offset",
479 a == X_AXIS ? "X" : "Y"));
485 Grob::pure_height (Grob *refp, int start, int end)
487 SCM iv_scm = get_pure_property ("Y-extent", start, end);
488 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
489 Real offset = pure_relative_y_coordinate (refp, start, end);
491 SCM min_ext = get_property ("minimum-Y-extent");
493 /* we don't add minimum-Y-extent if the extent is empty. This solves
494 a problem with Hara-kiri spanners. They would request_suicide and
495 return empty extents, but we would force them here to be large. */
496 if (!iv.is_empty () && is_number_pair (min_ext))
497 iv.unite (ly_scm2interval (min_ext));
500 iv.translate (offset);
505 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
507 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
511 Grob::spanned_rank_interval () const
513 return Interval_t<int> (-1, 0);
517 Grob::pure_is_visible (int /* start */, int /* end */) const
522 /* Sort grobs according to their starting column. */
524 Grob::less (Grob *g1, Grob *g2)
526 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
529 /****************************************************************
531 ****************************************************************/
533 /* Find the group-element which has both #this# and #s# */
535 Grob::common_refpoint (Grob const *s, Axis a) const
538 /* Catching the trivial cases is likely costlier than just running
539 through: one can't avoid going to the respective chain ends
540 anyway. We might save the second run through when the chain ends
541 differ, but keeping track of the ends makes the loop more costly.
548 for (c = this; c; ++balance)
549 c = c->dim_cache_[a].parent_;
551 for (d = s; d; --balance)
552 d = d->dim_cache_[a].parent_;
554 /* Cut down ancestry to same size */
556 for (c = this; balance > 0; --balance)
557 c = c->dim_cache_[a].parent_;
559 for (d = s; balance < 0; ++balance)
560 d = d->dim_cache_[a].parent_;
562 /* Now find point where our lineages converge */
565 c = c->dim_cache_[a].parent_;
566 d = d->dim_cache_[a].parent_;
573 Grob::set_parent (Grob *g, Axis a)
575 dim_cache_[a].parent_ = g;
579 Grob::get_parent (Axis a) const
581 return dim_cache_[a].parent_;
585 Grob::fixup_refpoint ()
587 for (int a = X_AXIS; a < NO_AXES; a++)
590 Grob *parent = get_parent (ax);
595 if (parent->get_system () != get_system () && get_system ())
597 Grob *newparent = parent->find_broken_piece (get_system ());
598 set_parent (newparent, ax);
601 if (Item *i = dynamic_cast<Item *> (this))
603 Item *parenti = dynamic_cast<Item *> (parent);
607 Direction my_dir = i->break_status_dir ();
608 if (my_dir != parenti->break_status_dir ())
610 Item *newparent = parenti->find_prebroken_piece (my_dir);
611 set_parent (newparent, ax);
618 /****************************************************************
620 ****************************************************************/
623 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
627 if (Align_interface::has_interface (g))
628 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
629 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
634 Grob::get_root_vertical_alignment (Grob *g)
636 return get_maybe_root_vertical_alignment (g, 0);
640 Grob::get_vertical_axis_group (Grob *g)
644 if (!g->get_parent (Y_AXIS))
646 if (Axis_group_interface::has_interface (g)
647 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
649 return get_vertical_axis_group (g->get_parent (Y_AXIS));
654 Grob::get_vertical_axis_group_index (Grob *g)
656 Grob *val = get_root_vertical_alignment (g);
659 Grob *vax = get_vertical_axis_group (g);
660 extract_grob_set (val, "elements", elts);
661 for (vsize i = 0; i < elts.size (); i++)
664 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
669 Grob::vertical_less (Grob *g1, Grob *g2)
671 return internal_vertical_less (g1, g2, false);
675 Grob::pure_vertical_less (Grob *g1, Grob *g2)
677 return internal_vertical_less (g1, g2, true);
681 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
683 Grob *vag = get_root_vertical_alignment (g1);
686 g1->programming_error ("grob does not belong to a VerticalAlignment?");
690 Grob *ag1 = get_vertical_axis_group (g1);
691 Grob *ag2 = get_vertical_axis_group (g2);
693 extract_grob_set (vag, "elements", elts);
695 if (ag1 == ag2 && !pure)
697 Grob *common = g1->common_refpoint (g2, Y_AXIS);
698 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
701 for (vsize i = 0; i < elts.size (); i++)
709 g1->programming_error ("could not place this grob in its axis group");
713 /****************************************************************
715 ****************************************************************/
717 Grob::programming_error (string s) const
719 SCM cause = self_scm ();
720 while (Grob *g = unsmob_grob (cause))
721 cause = g->get_property ("cause");
723 /* ES TODO: cause can't be Music*/
724 if (Music *m = unsmob_music (cause))
725 m->origin ()->programming_error (s);
726 else if (Stream_event *ev = unsmob_stream_event (cause))
727 ev->origin ()->programming_error (s);
729 ::programming_error (s);
733 Grob::warning (string s) const
735 SCM cause = self_scm ();
736 while (Grob *g = unsmob_grob (cause))
737 cause = g->get_property ("cause");
739 /* ES TODO: cause can't be Music*/
740 if (Music *m = unsmob_music (cause))
741 m->origin ()->warning (s);
742 else if (Stream_event *ev = unsmob_stream_event (cause))
743 ev->origin ()->warning (s);
751 SCM meta = get_property ("meta");
752 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
753 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
754 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
758 "A grob represents a piece of music notation.\n"
760 "All grobs have an X and Y@tie{}position on the page. These"
761 " X and Y@tie{}positions are stored in a relative format, thus"
762 " they can easily be combined by stacking them, hanging one"
763 " grob to the side of another, or coupling them into grouping"
766 "Each grob has a reference point (a.k.a.@: parent): The"
767 " position of a grob is stored relative to that reference"
768 " point. For example, the X@tie{}reference point of a staccato"
769 " dot usually is the note head that it applies to. When the"
770 " note head is moved, the staccato dot moves along"
773 "A grob is often associated with a symbol, but some grobs do"
774 " not print any symbols. They take care of grouping objects."
775 " For example, there is a separate grob that stacks staves"
776 " vertically. The @ref{NoteCollision} object is also an"
777 " abstract grob: It only moves around chords, but doesn't print"
780 "Grobs have properties (Scheme variables) that can be read and"
781 " set. Two types of them exist: immutable and mutable."
782 " Immutable variables define the default style and behavior."
783 " They are shared between many objects. They can be changed"
784 " using @code{\\override} and @code{\\revert}. Mutable"
785 " properties are variables that are specific to one grob."
786 " Typically, lists of other objects, or results from"
787 " computations are stored in mutable properties. In"
788 " particular, every call to @code{ly:grob-set-property!}"
789 " (or its C++ equivalent) sets a mutable property.\n"
791 "The properties @code{after-line-breaking} and"
792 " @code{before-line-breaking} are dummies that are not"
793 " user-serviceable.",
800 "after-line-breaking "
802 "axis-group-parent-X "
803 "axis-group-parent-Y "
804 "before-line-breaking "
812 "horizontal-skylines "
818 "outside-staff-horizontal-padding "
819 "outside-staff-padding "
820 "outside-staff-priority "
821 "pure-Y-offset-in-progress "
823 "skyline-horizontal-padding "
832 /****************************************************************
834 ****************************************************************/
837 grob_stencil_extent (Grob *me, Axis a)
839 Stencil *m = me->get_stencil ();
843 return ly_interval2scm (e);
846 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
848 Grob::stencil_height (SCM smob)
850 Grob *me = unsmob_grob (smob);
851 return grob_stencil_extent (me, Y_AXIS);
854 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
856 Grob::y_parent_positioning (SCM smob)
858 Grob *me = unsmob_grob (smob);
859 Grob *par = me->get_parent (Y_AXIS);
861 (void) par->get_property ("positioning-done");
863 return scm_from_double (0.0);
866 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
868 Grob::x_parent_positioning (SCM smob)
870 Grob *me = unsmob_grob (smob);
872 Grob *par = me->get_parent (X_AXIS);
874 (void) par->get_property ("positioning-done");
876 return scm_from_double (0.0);
879 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
881 Grob::stencil_width (SCM smob)
883 Grob *me = unsmob_grob (smob);
884 return grob_stencil_extent (me, X_AXIS);
888 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
890 for (; scm_is_pair (elist); elist = scm_cdr (elist))
891 if (Grob *s = unsmob_grob (scm_car (elist)))
894 common = common->common_refpoint (s, a);
903 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
905 for (vsize i = 0; i < arr.size (); i++)
907 common = common->common_refpoint (arr[i], a);
915 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
917 set<Grob *>::iterator it;
919 for (it = arr.begin (); it != arr.end (); it++)
921 common = common->common_refpoint (*it, a);
929 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
931 Interval ext = me->extent (refpoint, a);
933 ext.add_point (me->relative_coordinate (refpoint, a));
938 // Checks whether there is a vertical alignment in the chain of
939 // parents between this and commony.
941 Grob::check_cross_staff (Grob *commony)
943 if (Align_interface::has_interface (commony))
946 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
947 if (Align_interface::has_interface (g))