~ubuntu-branches/ubuntu/lucid/sawfish/lucid-updates

« back to all changes in this revision

Viewing changes to lisp/sawfish/wm/custom.jl

  • Committer: Bazaar Package Importer
  • Author(s): Christian Marillat
  • Date: 2002-01-20 17:42:28 UTC
  • Revision ID: james.westby@ubuntu.com-20020120174228-4q1ydztbkvfq1ht2
Tags: upstream-1.0.1.20020116
ImportĀ upstreamĀ versionĀ 1.0.1.20020116

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
;; custom.jl -- Emacs-like ``customizing'' (but more simple)
 
2
;; $Id: custom.jl,v 1.61 2001/04/11 21:48:37 jsh Exp $
 
3
 
 
4
;; Copyright (C) 1999 John Harper <john@dcs.warwick.ac.uk>
 
5
 
 
6
;; This file is part of sawmill.
 
7
 
 
8
;; sawmill is free software; you can redistribute it and/or modify it
 
9
;; under the terms of the GNU General Public License as published by
 
10
;; the Free Software Foundation; either version 2, or (at your option)
 
11
;; any later version.
 
12
 
 
13
;; sawmill is distributed in the hope that it will be useful, but
 
14
;; WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
;; GNU General Public License for more details.
 
17
 
 
18
;; You should have received a copy of the GNU General Public License
 
19
;; along with sawmill; see the file COPYING.  If not, write to
 
20
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
21
 
 
22
(define-structure sawfish.wm.custom
 
23
 
 
24
    (export custom-groups
 
25
            custom-option-alist
 
26
            custom-group-option-alist
 
27
            defcustom
 
28
            defgroup
 
29
            custom-declare-variable
 
30
            custom-declare-group
 
31
            custom-quote-keys
 
32
            define-custom-setter
 
33
            custom-set-property
 
34
            custom-set-group-property
 
35
            custom-get-group-property
 
36
            custom-add-option
 
37
            custom-get-options
 
38
            custom-find-group
 
39
            custom-set
 
40
            custom-set-variable
 
41
            custom-set-typed-variable
 
42
            variable-customized-p
 
43
            variable-type
 
44
            variable-default-value
 
45
            variable-declared-p
 
46
            make-custom-form
 
47
            custom-eval
 
48
            custom-serialize
 
49
            custom-deserialize
 
50
            define-custom-serializer
 
51
            define-custom-deserializer
 
52
            custom-load-user-file)
 
53
 
 
54
    (open rep
 
55
          rep.io.files
 
56
          rep.data.tables
 
57
          rep.structures
 
58
          rep.system
 
59
          sawfish.wm.commands
 
60
          sawfish.wm.gaol
 
61
          sawfish.wm.colors
 
62
          sawfish.wm.fonts
 
63
          sawfish.wm.misc)
 
64
 
 
65
  (define-structure-alias custom sawfish.wm.custom)
 
66
 
 
67
  ;; list associating groups with the list of variables in that group
 
68
  (define custom-groups (list 'root "Sawfish"))
 
69
 
 
70
  (defvar custom-user-file "~/.sawfish/custom"
 
71
    "File used to store user's configuration settings.")
 
72
 
 
73
  (defvar custom-default-file (expand-file-name "sawfish/wm/custom-defaults.jl"
 
74
                                                sawfish-lisp-lib-directory)
 
75
    "Lisp library storing default customization settings.")
 
76
 
 
77
  (define custom-quoted-keys
 
78
    '(:group :require :type :options :range :depends :user-level
 
79
      :layout :widget-flags)
 
80
    "defcustom keys whose values are quoted by the macro expansion.")
 
81
 
 
82
  (define custom-option-alist '((:group . custom-group)
 
83
                                (:require . custom-require)
 
84
                                (:type . custom-type)
 
85
                                (:type* . custom-type)
 
86
                                (:options . custom-options)
 
87
                                (:depends . custom-depends)
 
88
                                (:user-level . custom-user-level)
 
89
                                (:set . custom-set)
 
90
                                (:get . custom-get)
 
91
                                (:widget . custom-widget)
 
92
                                (:after-set . custom-after-set)
 
93
                                (:before-set . custom-before-set)
 
94
                                (:range . custom-range)
 
95
                                (:widget-flags . custom-widget-flags)))
 
96
 
 
97
  (define custom-group-option-alist '((:layout . custom-group-layout)
 
98
                                      (:require . custom-group-require)))
 
