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 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);
158 /* process whiteout */
159 if (to_boolean (get_property ("whiteout")))
161 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
162 /* to add a round-filled-box stencil to the stencil list */
164 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
165 retval.smobbed_copy()));
172 /****************************************************************
174 ****************************************************************/
176 Grob::do_break_processing ()
181 Grob::discretionary_processing ()
186 Grob::get_system () const
193 Grob::handle_broken_dependencies ()
195 Spanner *sp = dynamic_cast<Spanner *> (this);
196 if (original () && sp)
200 /* THIS, SP is the original spanner. We use a special function
201 because some Spanners have enormously long lists in their
202 properties, and a special function fixes FOO */
204 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
205 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
207 System *system = get_system ();
211 && common_refpoint (system, X_AXIS)
212 && common_refpoint (system, Y_AXIS))
213 substitute_object_links (system->self_scm (), object_alist_);
214 else if (dynamic_cast<System *> (this))
215 substitute_object_links (SCM_UNDEFINED, object_alist_);
217 /* THIS element is `invalid'; it has been removed from all
218 dependencies, so let's junk the element itself.
220 Do not do this for System, since that would remove references
221 to the originals of score-grobs, which get then GC'd (a bad
226 /* Note that we still want references to this element to be
227 rearranged, and not silently thrown away, so we keep pointers like
228 {broken_into_{drul, array}, original}
236 for (int a = X_AXIS; a < NO_AXES; a++)
237 dim_cache_[a].clear ();
239 mutable_property_alist_ = SCM_EOL;
240 object_alist_ = SCM_EOL;
241 immutable_property_alist_ = SCM_EOL;
242 interfaces_ = SCM_EOL;
246 Grob::handle_prebroken_dependencies ()
248 /* Don't do this in the derived method, since we want to keep access to
249 object_alist_ centralized. */
252 Item *it = dynamic_cast<Item *> (this);
253 substitute_object_links (scm_from_int (it->break_status_dir ()),
254 original ()->object_alist_);
259 Grob::find_broken_piece (System *) const
264 /****************************************************************
266 ****************************************************************/
269 Grob::translate_axis (Real y, Axis a)
271 if (isinf (y) || isnan (y))
273 programming_error (_ ("Infinity or NaN encountered"));
277 if (!dim_cache_[a].offset_)
278 dim_cache_[a].offset_ = new Real (y);
280 *dim_cache_[a].offset_ += y;
283 /* Find the offset relative to D. If D equals THIS, then it is 0.
284 Otherwise, it recursively defd as
286 OFFSET_ + PARENT_L_->relative_coordinate (D) */
288 Grob::relative_coordinate (Grob const *refp, Axis a) const
290 /* eaa - hmmm, should we do a programming_error() here? */
291 if ((this == NULL) || (refp == this))
294 /* We catch PARENT_L_ == nil case with this, but we crash if we did
295 not ask for the absolute coordinate (ie. REFP == nil.) */
296 Real off = get_offset (a);
297 if (refp == dim_cache_[a].parent_)
300 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
306 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
313 if (dim_cache_[Y_AXIS].offset_)
315 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
316 programming_error ("cyclic chain in pure-Y-offset callbacks");
318 off = *dim_cache_[Y_AXIS].offset_;
322 SCM proc = get_property_data ("Y-offset");
324 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
325 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
326 off = robust_scm2double (call_pure_function (proc,
327 scm_list_1 (self_scm ()),
330 del_property ("pure-Y-offset-in-progress");
331 delete dim_cache_[Y_AXIS].offset_;
332 dim_cache_[Y_AXIS].offset_ = 0;
335 /* we simulate positioning-done if we are the child of a VerticalAlignment,
336 but only if we don't have a cached offset. If we do have a cached offset,
337 it probably means that the Alignment was fixed and it has already been
340 if (Grob *p = get_parent (Y_AXIS))
343 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
344 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
346 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
351 /* Invoke callbacks to get offset relative to parent. */
353 Grob::get_offset (Axis a) const
355 if (dim_cache_[a].offset_)
356 return *dim_cache_[a].offset_;
358 Grob *me = (Grob *) this;
360 SCM sym = axis_offset_symbol (a);
361 me->dim_cache_[a].offset_ = new Real (0.0);
364 UGH: can't fold next 2 statements together. Apparently GCC thinks
365 dim_cache_[a].offset_ is unaliased.
367 Real off = robust_scm2double (internal_get_property (sym), 0.0);
368 if (me->dim_cache_[a].offset_)
370 *me->dim_cache_[a].offset_ += off;
371 me->del_property (sym);
372 return *me->dim_cache_[a].offset_;
379 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
381 if (pure && a != Y_AXIS)
382 programming_error ("tried to get pure X-offset");
383 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
384 : relative_coordinate (refp, a);
387 /****************************************************************
389 ****************************************************************/
392 Grob::flush_extent_cache (Axis axis)
394 if (dim_cache_[axis].extent_)
397 Ugh, this is not accurate; will flush property, causing
398 callback to be called if.
400 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
401 delete dim_cache_[axis].extent_;
402 dim_cache_[axis].extent_ = 0;
403 if (get_parent (axis))
404 get_parent (axis)->flush_extent_cache (axis);
410 Grob::extent (Grob *refp, Axis a) const
412 Real offset = relative_coordinate (refp, a);
414 if (dim_cache_[a].extent_)
416 real_ext = *dim_cache_[a].extent_;
421 Order is significant: ?-extent may trigger suicide.
425 ? ly_symbol2scm ("X-extent")
426 : ly_symbol2scm ("Y-extent");
428 SCM ext = internal_get_property (ext_sym);
429 if (is_number_pair (ext))
430 real_ext.unite (ly_scm2interval (ext));
434 ? ly_symbol2scm ("minimum-X-extent")
435 : ly_symbol2scm ("minimum-Y-extent");
436 SCM min_ext = internal_get_property (min_ext_sym);
437 if (is_number_pair (min_ext))
438 real_ext.unite (ly_scm2interval (min_ext));
440 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
443 real_ext.translate (offset);
449 Grob::pure_height (Grob *refp, int start, int end)
451 SCM iv_scm = get_pure_property ("Y-extent", start, end);
452 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
453 Real offset = pure_relative_y_coordinate (refp, start, end);
455 SCM min_ext = get_property ("minimum-Y-extent");
457 /* we don't add minimum-Y-extent if the extent is empty. This solves
458 a problem with Hara-kiri spanners. They would request_suicide and
459 return empty extents, but we would force them here to be large. */
460 if (!iv.is_empty () && is_number_pair (min_ext))
461 iv.unite (ly_scm2interval (min_ext));
464 iv.translate (offset);
469 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
471 if (pure && a != Y_AXIS)
472 programming_error ("tried to get pure width");
473 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
477 Grob::spanned_rank_interval () const
479 return Interval_t<int> (-1, 0);
482 /****************************************************************
484 ****************************************************************/
486 /* Find the group-element which has both #this# and #s# */
488 Grob::common_refpoint (Grob const *s, Axis a) const
490 /* I don't like the quadratic aspect of this code, but I see no
491 other way. The largest chain of parents might be 10 high or so,
492 so it shouldn't be a real issue. */
493 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
494 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
502 Grob::set_parent (Grob *g, Axis a)
504 dim_cache_[a].parent_ = g;
508 Grob::get_parent (Axis a) const
510 return dim_cache_[a].parent_;
515 Grob::fixup_refpoint ()
517 for (int a = X_AXIS; a < NO_AXES; a++)
520 Grob *parent = get_parent (ax);
525 if (parent->get_system () != get_system () && get_system ())
527 Grob *newparent = parent->find_broken_piece (get_system ());
528 set_parent (newparent, ax);
531 if (Item *i = dynamic_cast<Item *> (this))
533 Item *parenti = dynamic_cast<Item *> (parent);
537 Direction my_dir = i->break_status_dir ();
538 if (my_dir != parenti->break_status_dir ())
540 Item *newparent = parenti->find_prebroken_piece (my_dir);
541 set_parent (newparent, ax);
549 /****************************************************************
551 ****************************************************************/
553 Grob::warning (string s) const
555 if (get_program_option ("warning-as-error"))
558 SCM cause = self_scm ();
559 while (Grob *g = unsmob_grob (cause))
560 cause = g->get_property ("cause");
562 /* ES TODO: cause can't be Music*/
563 if (Music *m = unsmob_music (cause))
564 m->origin ()->warning (s);
565 else if (Stream_event *ev = unsmob_stream_event (cause))
566 ev->origin ()->warning (s);
575 SCM meta = get_property ("meta");
576 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
577 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
578 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
582 Grob::programming_error (string s) const
584 if (get_program_option ("warning-as-error"))
587 SCM cause = self_scm ();
588 while (Grob *g = unsmob_grob (cause))
589 cause = g->get_property ("cause");
591 s = _f ("programming error: %s", s);
593 /* ES TODO: cause can't be Music*/
594 if (Music *m = unsmob_music (cause))
595 m->origin ()->message (s);
596 else if (Stream_event *ev = unsmob_stream_event (cause))
597 ev->origin ()->message (s);
604 "A grob represents a piece of music notation.\n"
606 "All grobs have an X and Y@tie{}position on the page. These"
607 " X and Y@tie{}positions are stored in a relative format, thus"
608 " they can easily be combined by stacking them, hanging one"
609 " grob to the side of another, or coupling them into grouping"
612 "Each grob has a reference point (a.k.a.@: parent): The"
613 " position of a grob is stored relative to that reference"
614 " point. For example, the X@tie{}reference point of a staccato"
615 " dot usually is the note head that it applies to. When the"
616 " note head is moved, the staccato dot moves along"
619 "A grob is often associated with a symbol, but some grobs do"
620 " not print any symbols. They take care of grouping objects."
621 " For example, there is a separate grob that stacks staves"
622 " vertically. The @ref{NoteCollision} object is also an"
623 " abstract grob: It only moves around chords, but doesn't print"
626 "Grobs have properties (Scheme variables) that can be read and"
627 " set. Two types of them exist: immutable and mutable."
628 " Immutable variables define the default style and behavior."
629 " They are shared between many objects. They can be changed"
630 " using @code{\\override} and @code{\\revert}. Mutable"
631 " properties are variables that are specific to one grob."
632 " Typically, lists of other objects, or results from"
633 " computations are stored in mutable properties. In"
634 " particular, every call to @code{ly:grob-set-property!}"
635 " (or its C++ equivalent) sets a mutable property.\n"
637 "The properties @code{after-line-breaking} and"
638 " @code{before-line-breaking} are dummies that are not"
639 " user-serviceable.",
646 "after-line-breaking "
648 "axis-group-parent-X "
649 "axis-group-parent-Y "
650 "before-line-breaking "
662 "outside-staff-horizontal-padding "
663 "outside-staff-padding "
664 "outside-staff-priority "
665 "pure-Y-offset-in-progress "
674 /****************************************************************
676 ****************************************************************/
679 grob_stencil_extent (Grob *me, Axis a)
681 Stencil *m = me->get_stencil ();
685 return ly_interval2scm (e);
689 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
691 Grob::stencil_height (SCM smob)
693 Grob *me = unsmob_grob (smob);
694 return grob_stencil_extent (me, Y_AXIS);
697 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
699 Grob::y_parent_positioning (SCM smob)
701 Grob *me = unsmob_grob (smob);
702 Grob *par = me->get_parent (Y_AXIS);
704 (void) par->get_property ("positioning-done");
706 return scm_from_double (0.0);
710 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
712 Grob::x_parent_positioning (SCM smob)
714 Grob *me = unsmob_grob (smob);
716 Grob *par = me->get_parent (X_AXIS);
718 (void) par->get_property ("positioning-done");
720 return scm_from_double (0.0);
723 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
725 Grob::stencil_width (SCM smob)
727 Grob *me = unsmob_grob (smob);
728 return grob_stencil_extent (me, X_AXIS);
733 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
735 for (; scm_is_pair (elist); elist = scm_cdr (elist))
736 if (Grob *s = unsmob_grob (scm_car (elist)))
739 common = common->common_refpoint (s, a);
748 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
750 for (vsize i = 0; i < arr.size (); i++)
752 common = common->common_refpoint (arr[i], a);
760 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
762 Interval ext = me->extent (refpoint, a);
764 ext.add_point (me->relative_coordinate (refpoint, a));
769 // Checks whether there is a vertical alignment in the chain of
770 // parents between this and commony.
772 Grob::check_cross_staff (Grob *commony)
774 if (Align_interface::has_interface (commony))
777 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
778 if (Align_interface::has_interface (g))