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