2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 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_ = ly_deep_copy (s.mutable_property_alist_);
92 interfaces_ = s.interfaces_;
93 object_alist_ = SCM_EOL;
103 /****************************************************************
105 ****************************************************************/
108 Grob::get_stencil () const
113 SCM stil = get_property ("stencil");
114 return unsmob_stencil (stil);
118 Grob::get_print_stencil () const
120 SCM stil = get_property ("stencil");
123 if (Stencil *m = unsmob_stencil (stil))
126 bool transparent = to_boolean (get_property ("transparent"));
129 retval = Stencil (m->extent_box (), SCM_EOL);
132 SCM expr = m->expr ();
133 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
136 retval = Stencil (m->extent_box (), expr);
139 SCM rot = get_property ("rotation");
140 if (scm_is_pair (rot))
142 Real angle = scm_to_double (scm_car (rot));
143 Real x = scm_to_double (scm_cadr (rot));
144 Real y = scm_to_double (scm_caddr (rot));
146 retval.rotate_degrees (angle, Offset (x, y));
149 /* color support... see interpret_stencil_expression () for more... */
150 SCM color = get_property ("color");
151 if (scm_is_pair (color))
153 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
157 retval = Stencil (retval.extent_box (), expr);
160 /* process whiteout */
161 /* a grob has to be visible, otherwise the whiteout property has no effect */
162 if (!transparent && to_boolean (get_property ("whiteout")))
164 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
165 /* to add a round-filled-box stencil to the stencil list */
167 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
168 retval.smobbed_copy ()));
175 /****************************************************************
177 ****************************************************************/
179 Grob::do_break_processing ()
184 Grob::discretionary_processing ()
189 Grob::get_system () const
194 /* This version of get_system is more reliable than this->get_system ()
195 before line-breaking has been done, at which point there is only
196 one system in the whole score and we can find it just by following
199 Grob::get_system (Grob *me)
201 Grob *p = me->get_parent (X_AXIS);
202 return p ? get_system (p) : dynamic_cast<System *>(me);
206 Grob::handle_broken_dependencies ()
208 Spanner *sp = dynamic_cast<Spanner *> (this);
209 if (original () && sp)
213 /* THIS, SP is the original spanner. We use a special function
214 because some Spanners have enormously long lists in their
215 properties, and a special function fixes FOO */
217 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
218 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
220 System *system = get_system ();
224 && common_refpoint (system, X_AXIS)
225 && common_refpoint (system, Y_AXIS))
226 substitute_object_links (system->self_scm (), object_alist_);
227 else if (dynamic_cast<System *> (this))
228 substitute_object_links (SCM_UNDEFINED, object_alist_);
230 /* THIS element is `invalid'; it has been removed from all
231 dependencies, so let's junk the element itself.
233 Do not do this for System, since that would remove references
234 to the originals of score-grobs, which get then GC'd (a bad
239 /* Note that we still want references to this element to be
240 rearranged, and not silently thrown away, so we keep pointers like
241 {broken_into_{drul, array}, original}
249 for (int a = X_AXIS; a < NO_AXES; a++)
250 dim_cache_[a].clear ();
252 mutable_property_alist_ = SCM_EOL;
253 object_alist_ = SCM_EOL;
254 immutable_property_alist_ = SCM_EOL;
255 interfaces_ = SCM_EOL;
259 Grob::handle_prebroken_dependencies ()
261 /* Don't do this in the derived method, since we want to keep access to
262 object_alist_ centralized. */
265 Item *it = dynamic_cast<Item *> (this);
266 substitute_object_links (scm_from_int (it->break_status_dir ()),
267 original ()->object_alist_);
272 Grob::find_broken_piece (System *) const
277 /****************************************************************
279 ****************************************************************/
282 Grob::translate_axis (Real y, Axis a)
284 if (isinf (y) || isnan (y))
286 programming_error (_ ("Infinity or NaN encountered"));
290 if (!dim_cache_[a].offset_)
291 dim_cache_[a].offset_ = new Real (y);
293 *dim_cache_[a].offset_ += y;
296 /* Find the offset relative to D. If D equals THIS, then it is 0.
297 Otherwise, it recursively defd as
299 OFFSET_ + PARENT_L_->relative_coordinate (D) */
301 Grob::relative_coordinate (Grob const *refp, Axis a) const
303 /* eaa - hmmm, should we do a programming_error() here? */
304 if ((this == NULL) || (refp == this))
307 /* We catch PARENT_L_ == nil case with this, but we crash if we did
308 not ask for the absolute coordinate (ie. REFP == nil.) */
309 Real off = get_offset (a);
310 if (refp == dim_cache_[a].parent_)
313 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
319 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
326 if (dim_cache_[Y_AXIS].offset_)
328 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
329 programming_error ("cyclic chain in pure-Y-offset callbacks");
331 off = *dim_cache_[Y_AXIS].offset_;
335 SCM proc = get_property_data ("Y-offset");
337 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
338 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
339 off = robust_scm2double (call_pure_function (proc,
340 scm_list_1 (self_scm ()),
343 del_property ("pure-Y-offset-in-progress");
344 delete dim_cache_[Y_AXIS].offset_;
345 dim_cache_[Y_AXIS].offset_ = 0;
348 /* we simulate positioning-done if we are the child of a VerticalAlignment,
349 but only if we don't have a cached offset. If we do have a cached offset,
350 it probably means that the Alignment was fixed and it has already been
353 if (Grob *p = get_parent (Y_AXIS))
356 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
357 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
359 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
364 /* Invoke callbacks to get offset relative to parent. */
366 Grob::get_offset (Axis a) const
368 if (dim_cache_[a].offset_)
369 return *dim_cache_[a].offset_;
371 Grob *me = (Grob *) this;
373 SCM sym = axis_offset_symbol (a);
374 me->dim_cache_[a].offset_ = new Real (0.0);
377 UGH: can't fold next 2 statements together. Apparently GCC thinks
378 dim_cache_[a].offset_ is unaliased.
380 Real off = robust_scm2double (internal_get_property (sym), 0.0);
381 if (me->dim_cache_[a].offset_)
383 *me->dim_cache_[a].offset_ += off;
384 me->del_property (sym);
385 return *me->dim_cache_[a].offset_;
392 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
394 if (pure && a != Y_AXIS)
395 programming_error ("tried to get pure X-offset");
396 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
397 : relative_coordinate (refp, a);
400 /****************************************************************
402 ****************************************************************/
405 Grob::flush_extent_cache (Axis axis)
407 if (dim_cache_[axis].extent_)
410 Ugh, this is not accurate; will flush property, causing
411 callback to be called if.
413 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
414 delete dim_cache_[axis].extent_;
415 dim_cache_[axis].extent_ = 0;
416 if (get_parent (axis))
417 get_parent (axis)->flush_extent_cache (axis);
422 Grob::extent (Grob *refp, Axis a) const
424 Real offset = relative_coordinate (refp, a);
426 if (dim_cache_[a].extent_)
428 real_ext = *dim_cache_[a].extent_;
433 Order is significant: ?-extent may trigger suicide.
437 ? ly_symbol2scm ("X-extent")
438 : ly_symbol2scm ("Y-extent");
440 SCM ext = internal_get_property (ext_sym);
441 if (is_number_pair (ext))
442 real_ext.unite (ly_scm2interval (ext));
446 ? ly_symbol2scm ("minimum-X-extent")
447 : ly_symbol2scm ("minimum-Y-extent");
448 SCM min_ext = internal_get_property (min_ext_sym);
449 if (is_number_pair (min_ext))
450 real_ext.unite (ly_scm2interval (min_ext));
452 ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
455 real_ext.translate (offset);
461 Grob::pure_height (Grob *refp, int start, int end)
463 SCM iv_scm = get_pure_property ("Y-extent", start, end);
464 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
465 Real offset = pure_relative_y_coordinate (refp, start, end);
467 SCM min_ext = get_property ("minimum-Y-extent");
469 /* we don't add minimum-Y-extent if the extent is empty. This solves
470 a problem with Hara-kiri spanners. They would request_suicide and
471 return empty extents, but we would force them here to be large. */
472 if (!iv.is_empty () && is_number_pair (min_ext))
473 iv.unite (ly_scm2interval (min_ext));
476 iv.translate (offset);
481 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
483 if (pure && a != Y_AXIS)
484 programming_error ("tried to get pure width");
485 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
489 Grob::spanned_rank_interval () const
491 return Interval_t<int> (-1, 0);
495 Grob::pure_is_visible (int /* start */, int /* end */) const
500 /* Sort grobs according to their starting column. */
502 Grob::less (Grob *g1, Grob *g2)
504 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
507 /****************************************************************
509 ****************************************************************/
511 /* Find the group-element which has both #this# and #s# */
513 Grob::common_refpoint (Grob const *s, Axis a) const
515 /* I don't like the quadratic aspect of this code, but I see no
516 other way. The largest chain of parents might be 10 high or so,
517 so it shouldn't be a real issue. */
518 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
519 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
527 Grob::set_parent (Grob *g, Axis a)
529 dim_cache_[a].parent_ = g;
533 Grob::get_parent (Axis a) const
535 return dim_cache_[a].parent_;
539 Grob::fixup_refpoint ()
541 for (int a = X_AXIS; a < NO_AXES; a++)
544 Grob *parent = get_parent (ax);
549 if (parent->get_system () != get_system () && get_system ())
551 Grob *newparent = parent->find_broken_piece (get_system ());
552 set_parent (newparent, ax);
555 if (Item *i = dynamic_cast<Item *> (this))
557 Item *parenti = dynamic_cast<Item *> (parent);
561 Direction my_dir = i->break_status_dir ();
562 if (my_dir != parenti->break_status_dir ())
564 Item *newparent = parenti->find_prebroken_piece (my_dir);
565 set_parent (newparent, ax);
572 /****************************************************************
574 ****************************************************************/
576 Grob::programming_error (string s) const
578 SCM cause = self_scm ();
579 while (Grob *g = unsmob_grob (cause))
580 cause = g->get_property ("cause");
582 /* ES TODO: cause can't be Music*/
583 if (Music *m = unsmob_music (cause))
584 m->origin ()->programming_error (s);
585 else if (Stream_event *ev = unsmob_stream_event (cause))
586 ev->origin ()->programming_error (s);
588 ::programming_error (s);
592 Grob::warning (string s) const
594 SCM cause = self_scm ();
595 while (Grob *g = unsmob_grob (cause))
596 cause = g->get_property ("cause");
598 /* ES TODO: cause can't be Music*/
599 if (Music *m = unsmob_music (cause))
600 m->origin ()->warning (s);
601 else if (Stream_event *ev = unsmob_stream_event (cause))
602 ev->origin ()->warning (s);
610 SCM meta = get_property ("meta");
611 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
612 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
613 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
617 "A grob represents a piece of music notation.\n"
619 "All grobs have an X and Y@tie{}position on the page. These"
620 " X and Y@tie{}positions are stored in a relative format, thus"
621 " they can easily be combined by stacking them, hanging one"
622 " grob to the side of another, or coupling them into grouping"
625 "Each grob has a reference point (a.k.a.@: parent): The"
626 " position of a grob is stored relative to that reference"
627 " point. For example, the X@tie{}reference point of a staccato"
628 " dot usually is the note head that it applies to. When the"
629 " note head is moved, the staccato dot moves along"
632 "A grob is often associated with a symbol, but some grobs do"
633 " not print any symbols. They take care of grouping objects."
634 " For example, there is a separate grob that stacks staves"
635 " vertically. The @ref{NoteCollision} object is also an"
636 " abstract grob: It only moves around chords, but doesn't print"
639 "Grobs have properties (Scheme variables) that can be read and"
640 " set. Two types of them exist: immutable and mutable."
641 " Immutable variables define the default style and behavior."
642 " They are shared between many objects. They can be changed"
643 " using @code{\\override} and @code{\\revert}. Mutable"
644 " properties are variables that are specific to one grob."
645 " Typically, lists of other objects, or results from"
646 " computations are stored in mutable properties. In"
647 " particular, every call to @code{ly:grob-set-property!}"
648 " (or its C++ equivalent) sets a mutable property.\n"
650 "The properties @code{after-line-breaking} and"
651 " @code{before-line-breaking} are dummies that are not"
652 " user-serviceable.",
659 "after-line-breaking "
661 "axis-group-parent-X "
662 "axis-group-parent-Y "
663 "before-line-breaking "
675 "outside-staff-horizontal-padding "
676 "outside-staff-padding "
677 "outside-staff-priority "
678 "pure-Y-offset-in-progress "
687 /****************************************************************
689 ****************************************************************/
692 grob_stencil_extent (Grob *me, Axis a)
694 Stencil *m = me->get_stencil ();
698 return ly_interval2scm (e);
701 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
703 Grob::stencil_height (SCM smob)
705 Grob *me = unsmob_grob (smob);
706 return grob_stencil_extent (me, Y_AXIS);
709 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
711 Grob::y_parent_positioning (SCM smob)
713 Grob *me = unsmob_grob (smob);
714 Grob *par = me->get_parent (Y_AXIS);
716 (void) par->get_property ("positioning-done");
718 return scm_from_double (0.0);
721 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
723 Grob::x_parent_positioning (SCM smob)
725 Grob *me = unsmob_grob (smob);
727 Grob *par = me->get_parent (X_AXIS);
729 (void) par->get_property ("positioning-done");
731 return scm_from_double (0.0);
734 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
736 Grob::stencil_width (SCM smob)
738 Grob *me = unsmob_grob (smob);
739 return grob_stencil_extent (me, X_AXIS);
743 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
745 for (; scm_is_pair (elist); elist = scm_cdr (elist))
746 if (Grob *s = unsmob_grob (scm_car (elist)))
749 common = common->common_refpoint (s, a);
758 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
760 for (vsize i = 0; i < arr.size (); i++)
762 common = common->common_refpoint (arr[i], a);
770 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
772 Interval ext = me->extent (refpoint, a);
774 ext.add_point (me->relative_coordinate (refpoint, a));
779 // Checks whether there is a vertical alignment in the chain of
780 // parents between this and commony.
782 Grob::check_cross_staff (Grob *commony)
784 if (Align_interface::has_interface (commony))
787 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
788 if (Align_interface::has_interface (g))