2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2009 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 if (to_boolean (get_property ("transparent")))
127 retval = Stencil (m->extent_box (), SCM_EOL);
130 SCM expr = m->expr ();
131 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
134 retval = Stencil (m->extent_box (), expr);
137 SCM rot = get_property ("rotation");
138 if (scm_is_pair (rot))
140 Real angle = scm_to_double (scm_car (rot));
141 Real x = scm_to_double (scm_cadr (rot));
142 Real y = scm_to_double (scm_caddr (rot));
144 retval.rotate_degrees (angle, Offset (x, y));
147 /* color support... see interpret_stencil_expression () for more... */
148 SCM color = get_property ("color");
149 if (scm_is_pair (color))
151 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
155 retval = Stencil (retval.extent_box (), expr);
163 /****************************************************************
165 ****************************************************************/
167 Grob::do_break_processing ()
172 Grob::discretionary_processing ()
177 Grob::get_system () const
184 Grob::handle_broken_dependencies ()
186 Spanner *sp = dynamic_cast<Spanner *> (this);
187 if (original () && sp)
191 /* THIS, SP is the original spanner. We use a special function
192 because some Spanners have enormously long lists in their
193 properties, and a special function fixes FOO */
195 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
196 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
198 System *system = get_system ();
202 && common_refpoint (system, X_AXIS)
203 && common_refpoint (system, Y_AXIS))
204 substitute_object_links (system->self_scm (), object_alist_);
205 else if (dynamic_cast<System *> (this))
206 substitute_object_links (SCM_UNDEFINED, object_alist_);
208 /* THIS element is `invalid'; it has been removed from all
209 dependencies, so let's junk the element itself.
211 Do not do this for System, since that would remove references
212 to the originals of score-grobs, which get then GC'd (a bad
217 /* Note that we still want references to this element to be
218 rearranged, and not silently thrown away, so we keep pointers like
219 {broken_into_{drul, array}, original}
227 for (int a = X_AXIS; a < NO_AXES; a++)
228 dim_cache_[a].clear ();
230 mutable_property_alist_ = SCM_EOL;
231 object_alist_ = SCM_EOL;
232 immutable_property_alist_ = SCM_EOL;
233 interfaces_ = SCM_EOL;
237 Grob::handle_prebroken_dependencies ()
239 /* Don't do this in the derived method, since we want to keep access to
240 object_alist_ centralized. */
243 Item *it = dynamic_cast<Item *> (this);
244 substitute_object_links (scm_from_int (it->break_status_dir ()),
245 original ()->object_alist_);
250 Grob::find_broken_piece (System *) const
255 /****************************************************************
257 ****************************************************************/
260 Grob::translate_axis (Real y, Axis a)
262 if (isinf (y) || isnan (y))
264 programming_error (_ ("Infinity or NaN encountered"));
268 if (!dim_cache_[a].offset_)
269 dim_cache_[a].offset_ = new Real (y);
271 *dim_cache_[a].offset_ += y;
274 /* Find the offset relative to D. If D equals THIS, then it is 0.
275 Otherwise, it recursively defd as
277 OFFSET_ + PARENT_L_->relative_coordinate (D) */
279 Grob::relative_coordinate (Grob const *refp, Axis a) const
281 /* eaa - hmmm, should we do a programming_error() here? */
282 if ((this == NULL) || (refp == this))
285 /* We catch PARENT_L_ == nil case with this, but we crash if we did
286 not ask for the absolute coordinate (ie. REFP == nil.) */
287 Real off = get_offset (a);
288 if (refp == dim_cache_[a].parent_)
291 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
297 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
304 if (dim_cache_[Y_AXIS].offset_)
306 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
307 programming_error ("cyclic chain in pure-Y-offset callbacks");
309 off = *dim_cache_[Y_AXIS].offset_;
313 SCM proc = get_property_data ("Y-offset");
315 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
316 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
317 off = robust_scm2double (call_pure_function (proc,
318 scm_list_1 (self_scm ()),
321 del_property ("pure-Y-offset-in-progress");
322 delete dim_cache_[Y_AXIS].offset_;
323 dim_cache_[Y_AXIS].offset_ = 0;
326 /* we simulate positioning-done if we are the child of a VerticalAlignment,
327 but only if we don't have a cached offset. If we do have a cached offset,
328 it probably means that the Alignment was fixed and it has already been
331 if (Grob *p = get_parent (Y_AXIS))
334 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
335 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
337 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
342 /* Invoke callbacks to get offset relative to parent. */
344 Grob::get_offset (Axis a) const
346 if (dim_cache_[a].offset_)
347 return *dim_cache_[a].offset_;
349 Grob *me = (Grob *) this;
351 SCM sym = axis_offset_symbol (a);
352 me->dim_cache_[a].offset_ = new Real (0.0);
355 UGH: can't fold next 2 statements together. Apparently GCC thinks
356 dim_cache_[a].offset_ is unaliased.
358 Real off = robust_scm2double (internal_get_property (sym), 0.0);
359 if (me->dim_cache_[a].offset_)
361 *me->dim_cache_[a].offset_ += off;
362 me->del_property (sym);
363 return *me->dim_cache_[a].offset_;
370 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
372 if (pure && a != Y_AXIS)
373 programming_error ("tried to get pure X-offset");
374 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
375 : relative_coordinate (refp, a);
378 /****************************************************************
380 ****************************************************************/
383 Grob::flush_extent_cache (Axis axis)
385 if (dim_cache_[axis].extent_)
388 Ugh, this is not accurate; will flush property, causing
389 callback to be called if.
391 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
392 delete dim_cache_[axis].extent_;
393 dim_cache_[axis].extent_ = 0;
394 if (get_parent (axis))
395 get_parent (axis)->flush_extent_cache (axis);
401 Grob::extent (Grob *refp, Axis a) const
403 Real offset = relative_coordinate (refp, a);
405 if (dim_cache_[a].extent_)
407 real_ext = *dim_cache_[a].extent_;
412 Order is significant: ?-extent may trigger suicide.
416 ? ly_symbol2scm ("X-extent")
417 : ly_symbol2scm ("Y-extent");
419 SCM ext = internal_get_property (ext_sym);
420 if (is_number_pair (ext))
421 real_ext.unite (ly_scm2interval (ext));
425 ? ly_symbol2scm ("minimum-X-extent")
426 : ly_symbol2scm ("minimum-Y-extent");
427 SCM min_ext = internal_get_property (min_ext_sym);
428 if (is_number_pair (min_ext))
429 real_ext.unite (ly_scm2interval (min_ext));
431 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
434 real_ext.translate (offset);
440 Grob::pure_height (Grob *refp, int start, int end)
442 SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
443 SCM iv_scm = call_pure_function (proc,
444 scm_list_1 (self_scm ()),
446 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
447 Real offset = pure_relative_y_coordinate (refp, start, end);
449 SCM min_ext = get_property ("minimum-Y-extent");
451 /* we don't add minimum-Y-extent if the extent is empty. This solves
452 a problem with Hara-kiri spanners. They would request_suicide and
453 return empty extents, but we would force them here to be large. */
454 if (!iv.is_empty () && is_number_pair (min_ext))
455 iv.unite (ly_scm2interval (min_ext));
458 iv.translate (offset);
463 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
465 if (pure && a != Y_AXIS)
466 programming_error ("tried to get pure width");
467 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
471 Grob::spanned_rank_interval () const
473 return Interval_t<int> (-1, 0);
476 /****************************************************************
478 ****************************************************************/
480 /* Find the group-element which has both #this# and #s# */
482 Grob::common_refpoint (Grob const *s, Axis a) const
484 /* I don't like the quadratic aspect of this code, but I see no
485 other way. The largest chain of parents might be 10 high or so,
486 so it shouldn't be a real issue. */
487 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
488 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
496 Grob::set_parent (Grob *g, Axis a)
498 dim_cache_[a].parent_ = g;
502 Grob::get_parent (Axis a) const
504 return dim_cache_[a].parent_;
509 Grob::fixup_refpoint ()
511 for (int a = X_AXIS; a < NO_AXES; a++)
514 Grob *parent = get_parent (ax);
519 if (parent->get_system () != get_system () && get_system ())
521 Grob *newparent = parent->find_broken_piece (get_system ());
522 set_parent (newparent, ax);
525 if (Item *i = dynamic_cast<Item *> (this))
527 Item *parenti = dynamic_cast<Item *> (parent);
531 Direction my_dir = i->break_status_dir ();
532 if (my_dir != parenti->break_status_dir ())
534 Item *newparent = parenti->find_prebroken_piece (my_dir);
535 set_parent (newparent, ax);
543 /****************************************************************
545 ****************************************************************/
547 Grob::warning (string s) const
549 if (get_program_option ("warning-as-error"))
552 SCM cause = self_scm ();
553 while (Grob *g = unsmob_grob (cause))
554 cause = g->get_property ("cause");
556 /* ES TODO: cause can't be Music*/
557 if (Music *m = unsmob_music (cause))
558 m->origin ()->warning (s);
559 else if (Stream_event *ev = unsmob_stream_event (cause))
560 ev->origin ()->warning (s);
569 SCM meta = get_property ("meta");
570 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
571 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
572 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
576 Grob::programming_error (string s) const
578 if (get_program_option ("warning-as-error"))
581 SCM cause = self_scm ();
582 while (Grob *g = unsmob_grob (cause))
583 cause = g->get_property ("cause");
585 s = _f ("programming error: %s", s);
587 /* ES TODO: cause can't be Music*/
588 if (Music *m = unsmob_music (cause))
589 m->origin ()->message (s);
590 else if (Stream_event *ev = unsmob_stream_event (cause))
591 ev->origin ()->message (s);
598 "A grob represents a piece of music notation.\n"
600 "All grobs have an X and Y@tie{}position on the page. These"
601 " X and Y@tie{}positions are stored in a relative format, thus"
602 " they can easily be combined by stacking them, hanging one"
603 " grob to the side of another, or coupling them into grouping"
606 "Each grob has a reference point (a.k.a.@: parent): The"
607 " position of a grob is stored relative to that reference"
608 " point. For example, the X@tie{}reference point of a staccato"
609 " dot usually is the note head that it applies to. When the"
610 " note head is moved, the staccato dot moves along"
613 "A grob is often associated with a symbol, but some grobs do"
614 " not print any symbols. They take care of grouping objects."
615 " For example, there is a separate grob that stacks staves"
616 " vertically. The @ref{NoteCollision} object is also an"
617 " abstract grob: It only moves around chords, but doesn't print"
620 "Grobs have properties (Scheme variables) that can be read and"
621 " set. Two types of them exist: immutable and mutable."
622 " Immutable variables define the default style and behavior."
623 " They are shared between many objects. They can be changed"
624 " using @code{\\override} and @code{\\revert}. Mutable"
625 " properties are variables that are specific to one grob."
626 " Typically, lists of other objects, or results from"
627 " computations are stored in mutable properties. In"
628 " particular, every call to @code{ly:grob-set-property!}"
629 " (or its C++ equivalent) sets a mutable property.\n"
631 "The properties @code{after-line-breaking} and"
632 " @code{before-line-breaking} are dummies that are not"
633 " user-serviceable.",
640 "after-line-breaking "
642 "axis-group-parent-X "
643 "axis-group-parent-Y "
644 "before-line-breaking "
656 "outside-staff-horizontal-padding "
657 "outside-staff-padding "
658 "outside-staff-priority "
659 "pure-Y-offset-in-progress "
667 /****************************************************************
669 ****************************************************************/
672 grob_stencil_extent (Grob *me, Axis a)
674 Stencil *m = me->get_stencil ();
678 return ly_interval2scm (e);
682 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
684 Grob::stencil_height (SCM smob)
686 Grob *me = unsmob_grob (smob);
687 return grob_stencil_extent (me, Y_AXIS);
690 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
692 Grob::y_parent_positioning (SCM smob)
694 Grob *me = unsmob_grob (smob);
695 Grob *par = me->get_parent (Y_AXIS);
697 (void) par->get_property ("positioning-done");
699 return scm_from_double (0.0);
703 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
705 Grob::x_parent_positioning (SCM smob)
707 Grob *me = unsmob_grob (smob);
709 Grob *par = me->get_parent (X_AXIS);
711 (void) par->get_property ("positioning-done");
713 return scm_from_double (0.0);
716 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
718 Grob::stencil_width (SCM smob)
720 Grob *me = unsmob_grob (smob);
721 return grob_stencil_extent (me, X_AXIS);
726 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
728 for (; scm_is_pair (elist); elist = scm_cdr (elist))
729 if (Grob *s = unsmob_grob (scm_car (elist)))
732 common = common->common_refpoint (s, a);
741 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
743 for (vsize i = 0; i < arr.size (); i++)
745 common = common->common_refpoint (arr[i], a);
753 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
755 Interval ext = me->extent (refpoint, a);
757 ext.add_point (me->relative_coordinate (refpoint, a));
762 // Checks whether there is a vertical alignment in the chain of
763 // parents between this and commony.
765 Grob::check_cross_staff (Grob *commony)
767 if (Align_interface::has_interface (commony))
770 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
771 if (Align_interface::has_interface (g))