2 Implement storage and manipulation of grob properties.
9 #include "pointer-group-interface.hh"
11 #include "paper-score.hh"
12 #include "output-def.hh"
14 #include "international.hh"
18 #include "program-option.hh"
20 #include "simple-closure.hh"
22 #include "protected-scm.hh"
24 Protected_scm grob_property_callback_stack
= SCM_EOL
;
26 extern bool debug_property_callbacks
;
30 print_property_callback_stack ()
33 for (SCM s
= grob_property_callback_stack
; scm_is_pair (s
); s
= scm_cdr (s
))
34 message (_f ("%d: %s", frame
++, ly_scm_write_string (scm_car (s
)).c_str ()));
38 static SCM modification_callback
= SCM_EOL
;
39 static SCM cache_callback
= SCM_EOL
;
44 FIXME: this should use ly:set-option interface instead.
48 LY_DEFINE (ly_set_grob_modification_callback
, "ly:set-grob-modification-callback",
50 "Specify a procedure that will be called every time LilyPond"
51 " modifies a grob property. The callback will receive as"
52 " arguments the grob that is being modified, the name of the"
53 " C++ file in which the modification was requested, the line"
54 " number in the C++ file in which the modification was requested,"
55 " the name of the function in which the modification was"
56 " requested, the property to be changed, and the new value for"
59 modification_callback
= (ly_is_procedure (cb
)) ? cb
: SCM_BOOL_F
;
60 return SCM_UNSPECIFIED
;
63 LY_DEFINE (ly_set_property_cache_callback
, "ly:set-property-cache-callback",
65 "Specify a procedure that will be called whenever lilypond"
66 " calculates a callback function and caches the result. The"
67 " callback will receive as arguments the grob whose property it"
68 " is, the name of the property, the name of the callback that"
69 " calculated the property, and the new (cached) value of the"
72 cache_callback
= (ly_is_procedure (cb
)) ? cb
: SCM_BOOL_F
;
73 return SCM_UNSPECIFIED
;
78 Grob::instrumented_set_property (SCM sym
, SCM v
,
84 if (ly_is_procedure (modification_callback
))
85 scm_apply_0 (modification_callback
,
86 scm_list_n (self_scm (),
87 scm_from_locale_string (file
),
89 scm_from_locale_string (fun
),
90 sym
, v
, SCM_UNDEFINED
));
97 internal_set_property (sym
, v
);
101 Grob::get_property_alist_chain (SCM def
) const
103 return scm_list_n (mutable_property_alist_
,
104 immutable_property_alist_
,
109 extern void check_interfaces_for_property (Grob
const *me
, SCM sym
);
112 Grob::internal_set_property (SCM sym
, SCM v
)
114 internal_set_value_on_alist (&mutable_property_alist_
,
120 Grob::internal_set_value_on_alist (SCM
*alist
, SCM sym
, SCM v
)
122 /* Perhaps we simply do the assq_set, but what the heck. */
126 if (do_internal_type_checking_global
)
128 if (!ly_is_procedure (v
)
129 && !is_simple_closure (v
)
130 && v
!= ly_symbol2scm ("calculation-in-progress"))
131 type_check_assignment (sym
, v
, ly_symbol2scm ("backend-type?"));
133 check_interfaces_for_property (this, sym
);
136 *alist
= scm_assq_set_x (*alist
, sym
, v
);
140 Grob::internal_get_property_data (SCM sym
) const
143 if (profile_property_accesses
)
144 note_property_access (&grob_property_lookup_table
, sym
);
147 SCM handle
= scm_sloppy_assq (sym
, mutable_property_alist_
);
148 if (handle
!= SCM_BOOL_F
)
149 return scm_cdr (handle
);
151 handle
= scm_sloppy_assq (sym
, immutable_property_alist_
);
153 if (do_internal_type_checking_global
&& scm_is_pair (handle
))
155 SCM val
= scm_cdr (handle
);
156 if (!ly_is_procedure (val
) && !is_simple_closure (val
))
157 type_check_assignment (sym
, val
, ly_symbol2scm ("backend-type?"));
159 check_interfaces_for_property (this, sym
);
162 return (handle
== SCM_BOOL_F
) ? SCM_EOL
: scm_cdr (handle
);
166 Grob::internal_get_property (SCM sym
) const
168 SCM val
= get_property_data (sym
);
171 if (val
== ly_symbol2scm ("calculation-in-progress"))
173 programming_error (_f ("cyclic dependency: calculation-in-progress encountered for #'%s (%s)",
174 ly_symbol2string (sym
).c_str (),
176 if (debug_property_callbacks
)
178 message ("backtrace: ");
179 print_property_callback_stack ();
184 if (ly_is_procedure (val
)
185 || is_simple_closure (val
))
187 Grob
*me
= ((Grob
*)this);
188 val
= me
->try_callback_on_alist (&me
->mutable_property_alist_
, sym
, val
);
195 Grob::try_callback_on_alist (SCM
*alist
, SCM sym
, SCM proc
)
197 SCM marker
= ly_symbol2scm ("calculation-in-progress");
199 need to put a value in SYM to ensure that we don't get a
202 *alist
= scm_assq_set_x (*alist
, sym
, marker
);
205 if (debug_property_callbacks
)
206 grob_property_callback_stack
= scm_cons (scm_list_3 (self_scm (), sym
, proc
), grob_property_callback_stack
);
210 if (ly_is_procedure (proc
))
211 value
= scm_call_1 (proc
, self_scm ());
212 else if (is_simple_closure (proc
))
214 value
= evaluate_with_simple_closure (self_scm (),
215 simple_closure_expression (proc
),
220 if (debug_property_callbacks
)
221 grob_property_callback_stack
= scm_cdr (grob_property_callback_stack
);
225 If the function returns SCM_UNSPECIFIED, we assume the
226 property has been set with an explicit set_property ()
229 if (value
== SCM_UNSPECIFIED
)
231 value
= get_property_data (sym
);
232 assert (value
== SCM_EOL
|| value
== marker
);
234 *alist
= scm_assq_remove_x (*alist
, marker
);
239 if (ly_is_procedure (cache_callback
))
240 scm_apply_0 (cache_callback
,
241 scm_list_n (self_scm (),
247 internal_set_value_on_alist (alist
, sym
, value
);
254 Grob::internal_set_object (SCM s
, SCM v
)
256 /* Perhaps we simply do the assq_set, but what the heck. */
260 object_alist_
= scm_assq_set_x (object_alist_
, s
, v
);
264 Grob::internal_del_property (SCM sym
)
266 mutable_property_alist_
= scm_assq_remove_x (mutable_property_alist_
, sym
);
270 Grob::internal_get_object (SCM sym
) const
272 if (profile_property_accesses
)
273 note_property_access (&grob_property_lookup_table
, sym
);
275 SCM s
= scm_sloppy_assq (sym
, object_alist_
);
279 SCM val
= scm_cdr (s
);
280 if (ly_is_procedure (val
)
281 || is_simple_closure (val
))
283 Grob
*me
= ((Grob
*)this);
284 val
= me
->try_callback_on_alist (&me
->object_alist_
, sym
, val
);
294 Grob::is_live () const
296 return scm_is_pair (immutable_property_alist_
);
300 Grob::internal_has_interface (SCM k
)
302 return scm_c_memq (k
, interfaces_
) != SCM_BOOL_F
;
306 call_pure_function (SCM unpure
, SCM args
, int start
, int end
)
308 SCM scm_call_pure_function
= ly_lily_module_constant ("call-pure-function");
310 return scm_apply_0 (scm_call_pure_function
,
311 scm_list_4 (unpure
, args
, scm_from_int (start
), scm_from_int (end
)));