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)