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);
84 Grob::Grob (Grob const &s)
85 : dim_cache_ (s.dim_cache_)
87 original_ = (Grob *) & s;
90 immutable_property_alist_ = s.immutable_property_alist_;
91 mutable_property_alist_ = SCM_EOL;
92 interfaces_ = s.interfaces_;
93 object_alist_ = SCM_EOL;
99 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
106 /****************************************************************
108 ****************************************************************/
111 Grob::get_stencil () const
116 SCM stil = get_property ("stencil");
117 return unsmob_stencil (stil);
121 Grob::get_print_stencil () const
123 SCM stil = get_property ("stencil");
126 if (Stencil *m = unsmob_stencil (stil))
129 bool transparent = to_boolean (get_property ("transparent"));
132 retval = Stencil (m->extent_box (), SCM_EOL);
135 SCM expr = m->expr ();
136 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
139 retval = Stencil (m->extent_box (), expr);
142 SCM rot = get_property ("rotation");
143 if (scm_is_pair (rot))
145 Real angle = scm_to_double (scm_car (rot));
146 Real x = scm_to_double (scm_cadr (rot));
147 Real y = scm_to_double (scm_caddr (rot));
149 retval.rotate_degrees (angle, Offset (x, y));
152 /* color support... see interpret_stencil_expression () for more... */
153 SCM color = get_property ("color");
154 if (scm_is_pair (color))
156 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
160 retval = Stencil (retval.extent_box (), expr);
163 /* process whiteout */
164 /* a grob has to be visible, otherwise the whiteout property has no effect */
165 if (!transparent && to_boolean (get_property ("whiteout")))
167 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
168 /* to add a round-filled-box stencil to the stencil list */
170 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
171 retval.smobbed_copy ()));
174 SCM id = get_property ("id");
175 if (scm_is_string (id))
177 SCM expr = scm_list_3 (ly_symbol2scm ("id"),
181 retval = Stencil (retval.extent_box (), expr);
189 /****************************************************************
191 ****************************************************************/
193 Grob::do_break_processing ()
198 Grob::discretionary_processing ()
203 Grob::get_system () const
208 /* This version of get_system is more reliable than this->get_system ()
209 before line-breaking has been done, at which point there is only
210 one system in the whole score and we can find it just by following
213 Grob::get_system (Grob *me)
215 Grob *p = me->get_parent (X_AXIS);
216 return p ? get_system (p) : dynamic_cast<System *>(me);
220 Grob::handle_broken_dependencies ()
222 Spanner *sp = dynamic_cast<Spanner *> (this);
223 if (original () && sp)
227 /* THIS, SP is the original spanner. We use a special function
228 because some Spanners have enormously long lists in their
229 properties, and a special function fixes FOO */
231 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
232 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
234 System *system = get_system ();
238 && common_refpoint (system, X_AXIS)
239 && common_refpoint (system, Y_AXIS))
240 substitute_object_links (system->self_scm (), object_alist_);
241 else if (dynamic_cast<System *> (this))
242 substitute_object_links (SCM_UNDEFINED, object_alist_);
244 /* THIS element is `invalid'; it has been removed from all
245 dependencies, so let's junk the element itself.
247 Do not do this for System, since that would remove references
248 to the originals of score-grobs, which get then GC'd (a bad
253 /* Note that we still want references to this element to be
254 rearranged, and not silently thrown away, so we keep pointers like
255 {broken_into_{drul, array}, original}
263 for (int a = X_AXIS; a < NO_AXES; a++)
264 dim_cache_[a].clear ();
266 mutable_property_alist_ = SCM_EOL;
267 object_alist_ = SCM_EOL;
268 immutable_property_alist_ = SCM_EOL;
269 interfaces_ = SCM_EOL;
273 Grob::handle_prebroken_dependencies ()
275 /* Don't do this in the derived method, since we want to keep access to
276 object_alist_ centralized. */
279 Item *it = dynamic_cast<Item *> (this);
280 substitute_object_links (scm_from_int (it->break_status_dir ()),
281 original ()->object_alist_);
286 Grob::find_broken_piece (System *) const
291 /****************************************************************
293 ****************************************************************/
296 Grob::translate_axis (Real y, Axis a)
298 if (isinf (y) || isnan (y))
300 programming_error ("Infinity or NaN encountered");
304 if (!dim_cache_[a].offset_)
305 dim_cache_[a].offset_ = new Real (y);
307 *dim_cache_[a].offset_ += y;
310 /* Find the offset relative to D. If D equals THIS, then it is 0.
311 Otherwise, it recursively defd as
313 OFFSET_ + PARENT_L_->relative_coordinate (D) */
315 Grob::relative_coordinate (Grob const *refp, Axis a) const
317 /* eaa - hmmm, should we do a programming_error() here? */
318 if ((this == NULL) || (refp == this))
321 /* We catch PARENT_L_ == nil case with this, but we crash if we did
322 not ask for the absolute coordinate (ie. REFP == nil.) */
323 Real off = get_offset (a);
324 if (refp == dim_cache_[a].parent_)
327 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
333 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
340 if (dim_cache_[Y_AXIS].offset_)
342 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
343 programming_error ("cyclic chain in pure-Y-offset callbacks");
345 off = *dim_cache_[Y_AXIS].offset_;
349 SCM proc = get_property_data ("Y-offset");
351 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
352 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
353 off = robust_scm2double (call_pure_function (proc,
354 scm_list_1 (self_scm ()),
357 del_property ("pure-Y-offset-in-progress");
358 delete dim_cache_[Y_AXIS].offset_;
359 dim_cache_[Y_AXIS].offset_ = 0;
362 /* we simulate positioning-done if we are the child of a VerticalAlignment,
363 but only if we don't have a cached offset. If we do have a cached offset,
364 it probably means that the Alignment was fixed and it has already been
367 if (Grob *p = get_parent (Y_AXIS))
370 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
371 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
373 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
378 /* Invoke callbacks to get offset relative to parent. */
380 Grob::get_offset (Axis a) const
382 if (dim_cache_[a].offset_)
383 return *dim_cache_[a].offset_;
385 Grob *me = (Grob *) this;
387 SCM sym = axis_offset_symbol (a);
388 me->dim_cache_[a].offset_ = new Real (0.0);
391 UGH: can't fold next 2 statements together. Apparently GCC thinks
392 dim_cache_[a].offset_ is unaliased.
394 Real off = robust_scm2double (internal_get_property (sym), 0.0);
395 if (me->dim_cache_[a].offset_)
397 *me->dim_cache_[a].offset_ += off;
398 me->del_property (sym);
399 return *me->dim_cache_[a].offset_;
406 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
408 if (pure && a != Y_AXIS)
409 programming_error ("tried to get pure X-offset");
410 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
411 : relative_coordinate (refp, a);
414 /****************************************************************
416 ****************************************************************/
419 Grob::flush_extent_cache (Axis axis)
421 if (dim_cache_[axis].extent_)
424 Ugh, this is not accurate; will flush property, causing
425 callback to be called if.
427 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
428 delete dim_cache_[axis].extent_;
429 dim_cache_[axis].extent_ = 0;
430 if (get_parent (axis))
431 get_parent (axis)->flush_extent_cache (axis);
436 Grob::extent (Grob *refp, Axis a) const
438 Real offset = relative_coordinate (refp, a);
440 if (dim_cache_[a].extent_)
442 real_ext = *dim_cache_[a].extent_;
447 Order is significant: ?-extent may trigger suicide.
451 ? ly_symbol2scm ("X-extent")
452 : ly_symbol2scm ("Y-extent");
454 SCM ext = internal_get_property (ext_sym);
455 if (is_number_pair (ext))
456 real_ext.unite (ly_scm2interval (ext));
460 ? ly_symbol2scm ("minimum-X-extent")
461 : ly_symbol2scm ("minimum-Y-extent");
462 SCM min_ext = internal_get_property (min_ext_sym);
463 if (is_number_pair (min_ext))
464 real_ext.unite (ly_scm2interval (min_ext));
466 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
469 real_ext.translate (offset);
475 Grob::pure_height (Grob *refp, int start, int end)
477 SCM iv_scm = get_pure_property ("Y-extent", start, end);
478 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
479 Real offset = pure_relative_y_coordinate (refp, start, end);
481 SCM min_ext = get_property ("minimum-Y-extent");
483 /* we don't add minimum-Y-extent if the extent is empty. This solves
484 a problem with Hara-kiri spanners. They would request_suicide and
485 return empty extents, but we would force them here to be large. */
486 if (!iv.is_empty () && is_number_pair (min_ext))
487 iv.unite (ly_scm2interval (min_ext));
490 iv.translate (offset);
495 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
497 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
501 Grob::spanned_rank_interval () const
503 return Interval_t<int> (-1, 0);
507 Grob::pure_is_visible (int /* start */, int /* end */) const
512 /* Sort grobs according to their starting column. */
514 Grob::less (Grob *g1, Grob *g2)
516 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
519 /****************************************************************
521 ****************************************************************/
523 /* Find the group-element which has both #this# and #s# */
525 Grob::common_refpoint (Grob const *s, Axis a) const
528 /* Catching the trivial cases is likely costlier than just running
529 through: one can't avoid going to the respective chain ends
530 anyway. We might save the second run through when the chain ends
531 differ, but keeping track of the ends makes the loop more costly.
538 for (c = this; c; ++balance)
539 c = c->dim_cache_[a].parent_;
541 for (d = s; d; --balance)
542 d = d->dim_cache_[a].parent_;
544 /* Cut down ancestry to same size */
546 for (c = this; balance > 0; --balance)
547 c = c->dim_cache_[a].parent_;
549 for (d = s; balance < 0; ++balance)
550 d = d->dim_cache_[a].parent_;
552 /* Now find point where our lineages converge */
555 c = c->dim_cache_[a].parent_;
556 d = d->dim_cache_[a].parent_;
563 Grob::set_parent (Grob *g, Axis a)
565 dim_cache_[a].parent_ = g;
569 Grob::get_parent (Axis a) const
571 return dim_cache_[a].parent_;
575 Grob::fixup_refpoint ()
577 for (int a = X_AXIS; a < NO_AXES; a++)
580 Grob *parent = get_parent (ax);
585 if (parent->get_system () != get_system () && get_system ())
587 Grob *newparent = parent->find_broken_piece (get_system ());
588 set_parent (newparent, ax);
591 if (Item *i = dynamic_cast<Item *> (this))
593 Item *parenti = dynamic_cast<Item *> (parent);
597 Direction my_dir = i->break_status_dir ();
598 if (my_dir != parenti->break_status_dir ())
600 Item *newparent = parenti->find_prebroken_piece (my_dir);
601 set_parent (newparent, ax);
608 /****************************************************************
610 ****************************************************************/
613 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
617 if (Align_interface::has_interface (g))
618 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
619 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
624 Grob::get_root_vertical_alignment (Grob *g)
626 return get_maybe_root_vertical_alignment (g, 0);
630 Grob::get_vertical_axis_group (Grob *g)
634 if (!g->get_parent (Y_AXIS))
636 if (Axis_group_interface::has_interface (g)
637 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
639 return get_vertical_axis_group (g->get_parent (Y_AXIS));
644 Grob::get_vertical_axis_group_index (Grob *g)
646 Grob *val = get_root_vertical_alignment (g);
649 Grob *vax = get_vertical_axis_group (g);
650 extract_grob_set (val, "elements", elts);
651 for (vsize i = 0; i < elts.size (); i++)
654 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
659 Grob::vertical_less (Grob *g1, Grob *g2)
661 return internal_vertical_less (g1, g2, false);
665 Grob::pure_vertical_less (Grob *g1, Grob *g2)
667 return internal_vertical_less (g1, g2, true);
671 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
673 Grob *vag = get_root_vertical_alignment (g1);
676 g1->programming_error ("grob does not belong to a VerticalAlignment?");
680 Grob *ag1 = get_vertical_axis_group (g1);
681 Grob *ag2 = get_vertical_axis_group (g2);
683 extract_grob_set (vag, "elements", elts);
685 if (ag1 == ag2 && !pure)
687 Grob *common = g1->common_refpoint (g2, Y_AXIS);
688 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
691 for (vsize i = 0; i < elts.size (); i++)
699 g1->programming_error ("could not place this grob in its axis group");
703 /****************************************************************
705 ****************************************************************/
707 Grob::programming_error (string s) const
709 SCM cause = self_scm ();
710 while (Grob *g = unsmob_grob (cause))
711 cause = g->get_property ("cause");
713 /* ES TODO: cause can't be Music*/
714 if (Music *m = unsmob_music (cause))
715 m->origin ()->programming_error (s);
716 else if (Stream_event *ev = unsmob_stream_event (cause))
717 ev->origin ()->programming_error (s);
719 ::programming_error (s);
723 Grob::warning (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 ()->warning (s);
732 else if (Stream_event *ev = unsmob_stream_event (cause))
733 ev->origin ()->warning (s);
741 SCM meta = get_property ("meta");
742 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
743 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
744 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
748 "A grob represents a piece of music notation.\n"
750 "All grobs have an X and Y@tie{}position on the page. These"
751 " X and Y@tie{}positions are stored in a relative format, thus"
752 " they can easily be combined by stacking them, hanging one"
753 " grob to the side of another, or coupling them into grouping"
756 "Each grob has a reference point (a.k.a.@: parent): The"
757 " position of a grob is stored relative to that reference"
758 " point. For example, the X@tie{}reference point of a staccato"
759 " dot usually is the note head that it applies to. When the"
760 " note head is moved, the staccato dot moves along"
763 "A grob is often associated with a symbol, but some grobs do"
764 " not print any symbols. They take care of grouping objects."
765 " For example, there is a separate grob that stacks staves"
766 " vertically. The @ref{NoteCollision} object is also an"
767 " abstract grob: It only moves around chords, but doesn't print"
770 "Grobs have properties (Scheme variables) that can be read and"
771 " set. Two types of them exist: immutable and mutable."
772 " Immutable variables define the default style and behavior."
773 " They are shared between many objects. They can be changed"
774 " using @code{\\override} and @code{\\revert}. Mutable"
775 " properties are variables that are specific to one grob."
776 " Typically, lists of other objects, or results from"
777 " computations are stored in mutable properties. In"
778 " particular, every call to @code{ly:grob-set-property!}"
779 " (or its C++ equivalent) sets a mutable property.\n"
781 "The properties @code{after-line-breaking} and"
782 " @code{before-line-breaking} are dummies that are not"
783 " user-serviceable.",
790 "after-line-breaking "
792 "axis-group-parent-X "
793 "axis-group-parent-Y "
794 "before-line-breaking "
808 "outside-staff-horizontal-padding "
809 "outside-staff-padding "
810 "outside-staff-priority "
811 "pure-Y-offset-in-progress "
820 /****************************************************************
822 ****************************************************************/
825 grob_stencil_extent (Grob *me, Axis a)
827 Stencil *m = me->get_stencil ();
831 return ly_interval2scm (e);
834 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
836 Grob::stencil_height (SCM smob)
838 Grob *me = unsmob_grob (smob);
839 return grob_stencil_extent (me, Y_AXIS);
842 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
844 Grob::y_parent_positioning (SCM smob)
846 Grob *me = unsmob_grob (smob);
847 Grob *par = me->get_parent (Y_AXIS);
849 (void) par->get_property ("positioning-done");
851 return scm_from_double (0.0);
854 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
856 Grob::x_parent_positioning (SCM smob)
858 Grob *me = unsmob_grob (smob);
860 Grob *par = me->get_parent (X_AXIS);
862 (void) par->get_property ("positioning-done");
864 return scm_from_double (0.0);
867 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
869 Grob::stencil_width (SCM smob)
871 Grob *me = unsmob_grob (smob);
872 return grob_stencil_extent (me, X_AXIS);
876 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
878 for (; scm_is_pair (elist); elist = scm_cdr (elist))
879 if (Grob *s = unsmob_grob (scm_car (elist)))
882 common = common->common_refpoint (s, a);
891 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
893 for (vsize i = 0; i < arr.size (); i++)
895 common = common->common_refpoint (arr[i], a);
903 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
905 Interval ext = me->extent (refpoint, a);
907 ext.add_point (me->relative_coordinate (refpoint, a));
912 // Checks whether there is a vertical alignment in the chain of
913 // parents between this and commony.
915 Grob::check_cross_staff (Grob *commony)
917 if (Align_interface::has_interface (commony))
920 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
921 if (Align_interface::has_interface (g))