99
 
 
100
  ;; hash group names (lists of symbols) to alist of options 
 
101
  (define custom-group-table (make-table equal-hash equal))
 
102
 
 
103
 
 
104
;;; defining custom variables and groups
 
105
 
 
106
  (defmacro defcustom (symbol value doc #!rest keys)
 
107
    "Define a new customization variable SYMBOL which initially has value
 
108
VALUE (unless SYMBOL is already bound, in which case its value is not
 
109
altered), and documentations string DOC.
 
110
 
 
111
KEYS is a property-list containing any of the following:
 
112
 
 
113
        :group GROUP
 
114
        :require FEATURE
 
115
        :type TYPE
 
116
        :options OPTIONS
 
117
        :depends SYMBOL
 
118
        :user-level LEVEL               novice, intermediate, expert
 
119
        :range (MIN . MAX)              for `number' type
 
120
        :set FUNCTION
 
121
        :get FUNCTION
 
122
        :before-set FUNCTION
 
123
        :after-set FUNCTION
 
124
        :widget FUNCTION
 
125
 
 
126
TYPE may be `boolean', `number', `string', `symbol', `file-name',
 
127
`program-name', `font', `color'.
 
128
 
 
129
Note that the values of the `:group', `:require', `:type', `:options',
 
130
`:depends', `:user-level' and `:range' keys are not evaluated. All
 
131
other key values are evaluated.
 
132
 
 
133
Each defcustom'd symbol may have several special properties
 
134
 
 
135
        custom-set (FUNCTION SYMBOL VALUE)
 
136
        custom-get (FUNCTION SYMBOL)
 
137
        custom-widget (FUNCTION SYMBOL)
 
138
 
 
139
These functions are used while constructing and responding to the
 
140
customisation dialog. If not set in the symbol itself they may be
 
141
inherited from the plist of the type of the variable.
 
142
 
 
143
custom-set and custom-get may be used to translate data types to the
 
144
string representation required by some widget types. custom-widget may
 
145
construct the widget definition passed to the ui backend."
 
146
 
 
147
    (let* ((cell (memq ':tooltip keys))
 
148
           (tooltip (cadr cell))
 
149
           (tem (gensym)))
 
150
      (when cell
 
151
        (setq keys (delq (car cell) (delq (cadr cell) keys))))
 
152
      `(let ((,tem ,value))
 
153
         (defvar ,symbol ,tem ,(if tooltip (concat doc "\n\n" tooltip) doc))
 
154
         (custom-declare-variable ',symbol ,(custom-quote-keys keys) ,tem))))
 
155
 
 
156
  (defmacro defgroup (symbol doc #!rest keys)
 
157
    "Declare a new custom group called SYMBOL, with English name DOC. The
 
158
property list KEYS may contain the following key-value items:
 
159
 
 
160
        :group PARENT-GROUP
 
161
        :layout LAYOUT-TYPE
 
162
 
 
163
Note that the value of the `:group' key is not evaluated."
 
164
 
 
165
    `(custom-declare-group ',symbol ,doc ,(custom-quote-keys keys)))
 
166
 
 
167
  (define (custom-declare-variable symbol keys #!optional default-value)
 
168
    (let (type prop)
 
169
      (while keys
 
170
        (setq prop (cdr (assq (car keys) custom-option-alist)))
 
171
        (setq keys (cdr keys))
 
172
        (when prop
 
173
          (put symbol prop (car keys)))
 
174
        (setq keys (cdr keys)))
 
175
      (custom-add-to-group symbol (get symbol 'custom-group))
 
176
      (setq type (get symbol 'custom-type))
 
177
      (when (eq (car type) 'set)
 
178
        ;; backwards compatibility
 
179
        (put symbol 'custom-options (cdr type))
 
180
        (setq type 'symbol)
 
181
        (put symbol 'custom-type type))
 
182
      (when (and type (symbolp type))
 
183
        (when (and (not (get symbol 'custom-get)) (get type 'custom-get))
 
184
          (put symbol 'custom-get (get type 'custom-get)))
 
185
        (when (and (not (get symbol 'custom-set)) (get type 'custom-set))
 
186
          (put symbol 'custom-set (get type 'custom-set)))
 
187
        (when (and (not (get symbol 'custom-widget)) (get type 'custom-widget))
 
188
          (put symbol 'custom-widget (get type 'custom-widget))))
 
189
      (put symbol 'custom-default-value default-value)
 
190
      symbol))
 
191
 
 
192
  (define (custom-declare-group group #!optional doc keys)
 
193
    (let (container)
 
194
      (while keys
 
195
        (case (car keys)
 
196
          ((:group)
 
197
           (setq container (nth 1 keys))
 
198
           (unless (listp container)
 
199
             (setq container (list container))))
 
200
          ((:require)
 
201
           (custom-group-requires (append container (list group)) (cadr keys)))
 
202
          (t
 
203
           (custom-set-group-property (append container (list group))
 
204
                                      (car keys) (cadr keys))))
 
205
        (setq keys (cddr keys)))
 
206
      (custom-add-to-group (list group doc) container)
 
207
      (unless container
 
208
        ;; declare a command to customize this group
 
209
        (define-command (intern (concat "customize:" (symbol-name group)))
 
210
                        (lambda ()
 
211
                          (require 'sawfish.wm.customize)
 
212
                          (customize group))))))
 
213
 
 
214
  (define (custom-quote-keys keys)
 
215
    (let ((out '()))
 
216
      (while (and keys (cdr keys))
 
217
        (if (memq (car keys) custom-quoted-keys)
 
218
            (setq out (cons (list 'quote (nth 1 keys))
 
219
                            (cons (list 'quote (car keys)) out)))
 
220
          (setq out (cons (nth 1 keys) (cons (list 'quote (car keys)) out))))
 
221
        (setq keys (nthcdr 2 keys)))
 
222
      (cons 'list (nreverse out))))
 
223
 
 
224
 
 
225
;;; general management
 
226
 
 
227
  (define custom-setter-table (make-table symbol-hash eq))
 
228
 
 
229
  (define (define-custom-setter name def)
 
230
    (table-set custom-setter-table name def))
 
231
 
 
232
  (define (custom-setter name)
 
233
    (or (table-ref custom-setter-table name)
 
234
        (error "No such custom setter: %s" name)))
 
235
      
 
236
  (defmacro custom-set-property (sym prop value)
 
237
    "Set the custom key PROP for defcustom'd symbol SYM to value."
 
238
    (let ((tem (gensym)))
 
239
      `(let ((,tem (cdr (assq ,prop custom-option-alist))))
 
240
         (when ,tem
 
241
           (put ,sym ,tem ,value)))))
 
242
 
 
243
  (define (custom-set-group-property group prop value)
 
244
    "Set the custom key PROP for defgroup'd symbol SYM to value."
 
245
    (unless (listp group)
 
246
      (setq group (list group)))
 
247
    (let* ((alist (table-ref custom-group-table group))
 
248
           (cell (and alist (assq prop alist))))
 
249
      (if cell
 
250
          (rplacd cell value)
 
251
        (setq alist (cons (cons prop value) alist))
 
252
        (table-set custom-group-table group alist))))
 
253
 
 
254
  (define (custom-get-group-property group prop)
 
255
    (unless (listp group)
 
256
      (setq group (list group)))
 
257
    (let ((alist (table-ref custom-group-table group)))
 
258
      (cdr (assq prop alist))))
 
259
 
 
260
  (define (custom-group-requires group feature)
 
261
    (unless (listp group)
 
262
      (setq group (list group)))
 
263
    (custom-set-group-property
 
264
     group ':require
 
265
     (cons feature (delq feature
 
266
                         (custom-get-group-property ':require group)))))
 
267
 
 
268
  (defmacro custom-add-option (sym option)
 
269
    "Assuming that defcustom'd symbol SYM is of type `symbol', add the
 
270
symbol OPTION to the list of choices."
 
271
    `(put ,sym 'custom-options
 
272
          (nconc (get ,sym 'custom-options) (list ,option))))
 
273
 
 
274
  (defmacro custom-get-options (sym)
 
275
    "Assuming that defcustom'd symbol SYM is of type `symbol', return the
 
276
of choices."
 
277
    `(get ,sym 'custom-options))
 
278
 
 
279
  (define (custom-find-group full-group)
 
280
    (when (and (symbolp full-group) (not (null full-group)))
 
281
      (setq full-group (list full-group)))
 
282
    (let loop ((group full-group)
 
283
               (parent custom-groups))
 
284
      (if (null group)
 
285
          parent
 
286
        (loop (cdr group)
 
287
              (or (assq (car group) (cddr parent))
 
288
                  (error "No such group: %S" full-group))))))
 
289
 
 
290
  (define (custom-add-to-group cell full-group)
 
291
    (when (and (symbolp full-group) (not (null full-group)))
 
292
      (setq full-group (list full-group)))
 
293
    (let loop ((group full-group)
 
294
               (parent custom-groups))
 
295
      (if (null group)
 
296
          (unless (or (memq cell (cddr parent))
 
297
                      (assq (car cell) (cddr parent)))
 
298
            ;; reached the bottom most group
 
299
            (rplacd (cdr parent) (nconc (cddr parent) (list cell))))
 
300
        ;; keep on recursing
 
301
        (loop (cdr group)
 
302
              (or (assq (car group) (cddr parent))
 
303
                  (error "Unknown group %s" full-group)))
 
304
        (unless (cdr group)
 
305
          (rplacd (cdr custom-groups)
 
306
                  (nconc (sort (filter consp (cddr custom-groups))
 
307
                               (lambda (x y)
 
308
                                 (string-lessp (cadr x) (cadr y))))
 
309
                         (filter atom (cddr custom-groups))))))))
 
310
 
 
311
 
 
312
;;; setting values
 
313
 
 
314
  (define (custom-set setter symbol)
 
315
    (when (get symbol 'custom-before-set)
 
316
      ((get symbol 'custom-before-set) symbol))
 
317
    (make-variable-special symbol)
 
318
    (setter)
 
319
    (put symbol 'custom-user-value t)
 
320
    (when (get symbol 'custom-after-set)
 
321
      ((get symbol 'custom-after-set) symbol)))
 
322
 
 
323
  (define (custom-set-symbol setter symbol)
 
324
    (let* ((was-bound (boundp symbol))
 
325
           (old-value (and was-bound (symbol-value symbol))))
 
326
      (call-with-exception-handler
 
327
       (lambda ()
 
328
         (custom-set setter symbol))
 
329
       (lambda (ex)
 
330
         ;; error while setting SYMBOL; revert to its old state
 
331
         (if was-bound
 
332
             (set symbol old-value)
 
333
           (makunbound symbol))
 
334
         (raise-exception ex)))))
 
335
 
 
336
  (define (custom-set-variable symbol value #!optional req)
 
337
    ;; XXX kludge for old custom files..
 
338
    (when (eq value 'nil) (setq value nil))
 
339
    (when (and req value)
 
340
      ;; load in the user module in case it's a file of bare code
 
341
      (user-require req))
 
342
    (custom-set-symbol (lambda ()
 
343
                         (make-variable-special symbol)
 
344
                         (set symbol value)) symbol))
 
345
 
 
346
  (define (custom-set-typed-variable symbol value type #!optional req)
 
347
    ;; XXX kludge for old custom files..
 
348
    (when (eq value 'nil) (setq value nil))
 
349
    (when (and req value)
 
350
      (user-require req))
 
351
    (custom-set-symbol (lambda ()
 
352
                         (make-variable-special symbol)
 
353
                         (set symbol (custom-deserialize value type)))
 
354
                       symbol))
 
355
 
 
356
  (define (variable-customized-p symbol)
 
357
    "Returns `t' if the variable named SYMBOL has been customized by the user."
 
358
    (get symbol 'custom-user-value))
 
359
 
 
360
  (define (variable-type symbol)
 
361
    "Returns the customizable type of the variable named SYMBOL."
 
362
    (get symbol 'custom-type))
 
363
 
 
364
  (define (variable-default-value symbol)
 
365
    "Returns the default value of SYMBOL."
 
366
    (get symbol 'custom-default-value))
 
367
 
 
368
  ;; if the custom variable has been declared, then it will always
 
369
  ;; have a default-value property
 
370
  (define (variable-declared-p symbol)
 
371
    (memq 'custom-default-value (symbol-plist symbol)))
 
372
 
 
373
  (define (make-custom-form symbol value)
 
374
    (let ((fun (or (get symbol 'custom-set) 'custom-set-typed-variable))
 
375
          (custom-value (custom-serialize value (variable-type symbol))))
 
376
      `(,fun ',symbol ',custom-value
 
377
        ,@(and (eq fun 'custom-set-typed-variable)
 
378
               (list (list 'quote (variable-type symbol))))
 
379
        ,@(and (get symbol 'custom-require)
 
380
               (list (list 'quote (get symbol 'custom-require)))))))
 
381
 
 
382
  (define (custom-eval form)
 
383
    (apply (custom-setter (car form))
 
384
           (mapcar (lambda (x)
 
385
                     (if (eq (car x) 'quote)
 
386
                         (cadr x)
 
387
                       ;; XXX alternatives to user-eval
 
388
                       (user-eval x))) (cdr form))))
 
389
 
 
390
 
 
391
;;; serializing unreadable types
 
392
 
 
393
  ;; property name (symbol) to find type converters on during custom-convert
 
394
  (define custom-converter-property (make-fluid))
 
395
 
 
396
  ;; convert VALUE of TYPE to or from a printable object
 
397
  (define (custom-convert value type)
 
398
    (case (or (car type) type)
 
399
      ((optional)
 
400
       (custom-convert value (cadr type)))
 
401
 
 
402
      ((labelled)
 
403
       (custom-convert value (caddr type)))
 
404
 
 
405
      ((quoted)
 
406
       `(quote ,(custom-convert (cadr value) (cadr type))))
 
407
 
 
408
      ((pair)
 
409
       (cons (custom-convert (car value) (cadr type))
 
410
             (custom-convert (cdr value) (caddr type))))
 
411
 
 
412
      ((list)
 
413
       (mapcar (lambda (x) (custom-convert x (cadr type))) value))
 
414
 
 
415
      ((alist)
 
416
       (let ((k-type (or (car (cadr type)) (cadr type)))
 
417
             (v-type (or (car (caddr type)) (caddr type))))
 
418
         (mapcar (lambda (x)
 
419
                   (cons (custom-convert (car x) k-type)
 
420
                         (custom-convert (cdr x) v-type))) value)))
 
421
 
 
422
      ((and v-and h-and)
 
423
       (do ((values value (cdr values))
 
424
            (types (cdr type) (cdr types))
 
425
            (out '() (cons (custom-convert (car values) (car types)) out)))
 
426
           ((or (null types) (null values))
 
427
            (nreverse out))))
 
428
 
 
429
      ;; XXX handle `or' (needs type predicates)
 
430
 
 
431
      (t (let ((converter (get (or (car type) type)
 
432
                               (fluid custom-converter-property))))
 
433
           (if converter
 
434
               (converter value type)
 
435
             value)))))
 
436
 
 
437
  (define (custom-serialize value type)
 
438
    "Convert VALUE of TYPE to a printable value."
 
439
    (let-fluids ((custom-converter-property 'custom-serializer))
 
440
      (custom-convert value type)))
 
441
 
 
442
  (define (custom-deserialize value type)
 
443
    "Convert VALUE of TYPE back from a printable value."
 
444
    (let-fluids ((custom-converter-property 'custom-deserializer))
 
445
      (custom-convert value type)))
 
446
 
 
447
  (define (define-custom-serializer type fun)
 
448
    (put type 'custom-serializer fun))
 
449
 
 
450
  (define (define-custom-deserializer type fun)
 
451
    (put type 'custom-deserializer fun))
 
452
 
 
453
 
 
454
;;; support for font and color primitive types
 
455
 
 
456
  (define-custom-serializer 'font (lambda (value)
 
457
                                    (if (fontp value)
 
458
                                        (font-name value)
 
459
                                      value)))
 
460
 
 
461
  (define-custom-deserializer 'font (lambda (value)
 
462
                                      (if (stringp value)
 
463
                                          (get-font value)
 
464
                                        value)))
 
465
 
 
466
  (define-custom-serializer 'color (lambda (value)
 
467
                                     (if (colorp value)
 
468
                                         (color-name value)
 
469
                                       value)))
 
470
 
 
471
  (define-custom-deserializer 'color (lambda (value)
 
472
                                       (if (stringp value)
 
473
                                           (get-color value)
 
474
                                         value)))
 
475
 
 
476
 
 
477
;;; default groups
 
478
 
 
479
  (defgroup focus "Focus" :require sawfish.wm.ext.auto-raise)
 
480
  (defgroup move "Move/Resize" :require sawfish.wm.commands.move-resize)
 
481
  (defgroup placement "Placement")
 
482
  (defgroup appearance "Appearance")
 
483
  (defgroup workspace "Workspaces")
 
484
  (defgroup bindings "Bindings")
 
485
  (defgroup min-max "Minimizing/Maximizing")
 
486
  (defgroup iconify "Minimizing" :group min-max)
 
487
  (defgroup maximize "Maximizing" :group min-max)
 
488
  (defgroup misc "Miscellaneous")
 
489
 
 
490
 
 
491
;;; loading user's customisations
 
492
 
 
493
  (define (custom-load filename)
 
494
    (let ((file (open-file filename 'read)))
 
495
      (unwind-protect
 
496
          (condition-case nil
 
497
              (while t
 
498
                (let ((form (read file)))
 
499
                  (call-with-error-handler
 
500
                   (lambda () (custom-eval form)))))
 
501
            (end-of-stream))
 
502
        (close-file file))))
 
503
 
 
504
  (define (custom-load-user-file)
 
505
    "Load the user's customization file, or the custom-default-file."
 
506
    (cond ((file-exists-p custom-user-file)
 
507
           (custom-load custom-user-file))
 
508
          (custom-default-file
 
509
           (custom-load custom-default-file))))
 
510
 
 
511
 
 
512
;;; init
 
513
 
 
514
  (let ((tem (get-command-line-option "--custom-file" t)))
 
515
    (when tem
 
516
      (setq custom-user-file tem)))
 
517
 
 
518
  (define-custom-setter 'custom-set-variable custom-set-variable)
 
519
  (define-custom-setter 'custom-set-typed-variable custom-set-typed-variable)
 
520
 
 
521
  (gaol-add defcustom defgroup custom-declare-variable custom-declare-group
 
522
            custom-quote-keys custom-set-property custom-set-group-property
 
523
            custom-option-alist custom-group-option-alist))