]> git.donarmstrong.com Git - lilypond.git/blob - lily/context-property.cc
00108cf3149d8b3966b4c392a2dc523e143849c7
[lilypond.git] / lily / context-property.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2004--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "context.hh"
21 #include "engraver.hh"
22 #include "international.hh"
23 #include "item.hh"
24 #include "main.hh"
25 #include "simple-closure.hh"
26 #include "spanner.hh"
27 #include "unpure-pure-container.hh"
28 #include "warn.hh"
29
30 /*
31   like execute_general_pushpop_property(), but typecheck
32   grob_property_path and context_property.
33 */
34 void
35 general_pushpop_property (Context *context,
36                           SCM context_property,
37                           SCM grob_property_path,
38                           SCM new_value)
39 {
40   if (!scm_is_symbol (context_property)
41       || !scm_is_symbol (scm_car (grob_property_path)))
42     {
43       warning (_ ("need symbol arguments for \\override and \\revert"));
44       if (do_internal_type_checking_global)
45         assert (false);
46     }
47
48   sloppy_general_pushpop_property (context, context_property,
49                                    grob_property_path, new_value);
50 }
51
52 /*
53   Grob descriptions (ie. alists with layout properties) are
54   represented as a (ALIST . BASED-ON) pair, where BASED-ON is the
55   alist defined in a parent context. BASED-ON should always be a tail
56   of ALIST.
57
58   Push or pop (depending on value of VAL) a single entry from a
59   translator property list by name of PROP.  GROB_PROPERTY_PATH
60   indicates nested alists, eg. '(beamed-stem-lengths details)
61 */
62 void
63 execute_override_property (Context *context,
64                            SCM context_property,
65                            SCM grob_property_path,
66                            SCM new_value)
67 {
68   SCM current_context_val = SCM_EOL;
69
70   Context *where = context->where_defined (context_property,
71                                            &current_context_val);
72
73   /*
74     Don't mess with MIDI.
75   */
76   if (!where)
77     return;
78
79   if (where != context)
80     {
81       SCM base = updated_grob_properties (context, context_property);
82       current_context_val = scm_cons (base, base);
83       context->set_property (context_property, current_context_val);
84     }
85
86   if (!scm_is_pair (current_context_val))
87     {
88       programming_error ("Grob definition should be cons");
89       return;
90     }
91
92   SCM target_alist = scm_car (current_context_val);
93
94   SCM symbol = scm_car (grob_property_path);
95   if (scm_is_pair (scm_cdr (grob_property_path)))
96     {
97       new_value = nested_property_alist (ly_assoc_get (symbol, target_alist,
98                                                        SCM_EOL),
99                                          scm_cdr (grob_property_path),
100                                          new_value);
101     }
102
103   /* it's tempting to replace the head of the list if it's the same
104    property. However, we have to keep this info around, in case we have to
105    \revert back to it.
106   */
107   target_alist = scm_acons (symbol, new_value, target_alist);
108
109   bool ok = true;
110   bool pc = is_unpure_pure_container (new_value);
111   SCM vals[] = {pc ? unpure_pure_container_unpure_part (new_value) : new_value,
112                 pc ? unpure_pure_container_pure_part (new_value) : SCM_BOOL_F
113                };
114
115   for (int i = 0; i < 2; i++)
116     if (!ly_is_procedure (vals[i])
117         && !is_simple_closure (vals[i]))
118       ok = ok && type_check_assignment (symbol, vals[i],
119                                         ly_symbol2scm ("backend-type?"));
120
121   /*
122     tack onto alist.  We can use set_car, since
123     updated_grob_properties () in child contexts will check
124     for changes in the car.
125   */
126   if (ok)
127     {
128       scm_set_car_x (current_context_val, target_alist);
129     }
130 }
131
132 /*
133   do a pop (indicated by new_value==SCM_UNDEFINED) or push
134  */
135 void
136 sloppy_general_pushpop_property (Context *context,
137                                  SCM context_property,
138                                  SCM grob_property_path,
139                                  SCM new_value)
140 {
141   if (new_value == SCM_UNDEFINED)
142     execute_revert_property (context, context_property,
143                              grob_property_path);
144   else
145     execute_override_property (context, context_property,
146                                grob_property_path,
147                                new_value);
148 }
149
150 /*
151   Revert the property given by property_path.
152 */
153 void
154 execute_revert_property (Context *context,
155                          SCM context_property,
156                          SCM grob_property_path)
157 {
158   SCM current_context_val = SCM_EOL;
159   if (context->where_defined (context_property, &current_context_val)
160       == context)
161     {
162       SCM current_alist = scm_car (current_context_val);
163       SCM daddy = scm_cdr (current_context_val);
164
165       if (!scm_is_pair (grob_property_path)
166           || !scm_is_symbol (scm_car (grob_property_path)))
167         {
168           programming_error ("Grob property path should be list of symbols.");
169           return;
170         }
171
172       SCM symbol = scm_car (grob_property_path);
173       if (scm_is_pair (scm_cdr (grob_property_path)))
174         {
175           SCM current_sub_alist = ly_assoc_get (symbol, current_alist, SCM_EOL);
176           SCM new_val
177             = nested_property_revert_alist (current_sub_alist,
178                                             scm_cdr (grob_property_path));
179
180           if (scm_is_pair (current_alist)
181               && scm_caar (current_alist) == symbol
182               && current_alist != daddy)
183             current_alist = scm_cdr (current_alist);
184
185           current_alist = scm_acons (symbol, new_val, current_alist);
186           scm_set_car_x (current_context_val, current_alist);
187         }
188       else
189         {
190           SCM new_alist = evict_from_alist (symbol, current_alist, daddy);
191
192           if (new_alist == daddy)
193             context->unset_property (context_property);
194           else
195             context->set_property (context_property,
196                                    scm_cons (new_alist, daddy));
197         }
198     }
199 }
200 /*
201   Convenience: a push/pop grob property using a single grob_property
202   as argument.
203 */
204 void
205 execute_pushpop_property (Context *context,
206                           SCM context_property,
207                           SCM grob_property,
208                           SCM new_value)
209 {
210   general_pushpop_property (context, context_property,
211                             scm_list_1 (grob_property),
212                             new_value);
213 }
214
215 /*
216   PRE_INIT_OPS is in the order specified, and hence must be reversed.
217 */
218 void
219 apply_property_operations (Context *tg, SCM pre_init_ops)
220 {
221   for (SCM s = pre_init_ops; scm_is_pair (s); s = scm_cdr (s))
222     {
223       SCM entry = scm_car (s);
224       SCM type = scm_car (entry);
225       entry = scm_cdr (entry);
226
227       if (type == ly_symbol2scm ("push"))
228         {
229           SCM context_prop = scm_car (entry);
230           SCM val = scm_cadr (entry);
231           SCM grob_prop_path = scm_cddr (entry);
232           sloppy_general_pushpop_property (tg, context_prop, grob_prop_path, val);
233         }
234       else if (type == ly_symbol2scm ("pop"))
235         {
236           SCM context_prop = scm_car (entry);
237           SCM val = SCM_UNDEFINED;
238           SCM grob_prop_path = scm_cdr (entry);
239           sloppy_general_pushpop_property (tg, context_prop, grob_prop_path, val);
240         }
241       else if (type == ly_symbol2scm ("assign"))
242         tg->set_property (scm_car (entry), scm_cadr (entry));
243       else if (type == ly_symbol2scm ("apply"))
244         scm_apply_1 (scm_car (entry), tg->self_scm (), scm_cdr (entry));
245       else if (type == ly_symbol2scm ("unset"))
246         tg->unset_property (scm_car (entry));
247     }
248 }
249
250 /*
251   Return the object alist for SYM, checking if its base in enclosing
252   contexts has changed. The alist is updated if necessary.
253 */
254 SCM
255 updated_grob_properties (Context *tg, SCM sym)
256 {
257   assert (scm_is_symbol (sym));
258
259   SCM props;
260   tg = tg->where_defined (sym, &props);
261   if (!tg)
262     return SCM_EOL;
263
264   SCM daddy_props
265     = (tg->get_parent_context ())
266       ? updated_grob_properties (tg->get_parent_context (), sym)
267       : SCM_EOL;
268
269   if (!scm_is_pair (props))
270     {
271       programming_error ("grob props not a pair?");
272       return SCM_EOL;
273     }
274
275   SCM based_on = scm_cdr (props);
276   if (based_on == daddy_props)
277     return scm_car (props);
278   else
279     {
280       SCM copy = daddy_props;
281       SCM *tail = &copy;
282       SCM p = scm_car (props);
283       while (p != based_on)
284         {
285           *tail = scm_cons (scm_car (p), daddy_props);
286           tail = SCM_CDRLOC (*tail);
287           p = scm_cdr (p);
288         }
289
290       scm_set_car_x (props, copy);
291       scm_set_cdr_x (props, daddy_props);
292
293       return copy;
294     }
295 }