2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2010 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
196 Grob::handle_broken_dependencies ()
198 Spanner *sp = dynamic_cast<Spanner *> (this);
199 if (original () && sp)
203 /* THIS, SP is the original spanner. We use a special function
204 because some Spanners have enormously long lists in their
205 properties, and a special function fixes FOO */
207 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
208 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
210 System *system = get_system ();
214 && common_refpoint (system, X_AXIS)
215 && common_refpoint (system, Y_AXIS))
216 substitute_object_links (system->self_scm (), object_alist_);
217 else if (dynamic_cast<System *> (this))
218 substitute_object_links (SCM_UNDEFINED, object_alist_);
220 /* THIS element is `invalid'; it has been removed from all
221 dependencies, so let's junk the element itself.
223 Do not do this for System, since that would remove references
224 to the originals of score-grobs, which get then GC'd (a bad
229 /* Note that we still want references to this element to be
230 rearranged, and not silently thrown away, so we keep pointers like
231 {broken_into_{drul, array}, original}
239 for (int a = X_AXIS; a < NO_AXES; a++)
240 dim_cache_[a].clear ();
242 mutable_property_alist_ = SCM_EOL;
243 object_alist_ = SCM_EOL;
244 immutable_property_alist_ = SCM_EOL;
245 interfaces_ = SCM_EOL;
249 Grob::handle_prebroken_dependencies ()
251 /* Don't do this in the derived method, since we want to keep access to
252 object_alist_ centralized. */
255 Item *it = dynamic_cast<Item *> (this);
256 substitute_object_links (scm_from_int (it->break_status_dir ()),
257 original ()->object_alist_);
262 Grob::find_broken_piece (System *) const
267 /****************************************************************
269 ****************************************************************/
272 Grob::translate_axis (Real y, Axis a)
274 if (isinf (y) || isnan (y))
276 programming_error (_ ("Infinity or NaN encountered"));
280 if (!dim_cache_[a].offset_)
281 dim_cache_[a].offset_ = new Real (y);
283 *dim_cache_[a].offset_ += y;
286 /* Find the offset relative to D. If D equals THIS, then it is 0.
287 Otherwise, it recursively defd as
289 OFFSET_ + PARENT_L_->relative_coordinate (D) */
291 Grob::relative_coordinate (Grob const *refp, Axis a) const
293 /* eaa - hmmm, should we do a programming_error() here? */
294 if ((this == NULL) || (refp == this))
297 /* We catch PARENT_L_ == nil case with this, but we crash if we did
298 not ask for the absolute coordinate (ie. REFP == nil.) */
299 Real off = get_offset (a);
300 if (refp == dim_cache_[a].parent_)
303 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
309 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
316 if (dim_cache_[Y_AXIS].offset_)
318 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
319 programming_error ("cyclic chain in pure-Y-offset callbacks");
321 off = *dim_cache_[Y_AXIS].offset_;
325 SCM proc = get_property_data ("Y-offset");
327 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
328 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
329 off = robust_scm2double (call_pure_function (proc,
330 scm_list_1 (self_scm ()),
333 del_property ("pure-Y-offset-in-progress");
334 delete dim_cache_[Y_AXIS].offset_;
335 dim_cache_[Y_AXIS].offset_ = 0;
338 /* we simulate positioning-done if we are the child of a VerticalAlignment,
339 but only if we don't have a cached offset. If we do have a cached offset,
340 it probably means that the Alignment was fixed and it has already been
343 if (Grob *p = get_parent (Y_AXIS))
346 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
347 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
349 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
354 /* Invoke callbacks to get offset relative to parent. */
356 Grob::get_offset (Axis a) const
358 if (dim_cache_[a].offset_)
359 return *dim_cache_[a].offset_;
361 Grob *me = (Grob *) this;
363 SCM sym = axis_offset_symbol (a);
364 me->dim_cache_[a].offset_ = new Real (0.0);
367 UGH: can't fold next 2 statements together. Apparently GCC thinks
368 dim_cache_[a].offset_ is unaliased.
370 Real off = robust_scm2double (internal_get_property (sym), 0.0);
371 if (me->dim_cache_[a].offset_)
373 *me->dim_cache_[a].offset_ += off;
374 me->del_property (sym);
375 return *me->dim_cache_[a].offset_;
382 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
384 if (pure && a != Y_AXIS)
385 programming_error ("tried to get pure X-offset");
386 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
387 : relative_coordinate (refp, a);
390 /****************************************************************
392 ****************************************************************/
395 Grob::flush_extent_cache (Axis axis)
397 if (dim_cache_[axis].extent_)
400 Ugh, this is not accurate; will flush property, causing
401 callback to be called if.
403 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
404 delete dim_cache_[axis].extent_;
405 dim_cache_[axis].extent_ = 0;
406 if (get_parent (axis))
407 get_parent (axis)->flush_extent_cache (axis);
413 Grob::extent (Grob *refp, Axis a) const
415 Real offset = relative_coordinate (refp, a);
417 if (dim_cache_[a].extent_)
419 real_ext = *dim_cache_[a].extent_;
424 Order is significant: ?-extent may trigger suicide.
428 ? ly_symbol2scm ("X-extent")
429 : ly_symbol2scm ("Y-extent");
431 SCM ext = internal_get_property (ext_sym);
432 if (is_number_pair (ext))
433 real_ext.unite (ly_scm2interval (ext));
437 ? ly_symbol2scm ("minimum-X-extent")
438 : ly_symbol2scm ("minimum-Y-extent");
439 SCM min_ext = internal_get_property (min_ext_sym);
440 if (is_number_pair (min_ext))
441 real_ext.unite (ly_scm2interval (min_ext));
443 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
446 real_ext.translate (offset);
452 Grob::pure_height (Grob *refp, int start, int end)
454 SCM iv_scm = get_pure_property ("Y-extent", start, end);
455 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
456 Real offset = pure_relative_y_coordinate (refp, start, end);
458 SCM min_ext = get_property ("minimum-Y-extent");
460 /* we don't add minimum-Y-extent if the extent is empty. This solves
461 a problem with Hara-kiri spanners. They would request_suicide and
462 return empty extents, but we would force them here to be large. */
463 if (!iv.is_empty () && is_number_pair (min_ext))
464 iv.unite (ly_scm2interval (min_ext));
467 iv.translate (offset);
472 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
474 if (pure && a != Y_AXIS)
475 programming_error ("tried to get pure width");
476 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
480 Grob::spanned_rank_interval () const
482 return Interval_t<int> (-1, 0);
486 Grob::pure_is_visible (int start, int end) const
491 /* Sort grobs according to their starting column. */
493 Grob::less (Grob *g1, Grob *g2)
495 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
498 /****************************************************************
500 ****************************************************************/
502 /* Find the group-element which has both #this# and #s# */
504 Grob::common_refpoint (Grob const *s, Axis a) const
506 /* I don't like the quadratic aspect of this code, but I see no
507 other way. The largest chain of parents might be 10 high or so,
508 so it shouldn't be a real issue. */
509 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
510 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
518 Grob::set_parent (Grob *g, Axis a)
520 dim_cache_[a].parent_ = g;
524 Grob::get_parent (Axis a) const
526 return dim_cache_[a].parent_;
531 Grob::fixup_refpoint ()
533 for (int a = X_AXIS; a < NO_AXES; a++)
536 Grob *parent = get_parent (ax);
541 if (parent->get_system () != get_system () && get_system ())
543 Grob *newparent = parent->find_broken_piece (get_system ());
544 set_parent (newparent, ax);
547 if (Item *i = dynamic_cast<Item *> (this))
549 Item *parenti = dynamic_cast<Item *> (parent);
553 Direction my_dir = i->break_status_dir ();
554 if (my_dir != parenti->break_status_dir ())
556 Item *newparent = parenti->find_prebroken_piece (my_dir);
557 set_parent (newparent, ax);
565 /****************************************************************
567 ****************************************************************/
569 Grob::warning (string s) const
571 if (get_program_option ("warning-as-error"))
574 SCM cause = self_scm ();
575 while (Grob *g = unsmob_grob (cause))
576 cause = g->get_property ("cause");
578 /* ES TODO: cause can't be Music*/
579 if (Music *m = unsmob_music (cause))
580 m->origin ()->warning (s);
581 else if (Stream_event *ev = unsmob_stream_event (cause))
582 ev->origin ()->warning (s);
591 SCM meta = get_property ("meta");
592 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
593 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
594 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
598 Grob::programming_error (string s) const
600 if (get_program_option ("warning-as-error"))
603 SCM cause = self_scm ();
604 while (Grob *g = unsmob_grob (cause))
605 cause = g->get_property ("cause");
607 s = _f ("programming error: %s", s);
609 /* ES TODO: cause can't be Music*/
610 if (Music *m = unsmob_music (cause))
611 m->origin ()->message (s);
612 else if (Stream_event *ev = unsmob_stream_event (cause))
613 ev->origin ()->message (s);
620 "A grob represents a piece of music notation.\n"
622 "All grobs have an X and Y@tie{}position on the page. These"
623 " X and Y@tie{}positions are stored in a relative format, thus"
624 " they can easily be combined by stacking them, hanging one"
625 " grob to the side of another, or coupling them into grouping"
628 "Each grob has a reference point (a.k.a.@: parent): The"
629 " position of a grob is stored relative to that reference"
630 " point. For example, the X@tie{}reference point of a staccato"
631 " dot usually is the note head that it applies to. When the"
632 " note head is moved, the staccato dot moves along"
635 "A grob is often associated with a symbol, but some grobs do"
636 " not print any symbols. They take care of grouping objects."
637 " For example, there is a separate grob that stacks staves"
638 " vertically. The @ref{NoteCollision} object is also an"
639 " abstract grob: It only moves around chords, but doesn't print"
642 "Grobs have properties (Scheme variables) that can be read and"
643 " set. Two types of them exist: immutable and mutable."
644 " Immutable variables define the default style and behavior."
645 " They are shared between many objects. They can be changed"
646 " using @code{\\override} and @code{\\revert}. Mutable"
647 " properties are variables that are specific to one grob."
648 " Typically, lists of other objects, or results from"
649 " computations are stored in mutable properties. In"
650 " particular, every call to @code{ly:grob-set-property!}"
651 " (or its C++ equivalent) sets a mutable property.\n"
653 "The properties @code{after-line-breaking} and"
654 " @code{before-line-breaking} are dummies that are not"
655 " user-serviceable.",
662 "after-line-breaking "
664 "axis-group-parent-X "
665 "axis-group-parent-Y "
666 "before-line-breaking "
678 "outside-staff-horizontal-padding "
679 "outside-staff-padding "
680 "outside-staff-priority "
681 "pure-Y-offset-in-progress "
690 /****************************************************************
692 ****************************************************************/
695 grob_stencil_extent (Grob *me, Axis a)
697 Stencil *m = me->get_stencil ();
701 return ly_interval2scm (e);
705 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
707 Grob::stencil_height (SCM smob)
709 Grob *me = unsmob_grob (smob);
710 return grob_stencil_extent (me, Y_AXIS);
713 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
715 Grob::y_parent_positioning (SCM smob)
717 Grob *me = unsmob_grob (smob);
718 Grob *par = me->get_parent (Y_AXIS);
720 (void) par->get_property ("positioning-done");
722 return scm_from_double (0.0);
726 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
728 Grob::x_parent_positioning (SCM smob)
730 Grob *me = unsmob_grob (smob);
732 Grob *par = me->get_parent (X_AXIS);
734 (void) par->get_property ("positioning-done");
736 return scm_from_double (0.0);
739 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
741 Grob::stencil_width (SCM smob)
743 Grob *me = unsmob_grob (smob);
744 return grob_stencil_extent (me, X_AXIS);
749 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
751 for (; scm_is_pair (elist); elist = scm_cdr (elist))
752 if (Grob *s = unsmob_grob (scm_car (elist)))
755 common = common->common_refpoint (s, a);
764 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
766 for (vsize i = 0; i < arr.size (); i++)
768 common = common->common_refpoint (arr[i], a);
776 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
778 Interval ext = me->extent (refpoint, a);
780 ext.add_point (me->relative_coordinate (refpoint, a));
785 // Checks whether there is a vertical alignment in the chain of
786 // parents between this and commony.
788 Grob::check_cross_staff (Grob *commony)
790 if (Align_interface::has_interface (commony))
793 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
794 if (Align_interface::has_interface (g))