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"
39 #include "unpure-pure-container.hh"
42 #include "ly-smobs.icc"
47 return new Grob (*this);
50 Grob::Grob (SCM basicprops)
53 /* FIXME: default should be no callback. */
57 interfaces_ = SCM_EOL;
58 immutable_property_alist_ = basicprops;
59 mutable_property_alist_ = SCM_EOL;
60 object_alist_ = SCM_EOL;
62 /* We do smobify_self () as the first step. Since the object lives
63 on the heap, none of its SCM variables are protected from
64 GC. After smobify_self (), they are. */
67 SCM meta = get_property ("meta");
68 if (scm_is_pair (meta))
70 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
72 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
73 if (scm_is_pair (object_cbs))
75 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
76 set_object (scm_caar (s), scm_cdar (s));
80 if (get_property_data ("X-extent") == SCM_EOL)
81 set_property ("X-extent", Grob::stencil_width_proc);
82 if (get_property_data ("Y-extent") == SCM_EOL)
83 set_property ("Y-extent",
84 ly_make_unpure_pure_container (Grob::stencil_height_proc,
85 Grob::pure_stencil_height_proc));
86 if (get_property_data ("vertical-skylines") == SCM_EOL)
87 set_property ("vertical-skylines",
88 ly_make_unpure_pure_container (Grob::simple_vertical_skylines_from_extents_proc,
89 Grob::pure_simple_vertical_skylines_from_extents_proc));
90 if (get_property_data ("horizontal-skylines") == SCM_EOL)
91 set_property ("horizontal-skylines",
92 ly_make_unpure_pure_container (Grob::simple_horizontal_skylines_from_extents_proc,
93 Grob::pure_simple_horizontal_skylines_from_extents_proc));
96 Grob::Grob (Grob const &s)
98 original_ = (Grob *) & s;
101 immutable_property_alist_ = s.immutable_property_alist_;
102 mutable_property_alist_ = SCM_EOL;
104 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
105 dim_cache_ [a] = s.dim_cache_ [a];
107 interfaces_ = s.interfaces_;
108 object_alist_ = SCM_EOL;
114 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
121 /****************************************************************
123 ****************************************************************/
126 Grob::get_stencil () const
131 SCM stil = get_property ("stencil");
132 return unsmob_stencil (stil);
136 Grob::get_print_stencil () const
138 SCM stil = get_property ("stencil");
141 if (Stencil *m = unsmob_stencil (stil))
144 bool transparent = to_boolean (get_property ("transparent"));
147 retval = Stencil (m->extent_box (), SCM_EOL);
150 SCM expr = m->expr ();
151 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
154 retval = Stencil (m->extent_box (), expr);
157 SCM rot = get_property ("rotation");
158 if (scm_is_pair (rot))
160 Real angle = scm_to_double (scm_car (rot));
161 Real x = scm_to_double (scm_cadr (rot));
162 Real y = scm_to_double (scm_caddr (rot));
164 retval.rotate_degrees (angle, Offset (x, y));
167 /* color support... see interpret_stencil_expression () for more... */
168 SCM color = get_property ("color");
169 if (scm_is_pair (color))
171 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
175 retval = Stencil (retval.extent_box (), expr);
178 /* process whiteout */
179 /* a grob has to be visible, otherwise the whiteout property has no effect */
180 if (!transparent && to_boolean (get_property ("whiteout")))
182 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
183 /* to add a round-filled-box stencil to the stencil list */
185 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
186 retval.smobbed_copy ()));
189 SCM id = get_property ("id");
190 if (scm_is_string (id))
192 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
196 retval = Stencil (retval.extent_box (), expr);
204 /****************************************************************
206 ****************************************************************/
208 Grob::do_break_processing ()
213 Grob::discretionary_processing ()
218 Grob::get_system () const
223 /* This version of get_system is more reliable than this->get_system ()
224 before line-breaking has been done, at which point there is only
225 one system in the whole score and we can find it just by following
228 Grob::get_system (Grob *me)
230 Grob *p = me->get_parent (X_AXIS);
231 return p ? get_system (p) : dynamic_cast<System *>(me);
235 Grob::handle_broken_dependencies ()
237 Spanner *sp = dynamic_cast<Spanner *> (this);
238 if (original () && sp)
242 /* THIS, SP is the original spanner. We use a special function
243 because some Spanners have enormously long lists in their
244 properties, and a special function fixes FOO */
246 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
247 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
249 System *system = get_system ();
253 && common_refpoint (system, X_AXIS)
254 && common_refpoint (system, Y_AXIS))
255 substitute_object_links (system->self_scm (), object_alist_);
256 else if (dynamic_cast<System *> (this))
257 substitute_object_links (SCM_UNDEFINED, object_alist_);
259 /* THIS element is `invalid'; it has been removed from all
260 dependencies, so let's junk the element itself.
262 Do not do this for System, since that would remove references
263 to the originals of score-grobs, which get then GC'd (a bad
268 /* Note that we still want references to this element to be
269 rearranged, and not silently thrown away, so we keep pointers like
270 {broken_into_{drul, array}, original}
278 for (int a = X_AXIS; a < NO_AXES; a++)
279 dim_cache_[a].clear ();
281 mutable_property_alist_ = SCM_EOL;
282 object_alist_ = SCM_EOL;
283 immutable_property_alist_ = SCM_EOL;
284 interfaces_ = SCM_EOL;
288 Grob::handle_prebroken_dependencies ()
290 /* Don't do this in the derived method, since we want to keep access to
291 object_alist_ centralized. */
294 Item *it = dynamic_cast<Item *> (this);
295 substitute_object_links (scm_from_int (it->break_status_dir ()),
296 original ()->object_alist_);
301 Grob::find_broken_piece (System *) const
306 /****************************************************************
308 ****************************************************************/
311 Grob::translate_axis (Real y, Axis a)
313 if (isinf (y) || isnan (y))
315 programming_error ("Infinity or NaN encountered");
319 if (!dim_cache_[a].offset_)
320 dim_cache_[a].offset_ = new Real (y);
322 *dim_cache_[a].offset_ += y;
325 /* Find the offset relative to D. If D equals THIS, then it is 0.
326 Otherwise, it recursively defd as
328 OFFSET_ + PARENT_L_->relative_coordinate (D) */
330 Grob::relative_coordinate (Grob const *refp, Axis a) const
332 /* eaa - hmmm, should we do a programming_error() here? */
333 if ((this == NULL) || (refp == this))
336 /* We catch PARENT_L_ == nil case with this, but we crash if we did
337 not ask for the absolute coordinate (ie. REFP == nil.) */
338 Real off = get_offset (a);
339 if (refp == dim_cache_[a].parent_)
342 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
348 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
355 if (dim_cache_[Y_AXIS].offset_)
357 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
358 programming_error ("cyclic chain in pure-Y-offset callbacks");
360 off = *dim_cache_[Y_AXIS].offset_;
364 SCM proc = get_property_data ("Y-offset");
366 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
367 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
368 off = robust_scm2double (call_pure_function (proc,
369 scm_list_1 (self_scm ()),
372 del_property ("pure-Y-offset-in-progress");
373 delete dim_cache_[Y_AXIS].offset_;
374 dim_cache_[Y_AXIS].offset_ = 0;
377 /* we simulate positioning-done if we are the child of a VerticalAlignment,
378 but only if we don't have a cached offset. If we do have a cached offset,
379 it probably means that the Alignment was fixed and it has already been
382 if (Grob *p = get_parent (Y_AXIS))
385 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
386 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
388 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
393 /* Invoke callbacks to get offset relative to parent. */
395 Grob::get_offset (Axis a) const
397 if (dim_cache_[a].offset_)
398 return *dim_cache_[a].offset_;
400 Grob *me = (Grob *) this;
402 SCM sym = axis_offset_symbol (a);
403 me->dim_cache_[a].offset_ = new Real (0.0);
406 UGH: can't fold next 2 statements together. Apparently GCC thinks
407 dim_cache_[a].offset_ is unaliased.
409 Real off = robust_scm2double (internal_get_property (sym), 0.0);
410 if (me->dim_cache_[a].offset_)
412 *me->dim_cache_[a].offset_ += off;
413 me->del_property (sym);
414 return *me->dim_cache_[a].offset_;
421 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
423 if (pure && a != Y_AXIS)
424 programming_error ("tried to get pure X-offset");
425 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
426 : relative_coordinate (refp, a);
429 /****************************************************************
431 ****************************************************************/
434 Grob::flush_extent_cache (Axis axis)
436 if (dim_cache_[axis].extent_)
439 Ugh, this is not accurate; will flush property, causing
440 callback to be called if.
442 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
443 delete dim_cache_[axis].extent_;
444 dim_cache_[axis].extent_ = 0;
445 if (get_parent (axis))
446 get_parent (axis)->flush_extent_cache (axis);
451 Grob::extent (Grob *refp, Axis a) const
453 Real offset = relative_coordinate (refp, a);
455 if (dim_cache_[a].extent_)
457 real_ext = *dim_cache_[a].extent_;
462 Order is significant: ?-extent may trigger suicide.
466 ? ly_symbol2scm ("X-extent")
467 : ly_symbol2scm ("Y-extent");
469 SCM ext = internal_get_property (ext_sym);
470 if (is_number_pair (ext))
471 real_ext.unite (ly_scm2interval (ext));
475 ? ly_symbol2scm ("minimum-X-extent")
476 : ly_symbol2scm ("minimum-Y-extent");
477 SCM min_ext = internal_get_property (min_ext_sym);
478 if (is_number_pair (min_ext))
479 real_ext.unite (ly_scm2interval (min_ext));
481 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
484 // We never want nan, so we avoid shifting infinite values.
486 real_ext.translate(offset);
488 this->warning(_f ("ignored infinite %s-offset",
489 a == X_AXIS ? "X" : "Y"));
495 Grob::pure_height (Grob *refp, int start, int end)
497 SCM iv_scm = get_pure_property ("Y-extent", start, end);
498 Interval iv = robust_scm2interval (iv_scm, Interval ());
499 Real offset = pure_relative_y_coordinate (refp, start, end);
501 SCM min_ext = get_property ("minimum-Y-extent");
503 /* we don't add minimum-Y-extent if the extent is empty. This solves
504 a problem with Hara-kiri spanners. They would request_suicide and
505 return empty extents, but we would force them here to be large. */
506 if (!iv.is_empty () && is_number_pair (min_ext))
507 iv.unite (ly_scm2interval (min_ext));
510 iv.translate (offset);
515 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
517 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
521 Grob::spanned_rank_interval () const
523 return Interval_t<int> (-1, 0);
527 Grob::pure_is_visible (int /* start */, int /* end */) const
532 /* Sort grobs according to their starting column. */
534 Grob::less (Grob *g1, Grob *g2)
536 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
539 /****************************************************************
541 ****************************************************************/
543 /* Find the group-element which has both #this# and #s# */
545 Grob::common_refpoint (Grob const *s, Axis a) const
548 /* Catching the trivial cases is likely costlier than just running
549 through: one can't avoid going to the respective chain ends
550 anyway. We might save the second run through when the chain ends
551 differ, but keeping track of the ends makes the loop more costly.
558 for (c = this; c; ++balance)
559 c = c->dim_cache_[a].parent_;
561 for (d = s; d; --balance)
562 d = d->dim_cache_[a].parent_;
564 /* Cut down ancestry to same size */
566 for (c = this; balance > 0; --balance)
567 c = c->dim_cache_[a].parent_;
569 for (d = s; balance < 0; ++balance)
570 d = d->dim_cache_[a].parent_;
572 /* Now find point where our lineages converge */
575 c = c->dim_cache_[a].parent_;
576 d = d->dim_cache_[a].parent_;
583 Grob::set_parent (Grob *g, Axis a)
585 dim_cache_[a].parent_ = g;
589 Grob::get_parent (Axis a) const
591 return dim_cache_[a].parent_;
595 Grob::fixup_refpoint ()
597 for (int a = X_AXIS; a < NO_AXES; a++)
600 Grob *parent = get_parent (ax);
605 if (parent->get_system () != get_system () && get_system ())
607 Grob *newparent = parent->find_broken_piece (get_system ());
608 set_parent (newparent, ax);
611 if (Item *i = dynamic_cast<Item *> (this))
613 Item *parenti = dynamic_cast<Item *> (parent);
617 Direction my_dir = i->break_status_dir ();
618 if (my_dir != parenti->break_status_dir ())
620 Item *newparent = parenti->find_prebroken_piece (my_dir);
621 set_parent (newparent, ax);
628 /****************************************************************
630 ****************************************************************/
633 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
637 if (Align_interface::has_interface (g))
638 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
639 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
644 Grob::get_root_vertical_alignment (Grob *g)
646 return get_maybe_root_vertical_alignment (g, 0);
650 Grob::get_vertical_axis_group (Grob *g)
654 if (!g->get_parent (Y_AXIS))
656 if (Axis_group_interface::has_interface (g)
657 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
659 return get_vertical_axis_group (g->get_parent (Y_AXIS));
664 Grob::get_vertical_axis_group_index (Grob *g)
666 Grob *val = get_root_vertical_alignment (g);
669 Grob *vax = get_vertical_axis_group (g);
670 extract_grob_set (val, "elements", elts);
671 for (vsize i = 0; i < elts.size (); i++)
674 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
679 Grob::vertical_less (Grob *g1, Grob *g2)
681 return internal_vertical_less (g1, g2, false);
685 Grob::pure_vertical_less (Grob *g1, Grob *g2)
687 return internal_vertical_less (g1, g2, true);
691 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
693 Grob *vag = get_root_vertical_alignment (g1);
696 g1->programming_error ("grob does not belong to a VerticalAlignment?");
700 Grob *ag1 = get_vertical_axis_group (g1);
701 Grob *ag2 = get_vertical_axis_group (g2);
703 extract_grob_set (vag, "elements", elts);
705 if (ag1 == ag2 && !pure)
707 Grob *common = g1->common_refpoint (g2, Y_AXIS);
708 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
711 for (vsize i = 0; i < elts.size (); i++)
719 g1->programming_error ("could not place this grob in its axis group");
723 /****************************************************************
725 ****************************************************************/
727 Grob::programming_error (string s) const
729 SCM cause = self_scm ();
730 while (Grob *g = unsmob_grob (cause))
731 cause = g->get_property ("cause");
733 /* ES TODO: cause can't be Music*/
734 if (Music *m = unsmob_music (cause))
735 m->origin ()->programming_error (s);
736 else if (Stream_event *ev = unsmob_stream_event (cause))
737 ev->origin ()->programming_error (s);
739 ::programming_error (s);
743 Grob::warning (string s) const
745 SCM cause = self_scm ();
746 while (Grob *g = unsmob_grob (cause))
747 cause = g->get_property ("cause");
749 /* ES TODO: cause can't be Music*/
750 if (Music *m = unsmob_music (cause))
751 m->origin ()->warning (s);
752 else if (Stream_event *ev = unsmob_stream_event (cause))
753 ev->origin ()->warning (s);
761 SCM meta = get_property ("meta");
762 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
763 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
764 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
768 "A grob represents a piece of music notation.\n"
770 "All grobs have an X and Y@tie{}position on the page. These"
771 " X and Y@tie{}positions are stored in a relative format, thus"
772 " they can easily be combined by stacking them, hanging one"
773 " grob to the side of another, or coupling them into grouping"
776 "Each grob has a reference point (a.k.a.@: parent): The"
777 " position of a grob is stored relative to that reference"
778 " point. For example, the X@tie{}reference point of a staccato"
779 " dot usually is the note head that it applies to. When the"
780 " note head is moved, the staccato dot moves along"
783 "A grob is often associated with a symbol, but some grobs do"
784 " not print any symbols. They take care of grouping objects."
785 " For example, there is a separate grob that stacks staves"
786 " vertically. The @ref{NoteCollision} object is also an"
787 " abstract grob: It only moves around chords, but doesn't print"
790 "Grobs have properties (Scheme variables) that can be read and"
791 " set. Two types of them exist: immutable and mutable."
792 " Immutable variables define the default style and behavior."
793 " They are shared between many objects. They can be changed"
794 " using @code{\\override} and @code{\\revert}. Mutable"
795 " properties are variables that are specific to one grob."
796 " Typically, lists of other objects, or results from"
797 " computations are stored in mutable properties. In"
798 " particular, every call to @code{ly:grob-set-property!}"
799 " (or its C++ equivalent) sets a mutable property.\n"
801 "The properties @code{after-line-breaking} and"
802 " @code{before-line-breaking} are dummies that are not"
803 " user-serviceable.",
810 "after-line-breaking "
812 "axis-group-parent-X "
813 "axis-group-parent-Y "
814 "before-line-breaking "
822 "horizontal-skylines "
828 "outside-staff-horizontal-padding "
829 "outside-staff-padding "
830 "outside-staff-priority "
831 "pure-Y-offset-in-progress "
833 "skyline-horizontal-padding "
842 /****************************************************************
844 ****************************************************************/
847 grob_stencil_extent (Grob *me, Axis a)
849 Stencil *m = me->get_stencil ();
853 return ly_interval2scm (e);
856 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
858 Grob::stencil_height (SCM smob)
860 Grob *me = unsmob_grob (smob);
861 return grob_stencil_extent (me, Y_AXIS);
864 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
866 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
868 Grob *me = unsmob_grob (smob);
869 if (unsmob_stencil (me->get_property_data ("stencil")))
870 return grob_stencil_extent (me, Y_AXIS);
872 return ly_interval2scm (Interval ());
876 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
878 Grob::y_parent_positioning (SCM smob)
880 Grob *me = unsmob_grob (smob);
881 Grob *par = me->get_parent (Y_AXIS);
883 (void) par->get_property ("positioning-done");
885 return scm_from_double (0.0);
888 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
890 Grob::x_parent_positioning (SCM smob)
892 Grob *me = unsmob_grob (smob);
894 Grob *par = me->get_parent (X_AXIS);
896 (void) par->get_property ("positioning-done");
898 return scm_from_double (0.0);
901 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
903 Grob::stencil_width (SCM smob)
905 Grob *me = unsmob_grob (smob);
906 return grob_stencil_extent (me, X_AXIS);
910 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
912 for (; scm_is_pair (elist); elist = scm_cdr (elist))
913 if (Grob *s = unsmob_grob (scm_car (elist)))
916 common = common->common_refpoint (s, a);
925 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
927 for (vsize i = 0; i < arr.size (); i++)
929 common = common->common_refpoint (arr[i], a);
937 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
939 set<Grob *>::iterator it;
941 for (it = arr.begin (); it != arr.end (); it++)
943 common = common->common_refpoint (*it, a);
951 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
953 Interval ext = me->extent (refpoint, a);
955 ext.add_point (me->relative_coordinate (refpoint, a));
960 // Checks whether there is a vertical alignment in the chain of
961 // parents between this and commony.
963 Grob::check_cross_staff (Grob *commony)
965 if (Align_interface::has_interface (commony))
968 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
969 if (Align_interface::has_interface (g))