2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
13 #include "align-interface.hh"
14 #include "axis-group-interface.hh"
16 #include "international.hh"
21 #include "output-def.hh"
22 #include "pointer-group-interface.hh"
23 #include "program-option.hh"
25 #include "stream-event.hh"
29 #include "ly-smobs.icc"
34 return new Grob (*this);
37 Grob::Grob (SCM basicprops
)
40 /* FIXME: default should be no callback. */
44 interfaces_
= SCM_EOL
;
45 immutable_property_alist_
= basicprops
;
46 mutable_property_alist_
= SCM_EOL
;
47 object_alist_
= SCM_EOL
;
49 /* We do smobify_self () as the first step. Since the object lives
50 on the heap, none of its SCM variables are protected from
51 GC. After smobify_self (), they are. */
54 SCM meta
= get_property ("meta");
55 if (scm_is_pair (meta
))
57 interfaces_
= scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta
));
59 SCM object_cbs
= scm_assq (ly_symbol2scm ("object-callbacks"), meta
);
60 if (scm_is_pair (object_cbs
))
62 for (SCM s
= scm_cdr (object_cbs
); scm_is_pair (s
); s
= scm_cdr (s
))
63 set_object (scm_caar (s
), scm_cdar (s
));
67 if (get_property_data ("X-extent") == SCM_EOL
)
68 set_property ("X-extent", Grob::stencil_width_proc
);
69 if (get_property_data ("Y-extent") == SCM_EOL
)
70 set_property ("Y-extent", Grob::stencil_height_proc
);
73 Grob::Grob (Grob
const &s
)
74 : dim_cache_ (s
.dim_cache_
)
76 original_
= (Grob
*) & s
;
79 immutable_property_alist_
= s
.immutable_property_alist_
;
80 mutable_property_alist_
= ly_deep_copy (s
.mutable_property_alist_
);
81 interfaces_
= s
.interfaces_
;
82 object_alist_
= SCM_EOL
;
92 /****************************************************************
94 ****************************************************************/
97 Grob::get_stencil () const
102 SCM stil
= get_property ("stencil");
103 return unsmob_stencil (stil
);
107 Grob::get_print_stencil () const
109 SCM stil
= get_property ("stencil");
112 if (Stencil
*m
= unsmob_stencil (stil
))
115 if (to_boolean (get_property ("transparent")))
116 retval
= Stencil (m
->extent_box (), SCM_EOL
);
119 SCM expr
= m
->expr ();
120 expr
= scm_list_3 (ly_symbol2scm ("grob-cause"),
123 retval
= Stencil (m
->extent_box (), expr
);
126 SCM rot
= get_property ("rotation");
127 if (scm_is_pair (rot
))
129 Real angle
= scm_to_double (scm_car (rot
));
130 Real x
= scm_to_double (scm_cadr (rot
));
131 Real y
= scm_to_double (scm_caddr (rot
));
133 retval
.rotate_degrees (angle
, Offset (x
, y
));
136 /* color support... see interpret_stencil_expression () for more... */
137 SCM color
= get_property ("color");
138 if (scm_is_pair (color
))
140 SCM expr
= scm_list_3 (ly_symbol2scm ("color"),
144 retval
= Stencil (retval
.extent_box (), expr
);
152 /****************************************************************
154 ****************************************************************/
156 Grob::do_break_processing ()
161 Grob::discretionary_processing ()
166 Grob::get_system () const
173 Grob::handle_broken_dependencies ()
175 Spanner
*sp
= dynamic_cast<Spanner
*> (this);
176 if (original () && sp
)
180 /* THIS, SP is the original spanner. We use a special function
181 because some Spanners have enormously long lists in their
182 properties, and a special function fixes FOO */
184 for (SCM s
= object_alist_
; scm_is_pair (s
); s
= scm_cdr (s
))
185 sp
->substitute_one_mutable_property (scm_caar (s
), scm_cdar (s
));
187 System
*system
= get_system ();
191 && common_refpoint (system
, X_AXIS
)
192 && common_refpoint (system
, Y_AXIS
))
193 substitute_object_links (system
->self_scm (), object_alist_
);
194 else if (dynamic_cast<System
*> (this))
195 substitute_object_links (SCM_UNDEFINED
, object_alist_
);
197 /* THIS element is `invalid'; it has been removed from all
198 dependencies, so let's junk the element itself.
200 Do not do this for System, since that would remove references
201 to the originals of score-grobs, which get then GC'd (a bad
206 /* Note that we still want references to this element to be
207 rearranged, and not silently thrown away, so we keep pointers like
208 {broken_into_{drul, array}, original}
216 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
217 dim_cache_
[a
].clear ();
219 mutable_property_alist_
= SCM_EOL
;
220 object_alist_
= SCM_EOL
;
221 immutable_property_alist_
= SCM_EOL
;
222 interfaces_
= SCM_EOL
;
226 Grob::handle_prebroken_dependencies ()
228 /* Don't do this in the derived method, since we want to keep access to
229 object_alist_ centralized. */
232 Item
*it
= dynamic_cast<Item
*> (this);
233 substitute_object_links (scm_from_int (it
->break_status_dir ()),
234 original ()->object_alist_
);
239 Grob::find_broken_piece (System
*) const
244 /****************************************************************
246 ****************************************************************/
249 Grob::translate_axis (Real y
, Axis a
)
251 if (isinf (y
) || isnan (y
))
253 programming_error (_ ("Infinity or NaN encountered"));
257 if (!dim_cache_
[a
].offset_
)
258 dim_cache_
[a
].offset_
= new Real (y
);
260 *dim_cache_
[a
].offset_
+= y
;
263 /* Find the offset relative to D. If D equals THIS, then it is 0.
264 Otherwise, it recursively defd as
266 OFFSET_ + PARENT_L_->relative_coordinate (D) */
268 Grob::relative_coordinate (Grob
const *refp
, Axis a
) const
270 /* eaa - hmmm, should we do a programming_error() here? */
271 if ((this == NULL
) || (refp
== this))
274 /* We catch PARENT_L_ == nil case with this, but we crash if we did
275 not ask for the absolute coordinate (ie. REFP == nil.) */
276 Real off
= get_offset (a
);
277 if (refp
== dim_cache_
[a
].parent_
)
280 off
+= dim_cache_
[a
].parent_
->relative_coordinate (refp
, a
);
286 Grob::pure_relative_y_coordinate (Grob
const *refp
, int start
, int end
)
293 if (dim_cache_
[Y_AXIS
].offset_
)
295 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
296 programming_error ("cyclic chain in pure-Y-offset callbacks");
298 off
= *dim_cache_
[Y_AXIS
].offset_
;
302 SCM proc
= get_property_data ("Y-offset");
304 dim_cache_
[Y_AXIS
].offset_
= new Real (0.0);
305 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T
);
306 off
= robust_scm2double (call_pure_function (proc
,
307 scm_list_1 (self_scm ()),
310 del_property ("pure-Y-offset-in-progress");
311 delete dim_cache_
[Y_AXIS
].offset_
;
312 dim_cache_
[Y_AXIS
].offset_
= 0;
315 /* we simulate positioning-done if we are the child of a VerticalAlignment,
316 but only if we don't have a cached offset. If we do have a cached offset,
317 it probably means that the Alignment was fixed and it has already been
320 if (Grob
*p
= get_parent (Y_AXIS
))
323 if (Align_interface::has_interface (p
) && !dim_cache_
[Y_AXIS
].offset_
)
324 trans
= Align_interface::get_pure_child_y_translation (p
, this, start
, end
);
326 return off
+ trans
+ p
->pure_relative_y_coordinate (refp
, start
, end
);
331 /* Invoke callbacks to get offset relative to parent. */
333 Grob::get_offset (Axis a
) const
335 if (dim_cache_
[a
].offset_
)
336 return *dim_cache_
[a
].offset_
;
338 Grob
*me
= (Grob
*) this;
340 SCM sym
= axis_offset_symbol (a
);
341 me
->dim_cache_
[a
].offset_
= new Real (0.0);
344 UGH: can't fold next 2 statements together. Apparently GCC thinks
345 dim_cache_[a].offset_ is unaliased.
347 Real off
= robust_scm2double (internal_get_property (sym
), 0.0);
348 if (me
->dim_cache_
[a
].offset_
)
350 *me
->dim_cache_
[a
].offset_
+= off
;
351 me
->del_property (sym
);
352 return *me
->dim_cache_
[a
].offset_
;
359 Grob::maybe_pure_coordinate (Grob
const *refp
, Axis a
, bool pure
, int start
, int end
)
361 if (pure
&& a
!= Y_AXIS
)
362 programming_error ("tried to get pure X-offset");
363 return (pure
&& a
== Y_AXIS
) ? pure_relative_y_coordinate (refp
, start
, end
)
364 : relative_coordinate (refp
, a
);
367 /****************************************************************
369 ****************************************************************/
372 Grob::flush_extent_cache (Axis axis
)
374 if (dim_cache_
[axis
].extent_
)
377 Ugh, this is not accurate; will flush property, causing
378 callback to be called if.
380 del_property ((axis
== X_AXIS
) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
381 delete dim_cache_
[axis
].extent_
;
382 dim_cache_
[axis
].extent_
= 0;
383 if (get_parent (axis
))
384 get_parent (axis
)->flush_extent_cache (axis
);
390 Grob::extent (Grob
*refp
, Axis a
) const
392 Real offset
= relative_coordinate (refp
, a
);
394 if (dim_cache_
[a
].extent_
)
396 real_ext
= *dim_cache_
[a
].extent_
;
401 Order is significant: ?-extent may trigger suicide.
405 ? ly_symbol2scm ("X-extent")
406 : ly_symbol2scm ("Y-extent");
408 SCM ext
= internal_get_property (ext_sym
);
409 if (is_number_pair (ext
))
410 real_ext
.unite (ly_scm2interval (ext
));
414 ? ly_symbol2scm ("minimum-X-extent")
415 : ly_symbol2scm ("minimum-Y-extent");
416 SCM min_ext
= internal_get_property (min_ext_sym
);
417 if (is_number_pair (min_ext
))
418 real_ext
.unite (ly_scm2interval (min_ext
));
420 ((Grob
*)this)->dim_cache_
[a
].extent_
= new Interval (real_ext
);
423 real_ext
.translate (offset
);
429 Grob::pure_height (Grob
*refp
, int start
, int end
)
431 SCM proc
= get_property_data (ly_symbol2scm ("Y-extent"));
432 SCM iv_scm
= call_pure_function (proc
,
433 scm_list_1 (self_scm ()),
435 Interval iv
= robust_scm2interval (iv_scm
, Interval (0, 0));
436 Real offset
= pure_relative_y_coordinate (refp
, start
, end
);
438 SCM min_ext
= get_property ("minimum-Y-extent");
440 /* we don't add minimum-Y-extent if the extent is empty. This solves
441 a problem with Hara-kiri spanners. They would request_suicide and
442 return empty extents, but we would force them here to be large. */
443 if (!iv
.is_empty () && is_number_pair (min_ext
))
444 iv
.unite (ly_scm2interval (min_ext
));
447 iv
.translate (offset
);
452 Grob::maybe_pure_extent (Grob
*refp
, Axis a
, bool pure
, int start
, int end
)
454 if (pure
&& a
!= Y_AXIS
)
455 programming_error ("tried to get pure width");
456 return (pure
&& a
== Y_AXIS
) ? pure_height (refp
, start
, end
) : extent (refp
, a
);
460 Grob::spanned_rank_interval () const
462 return Interval_t
<int> (-1, 0);
465 /****************************************************************
467 ****************************************************************/
469 /* Find the group-element which has both #this# and #s# */
471 Grob::common_refpoint (Grob
const *s
, Axis a
) const
473 /* I don't like the quadratic aspect of this code, but I see no
474 other way. The largest chain of parents might be 10 high or so,
475 so it shouldn't be a real issue. */
476 for (Grob
const *c
= this; c
; c
= c
->dim_cache_
[a
].parent_
)
477 for (Grob
const *d
= s
; d
; d
= d
->dim_cache_
[a
].parent_
)
485 Grob::set_parent (Grob
*g
, Axis a
)
487 dim_cache_
[a
].parent_
= g
;
491 Grob::get_parent (Axis a
) const
493 return dim_cache_
[a
].parent_
;
498 Grob::fixup_refpoint ()
500 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
503 Grob
*parent
= get_parent (ax
);
508 if (parent
->get_system () != get_system () && get_system ())
510 Grob
*newparent
= parent
->find_broken_piece (get_system ());
511 set_parent (newparent
, ax
);
514 if (Item
*i
= dynamic_cast<Item
*> (this))
516 Item
*parenti
= dynamic_cast<Item
*> (parent
);
520 Direction my_dir
= i
->break_status_dir ();
521 if (my_dir
!= parenti
->break_status_dir ())
523 Item
*newparent
= parenti
->find_prebroken_piece (my_dir
);
524 set_parent (newparent
, ax
);
532 /****************************************************************
534 ****************************************************************/
536 Grob::warning (string s
) const
538 if (get_program_option ("warning-as-error"))
541 SCM cause
= self_scm ();
542 while (Grob
*g
= unsmob_grob (cause
))
543 cause
= g
->get_property ("cause");
545 /* ES TODO: cause can't be Music*/
546 if (Music
*m
= unsmob_music (cause
))
547 m
->origin ()->warning (s
);
548 else if (Stream_event
*ev
= unsmob_stream_event (cause
))
549 ev
->origin ()->warning (s
);
558 SCM meta
= get_property ("meta");
559 SCM nm
= scm_assq (ly_symbol2scm ("name"), meta
);
560 nm
= (scm_is_pair (nm
)) ? scm_cdr (nm
) : SCM_EOL
;
561 return scm_is_symbol (nm
) ? ly_symbol2string (nm
) : this->class_name ();
565 Grob::programming_error (string s
) const
567 if (get_program_option ("warning-as-error"))
570 SCM cause
= self_scm ();
571 while (Grob
*g
= unsmob_grob (cause
))
572 cause
= g
->get_property ("cause");
574 s
= _f ("programming error: %s", s
);
576 /* ES TODO: cause can't be Music*/
577 if (Music
*m
= unsmob_music (cause
))
578 m
->origin ()->message (s
);
579 else if (Stream_event
*ev
= unsmob_stream_event (cause
))
580 ev
->origin ()->message (s
);
587 "A grob represents a piece of music notation.\n"
589 "All grobs have an X and Y@tie{}position on the page. These"
590 " X and Y@tie{}positions are stored in a relative format, thus"
591 " they can easily be combined by stacking them, hanging one"
592 " grob to the side of another, or coupling them into grouping"
595 "Each grob has a reference point (a.k.a.@: parent): The"
596 " position of a grob is stored relative to that reference"
597 " point. For example, the X@tie{}reference point of a staccato"
598 " dot usually is the note head that it applies to. When the"
599 " note head is moved, the staccato dot moves along"
602 "A grob is often associated with a symbol, but some grobs do"
603 " not print any symbols. They take care of grouping objects."
604 " For example, there is a separate grob that stacks staves"
605 " vertically. The @ref{NoteCollision} object is also an"
606 " abstract grob: It only moves around chords, but doesn't print"
609 "Grobs have properties (Scheme variables) that can be read and"
610 " set. Two types of them exist: immutable and mutable."
611 " Immutable variables define the default style and behavior."
612 " They are shared between many objects. They can be changed"
613 " using @code{\\override} and @code{\\revert}. Mutable"
614 " properties are variables that are specific to one grob."
615 " Typically, lists of other objects, or results from"
616 " computations are stored in mutable properties. In"
617 " particular, every call to @code{ly:grob-set-property!}"
618 " (or its C++ equivalent) sets a mutable property.\n"
620 "The properties @code{after-line-breaking} and"
621 " @code{before-line-breaking} are dummies that are not"
622 " user-serviceable.",
629 "after-line-breaking "
631 "axis-group-parent-X "
632 "axis-group-parent-Y "
633 "before-line-breaking "
645 "outside-staff-horizontal-padding "
646 "outside-staff-padding "
647 "outside-staff-priority "
648 "pure-Y-offset-in-progress "
656 /****************************************************************
658 ****************************************************************/
661 grob_stencil_extent (Grob
*me
, Axis a
)
663 Stencil
*m
= me
->get_stencil ();
667 return ly_interval2scm (e
);
671 MAKE_SCHEME_CALLBACK (Grob
, stencil_height
, 1);
673 Grob::stencil_height (SCM smob
)
675 Grob
*me
= unsmob_grob (smob
);
676 return grob_stencil_extent (me
, Y_AXIS
);
679 MAKE_SCHEME_CALLBACK (Grob
, y_parent_positioning
, 1);
681 Grob::y_parent_positioning (SCM smob
)
683 Grob
*me
= unsmob_grob (smob
);
684 Grob
*par
= me
->get_parent (Y_AXIS
);
686 (void) par
->get_property ("positioning-done");
688 return scm_from_double (0.0);
692 MAKE_SCHEME_CALLBACK (Grob
, x_parent_positioning
, 1);
694 Grob::x_parent_positioning (SCM smob
)
696 Grob
*me
= unsmob_grob (smob
);
698 Grob
*par
= me
->get_parent (X_AXIS
);
700 (void) par
->get_property ("positioning-done");
702 return scm_from_double (0.0);
705 MAKE_SCHEME_CALLBACK (Grob
, stencil_width
, 1);
707 Grob::stencil_width (SCM smob
)
709 Grob
*me
= unsmob_grob (smob
);
710 return grob_stencil_extent (me
, X_AXIS
);
715 common_refpoint_of_list (SCM elist
, Grob
*common
, Axis a
)
717 for (; scm_is_pair (elist
); elist
= scm_cdr (elist
))
718 if (Grob
*s
= unsmob_grob (scm_car (elist
)))
721 common
= common
->common_refpoint (s
, a
);
730 common_refpoint_of_array (vector
<Grob
*> const &arr
, Grob
*common
, Axis a
)
732 for (vsize i
= 0; i
< arr
.size (); i
++)
734 common
= common
->common_refpoint (arr
[i
], a
);
742 robust_relative_extent (Grob
*me
, Grob
*refpoint
, Axis a
)
744 Interval ext
= me
->extent (refpoint
, a
);
746 ext
.add_point (me
->relative_coordinate (refpoint
, a
));
751 // Checks whether there is a vertical alignment in the chain of
752 // parents between this and commony.
754 Grob::check_cross_staff (Grob
*commony
)
756 if (Align_interface::has_interface (commony
))
759 for (Grob
*g
= this; g
&& g
!= commony
; g
= g
->get_parent (Y_AXIS
))
760 if (Align_interface::has_interface (g
))