2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
14 #include "input-smob.hh"
16 #include "group-interface.hh"
18 #include "paper-score.hh"
30 #include "ly-smobs.icc"
35 remove dynamic_cast<Spanner,Item> and put this code into respective
39 //#define HASHING_FOR_MUTABLE_PROPS
41 #define INFINITY_MSG "Infinity or NaN encountered"
43 Grob::Grob (SCM basicprops
)
46 fixme: default should be no callback.
52 immutable_property_alist_
= basicprops
;
53 mutable_property_alist_
= SCM_EOL
;
56 We do smobify_self () as the first step. Since the object lives on
57 the heap, none of its SCM variables are protected from GC. After
58 smobify_self (), they are.
63 #ifdef HASHING_FOR_MUTABLE_PROPS
64 mutable_property_alist_
= scm_c_make_hash_table (HASH_SIZE
);
67 SCM meta
= get_property ("meta");
70 SCM ifs
= scm_assoc (ly_symbol2scm ("interfaces"), meta
);
73 Switch off interface checks for the moment.
75 bool itc
= internal_type_checking_global_b
;
76 internal_type_checking_global_b
= false;
77 internal_set_property (ly_symbol2scm ("interfaces"), ly_cdr (ifs
));
78 internal_type_checking_global_b
= itc
;
84 destill this into a function, so we can re-init the immutable
85 properties with a new BASICPROPS value after creation. Convenient
86 eg. when using \override with StaffSymbol. */
88 char const*onames
[] = {"X-offset-callbacks", "Y-offset-callbacks"};
89 char const*xnames
[] = {"X-extent", "Y-extent"};
90 char const*enames
[] = {"X-extent-callback", "Y-extent-callback"};
92 for (int a
= X_AXIS
; a
<= Y_AXIS
; a
++)
94 SCM l
= get_property (onames
[a
]);
96 if (scm_ilength (l
) >=0)
98 dim_cache_
[a
].offset_callbacks_
= l
;
99 dim_cache_
[a
].offsets_left_
= scm_ilength (l
);
103 programming_error ("[XY]-offset-callbacks must be a list");
106 SCM cb
= get_property (enames
[a
]);
107 SCM xt
= get_property (xnames
[a
]);
110 Should change default to empty?
112 if (is_number_pair (xt
))
114 else if (cb
!= SCM_BOOL_F
115 && !ly_procedure_p (cb
) && !ly_pair_p (cb
)
116 && ly_procedure_p (get_property ("print-function")))
117 cb
= stencil_extent_proc
;
119 dim_cache_
[a
].dimension_
= cb
;
124 Grob::Grob (Grob
const&s
)
125 : dim_cache_ (s
.dim_cache_
)
127 original_
= (Grob
*) &s
;
130 immutable_property_alist_
= s
.immutable_property_alist_
;
131 mutable_property_alist_
= SCM_EOL
;
134 No properties are copied. That is the job of handle_broken_dependencies.
142 #ifdef HASHING_FOR_MUTABLE_PROPS
143 mutable_property_alist_
= scm_c_make_hash_table (HASH_SIZE
);
150 do nothing scm-ish and no unprotecting here.
155 MAKE_SCHEME_CALLBACK (Grob
,stencil_extent
,2);
157 Grob::stencil_extent (SCM element_smob
, SCM scm_axis
)
159 Grob
*s
= unsmob_grob (element_smob
);
160 Axis a
= (Axis
) ly_scm2int (scm_axis
);
162 Stencil
*m
= s
->get_stencil ();
166 return ly_interval2scm (e
);
170 Grob::get_paper () const
172 return pscore_
? pscore_
->paper_
: 0;
176 Grob::calculate_dependencies (int final
, int busy
, SCM funcname
)
178 if (status_
>= final
)
183 programming_error ("Element is busy, come back later");
189 for (SCM d
= get_property ("dependencies"); ly_pair_p (d
);
192 unsmob_grob (ly_car (d
))
193 ->calculate_dependencies (final
, busy
, funcname
);
197 SCM proc
= internal_get_property (funcname
);
198 if (ly_procedure_p (proc
))
199 scm_call_1 (proc
, this->self_scm ());
205 Grob::get_stencil () const
212 SCM mol
= get_property ("stencil");
213 if (unsmob_stencil (mol
))
214 return unsmob_stencil (mol
);
216 mol
= get_uncached_stencil ();
220 Grob
*me
= (Grob
*)this;
221 me
->set_property ("stencil", mol
);
224 return unsmob_stencil (mol
);
228 Grob::get_uncached_stencil ()const
230 SCM proc
= get_property ("print-function");
233 if (ly_procedure_p (proc
))
234 mol
= scm_apply_0 (proc
, scm_list_n (this->self_scm (), SCM_UNDEFINED
));
236 Stencil
*m
= unsmob_stencil (mol
);
238 if (unsmob_stencil (mol
))
240 SCM origin
= ly_symbol2scm ("no-origin");
242 if (store_locations_global_b
)
244 SCM cause
= get_property ("cause");
245 if (Music
*m
= unsmob_music (cause
))
247 SCM music_origin
= m
->get_property ("origin");
248 if (unsmob_input (music_origin
))
249 origin
= music_origin
;
255 mol
= Stencil (m
->extent_box (),
256 scm_list_n (origin
, m
->get_expr (), SCM_UNDEFINED
)
259 m
= unsmob_stencil (mol
);
263 transparent retains dimensions of element.
265 if (m
&& to_boolean (get_property ("transparent")))
266 mol
= Stencil (m
->extent_box (), SCM_EOL
).smobbed_copy ();
277 Grob::do_break_processing ()
282 Grob::get_system () const
288 Grob::add_dependency (Grob
*e
)
292 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e
);
295 programming_error ("Null dependency added");
300 Grob::handle_broken_dependencies ()
302 Spanner
* sp
= dynamic_cast<Spanner
*> (this);
309 This is the original spanner. We use a special function
310 because some Spanners have enormously long lists in their
313 for (SCM s
= mutable_property_alist_
; ly_pair_p (s
);
316 sp
->substitute_one_mutable_property (ly_caar (s
),
321 System
*system
= get_system ();
324 && system
&& common_refpoint (system
, X_AXIS
) && common_refpoint (system
, Y_AXIS
))
326 substitute_mutable_properties (system
? system
->self_scm () : SCM_UNDEFINED
,
327 mutable_property_alist_
);
329 else if (dynamic_cast <System
*> (this))
331 substitute_mutable_properties (SCM_UNDEFINED
, mutable_property_alist_
);
336 This element is `invalid'; it has been removed from all
337 dependencies, so let's junk the element itself.
339 do not do this for System, since that would remove references
340 to the originals of score-grobs, which get then GC'd (a bad
349 Note that we still want references to this element to be
350 rearranged, and not silently thrown away, so we keep pointers
351 like {broken_into_{drul,array}, original}
363 mutable_property_alist_
= SCM_EOL
;
364 immutable_property_alist_
= SCM_EOL
;
366 set_extent (SCM_EOL
, Y_AXIS
);
367 set_extent (SCM_EOL
, X_AXIS
);
369 for (int a
= X_AXIS
; a
<= Y_AXIS
; a
++)
371 dim_cache_
[a
].offset_callbacks_
= SCM_EOL
;
372 dim_cache_
[a
].offsets_left_
= 0;
377 This can make debugging a little easier: we can still know what
378 the object used to be. However, since all its links have been
379 broken, it's usually more convenient to set a conditional
380 breakpoint in GDB before the property lists are wiped.
382 mutable_property_alist_
= scm_acons (ly_symbol2scm ("name"),
383 scm_makfrom0str (nm
.to_str0()),
384 mutable_property_alist_
390 Grob::handle_prebroken_dependencies ()
393 Don't do this in the derived method, since we want to keep access to
394 mutable_property_alist_ centralized.
398 Item
* it
= dynamic_cast<Item
*> (this);
399 substitute_mutable_properties (scm_int2num (it
->break_status_dir ()),
400 original_
->mutable_property_alist_
);
405 Grob::find_broken_piece (System
*) const
411 translate in one direction
414 Grob::translate_axis (Real y
, Axis a
)
416 if (isinf (y
) || isnan (y
))
417 programming_error (_ (INFINITY_MSG
));
420 dim_cache_
[a
].offset_
+= y
;
426 Find the offset relative to D. If D equals THIS, then it is 0.
427 Otherwise, it recursively defd as
429 OFFSET_ + PARENT_L_->relative_coordinate (D)
432 Grob::relative_coordinate (Grob
const*refp
, Axis a
) const
438 We catch PARENT_L_ == nil case with this, but we crash if we did
439 not ask for the absolute coordinate (ie. REFP == nil.)
442 if (refp
== dim_cache_
[a
].parent_
)
443 return get_offset (a
);
445 return get_offset (a
) + dim_cache_
[a
].parent_
->relative_coordinate (refp
, a
);
451 Invoke callbacks to get offset relative to parent.
454 Grob::get_offset (Axis a
) const
456 Grob
*me
= (Grob
*) this;
457 while (dim_cache_
[a
].offsets_left_
)
459 int l
= --me
->dim_cache_
[a
].offsets_left_
;
460 SCM cb
= scm_list_ref (dim_cache_
[a
].offset_callbacks_
, scm_int2num (l
));
461 SCM retval
= scm_call_2 (cb
, self_scm (), scm_int2num (a
));
463 Real r
= ly_scm2double (retval
);
464 if (isinf (r
) || isnan (r
))
466 programming_error (INFINITY_MSG
);
469 me
->dim_cache_
[a
].offset_
+=r
;
471 return dim_cache_
[a
].offset_
;
476 Grob::is_empty (Axis a
)const
478 return ! (ly_pair_p (dim_cache_
[a
].dimension_
) ||
479 ly_procedure_p (dim_cache_
[a
].dimension_
));
483 Grob::extent (Grob
* refp
, Axis a
) const
485 Real x
= relative_coordinate (refp
, a
);
488 Dimension_cache
* d
= (Dimension_cache
*)&dim_cache_
[a
];
490 if (ly_pair_p (d
->dimension_
))
492 else if (ly_procedure_p (d
->dimension_
))
495 FIXME: add doco on types, and should typecheck maybe?
497 d
->dimension_
= scm_call_2 (d
->dimension_
, self_scm (), scm_int2num (a
));
502 if (!ly_pair_p (d
->dimension_
))
505 ext
= ly_scm2interval (d
->dimension_
);
507 SCM extra
= get_property (a
== X_AXIS
514 if (ly_pair_p (extra
))
516 ext
[BIGGER
] += ly_scm2double (ly_cdr (extra
));
517 ext
[SMALLER
] += ly_scm2double (ly_car (extra
));
520 extra
= get_property (a
== X_AXIS
522 : "minimum-Y-extent");
523 if (ly_pair_p (extra
))
525 ext
.unite (Interval (ly_scm2double (ly_car (extra
)),
526 ly_scm2double (ly_cdr (extra
))));
535 Find the group-element which has both #this# and #s#
538 Grob::common_refpoint (Grob
const* s
, Axis a
) const
541 I don't like the quadratic aspect of this code, but I see no other
542 way. The largest chain of parents might be 10 high or so, so
543 it shouldn't be a real issue. */
544 for (Grob
const *c
= this; c
; c
= c
->dim_cache_
[a
].parent_
)
545 for (Grob
const * d
= s
; d
; d
= d
->dim_cache_
[a
].parent_
)
554 common_refpoint_of_list (SCM elist
, Grob
*common
, Axis a
)
556 for (; ly_pair_p (elist
); elist
= ly_cdr (elist
))
558 Grob
* s
= unsmob_grob (ly_car (elist
));
562 common
= common
->common_refpoint (s
, a
);
573 common_refpoint_of_array (Link_array
<Grob
> const &arr
, Grob
*common
, Axis a
)
575 for (int i
= arr
.size () ; i
--; )
582 common
= common
->common_refpoint (s
, a
);
593 SCM meta
= get_property ("meta");
594 SCM nm
= scm_assoc (ly_symbol2scm ("name"), meta
);
595 nm
= (ly_pair_p (nm
)) ? ly_cdr (nm
) : SCM_EOL
;
596 return ly_symbol_p (nm
) ? ly_symbol2string (nm
) : classname (this);
600 Grob::add_offset_callback (SCM cb
, Axis a
)
602 if (!has_offset_callback (cb
, a
))
604 dim_cache_
[a
].offset_callbacks_
= scm_cons (cb
, dim_cache_
[a
].offset_callbacks_
);
605 dim_cache_
[a
].offsets_left_
++;
610 Grob::has_extent_callback (SCM cb
, Axis a
)const
612 return scm_equal_p (cb
, dim_cache_
[a
].dimension_
) == SCM_BOOL_T
;
617 Grob::has_offset_callback (SCM cb
, Axis a
)const
619 return scm_c_memq (cb
, dim_cache_
[a
].offset_callbacks_
) != SCM_BOOL_F
;
623 Grob::set_extent (SCM dc
, Axis a
)
625 dim_cache_
[a
].dimension_
=dc
;
629 Grob::set_parent (Grob
*g
, Axis a
)
631 dim_cache_
[a
].parent_
= g
;
634 MAKE_SCHEME_CALLBACK (Grob
,fixup_refpoint
,1);
636 Grob::fixup_refpoint (SCM smob
)
638 Grob
*me
= unsmob_grob (smob
);
639 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
642 Grob
* parent
= me
->get_parent (ax
);
647 if (parent
->get_system () != me
->get_system () && me
->get_system ())
649 Grob
* newparent
= parent
->find_broken_piece (me
->get_system ());
650 me
->set_parent (newparent
, ax
);
653 if (Item
* i
= dynamic_cast<Item
*> (me
))
655 Item
*parenti
= dynamic_cast<Item
*> (parent
);
659 Direction my_dir
= i
->break_status_dir () ;
660 if (my_dir
!= parenti
->break_status_dir ())
662 Item
*newparent
= parenti
->find_prebroken_piece (my_dir
);
663 me
->set_parent (newparent
, ax
);
672 Grob::warning (String s
)const
674 SCM cause
= self_scm ();
675 while (Grob
* g
= unsmob_grob (cause
))
677 cause
= g
->get_property ("cause");
680 if (Music
*m
= unsmob_music (cause
))
682 m
->origin ()->warning (s
);
689 Grob::programming_error (String s
)const
691 s
= "Programming error: " + s
;
696 /****************************************************
698 ****************************************************/
702 IMPLEMENT_SMOBS (Grob
);
703 IMPLEMENT_DEFAULT_EQUAL_P (Grob
);
706 Grob::mark_smob (SCM ses
)
708 Grob
* s
= (Grob
*) SCM_CELL_WORD_1 (ses
);
709 scm_gc_mark (s
->immutable_property_alist_
);
711 for (int a
=0 ; a
< 2; a
++)
713 scm_gc_mark (s
->dim_cache_
[a
].offset_callbacks_
);
714 scm_gc_mark (s
->dim_cache_
[a
].dimension_
);
717 don't mark the parents. The pointers in the mutable property
718 list form two tree like structures (one for X relations, one
719 for Y relations). Marking these can be done in limited stack
720 space. If we add the parents, we will jump between X and Y in
721 an erratic manner, leading to much more recursion depth (and
722 core dumps if we link to pthreads.)
727 scm_gc_mark (s
->original_
->self_scm ());
729 s
->do_derived_mark ();
730 return s
->mutable_property_alist_
;
734 Grob::print_smob (SCM s
, SCM port
, scm_print_state
*)
736 Grob
*sc
= (Grob
*) ly_cdr (s
);
738 scm_puts ("#<Grob ", port
);
739 scm_puts ((char *)sc
->name ().to_str0 (), port
);
742 don't try to print properties, that is too much hassle.
744 scm_puts (" >", port
);
749 Grob::do_derived_mark () const
757 Grob::discretionary_processing ()
762 Grob::internal_has_interface (SCM k
)
764 SCM ifs
= get_property ("interfaces");
766 return scm_c_memq (k
, ifs
) != SCM_BOOL_F
;
770 /** Return Array of Grobs in SCM list L */
774 Link_array
<Grob
> arr
;
776 for (SCM s
= l
; ly_pair_p (s
); s
= ly_cdr (s
))
779 arr
.push (unsmob_grob (e
));
786 /** Return SCM list of Grob array A */
788 ly_grobs2scm (Link_array
<Grob
> a
)
791 for (int i
= a
.size (); i
; i
--)
792 s
= scm_cons (a
[i
-1]->self_scm (), s
);
798 IMPLEMENT_TYPE_P (Grob
, "ly:grob?");
800 ADD_INTERFACE (Grob
, "grob-interface",
801 "A grob represents a piece of music notation\n"
803 "All grobs have an X and Y-position on the page. These X and Y positions\n"
804 "are stored in a relative format, so they can easily be combined by\n"
805 "stacking them, hanging one grob to the side of another, and coupling\n"
806 "them into a grouping objects.\n"
808 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
809 "is stored relative to that reference point. For example the X-reference\n"
810 "point of a staccato dot usually is the note head that it applies\n"
811 "to. When the note head is moved, the staccato dot moves along\n"
814 "A grob is often associated with a symbol, but some grobs do not print\n"
815 "any symbols. They take care of grouping objects. For example, there is a\n"
816 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
817 "is also an abstract grob: it only moves around chords, but doesn't print\n"
820 "Grobs have a properties: Scheme variables, that can be read and set. "
821 "They have two types. Immutable variables "
822 "define the default style and behavior. They are shared between many objects. "
823 "They can be changed using @code{\\override} and @code{\\revert}. "
825 "Mutable properties are variables that are specific to one grob. Typically, "
826 "lists of other objects, or results from computations are stored in"
827 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
828 "sets a mutable property. "
831 "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
832 "Y-extent-callback print-function extra-offset spacing-procedure "
833 "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
834 "meta layer before-line-breaking-callback "
835 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
836 "minimum-Y-extent transparent");