]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob-property.cc
Web-ja: update introduction
[lilypond.git] / lily / grob-property.cc
1 /*
2   Implement storage and manipulation of grob properties.
3 */
4
5 #include <cstring>
6
7 #include "main.hh"
8 #include "input.hh"
9 #include "pointer-group-interface.hh"
10 #include "misc.hh"
11 #include "paper-score.hh"
12 #include "output-def.hh"
13 #include "spanner.hh"
14 #include "international.hh"
15 #include "item.hh"
16 #include "program-option.hh"
17 #include "profile.hh"
18 #include "unpure-pure-container.hh"
19 #include "warn.hh"
20 #include "protected-scm.hh"
21
22 Protected_scm grob_property_callback_stack (SCM_EOL);
23
24 extern bool debug_property_callbacks;
25
26 #ifdef DEBUG
27 static void
28 print_property_callback_stack ()
29 {
30   int frame = 0;
31   for (SCM s = grob_property_callback_stack; scm_is_pair (s); s = scm_cdr (s))
32     message (_f ("%d: %s", frame++, ly_scm_write_string (scm_car (s)).c_str ()));
33 }
34 #endif
35
36 static Protected_scm modification_callback (SCM_EOL);
37 static Protected_scm cache_callback (SCM_EOL);
38
39 /*
40 FIXME: this should use ly:set-option interface instead.
41 */
42
43 LY_DEFINE (ly_set_grob_modification_callback, "ly:set-grob-modification-callback",
44            1, 0, 0, (SCM cb),
45            "Specify a procedure that will be called every time LilyPond"
46            " modifies a grob property.  The callback will receive as"
47            " arguments the grob that is being modified, the name of the"
48            " C++ file in which the modification was requested, the line"
49            " number in the C++ file in which the modification was requested,"
50            " the name of the function in which the modification was"
51            " requested, the property to be changed, and the new value for"
52            " the property.")
53 {
54   modification_callback = (ly_is_procedure (cb)) ? cb : SCM_BOOL_F;
55   return SCM_UNSPECIFIED;
56 }
57
58 LY_DEFINE (ly_set_property_cache_callback, "ly:set-property-cache-callback",
59            1, 0, 0, (SCM cb),
60            "Specify a procedure that will be called whenever lilypond"
61            " calculates a callback function and caches the result.  The"
62            " callback will receive as arguments the grob whose property it"
63            " is, the name of the property, the name of the callback that"
64            " calculated the property, and the new (cached) value of the"
65            " property.")
66 {
67   cache_callback = (ly_is_procedure (cb)) ? cb : SCM_BOOL_F;
68   return SCM_UNSPECIFIED;
69 }
70
71 void
72 Grob::instrumented_set_property (SCM sym, SCM v,
73                                  char const *file,
74                                  int line,
75                                  char const *fun)
76 {
77 #ifdef DEBUG
78   if (ly_is_procedure (modification_callback))
79     scm_apply_0 (modification_callback,
80                  scm_list_n (self_scm (),
81                              scm_from_locale_string (file),
82                              scm_from_int (line),
83                              scm_from_ascii_string (fun),
84                              sym, v, SCM_UNDEFINED));
85 #else
86   (void) file;
87   (void) line;
88   (void) fun;
89 #endif
90
91   internal_set_property (sym, v);
92 }
93
94 SCM
95 Grob::get_property_alist_chain (SCM def) const
96 {
97   return scm_list_3 (mutable_property_alist_,
98                      immutable_property_alist_,
99                      def);
100 }
101
102 extern void check_interfaces_for_property (Grob const *me, SCM sym);
103
104 void
105 Grob::internal_set_property (SCM sym, SCM v)
106 {
107   internal_set_value_on_alist (&mutable_property_alist_,
108                                sym, v);
109
110 }
111
112 void
113 Grob::internal_set_value_on_alist (SCM *alist, SCM sym, SCM v)
114 {
115   /* Perhaps we simply do the assq_set, but what the heck. */
116   if (!is_live ())
117     return;
118
119   if (do_internal_type_checking_global)
120     {
121       if (!ly_is_procedure (v)
122           && !unsmob<Unpure_pure_container> (v)
123           && !scm_is_eq (v, ly_symbol2scm ("calculation-in-progress")))
124         type_check_assignment (sym, v, ly_symbol2scm ("backend-type?"));
125
126       check_interfaces_for_property (this, sym);
127     }
128
129   *alist = scm_assq_set_x (*alist, sym, v);
130 }
131
132 SCM
133 Grob::internal_get_property_data (SCM sym) const
134 {
135 #ifdef DEBUG
136   if (profile_property_accesses)
137     note_property_access (&grob_property_lookup_table, sym);
138 #endif
139
140   SCM handle = scm_sloppy_assq (sym, mutable_property_alist_);
141   if (scm_is_true (handle))
142     return scm_cdr (handle);
143
144   handle = scm_sloppy_assq (sym, immutable_property_alist_);
145
146   if (do_internal_type_checking_global && scm_is_pair (handle))
147     {
148       SCM val = scm_cdr (handle);
149       if (!ly_is_procedure (val)
150           && !unsmob<Unpure_pure_container> (val))
151         type_check_assignment (sym, val, ly_symbol2scm ("backend-type?"));
152
153       check_interfaces_for_property (this, sym);
154     }
155
156   return scm_is_false (handle) ? SCM_EOL : scm_cdr (handle);
157 }
158
159 SCM
160 Grob::internal_get_property (SCM sym) const
161 {
162   SCM val = get_property_data (sym);
163
164 #ifdef DEBUG
165   if (scm_is_eq (val, ly_symbol2scm ("calculation-in-progress")))
166     {
167       programming_error (to_string ("cyclic dependency: calculation-in-progress encountered for #'%s (%s)",
168                                     ly_symbol2string (sym).c_str (),
169                                     name ().c_str ()));//assert (1==0);
170       if (debug_property_callbacks)
171         {
172           message ("backtrace: ");
173           print_property_callback_stack ();
174         }
175     }
176 #endif
177
178   if (Unpure_pure_container *upc = unsmob<Unpure_pure_container> (val))
179     val = upc->unpure_part ();
180
181   if (ly_is_procedure (val))
182     {
183       Grob *me = ((Grob *)this);
184       val = me->try_callback_on_alist (&me->mutable_property_alist_, sym, val);
185     }
186
187   return val;
188 }
189
190 /* Unlike internal_get_property, this function does no caching. Use it, therefore, with caution. */
191 SCM
192 Grob::internal_get_pure_property (SCM sym, int start, int end) const
193 {
194   SCM val = internal_get_property_data (sym);
195   if (ly_is_procedure (val))
196     return call_pure_function (val, scm_list_1 (self_scm ()), start, end);
197
198   if (Unpure_pure_container *upc = unsmob<Unpure_pure_container> (val)) {
199     // Do cache, if the function ignores 'start' and 'end'
200     if (upc->is_unchanging ())
201       return internal_get_property (sym);
202     else
203       return call_pure_function (val, scm_list_1 (self_scm ()), start, end);
204   }
205
206   return val;
207 }
208
209 SCM
210 Grob::internal_get_maybe_pure_property (SCM sym, bool pure, int start, int end) const
211 {
212   return pure ? internal_get_pure_property (sym, start, end) : internal_get_property (sym);
213 }
214
215 SCM
216 Grob::try_callback_on_alist (SCM *alist, SCM sym, SCM proc)
217 {
218   SCM marker = ly_symbol2scm ("calculation-in-progress");
219   /*
220     need to put a value in SYM to ensure that we don't get a
221     cyclic call chain.
222   */
223   *alist = scm_assq_set_x (*alist, sym, marker);
224
225 #ifdef DEBUG
226   if (debug_property_callbacks)
227     grob_property_callback_stack = scm_cons (scm_list_3 (self_scm (), sym, proc), grob_property_callback_stack);
228 #endif
229
230   SCM value = SCM_EOL;
231   if (ly_is_procedure (proc))
232     value = scm_call_1 (proc, self_scm ());
233
234 #ifdef DEBUG
235   if (debug_property_callbacks)
236     grob_property_callback_stack = scm_cdr (grob_property_callback_stack);
237 #endif
238
239   if (scm_is_eq (value, SCM_UNSPECIFIED))
240     {
241       value = get_property_data (sym);
242       assert (scm_is_null (value) || scm_is_eq (value, marker));
243       if (scm_is_eq (value, marker))
244         *alist = scm_assq_remove_x (*alist, sym);
245     }
246   else
247     {
248 #ifdef DEBUG
249       if (ly_is_procedure (cache_callback))
250         scm_call_4 (cache_callback,
251                     self_scm (),
252                     sym,
253                     proc,
254                     value);
255 #endif
256       internal_set_value_on_alist (alist, sym, value);
257     }
258
259   return value;
260 }
261
262 void
263 Grob::internal_set_object (SCM s, SCM v)
264 {
265   /* Perhaps we simply do the assq_set, but what the heck. */
266   if (!is_live ())
267     return;
268
269   object_alist_ = scm_assq_set_x (object_alist_, s, v);
270 }
271
272 void
273 Grob::internal_del_property (SCM sym)
274 {
275   mutable_property_alist_ = scm_assq_remove_x (mutable_property_alist_, sym);
276 }
277
278 SCM
279 Grob::internal_get_object (SCM sym) const
280 {
281   if (profile_property_accesses)
282     note_property_access (&grob_property_lookup_table, sym);
283
284   SCM s = scm_sloppy_assq (sym, object_alist_);
285
286   if (scm_is_true (s))
287     {
288       SCM val = scm_cdr (s);
289       if (ly_is_procedure (val)
290           || unsmob<Unpure_pure_container> (val))
291         {
292           Grob *me = ((Grob *)this);
293           val = me->try_callback_on_alist (&me->object_alist_, sym, val);
294         }
295
296       return val;
297     }
298
299   return SCM_EOL;
300 }
301
302 bool
303 Grob::is_live () const
304 {
305   return scm_is_pair (immutable_property_alist_);
306 }
307
308 bool
309 Grob::internal_has_interface (SCM k)
310 {
311   return scm_is_true (scm_c_memq (k, interfaces_));
312 }
313
314 SCM
315 call_pure_function (SCM value, SCM args, int start, int end)
316 {
317   if (Unpure_pure_container *upc = unsmob<Unpure_pure_container> (value))
318     {
319       if (upc->is_unchanging ())
320         {
321           // Don't bother forming an Unpure_pure_call here.
322           value = upc->unpure_part ();
323
324           if (ly_is_procedure (value))
325             return scm_apply_0 (value, args);
326           return value;
327         }
328
329       value = upc->pure_part ();
330
331       if (ly_is_procedure (value))
332         return scm_apply_3 (value,
333                             scm_car (args),
334                             scm_from_int (start),
335                             scm_from_int (end),
336                             scm_cdr (args));
337
338       return value;
339     }
340
341   if (!ly_is_procedure (value))
342     return value;
343
344   return SCM_BOOL_F;
345 }