2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
14 #include "input-smob.hh"
16 #include "pointer-group-interface.hh"
18 #include "paper-score.hh"
27 #include "paper-score.hh"
28 #include "ly-smobs.icc"
29 #include "output-def.hh"
36 Grob::clone (int count
) const
38 return new Grob (*this, count
);
41 Grob::Grob (SCM basicprops
,
42 Object_key
const *key
)
45 /* FIXME: default should be no callback. */
49 interfaces_
= SCM_EOL
;
50 immutable_property_alist_
= basicprops
;
51 mutable_property_alist_
= SCM_EOL
;
52 object_alist_
= SCM_EOL
;
54 /* We do smobify_self () as the first step. Since the object lives
55 on the heap, none of its SCM variables are protected from
56 GC. After smobify_self (), they are. */
60 We always get a new key object for a new grob.
63 ((Object_key
*)key_
)->unprotect ();
65 SCM meta
= get_property ("meta");
66 if (scm_is_pair (meta
))
67 interfaces_
= scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta
));
69 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL
)
70 set_property ("X-extent", Grob::stencil_width_proc
);
71 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL
)
72 set_property ("Y-extent", Grob::stencil_height_proc
);
75 Grob::Grob (Grob
const &s
, int copy_index
)
76 : dim_cache_ (s
.dim_cache_
)
78 key_
= (use_object_keys
) ? new Copied_key (s
.key_
, copy_index
) : 0;
79 original_
= (Grob
*) & s
;
82 immutable_property_alist_
= s
.immutable_property_alist_
;
83 mutable_property_alist_
= ly_deep_copy (s
.mutable_property_alist_
);
84 interfaces_
= s
.interfaces_
;
85 object_alist_
= SCM_EOL
;
91 ((Object_key
*)key_
)->unprotect ();
97 /****************************************************************
99 ****************************************************************/
102 Grob::get_stencil () const
107 SCM stil
= get_property ("stencil");
108 return unsmob_stencil (stil
);
112 Grob::get_print_stencil () const
114 SCM stil
= get_property ("stencil");
117 if (Stencil
*m
= unsmob_stencil (stil
))
120 if (to_boolean (get_property ("transparent")))
121 retval
= Stencil (m
->extent_box (), SCM_EOL
);
124 SCM expr
= m
->expr ();
125 if (point_and_click_global
)
126 expr
= scm_list_3 (ly_symbol2scm ("grob-cause"),
129 retval
= Stencil (m
->extent_box (), expr
);
132 /* color support... see interpret_stencil_expression () for more... */
133 SCM color
= get_property ("color");
134 if (color
!= SCM_EOL
)
136 m
= unsmob_stencil (stil
);
137 SCM expr
= scm_list_3 (ly_symbol2scm ("color"),
141 retval
= Stencil (m
->extent_box (), expr
);
149 /****************************************************************
151 ****************************************************************/
153 Grob::do_break_processing ()
158 Grob::discretionary_processing ()
163 Grob::get_system () const
170 Grob::handle_broken_dependencies ()
172 Spanner
*sp
= dynamic_cast<Spanner
*> (this);
173 if (original () && sp
)
177 /* THIS, SP is the original spanner. We use a special function
178 because some Spanners have enormously long lists in their
179 properties, and a special function fixes FOO */
181 for (SCM s
= object_alist_
; scm_is_pair (s
); s
= scm_cdr (s
))
182 sp
->substitute_one_mutable_property (scm_caar (s
), scm_cdar (s
));
184 System
*system
= get_system ();
188 && common_refpoint (system
, X_AXIS
)
189 && common_refpoint (system
, Y_AXIS
))
190 substitute_object_links (system
->self_scm (), object_alist_
);
191 else if (dynamic_cast<System
*> (this))
192 substitute_object_links (SCM_UNDEFINED
, object_alist_
);
194 /* THIS element is `invalid'; it has been removed from all
195 dependencies, so let's junk the element itself.
197 Do not do this for System, since that would remove references
198 to the originals of score-grobs, which get then GC'd (a bad
203 /* Note that we still want references to this element to be
204 rearranged, and not silently thrown away, so we keep pointers like
205 {broken_into_{drul, array}, original}
213 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
214 dim_cache_
[a
].clear ();
216 mutable_property_alist_
= SCM_EOL
;
217 object_alist_
= SCM_EOL
;
218 immutable_property_alist_
= SCM_EOL
;
219 interfaces_
= SCM_EOL
;
223 Grob::handle_prebroken_dependencies ()
225 /* Don't do this in the derived method, since we want to keep access to
226 object_alist_ centralized. */
229 Item
*it
= dynamic_cast<Item
*> (this);
230 substitute_object_links (scm_from_int (it
->break_status_dir ()),
231 original ()->object_alist_
);
236 Grob::find_broken_piece (System
*) const
241 /****************************************************************
243 ****************************************************************/
246 Grob::translate_axis (Real y
, Axis a
)
248 if (isinf (y
) || isnan (y
))
250 programming_error (_ ("Infinity or NaN encountered"));
254 if (!dim_cache_
[a
].offset_
)
255 dim_cache_
[a
].offset_
= new Real (y
);
257 *dim_cache_
[a
].offset_
+= y
;
260 /* Find the offset relative to D. If D equals THIS, then it is 0.
261 Otherwise, it recursively defd as
263 OFFSET_ + PARENT_L_->relative_coordinate (D) */
265 Grob::relative_coordinate (Grob
const *refp
, Axis a
) const
270 /* We catch PARENT_L_ == nil case with this, but we crash if we did
271 not ask for the absolute coordinate (ie. REFP == nil.) */
272 Real off
= get_offset (a
);
273 if (refp
== dim_cache_
[a
].parent_
)
276 off
+= dim_cache_
[a
].parent_
->relative_coordinate (refp
, a
);
281 /* Invoke callbacks to get offset relative to parent. */
283 Grob::get_offset (Axis a
) const
285 if (dim_cache_
[a
].offset_
)
286 return *dim_cache_
[a
].offset_
;
288 Grob
*me
= (Grob
*) this;
290 SCM sym
= axis_offset_symbol (a
);
291 me
->dim_cache_
[a
].offset_
= new Real (0.0);
294 UGH: can't fold next 2 statements together. Apparently GCC thinks
295 dim_cache_[a].offset_ is unaliased.
297 Real off
= robust_scm2double (internal_get_property (sym
), 0.0);
298 if (me
->dim_cache_
[a
].offset_
)
300 *me
->dim_cache_
[a
].offset_
+= off
;
301 me
->del_property (sym
);
302 return *me
->dim_cache_
[a
].offset_
;
309 /****************************************************************
311 ****************************************************************/
314 Grob::flush_extent_cache (Axis axis
)
316 if (dim_cache_
[axis
].extent_
)
319 Ugh, this is not accurate; will flush property, causing
320 callback to be called if.
322 del_property ((axis
== X_AXIS
) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
323 delete dim_cache_
[axis
].extent_
;
324 dim_cache_
[axis
].extent_
= 0;
325 if (get_parent (axis
))
326 get_parent (axis
)->flush_extent_cache (axis
);
332 Grob::extent (Grob
*refp
, Axis a
) const
334 Real offset
= relative_coordinate (refp
, a
);
336 if (dim_cache_
[a
].extent_
)
338 real_ext
= *dim_cache_
[a
].extent_
;
343 Order is significant: ?-extent may trigger suicide.
347 ? ly_symbol2scm ("X-extent")
348 : ly_symbol2scm ("Y-extent");
350 SCM ext
= internal_get_property (ext_sym
);
351 if (is_number_pair (ext
))
352 real_ext
.unite (ly_scm2interval (ext
));
356 ? ly_symbol2scm ("minimum-X-extent")
357 : ly_symbol2scm ("minimum-Y-extent");
358 SCM min_ext
= internal_get_property (min_ext_sym
);
359 if (is_number_pair (min_ext
))
360 real_ext
.unite (ly_scm2interval (min_ext
));
361 ((Grob
*)this)->del_property (ext_sym
);
362 ((Grob
*)this)->dim_cache_
[a
].extent_
= new Interval (real_ext
);
365 real_ext
.translate (offset
);
370 /****************************************************************
372 ****************************************************************/
374 /* Find the group-element which has both #this# and #s# */
376 Grob::common_refpoint (Grob
const *s
, Axis a
) const
378 /* I don't like the quadratic aspect of this code, but I see no
379 other way. The largest chain of parents might be 10 high or so,
380 so it shouldn't be a real issue. */
381 for (Grob
const *c
= this; c
; c
= c
->dim_cache_
[a
].parent_
)
382 for (Grob
const *d
= s
; d
; d
= d
->dim_cache_
[a
].parent_
)
390 Grob::set_parent (Grob
*g
, Axis a
)
392 dim_cache_
[a
].parent_
= g
;
396 Grob::get_parent (Axis a
) const
398 return dim_cache_
[a
].parent_
;
403 Grob::fixup_refpoint ()
405 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
408 Grob
*parent
= get_parent (ax
);
413 if (parent
->get_system () != get_system () && get_system ())
415 Grob
*newparent
= parent
->find_broken_piece (get_system ());
416 set_parent (newparent
, ax
);
419 if (Item
*i
= dynamic_cast<Item
*> (this))
421 Item
*parenti
= dynamic_cast<Item
*> (parent
);
425 Direction my_dir
= i
->break_status_dir ();
426 if (my_dir
!= parenti
->break_status_dir ())
428 Item
*newparent
= parenti
->find_prebroken_piece (my_dir
);
429 set_parent (newparent
, ax
);
437 /****************************************************************
439 ****************************************************************/
441 Grob::warning (String s
) const
443 SCM cause
= self_scm ();
444 while (Grob
*g
= unsmob_grob (cause
))
445 cause
= g
->get_property ("cause");
447 if (Music
*m
= unsmob_music (cause
))
448 m
->origin ()->warning (s
);
457 SCM meta
= get_property ("meta");
458 SCM nm
= scm_assq (ly_symbol2scm ("name"), meta
);
459 nm
= (scm_is_pair (nm
)) ? scm_cdr (nm
) : SCM_EOL
;
460 return scm_is_symbol (nm
) ? ly_symbol2string (nm
) : this->class_name ();
464 Grob::programming_error (String s
) const
466 SCM cause
= self_scm ();
467 while (Grob
*g
= unsmob_grob (cause
))
468 cause
= g
->get_property ("cause");
470 s
= _f ("programming error: %s", s
);
472 if (Music
*m
= unsmob_music (cause
))
473 m
->origin ()->message (s
);
479 ADD_INTERFACE (Grob
, "grob-interface",
480 "A grob represents a piece of music notation\n"
482 "All grobs have an X and Y-position on the page. These X and Y positions\n"
483 "are stored in a relative format, so they can easily be combined by\n"
484 "stacking them, hanging one grob to the side of another, and coupling\n"
485 "them into a grouping objects.\n"
487 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
488 "is stored relative to that reference point. For example the X-reference\n"
489 "point of a staccato dot usually is the note head that it applies\n"
490 "to. When the note head is moved, the staccato dot moves along\n"
493 "A grob is often associated with a symbol, but some grobs do not print\n"
494 "any symbols. They take care of grouping objects. For example, there is a\n"
495 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
496 "is also an abstract grob: it only moves around chords, but doesn't print\n"
499 "Grobs have a properties: Scheme variables, that can be read and set. "
500 "They have two types. Immutable variables "
501 "define the default style and behavior. They are shared between many objects. "
502 "They can be changed using @code{\\override} and @code{\\revert}. "
504 "Mutable properties are variables that are specific to one grob. Typically, "
505 "lists of other objects, or results from computations are stored in"
506 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
507 "sets a mutable property. "
509 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
510 "are dummies that are not user-serviceable. "
519 "after-line-breaking "
520 "axis-group-parent-X "
521 "axis-group-parent-Y "
522 "before-line-breaking "
543 /****************************************************************
545 ****************************************************************/
549 grob_stencil_extent (Grob
*me
, Axis a
)
551 Stencil
*m
= me
->get_stencil ();
555 return ly_interval2scm (e
);
559 MAKE_SCHEME_CALLBACK (Grob
, stencil_height
, 1);
561 Grob::stencil_height (SCM smob
)
563 Grob
*me
= unsmob_grob (smob
);
564 return grob_stencil_extent (me
, Y_AXIS
);
567 MAKE_SCHEME_CALLBACK(Grob
, y_parent_positioning
, 1);
569 Grob::y_parent_positioning (SCM smob
)
571 Grob
*me
= unsmob_grob (smob
);
572 Grob
*par
= me
->get_parent (Y_AXIS
);
574 (void) par
->get_property ("positioning-done");
576 return scm_from_double (0.0);
580 MAKE_SCHEME_CALLBACK(Grob
, x_parent_positioning
, 1);
582 Grob::x_parent_positioning (SCM smob
)
584 Grob
*me
= unsmob_grob (smob
);
586 Grob
*par
= me
->get_parent (X_AXIS
);
588 (void) par
->get_property ("positioning-done");
590 return scm_from_double (0.0);
593 MAKE_SCHEME_CALLBACK (Grob
, stencil_width
, 1);
595 Grob::stencil_width (SCM smob
)
597 Grob
*me
= unsmob_grob (smob
);
598 return grob_stencil_extent (me
, X_AXIS
);
604 common_refpoint_of_list (SCM elist
, Grob
*common
, Axis a
)
606 for (; scm_is_pair (elist
); elist
= scm_cdr (elist
))
607 if (Grob
*s
= unsmob_grob (scm_car (elist
)))
610 common
= common
->common_refpoint (s
, a
);
619 common_refpoint_of_array (Link_array
<Grob
> const &arr
, Grob
*common
, Axis a
)
621 for (int i
= arr
.size (); i
--;)
622 if (Grob
*s
= arr
[i
])
625 common
= common
->common_refpoint (s
, a
);
634 robust_relative_extent (Grob
*me
, Grob
*refpoint
, Axis a
)
636 Interval ext
= me
->extent (refpoint
, a
);
638 ext
.add_point (me
->relative_coordinate (refpoint
, a
));