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 // We never want nan, so we avoid shifting infinite values.
470 for (LEFT_and_RIGHT (d))
471 if (!isinf (real_ext[d]))
472 real_ext[d] += offset;
478 Grob::pure_height (Grob *refp, int start, int end)
480 SCM iv_scm = get_pure_property ("Y-extent", start, end);
481 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
482 Real offset = pure_relative_y_coordinate (refp, start, end);
484 SCM min_ext = get_property ("minimum-Y-extent");
486 /* we don't add minimum-Y-extent if the extent is empty. This solves
487 a problem with Hara-kiri spanners. They would request_suicide and
488 return empty extents, but we would force them here to be large. */
489 if (!iv.is_empty () && is_number_pair (min_ext))
490 iv.unite (ly_scm2interval (min_ext));
493 iv.translate (offset);
498 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
500 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
504 Grob::spanned_rank_interval () const
506 return Interval_t<int> (-1, 0);
510 Grob::pure_is_visible (int /* start */, int /* end */) const
515 /* Sort grobs according to their starting column. */
517 Grob::less (Grob *g1, Grob *g2)
519 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
522 /****************************************************************
524 ****************************************************************/
526 /* Find the group-element which has both #this# and #s# */
528 Grob::common_refpoint (Grob const *s, Axis a) const
531 /* Catching the trivial cases is likely costlier than just running
532 through: one can't avoid going to the respective chain ends
533 anyway. We might save the second run through when the chain ends
534 differ, but keeping track of the ends makes the loop more costly.
541 for (c = this; c; ++balance)
542 c = c->dim_cache_[a].parent_;
544 for (d = s; d; --balance)
545 d = d->dim_cache_[a].parent_;
547 /* Cut down ancestry to same size */
549 for (c = this; balance > 0; --balance)
550 c = c->dim_cache_[a].parent_;
552 for (d = s; balance < 0; ++balance)
553 d = d->dim_cache_[a].parent_;
555 /* Now find point where our lineages converge */
558 c = c->dim_cache_[a].parent_;
559 d = d->dim_cache_[a].parent_;
566 Grob::set_parent (Grob *g, Axis a)
568 dim_cache_[a].parent_ = g;
572 Grob::get_parent (Axis a) const
574 return dim_cache_[a].parent_;
578 Grob::fixup_refpoint ()
580 for (int a = X_AXIS; a < NO_AXES; a++)
583 Grob *parent = get_parent (ax);
588 if (parent->get_system () != get_system () && get_system ())
590 Grob *newparent = parent->find_broken_piece (get_system ());
591 set_parent (newparent, ax);
594 if (Item *i = dynamic_cast<Item *> (this))
596 Item *parenti = dynamic_cast<Item *> (parent);
600 Direction my_dir = i->break_status_dir ();
601 if (my_dir != parenti->break_status_dir ())
603 Item *newparent = parenti->find_prebroken_piece (my_dir);
604 set_parent (newparent, ax);
611 /****************************************************************
613 ****************************************************************/
616 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
620 if (Align_interface::has_interface (g))
621 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
622 return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
627 Grob::get_root_vertical_alignment (Grob *g)
629 return get_maybe_root_vertical_alignment (g, 0);
633 Grob::get_vertical_axis_group (Grob *g)
637 if (!g->get_parent (Y_AXIS))
639 if (Axis_group_interface::has_interface (g)
640 && Align_interface::has_interface (g->get_parent (Y_AXIS)))
642 return get_vertical_axis_group (g->get_parent (Y_AXIS));
647 Grob::get_vertical_axis_group_index (Grob *g)
649 Grob *val = get_root_vertical_alignment (g);
652 Grob *vax = get_vertical_axis_group (g);
653 extract_grob_set (val, "elements", elts);
654 for (vsize i = 0; i < elts.size (); i++)
657 g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
662 Grob::vertical_less (Grob *g1, Grob *g2)
664 return internal_vertical_less (g1, g2, false);
668 Grob::pure_vertical_less (Grob *g1, Grob *g2)
670 return internal_vertical_less (g1, g2, true);
674 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
676 Grob *vag = get_root_vertical_alignment (g1);
679 g1->programming_error ("grob does not belong to a VerticalAlignment?");
683 Grob *ag1 = get_vertical_axis_group (g1);
684 Grob *ag2 = get_vertical_axis_group (g2);
686 extract_grob_set (vag, "elements", elts);
688 if (ag1 == ag2 && !pure)
690 Grob *common = g1->common_refpoint (g2, Y_AXIS);
691 return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
694 for (vsize i = 0; i < elts.size (); i++)
702 g1->programming_error ("could not place this grob in its axis group");
706 /****************************************************************
708 ****************************************************************/
710 Grob::programming_error (string s) const
712 SCM cause = self_scm ();
713 while (Grob *g = unsmob_grob (cause))
714 cause = g->get_property ("cause");
716 /* ES TODO: cause can't be Music*/
717 if (Music *m = unsmob_music (cause))
718 m->origin ()->programming_error (s);
719 else if (Stream_event *ev = unsmob_stream_event (cause))
720 ev->origin ()->programming_error (s);
722 ::programming_error (s);
726 Grob::warning (string s) const
728 SCM cause = self_scm ();
729 while (Grob *g = unsmob_grob (cause))
730 cause = g->get_property ("cause");
732 /* ES TODO: cause can't be Music*/
733 if (Music *m = unsmob_music (cause))
734 m->origin ()->warning (s);
735 else if (Stream_event *ev = unsmob_stream_event (cause))
736 ev->origin ()->warning (s);
744 SCM meta = get_property ("meta");
745 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
746 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
747 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
751 "A grob represents a piece of music notation.\n"
753 "All grobs have an X and Y@tie{}position on the page. These"
754 " X and Y@tie{}positions are stored in a relative format, thus"
755 " they can easily be combined by stacking them, hanging one"
756 " grob to the side of another, or coupling them into grouping"
759 "Each grob has a reference point (a.k.a.@: parent): The"
760 " position of a grob is stored relative to that reference"
761 " point. For example, the X@tie{}reference point of a staccato"
762 " dot usually is the note head that it applies to. When the"
763 " note head is moved, the staccato dot moves along"
766 "A grob is often associated with a symbol, but some grobs do"
767 " not print any symbols. They take care of grouping objects."
768 " For example, there is a separate grob that stacks staves"
769 " vertically. The @ref{NoteCollision} object is also an"
770 " abstract grob: It only moves around chords, but doesn't print"
773 "Grobs have properties (Scheme variables) that can be read and"
774 " set. Two types of them exist: immutable and mutable."
775 " Immutable variables define the default style and behavior."
776 " They are shared between many objects. They can be changed"
777 " using @code{\\override} and @code{\\revert}. Mutable"
778 " properties are variables that are specific to one grob."
779 " Typically, lists of other objects, or results from"
780 " computations are stored in mutable properties. In"
781 " particular, every call to @code{ly:grob-set-property!}"
782 " (or its C++ equivalent) sets a mutable property.\n"
784 "The properties @code{after-line-breaking} and"
785 " @code{before-line-breaking} are dummies that are not"
786 " user-serviceable.",
793 "after-line-breaking "
795 "axis-group-parent-X "
796 "axis-group-parent-Y "
797 "before-line-breaking "
812 "outside-staff-horizontal-padding "
813 "outside-staff-padding "
814 "outside-staff-priority "
815 "pure-Y-offset-in-progress "
824 /****************************************************************
826 ****************************************************************/
829 grob_stencil_extent (Grob *me, Axis a)
831 Stencil *m = me->get_stencil ();
835 return ly_interval2scm (e);
838 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
840 Grob::stencil_height (SCM smob)
842 Grob *me = unsmob_grob (smob);
843 return grob_stencil_extent (me, Y_AXIS);
846 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
848 Grob::y_parent_positioning (SCM smob)
850 Grob *me = unsmob_grob (smob);
851 Grob *par = me->get_parent (Y_AXIS);
853 (void) par->get_property ("positioning-done");
855 return scm_from_double (0.0);
858 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
860 Grob::x_parent_positioning (SCM smob)
862 Grob *me = unsmob_grob (smob);
864 Grob *par = me->get_parent (X_AXIS);
866 (void) par->get_property ("positioning-done");
868 return scm_from_double (0.0);
871 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
873 Grob::stencil_width (SCM smob)
875 Grob *me = unsmob_grob (smob);
876 return grob_stencil_extent (me, X_AXIS);
880 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
882 for (; scm_is_pair (elist); elist = scm_cdr (elist))
883 if (Grob *s = unsmob_grob (scm_car (elist)))
886 common = common->common_refpoint (s, a);
895 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
897 for (vsize i = 0; i < arr.size (); i++)
899 common = common->common_refpoint (arr[i], a);
907 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
909 Interval ext = me->extent (refpoint, a);
911 ext.add_point (me->relative_coordinate (refpoint, a));
916 // Checks whether there is a vertical alignment in the chain of
917 // parents between this and commony.
919 Grob::check_cross_staff (Grob *commony)
921 if (Align_interface::has_interface (commony))
924 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
925 if (Align_interface::has_interface (g))