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)
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 // TODO: Why is this Interval (0,0)
489 // Shouldn't it just be an empty interval?
490 // 0,0 puts an arbitrary point at 0,0 which will influence spacing
491 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
492 Real offset = pure_relative_y_coordinate (refp, start, end);
494 SCM min_ext = get_property ("minimum-Y-extent");
496 /* we don't add minimum-Y-extent if the extent is empty. This solves
497 a problem with Hara-kiri spanners. They would request_suicide and
498 return empty extents, but we would force them here to be large. */
499 if (!iv.is_empty () && is_number_pair (min_ext))
500 iv.unite (ly_scm2interval (min_ext));
503 iv.translate (offset);
508 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
510 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
514 Grob::spanned_rank_interval () const
516 return Interval_t<int> (-1, 0);
520 Grob::pure_is_visible (int /* start */, int /* end */) const
525 /* Sort grobs according to their starting column. */
527 Grob::less (Grob *g1, Grob *g2)
529 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
532 /****************************************************************
534 ****************************************************************/
536 /* Find the group-element which has both #this# and #s# */
538 Grob::common_refpoint (Grob const *s, Axis a) const
541 /* Catching the trivial cases is likely costlier than just running
542 through: one can't avoid going to the respective chain ends
543 anyway. We might save the second run through when the chain ends
544 differ, but keeping track of the ends makes the loop more costly.
551 for (c = this; c; ++balance)
552 c = c->dim_cache_[a].parent_;
554 for (d = s; d; --balance)
555 d = d->dim_cache_[a].parent_;
557 /* Cut down ancestry to same size */
559 for (c = this; balance > 0; --balance)
560 c = c->dim_cache_[a].parent_;
562 for (d = s; balance < 0; ++balance)
563 d = d->dim_cache_[a].parent_;
565 /* Now find point where our lineages converge */
568 c = c->dim_cache_[a].parent_;
569 d = d->dim_cache_[a].parent_;
576 Grob::set_parent (Grob *g, Axis a)
578 dim_cache_[a].parent_ = g;
582 Grob::get_parent (Axis a) const
584 return dim_cache_[a].parent_;
588 Grob::fixup_refpoint ()
590 for (int a = X_AXIS; a < NO_AXES; a++)
593 Grob *parent = get_parent (ax);
598 if (parent->get_system () != get_system () && get_system ())
600 Grob *newparent = parent->find_broken_piece (get_system ());
601 set_parent (newparent, ax);
604 if (Item *i = dynamic_cast<Item *> (this))
606 Item *parenti = dynamic_cast<Item *> (parent);
610 Direction my_dir = i->break_status_dir ();
611 if (my_dir != parenti->break_status_dir ())
613 Item *newparent = parenti->find_prebroken_piece (my_dir);
614 set_parent (newparent, ax);
621 /****************************************************************
623 ****************************************************************/
626 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
630 if (Align_interface::has_interface (g))
631 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
632 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
637 Grob::get_root_vertical_alignment (Grob *g)
639 return get_maybe_root_vertical_alignment (g, 0);
643 Grob::get_vertical_axis_group (Grob *g)
647 if (!g->get_parent (Y_AXIS))
649 if (Axis_group_interface::has_interface (g)
650 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
652 return get_vertical_axis_group (g->get_parent (Y_AXIS));
657 Grob::get_vertical_axis_group_index (Grob *g)
659 Grob *val = get_root_vertical_alignment (g);
662 Grob *vax = get_vertical_axis_group (g);
663 extract_grob_set (val, "elements", elts);
664 for (vsize i = 0; i < elts.size (); i++)
667 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
672 Grob::vertical_less (Grob *g1, Grob *g2)
674 return internal_vertical_less (g1, g2, false);
678 Grob::pure_vertical_less (Grob *g1, Grob *g2)
680 return internal_vertical_less (g1, g2, true);
684 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
686 Grob *vag = get_root_vertical_alignment (g1);
689 g1->programming_error ("grob does not belong to a VerticalAlignment?");
693 Grob *ag1 = get_vertical_axis_group (g1);
694 Grob *ag2 = get_vertical_axis_group (g2);
696 extract_grob_set (vag, "elements", elts);
698 if (ag1 == ag2 && !pure)
700 Grob *common = g1->common_refpoint (g2, Y_AXIS);
701 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
704 for (vsize i = 0; i < elts.size (); i++)
712 g1->programming_error ("could not place this grob in its axis group");
716 /****************************************************************
718 ****************************************************************/
720 Grob::programming_error (string s) const
722 SCM cause = self_scm ();
723 while (Grob *g = unsmob_grob (cause))
724 cause = g->get_property ("cause");
726 /* ES TODO: cause can't be Music*/
727 if (Music *m = unsmob_music (cause))
728 m->origin ()->programming_error (s);
729 else if (Stream_event *ev = unsmob_stream_event (cause))
730 ev->origin ()->programming_error (s);
732 ::programming_error (s);
736 Grob::warning (string s) const
738 SCM cause = self_scm ();
739 while (Grob *g = unsmob_grob (cause))
740 cause = g->get_property ("cause");
742 /* ES TODO: cause can't be Music*/
743 if (Music *m = unsmob_music (cause))
744 m->origin ()->warning (s);
745 else if (Stream_event *ev = unsmob_stream_event (cause))
746 ev->origin ()->warning (s);
754 SCM meta = get_property ("meta");
755 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
756 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
757 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
761 "A grob represents a piece of music notation.\n"
763 "All grobs have an X and Y@tie{}position on the page. These"
764 " X and Y@tie{}positions are stored in a relative format, thus"
765 " they can easily be combined by stacking them, hanging one"
766 " grob to the side of another, or coupling them into grouping"
769 "Each grob has a reference point (a.k.a.@: parent): The"
770 " position of a grob is stored relative to that reference"
771 " point. For example, the X@tie{}reference point of a staccato"
772 " dot usually is the note head that it applies to. When the"
773 " note head is moved, the staccato dot moves along"
776 "A grob is often associated with a symbol, but some grobs do"
777 " not print any symbols. They take care of grouping objects."
778 " For example, there is a separate grob that stacks staves"
779 " vertically. The @ref{NoteCollision} object is also an"
780 " abstract grob: It only moves around chords, but doesn't print"
783 "Grobs have properties (Scheme variables) that can be read and"
784 " set. Two types of them exist: immutable and mutable."
785 " Immutable variables define the default style and behavior."
786 " They are shared between many objects. They can be changed"
787 " using @code{\\override} and @code{\\revert}. Mutable"
788 " properties are variables that are specific to one grob."
789 " Typically, lists of other objects, or results from"
790 " computations are stored in mutable properties. In"
791 " particular, every call to @code{ly:grob-set-property!}"
792 " (or its C++ equivalent) sets a mutable property.\n"
794 "The properties @code{after-line-breaking} and"
795 " @code{before-line-breaking} are dummies that are not"
796 " user-serviceable.",
803 "after-line-breaking "
805 "axis-group-parent-X "
806 "axis-group-parent-Y "
807 "before-line-breaking "
815 "horizontal-skylines "
821 "outside-staff-horizontal-padding "
822 "outside-staff-padding "
823 "outside-staff-priority "
824 "pure-Y-offset-in-progress "
826 "skyline-horizontal-padding "
835 /****************************************************************
837 ****************************************************************/
840 grob_stencil_extent (Grob *me, Axis a)
842 Stencil *m = me->get_stencil ();
846 return ly_interval2scm (e);
849 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
851 Grob::stencil_height (SCM smob)
853 Grob *me = unsmob_grob (smob);
854 return grob_stencil_extent (me, Y_AXIS);
857 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
859 Grob::y_parent_positioning (SCM smob)
861 Grob *me = unsmob_grob (smob);
862 Grob *par = me->get_parent (Y_AXIS);
864 (void) par->get_property ("positioning-done");
866 return scm_from_double (0.0);
869 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
871 Grob::x_parent_positioning (SCM smob)
873 Grob *me = unsmob_grob (smob);
875 Grob *par = me->get_parent (X_AXIS);
877 (void) par->get_property ("positioning-done");
879 return scm_from_double (0.0);
882 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
884 Grob::stencil_width (SCM smob)
886 Grob *me = unsmob_grob (smob);
887 return grob_stencil_extent (me, X_AXIS);
891 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
893 for (; scm_is_pair (elist); elist = scm_cdr (elist))
894 if (Grob *s = unsmob_grob (scm_car (elist)))
897 common = common->common_refpoint (s, a);
906 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
908 for (vsize i = 0; i < arr.size (); i++)
910 common = common->common_refpoint (arr[i], a);
918 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
920 set<Grob *>::iterator it;
922 for (it = arr.begin (); it != arr.end (); it++)
924 common = common->common_refpoint (*it, a);
932 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
934 Interval ext = me->extent (refpoint, a);
936 ext.add_point (me->relative_coordinate (refpoint, a));
941 // Checks whether there is a vertical alignment in the chain of
942 // parents between this and commony.
944 Grob::check_cross_staff (Grob *commony)
946 if (Align_interface::has_interface (commony))
949 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
950 if (Align_interface::has_interface (g))