Mercurial > hg > xemacs-beta
comparison lisp/specifier.el @ 209:41ff10fd062f r20-4b3
Import from CVS: tag r20-4b3
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:04:58 +0200 |
parents | |
children | 78478c60bfcd |
comparison
equal
deleted
inserted
replaced
208:f427b8ec4379 | 209:41ff10fd062f |
---|---|
1 ;;; specifier.el --- Lisp interface to specifiers | |
2 | |
3 ;; Copyright (C) 1997 Free Software Foundation, Inc. | |
4 ;; Copyright (C) 1995, 1996 Ben Wing. | |
5 | |
6 ;; Author: Ben Wing <wing@666.com> | |
7 ;; Keywords: internal, dumped | |
8 | |
9 ;;; Synched up with: Not in FSF. | |
10 | |
11 ;; This file is part of XEmacs. | |
12 | |
13 ;; XEmacs is free software; you can redistribute it and/or modify it | |
14 ;; under the terms of the GNU General Public License as published by | |
15 ;; the Free Software Foundation; either version 2, or (at your option) | |
16 ;; any later version. | |
17 | |
18 ;; XEmacs is distributed in the hope that it will be useful, but | |
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 ;; General Public License for more details. | |
22 | |
23 ;; You should have received a copy of the GNU General Public License | |
24 ;; along with XEmacs; see the file COPYING. If not, write to the | |
25 ;; Free Software Foundation, 59 Temple Place - Suite 330, | |
26 ;; Boston, MA 02111-1307, USA. | |
27 | |
28 ;;; Commentary: | |
29 | |
30 ;; This file is dumped with XEmacs. | |
31 | |
32 ;;; Code: | |
33 | |
34 (defun make-specifier-and-init (type spec-list &optional dont-canonicalize) | |
35 "Create and initialize a new specifier. | |
36 | |
37 This is a front-end onto `make-specifier' that allows you to create a | |
38 specifier and add specs to it at the same time. TYPE specifies the | |
39 specifier type. SPEC-LIST supplies the specification(s) to be added | |
40 to the specifier. Normally, almost any reasonable abbreviation of the | |
41 full spec-list form is accepted, and is converted to the full form; | |
42 however, if optional argument DONT-CANONICALIZE is non-nil, this | |
43 conversion is not performed, and the SPEC-LIST must already be in full | |
44 form. See `canonicalize-spec-list'." | |
45 (let ((sp (make-specifier type))) | |
46 (if (not dont-canonicalize) | |
47 (setq spec-list (canonicalize-spec-list spec-list type))) | |
48 (add-spec-list-to-specifier sp spec-list) | |
49 sp)) | |
50 | |
51 ;; God damn, do I hate dynamic scoping. | |
52 | |
53 (defun map-specifier (ms-specifier ms-func &optional ms-locale ms-maparg) | |
54 "Apply MS-FUNC to the specification(s) for MS-LOCALE in MS-SPECIFIER. | |
55 | |
56 If MS-LOCALE is a locale, MS-FUNC will be called for that locale. | |
57 If MS-LOCALE is a locale type, MS-FUNC will be mapped over all locales | |
58 of that type. If MS-LOCALE is 'all or nil, MS-FUNC will be mapped | |
59 over all locales in MS-SPECIFIER. | |
60 | |
61 MS-FUNC is called with four arguments: the MS-SPECIFIER, the locale | |
62 being mapped over, the inst-list for that locale, and the | |
63 optional MS-MAPARG. If any invocation of MS-FUNC returns non-nil, | |
64 the mapping will stop and the returned value becomes the | |
65 value returned from `map-specifier'. Otherwise, `map-specifier' | |
66 returns nil." | |
67 (let ((ms-specs (specifier-spec-list ms-specifier ms-locale)) | |
68 ms-result) | |
69 (while (and ms-specs (not ms-result)) | |
70 (let ((ms-this-spec (car ms-specs))) | |
71 (setq ms-result (funcall ms-func ms-specifier (car ms-this-spec) | |
72 (cdr ms-this-spec) ms-maparg)) | |
73 (setq ms-specs (cdr ms-specs)))) | |
74 ms-result)) | |
75 | |
76 (defun canonicalize-inst-pair (inst-pair specifier-type &optional noerror) | |
77 "Canonicalize the given INST-PAIR. | |
78 | |
79 SPECIFIER-TYPE specifies the type of specifier that this SPEC-LIST | |
80 will be used for. | |
81 | |
82 Canonicalizing means converting to the full form for an inst-pair, i.e. | |
83 `(TAG-SET . INSTANTIATOR)'. A single, untagged instantiator is given | |
84 a tag set of nil (the empty set), and a single tag is converted into | |
85 a tag set consisting only of that tag. | |
86 | |
87 If NOERROR is non-nil, signal an error if the inst-pair is invalid; | |
88 otherwise return t." | |
89 ;; OK, the possibilities are: | |
90 ;; | |
91 ;; a) a single instantiator | |
92 ;; b) a cons of a tag and an instantiator | |
93 ;; c) a cons of a tag set and an instantiator | |
94 (cond ((valid-instantiator-p inst-pair specifier-type) | |
95 ;; case (a) | |
96 (cons nil inst-pair)) | |
97 | |
98 ((not (consp inst-pair)) | |
99 ;; not an inst-pair | |
100 (if noerror t | |
101 ;; this will signal an appropriate error. | |
102 (check-valid-instantiator inst-pair specifier-type))) | |
103 | |
104 ((and (valid-specifier-tag-p (car inst-pair)) | |
105 (valid-instantiator-p (cdr inst-pair) specifier-type)) | |
106 ;; case (b) | |
107 (cons (list (car inst-pair)) (cdr inst-pair))) | |
108 | |
109 ((and (valid-specifier-tag-set-p (car inst-pair)) | |
110 (valid-instantiator-p (cdr inst-pair) specifier-type)) | |
111 ;; case (c) | |
112 inst-pair) | |
113 | |
114 (t | |
115 (if noerror t | |
116 (signal 'error (list "Invalid specifier tag set" | |
117 (car inst-pair))))))) | |
118 | |
119 (defun canonicalize-inst-list (inst-list specifier-type &optional noerror) | |
120 "Canonicalize the given INST-LIST (a list of inst-pairs). | |
121 | |
122 SPECIFIER-TYPE specifies the type of specifier that this INST-LIST | |
123 will be used for. | |
124 | |
125 Canonicalizing means converting to the full form for an inst-list, i.e. | |
126 `((TAG-SET . INSTANTIATOR) ...)'. This function accepts a single | |
127 inst-pair or any abbreviation thereof or a list of (possibly | |
128 abbreviated) inst-pairs. (See `canonicalize-inst-pair'.) | |
129 | |
130 If NOERROR is non-nil, signal an error if the inst-list is invalid; | |
131 otherwise return t." | |
132 | |
133 ;; OK, the possibilities are: | |
134 ;; | |
135 ;; a) an inst-pair or various abbreviations thereof | |
136 ;; b) a list of (a) | |
137 (let ((result (canonicalize-inst-pair inst-list specifier-type t))) | |
138 (if (not (eq result t)) | |
139 ;; case (a) | |
140 (list result) | |
141 | |
142 (if (not (consp inst-list)) | |
143 ;; not an inst-list. | |
144 (if noerror t | |
145 ;; this will signal an appropriate error. | |
146 (check-valid-instantiator inst-list specifier-type)) | |
147 | |
148 ;; case (b) | |
149 (catch 'cann-inst-list | |
150 ;; don't use mapcar here; we need to catch the case of | |
151 ;; an invalid list. | |
152 (let ((rest inst-list) | |
153 (result nil)) | |
154 (while rest | |
155 (if (not (consp rest)) | |
156 (if noerror (throw 'cann-inst-list t) | |
157 (signal 'error (list "Invalid list format" inst-list))) | |
158 (let ((res2 (canonicalize-inst-pair (car rest) specifier-type | |
159 noerror))) | |
160 (if (eq res2 t) | |
161 ;; at this point, we know we're noerror because | |
162 ;; otherwise canonicalize-inst-pair would have | |
163 ;; signalled an error. | |
164 (throw 'cann-inst-list t) | |
165 (setq result (cons res2 result))))) | |
166 (setq rest (cdr rest))) | |
167 (nreverse result))))))) | |
168 | |
169 (defun canonicalize-spec (spec specifier-type &optional noerror) | |
170 "Canonicalize the given SPEC (a specification). | |
171 | |
172 SPECIFIER-TYPE specifies the type of specifier that this SPEC-LIST | |
173 will be used for. | |
174 | |
175 Canonicalizing means converting to the full form for a spec, i.e. | |
176 `(LOCALE (TAG-SET . INSTANTIATOR) ...)'. This function accepts a | |
177 possibly abbreviated inst-list or a cons of a locale and a possibly | |
178 abbreviated inst-list. (See `canonicalize-inst-list'.) | |
179 | |
180 If NOERROR is nil, signal an error if the specification is invalid; | |
181 otherwise return t." | |
182 ;; OK, the possibilities are: | |
183 ;; | |
184 ;; a) an inst-list or some abbreviation thereof | |
185 ;; b) a cons of a locale and an inst-list | |
186 (let ((result (canonicalize-inst-list spec specifier-type t))) | |
187 (if (not (eq result t)) | |
188 ;; case (a) | |
189 (cons 'global result) | |
190 | |
191 (if (not (consp spec)) | |
192 ;; not a spec. | |
193 (if noerror t | |
194 ;; this will signal an appropriate error. | |
195 (check-valid-instantiator spec specifier-type)) | |
196 | |
197 (if (not (valid-specifier-locale-p (car spec))) | |
198 ;; invalid locale. | |
199 (if noerror t | |
200 (signal 'error (list "Invalid specifier locale" (car spec)))) | |
201 | |
202 ;; case (b) | |
203 (let ((result (canonicalize-inst-list (cdr spec) specifier-type | |
204 noerror))) | |
205 (if (eq result t) | |
206 ;; at this point, we know we're noerror because | |
207 ;; otherwise canonicalize-inst-list would have | |
208 ;; signalled an error. | |
209 t | |
210 (cons (car spec) result)))))))) | |
211 | |
212 (defun canonicalize-spec-list (spec-list specifier-type &optional noerror) | |
213 "Canonicalize the given SPEC-LIST (a list of specifications). | |
214 | |
215 SPECIFIER-TYPE specifies the type of specifier that this SPEC-LIST | |
216 will be used for. | |
217 | |
218 Canonicalizing means converting to the full form for a spec-list, i.e. | |
219 `((LOCALE (TAG-SET . INSTANTIATOR) ...) ...)'. This function accepts | |
220 a possibly abbreviated specification or a list of such things. (See | |
221 `canonicalize-spec'.) This is the function used to convert spec-lists | |
222 accepted by `set-specifier' and such into a form suitable for | |
223 `add-spec-list-to-specifier'. | |
224 | |
225 This function tries extremely hard to resolve any ambiguities, | |
226 and the built-in specifier types (font, image, toolbar, etc.) are | |
227 designed so that there won't be any ambiguities. | |
228 | |
229 If NOERROR is nil, signal an error if the spec-list is invalid; | |
230 otherwise return t." | |
231 ;; OK, the possibilities are: | |
232 ;; | |
233 ;; a) a spec or various abbreviations thereof | |
234 ;; b) a list of (a) | |
235 (let ((result (canonicalize-spec spec-list specifier-type t))) | |
236 (if (not (eq result t)) | |
237 ;; case (a) | |
238 (list result) | |
239 | |
240 (if (not (consp spec-list)) | |
241 ;; not a spec-list. | |
242 (if noerror t | |
243 ;; this will signal an appropriate error. | |
244 (check-valid-instantiator spec-list specifier-type)) | |
245 | |
246 ;; case (b) | |
247 (catch 'cann-spec-list | |
248 ;; don't use mapcar here; we need to catch the case of | |
249 ;; an invalid list. | |
250 (let ((rest spec-list) | |
251 (result nil)) | |
252 (while rest | |
253 (if (not (consp rest)) | |
254 (if noerror (throw 'cann-spec-list t) | |
255 (signal 'error (list "Invalid list format" spec-list))) | |
256 (let ((res2 (canonicalize-spec (car rest) specifier-type | |
257 noerror))) | |
258 (if (eq res2 t) | |
259 ;; at this point, we know we're noerror because | |
260 ;; otherwise canonicalize-spec would have | |
261 ;; signalled an error. | |
262 (throw 'cann-spec-list t) | |
263 (setq result (cons res2 result))))) | |
264 (setq rest (cdr rest))) | |
265 (nreverse result))))))) | |
266 | |
267 (defun set-specifier (specifier value &optional locale tag-set how-to-add) | |
268 "Add a specification or specifications to SPECIFIER. | |
269 | |
270 This function adds a specification of VALUE in locale LOCALE. | |
271 LOCALE indicates where this specification is active, and should be | |
272 a buffer, a window, a frame, a device, or the symbol `global' to | |
273 indicate that it applies everywhere. LOCALE usually defaults to | |
274 `global' if omitted. | |
275 | |
276 VALUE is usually what is called an \"instantiator\" (which, roughly | |
277 speaking, corresponds to the \"value\" of the property governed by | |
278 SPECIFIER). The valid instantiators for SPECIFIER depend on the | |
279 type of SPECIFIER (which you can determine using `specifier-type'). | |
280 The specifier `scrollbar-width', for example, is of type `integer', | |
281 meaning its valid instantiators are integers. The specifier | |
282 governing the background color of the `default' face (you can | |
283 retrieve this specifier using `(face-background 'default)') is | |
284 of type `color', meaning its valid instantiators are strings naming | |
285 colors and color-instance objects. For some types of specifiers, | |
286 such as `image' and `toolbar', the instantiators can be very | |
287 complex. Generally this is documented in the appropriate predicate | |
288 function -- `color-specifier-p', `image-specifier-p', | |
289 `toolbar-specifier-p', etc. | |
290 | |
291 NOTE: It does *not* work to give a VALUE of nil as a way of | |
292 removing the specifications for a locale. Use `remove-specifier' | |
293 instead. (And keep in mind that, if you omit the LOCALE argument | |
294 to `remove-specifier', it removes *all* specifications! If you | |
295 want to remove just the `global' specification, make sure to | |
296 specify a LOCALE of `global'.) | |
297 | |
298 VALUE can also be a list of instantiators. This means basically, | |
299 \"try each one in turn until you get one that works\". This allows | |
300 you to give funky instantiators that may only work in some cases, | |
301 and provide more normal backups for the other cases. (For example, | |
302 you might like the color \"darkseagreen2\", but some X servers | |
303 don't recognize this color, so you could provide a backup | |
304 \"forest green\". Color TTY devices probably won't recognize this | |
305 either, so you could provide a second backup \"green\". You'd | |
306 do this by specifying this list of instantiators: | |
307 | |
308 '(\"darkseagreen2\" \"forest green\" \"green\") | |
309 | |
310 VALUE can also be various more complicated forms; see below. | |
311 | |
312 Optional argument TAG-SET is a tag or a list of tags, to be associated | |
313 with the VALUE. Tags are symbols (usually naming device types, such | |
314 as `x' and `tty', or device classes, such as `color', `mono', and | |
315 `grayscale'); specifying a TAG-SET restricts the scope of VALUE to | |
316 devices that match all specified tags. (You can also create your | |
317 own tags using `define-specifier-tag', and use them to identify | |
318 specifications added by you, so you can remove them later.) | |
319 | |
320 Optional argument HOW-TO-ADD should be either nil or one of the | |
321 symbols `prepend', `append', `remove-tag-set-prepend', | |
322 `remove-tag-set-append', `remove-locale', `remove-locale-type', | |
323 or `remove-all'. This specifies what to do with existing | |
324 specifications in LOCALE (and possibly elsewhere in the specifier). | |
325 Most of the time, you do not need to worry about this argument; | |
326 the default behavior of `remove-tag-set-prepend' is usually fine. | |
327 See `copy-specifier' and `add-spec-to-specifier' for a full | |
328 description of what each of these means. | |
329 | |
330 VALUE can actually be anything acceptable to `canonicalize-spec-list'; | |
331 this includes, among other things: | |
332 | |
333 -- a cons of a locale and an instantiator (or list of instantiators) | |
334 -- a cons of a tag or tag-set and an instantiator (or list of | |
335 instantiators) | |
336 -- a cons of a locale and the previous type of item | |
337 -- a list of one or more of any of the previous types of items | |
338 | |
339 However, in these cases, you cannot give a LOCALE or TAG-SET, | |
340 because they do not make sense. (You will probably get an error if | |
341 you try this.) | |
342 | |
343 Finally, VALUE can itself be a specifier (of the same type as | |
344 SPECIFIER), if you want to copy specifications from one specifier | |
345 to another; this is equivalent to calling `copy-specifier', and | |
346 LOCALE, TAG-SET, and HOW-TO-ADD have the same semantics as with | |
347 that function. | |
348 | |
349 Note that `set-specifier' is exactly complementary to `specifier-specs' | |
350 except in the case where SPECIFIER has no specs at all in it but nil | |
351 is a valid instantiator (in that case, `specifier-specs' will return | |
352 nil (meaning no specs) and `set-specifier' will interpret the `nil' | |
353 as meaning \"I'm adding a global instantiator and its value is `nil'\"), | |
354 or in strange cases where there is an ambiguity between a spec-list | |
355 and an inst-list, etc. (The built-in specifier types are designed | |
356 in such a way as to avoid any such ambiguities.) | |
357 | |
358 NOTE: If you want to work with spec-lists, you should probably not | |
359 use either `set-specifier' or `specifier-specs', but should use the | |
360 lower-level functions `add-spec-list-to-specifier' and `specifier-spec-list'. | |
361 These functions always work with fully-qualified spec-lists; thus, there | |
362 is no possibility for ambiguity and no need to go through the function | |
363 `canonicalize-spec-list', which is potentially time-consuming." | |
364 | |
365 ;; backward compatibility: the old function had HOW-TO-ADD as the | |
366 ;; third argument and no arguments after that. | |
367 ;; #### this should disappear at some point. | |
368 (if (and (null how-to-add) | |
369 (memq locale '(prepend append remove-tag-set-prepend | |
370 remove-tag-set-append remove-locale | |
371 remove-locale-type remove-all))) | |
372 (progn | |
373 (setq how-to-add locale) | |
374 (setq locale nil))) | |
375 | |
376 ;; proper beginning of the function. | |
377 (let ((is-valid (valid-instantiator-p value (specifier-type specifier))) | |
378 (nval value)) | |
379 (cond ((and (not is-valid) (specifierp nval)) | |
380 (copy-specifier nval specifier locale tag-set nil how-to-add)) | |
381 (t | |
382 (if tag-set | |
383 (progn | |
384 (if (not (listp tag-set)) | |
385 (setq tag-set (list tag-set))) | |
386 ;; You tend to get more accurate errors | |
387 ;; for a variety of cases if you call | |
388 ;; canonicalize-tag-set here. | |
389 (setq tag-set (canonicalize-tag-set tag-set)) | |
390 (if (and (not is-valid) (consp nval)) | |
391 (setq nval | |
392 (mapcar #'(lambda (x) | |
393 (check-valid-instantiator | |
394 x (specifier-type specifier)) | |
395 (cons tag-set x)) | |
396 nval)) | |
397 (setq nval (cons tag-set nval))))) | |
398 (if locale | |
399 (setq nval (cons locale nval))) | |
400 (add-spec-list-to-specifier | |
401 specifier | |
402 (canonicalize-spec-list nval (specifier-type specifier)) | |
403 how-to-add)))) | |
404 value) | |
405 | |
406 (define-specifier-tag 'win 'device-on-window-system-p) | |
407 | |
408 ;;; specifier.el ends here |