2 context-property.cc -- implement manipulation of immutable Grob
5 source file of the GNU LilyPond music typesetter
7 (c) 2004--2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
11 #include "engraver.hh"
16 #include "paper-column.hh"
19 lookup_nested_property (SCM alist,
20 SCM grob_property_path)
22 if (scm_is_pair (grob_property_path))
24 SCM sym = scm_car (grob_property_path);
25 SCM handle = scm_assq (sym, alist);
27 if (handle == SCM_BOOL_F)
30 return lookup_nested_property (scm_cdr (handle),
31 scm_cdr (grob_property_path));
38 copy ALIST leaving out SYMBOL. Copying stops at ALIST_END
41 evict_from_alist (SCM symbol,
45 SCM new_alist = SCM_EOL;
46 SCM *tail = &new_alist;
48 while (alist != alist_end)
50 if (ly_is_equal (scm_caar (alist), symbol))
52 alist = scm_cdr (alist);
56 *tail = scm_cons (scm_car (alist), SCM_EOL);
57 tail = SCM_CDRLOC (*tail);
58 alist = scm_cdr (alist);
66 general_pushpop_property (Context *context,
68 SCM grob_property_path,
72 if (!scm_is_symbol (context_property)
73 || !scm_is_symbol (scm_car (grob_property_path)))
75 warning (_ ("need symbol arguments for \\override and \\revert"));
76 if (do_internal_type_checking_global)
80 execute_general_pushpop_property (context, context_property,
81 grob_property_path, new_value);
87 Grob descriptions (ie. alists with layout properties) are
88 represented as a (ALIST . BASED-ON) pair, where BASED-ON is the
89 alist defined in a parent context. BASED-ON should always be a tail
92 Push or pop (depending on value of VAL) a single entry entry from a
93 translator property list by name of PROP. GROB_PROPERTY_PATH
94 indicates nested alists, eg. '(beamed-stem-lengths details)
98 execute_general_pushpop_property (Context *context,
100 SCM grob_property_path,
104 SCM current_context_val = SCM_EOL;
105 if (new_value != SCM_UNDEFINED)
107 Context *where = context->where_defined (context_property, ¤t_context_val);
110 Don't mess with MIDI.
115 if (where != context)
117 SCM base = updated_grob_properties (context, context_property);
118 current_context_val = scm_cons (base, base);
119 context->internal_set_property (context_property, current_context_val);
122 if (!scm_is_pair (current_context_val))
124 programming_error ("Grob definition should be cons");
128 SCM prev_alist = scm_car (current_context_val);
129 SCM symbol = scm_car (grob_property_path);
131 = lookup_nested_property (prev_alist,
132 scm_reverse (scm_cdr (grob_property_path)));
134 target_alist = scm_acons (symbol, new_value, target_alist);
137 if (!scm_is_pair (scm_cdr (grob_property_path)))
139 if (!ly_is_procedure (new_value)
140 && !is_callback_chain (new_value))
141 ok = type_check_assignment (symbol, new_value,
142 ly_symbol2scm ("backend-type?"));
145 tack onto alist. We can use set_car, since
146 updated_grob_properties() in child contexts will check
147 for changes in the car.
151 scm_set_car_x (current_context_val, target_alist);
156 execute_general_pushpop_property (context,
158 scm_cdr (grob_property_path),
163 else if (context->where_defined (context_property, ¤t_context_val) == context)
165 SCM current_value = scm_car (current_context_val);
166 SCM daddy = scm_cdr (current_context_val);
168 if (!scm_is_pair (grob_property_path)
169 || !scm_is_symbol (scm_car (grob_property_path)))
171 programming_error ("Grob property path should be list of symbols.");
175 SCM symbol = scm_car (grob_property_path);
176 SCM new_alist = evict_from_alist (symbol, current_value, daddy);
178 if (new_alist == daddy)
179 context->unset_property (context_property);
181 context->internal_set_property (context_property, scm_cons (new_alist, daddy));
186 execute_pushpop_property (Context *context,
187 SCM context_property,
192 general_pushpop_property (context, context_property,
193 scm_list_1 (grob_property),
198 PRE_INIT_OPS is in the order specified, and hence must be reversed.
201 apply_property_operations (Context *tg, SCM pre_init_ops)
203 SCM correct_order = scm_reverse (pre_init_ops);
204 for (SCM s = correct_order; scm_is_pair (s); s = scm_cdr (s))
206 SCM entry = scm_car (s);
207 SCM type = scm_car (entry);
208 entry = scm_cdr (entry);
210 if (type == ly_symbol2scm ("push"))
212 SCM context_prop = scm_car (entry);
213 SCM val = scm_cadr (entry);
214 SCM grob_prop_path = scm_cddr (entry);
215 execute_general_pushpop_property (tg, context_prop, grob_prop_path, val);
217 else if (type == ly_symbol2scm ("pop"))
219 SCM context_prop = scm_car (entry);
220 SCM val = SCM_UNDEFINED;
221 SCM grob_prop_path = scm_cdr (entry);
222 execute_general_pushpop_property (tg, context_prop, grob_prop_path, val);
224 else if (type == ly_symbol2scm ("assign"))
225 tg->internal_set_property (scm_car (entry), scm_cadr (entry));
230 Return the object alist for SYM, checking if its base in enclosing
231 contexts has changed. The alist is updated if necessary.
234 updated_grob_properties (Context *tg, SCM sym)
236 assert (scm_is_symbol (sym));
239 tg = tg->where_defined (sym, &props);
244 = (tg->get_parent_context ())
245 ? updated_grob_properties (tg->get_parent_context (), sym)
248 if (!scm_is_pair (props))
250 programming_error ("grob props not a pair?");
254 SCM based_on = scm_cdr (props);
255 if (based_on == daddy_props)
256 return scm_car (props);
259 SCM copy = daddy_props;
261 SCM p = scm_car (props);
262 while (p != based_on)
264 *tail = scm_cons (scm_car (p), daddy_props);
265 tail = SCM_CDRLOC (*tail);
269 scm_set_car_x (props, copy);
270 scm_set_cdr_x (props, daddy_props);
277 make_grob_from_properties (Engraver *tr, SCM symbol, SCM cause, char const *name)
279 Context *context = tr->context ();
281 SCM props = updated_grob_properties (context, symbol);
283 Object_key const *key = context->get_grob_key (name);
286 SCM handle = scm_sloppy_assq (ly_symbol2scm ("meta"), props);
287 SCM klass = scm_cdr (scm_sloppy_assq (ly_symbol2scm ("class"), scm_cdr (handle)));
289 if (klass == ly_symbol2scm ("Item"))
290 grob = new Item (props, key);
291 else if (klass == ly_symbol2scm ("Spanner"))
292 grob = new Spanner (props, key);
293 else if (klass == ly_symbol2scm ("Paper_column"))
294 grob = new Paper_column (props, key);
297 dynamic_cast<Engraver *> (tr)->announce_grob (grob, cause);
303 make_item_from_properties (Engraver *tr, SCM x, SCM cause, char const *name)
305 Item *it = dynamic_cast<Item *> (make_grob_from_properties (tr, x, cause, name));
311 make_paper_column_from_properties (Engraver *tr, SCM x, char const *name)
313 return dynamic_cast<Paper_column *> (make_grob_from_properties (tr, x, SCM_EOL, name));
317 make_spanner_from_properties (Engraver *tr, SCM x, SCM cause, char const *name)
319 Spanner *sp = dynamic_cast<Spanner *> (make_grob_from_properties (tr, x, cause, name));