diff options
| author | Per Abrahamsen | 1997-04-07 13:42:59 +0000 |
|---|---|---|
| committer | Per Abrahamsen | 1997-04-07 13:42:59 +0000 |
| commit | d543e20b611fc289b174aa82cab940d873a586ff (patch) | |
| tree | e386ce6041bb396f332302365940779dfa76e33a /lisp | |
| parent | 383ebe4953db084cb3695eba5486e1c905907eb5 (diff) | |
| download | emacs-d543e20b611fc289b174aa82cab940d873a586ff.tar.gz emacs-d543e20b611fc289b174aa82cab940d873a586ff.zip | |
Initial revision
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/cus-edit.el | 1993 | ||||
| -rw-r--r-- | lisp/cus-face.el | 590 | ||||
| -rw-r--r-- | lisp/custom.el | 2726 | ||||
| -rw-r--r-- | lisp/wid-browse.el | 232 | ||||
| -rw-r--r-- | lisp/wid-edit.el | 2542 | ||||
| -rw-r--r-- | lisp/widget.el | 76 |
6 files changed, 5726 insertions, 2433 deletions
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el new file mode 100644 index 00000000000..0327c7aa286 --- /dev/null +++ b/lisp/cus-edit.el | |||
| @@ -0,0 +1,1993 @@ | |||
| 1 | ;;; cus-edit.el --- Tools for customization Emacs. | ||
| 2 | ;; | ||
| 3 | ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc. | ||
| 4 | ;; | ||
| 5 | ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> | ||
| 6 | ;; Keywords: help, faces | ||
| 7 | ;; Version: 1.71 | ||
| 8 | ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ | ||
| 9 | |||
| 10 | ;;; Commentary: | ||
| 11 | ;; | ||
| 12 | ;; See `custom.el'. | ||
| 13 | |||
| 14 | ;;; Code: | ||
| 15 | |||
| 16 | (require 'cus-face) | ||
| 17 | (require 'wid-edit) | ||
| 18 | (require 'easymenu) | ||
| 19 | |||
| 20 | (define-widget-keywords :custom-prefixes :custom-menu :custom-show | ||
| 21 | :custom-magic :custom-state :custom-level :custom-form | ||
| 22 | :custom-set :custom-save :custom-reset-current :custom-reset-saved | ||
| 23 | :custom-reset-factory) | ||
| 24 | |||
| 25 | ;;; Customization Groups. | ||
| 26 | |||
| 27 | (defgroup emacs nil | ||
| 28 | "Customization of the One True Editor." | ||
| 29 | :link '(custom-manual "(emacs)Top")) | ||
| 30 | |||
| 31 | ;; Most of these groups are stolen from `finder.el', | ||
| 32 | (defgroup editing nil | ||
| 33 | "Basic text editing facilities." | ||
| 34 | :group 'emacs) | ||
| 35 | |||
| 36 | (defgroup abbrev nil | ||
| 37 | "Abbreviation handling, typing shortcuts, macros." | ||
| 38 | :tag "Abbreviations" | ||
| 39 | :group 'editing) | ||
| 40 | |||
| 41 | (defgroup matching nil | ||
| 42 | "Various sorts of searching and matching." | ||
| 43 | :group 'editing) | ||
| 44 | |||
| 45 | (defgroup emulations nil | ||
| 46 | "Emulations of other editors." | ||
| 47 | :group 'editing) | ||
| 48 | |||
| 49 | (defgroup mouse nil | ||
| 50 | "Mouse support." | ||
| 51 | :group 'editing) | ||
| 52 | |||
| 53 | (defgroup outlines nil | ||
| 54 | "Support for hierarchical outlining." | ||
| 55 | :group 'editing) | ||
| 56 | |||
| 57 | (defgroup external nil | ||
| 58 | "Interfacing to external utilities." | ||
| 59 | :group 'emacs) | ||
| 60 | |||
| 61 | (defgroup bib nil | ||
| 62 | "Code related to the `bib' bibliography processor." | ||
| 63 | :tag "Bibliography" | ||
| 64 | :group 'external) | ||
| 65 | |||
| 66 | (defgroup processes nil | ||
| 67 | "Process, subshell, compilation, and job control support." | ||
| 68 | :group 'external | ||
| 69 | :group 'development) | ||
| 70 | |||
| 71 | (defgroup programming nil | ||
| 72 | "Support for programming in other languages." | ||
| 73 | :group 'emacs) | ||
| 74 | |||
| 75 | (defgroup languages nil | ||
| 76 | "Specialized modes for editing programming languages." | ||
| 77 | :group 'programming) | ||
| 78 | |||
| 79 | (defgroup lisp nil | ||
| 80 | "Lisp support, including Emacs Lisp." | ||
| 81 | :group 'languages | ||
| 82 | :group 'development) | ||
| 83 | |||
| 84 | (defgroup c nil | ||
| 85 | "Support for the C language and related languages." | ||
| 86 | :group 'languages) | ||
| 87 | |||
| 88 | (defgroup tools nil | ||
| 89 | "Programming tools." | ||
| 90 | :group 'programming) | ||
| 91 | |||
| 92 | (defgroup oop nil | ||
| 93 | "Support for object-oriented programming." | ||
| 94 | :group 'programming) | ||
| 95 | |||
| 96 | (defgroup applications nil | ||
| 97 | "Applications written in Emacs." | ||
| 98 | :group 'emacs) | ||
| 99 | |||
| 100 | (defgroup calendar nil | ||
| 101 | "Calendar and time management support." | ||
| 102 | :group 'applications) | ||
| 103 | |||
| 104 | (defgroup mail nil | ||
| 105 | "Modes for electronic-mail handling." | ||
| 106 | :group 'applications) | ||
| 107 | |||
| 108 | (defgroup news nil | ||
| 109 | "Support for netnews reading and posting." | ||
| 110 | :group 'applications) | ||
| 111 | |||
| 112 | (defgroup games nil | ||
| 113 | "Games, jokes and amusements." | ||
| 114 | :group 'applications) | ||
| 115 | |||
| 116 | (defgroup development nil | ||
| 117 | "Support for further development of Emacs." | ||
| 118 | :group 'emacs) | ||
| 119 | |||
| 120 | (defgroup docs nil | ||
| 121 | "Support for Emacs documentation." | ||
| 122 | :group 'development) | ||
| 123 | |||
| 124 | (defgroup extensions nil | ||
| 125 | "Emacs Lisp language extensions." | ||
| 126 | :group 'development) | ||
| 127 | |||
| 128 | (defgroup internal nil | ||
| 129 | "Code for Emacs internals, build process, defaults." | ||
| 130 | :group 'development) | ||
| 131 | |||
| 132 | (defgroup maint nil | ||
| 133 | "Maintenance aids for the Emacs development group." | ||
| 134 | :tag "Maintenance" | ||
| 135 | :group 'development) | ||
| 136 | |||
| 137 | (defgroup environment nil | ||
| 138 | "Fitting Emacs with its environment." | ||
| 139 | :group 'emacs) | ||
| 140 | |||
| 141 | (defgroup comm nil | ||
| 142 | "Communications, networking, remote access to files." | ||
| 143 | :tag "Communication" | ||
| 144 | :group 'environment) | ||
| 145 | |||
| 146 | (defgroup hardware nil | ||
| 147 | "Support for interfacing with exotic hardware." | ||
| 148 | :group 'environment) | ||
| 149 | |||
| 150 | (defgroup terminals nil | ||
| 151 | "Support for terminal types." | ||
| 152 | :group 'environment) | ||
| 153 | |||
| 154 | (defgroup unix nil | ||
| 155 | "Front-ends/assistants for, or emulators of, UNIX features." | ||
| 156 | :group 'environment) | ||
| 157 | |||
| 158 | (defgroup vms nil | ||
| 159 | "Support code for vms." | ||
| 160 | :group 'environment) | ||
| 161 | |||
| 162 | (defgroup i18n nil | ||
| 163 | "Internationalization and alternate character-set support." | ||
| 164 | :group 'environment | ||
| 165 | :group 'editing) | ||
| 166 | |||
| 167 | (defgroup frames nil | ||
| 168 | "Support for Emacs frames and window systems." | ||
| 169 | :group 'environment) | ||
| 170 | |||
| 171 | (defgroup data nil | ||
| 172 | "Support editing files of data." | ||
| 173 | :group 'emacs) | ||
| 174 | |||
| 175 | (defgroup wp nil | ||
| 176 | "Word processing." | ||
| 177 | :group 'emacs) | ||
| 178 | |||
| 179 | (defgroup tex nil | ||
| 180 | "Code related to the TeX formatter." | ||
| 181 | :group 'wp) | ||
| 182 | |||
| 183 | (defgroup faces nil | ||
| 184 | "Support for multiple fonts." | ||
| 185 | :group 'emacs) | ||
| 186 | |||
| 187 | (defgroup hypermedia nil | ||
| 188 | "Support for links between text or other media types." | ||
| 189 | :group 'emacs) | ||
| 190 | |||
| 191 | (defgroup help nil | ||
| 192 | "Support for on-line help systems." | ||
| 193 | :group 'emacs) | ||
| 194 | |||
| 195 | (defgroup local nil | ||
| 196 | "Code local to your site." | ||
| 197 | :group 'emacs) | ||
| 198 | |||
| 199 | (defgroup customize '((widgets custom-group)) | ||
| 200 | "Customization of the Customization support." | ||
| 201 | :link '(custom-manual "(custom)Top") | ||
| 202 | :link '(url-link :tag "Development Page" | ||
| 203 | "http://www.dina.kvl.dk/~abraham/custom/") | ||
| 204 | :prefix "custom-" | ||
| 205 | :group 'help | ||
| 206 | :group 'faces) | ||
| 207 | |||
| 208 | ;;; Utilities. | ||
| 209 | |||
| 210 | (defun custom-quote (sexp) | ||
| 211 | "Quote SEXP iff it is not self quoting." | ||
| 212 | (if (or (memq sexp '(t nil)) | ||
| 213 | (and (symbolp sexp) | ||
| 214 | (eq (aref (symbol-name sexp) 0) ?:)) | ||
| 215 | (and (listp sexp) | ||
| 216 | (memq (car sexp) '(lambda))) | ||
| 217 | (stringp sexp) | ||
| 218 | (numberp sexp) | ||
| 219 | (and (fboundp 'characterp) | ||
| 220 | (characterp sexp))) | ||
| 221 | sexp | ||
| 222 | (list 'quote sexp))) | ||
| 223 | |||
| 224 | (defun custom-split-regexp-maybe (regexp) | ||
| 225 | "If REGEXP is a string, split it to a list at `\\|'. | ||
| 226 | You can get the original back with from the result with: | ||
| 227 | (mapconcat 'identity result \"\\|\") | ||
| 228 | |||
| 229 | IF REGEXP is not a string, return it unchanged." | ||
| 230 | (if (stringp regexp) | ||
| 231 | (let ((start 0) | ||
| 232 | all) | ||
| 233 | (while (string-match "\\\\|" regexp start) | ||
| 234 | (setq all (cons (substring regexp start (match-beginning 0)) all) | ||
| 235 | start (match-end 0))) | ||
| 236 | (nreverse (cons (substring regexp start) all))) | ||
| 237 | regexp)) | ||
| 238 | |||
| 239 | (defvar custom-prefix-list nil | ||
| 240 | "List of prefixes that should be ignored by `custom-unlispify'") | ||
| 241 | |||
| 242 | (defcustom custom-unlispify-menu-entries t | ||
| 243 | "Display menu entries as words instead of symbols if non nil." | ||
| 244 | :group 'customize | ||
| 245 | :type 'boolean) | ||
| 246 | |||
| 247 | (defun custom-unlispify-menu-entry (symbol &optional no-suffix) | ||
| 248 | "Convert symbol into a menu entry." | ||
| 249 | (cond ((not custom-unlispify-menu-entries) | ||
| 250 | (symbol-name symbol)) | ||
| 251 | ((get symbol 'custom-tag) | ||
| 252 | (if no-suffix | ||
| 253 | (get symbol 'custom-tag) | ||
| 254 | (concat (get symbol 'custom-tag) "..."))) | ||
| 255 | (t | ||
| 256 | (save-excursion | ||
| 257 | (set-buffer (get-buffer-create " *Custom-Work*")) | ||
| 258 | (erase-buffer) | ||
| 259 | (princ symbol (current-buffer)) | ||
| 260 | (goto-char (point-min)) | ||
| 261 | (let ((prefixes custom-prefix-list) | ||
| 262 | prefix) | ||
| 263 | (while prefixes | ||
| 264 | (setq prefix (car prefixes)) | ||
| 265 | (if (search-forward prefix (+ (point) (length prefix)) t) | ||
| 266 | (progn | ||
| 267 | (setq prefixes nil) | ||
| 268 | (delete-region (point-min) (point))) | ||
| 269 | (setq prefixes (cdr prefixes))))) | ||
| 270 | (subst-char-in-region (point-min) (point-max) ?- ?\ t) | ||
| 271 | (capitalize-region (point-min) (point-max)) | ||
| 272 | (unless no-suffix | ||
| 273 | (goto-char (point-max)) | ||
| 274 | (insert "...")) | ||
| 275 | (buffer-string))))) | ||
| 276 | |||
| 277 | (defcustom custom-unlispify-tag-names t | ||
| 278 | "Display tag names as words instead of symbols if non nil." | ||
| 279 | :group 'customize | ||
| 280 | :type 'boolean) | ||
| 281 | |||
| 282 | (defun custom-unlispify-tag-name (symbol) | ||
| 283 | "Convert symbol into a menu entry." | ||
| 284 | (let ((custom-unlispify-menu-entries custom-unlispify-tag-names)) | ||
| 285 | (custom-unlispify-menu-entry symbol t))) | ||
| 286 | |||
| 287 | (defun custom-prefix-add (symbol prefixes) | ||
| 288 | ;; Addd SYMBOL to list of ignored PREFIXES. | ||
| 289 | (cons (or (get symbol 'custom-prefix) | ||
| 290 | (concat (symbol-name symbol) "-")) | ||
| 291 | prefixes)) | ||
| 292 | |||
| 293 | ;;; The Custom Mode. | ||
| 294 | |||
| 295 | (defvar custom-options nil | ||
| 296 | "Customization widgets in the current buffer.") | ||
| 297 | |||
| 298 | (defvar custom-mode-map nil | ||
| 299 | "Keymap for `custom-mode'.") | ||
| 300 | |||
| 301 | (unless custom-mode-map | ||
| 302 | (setq custom-mode-map (make-sparse-keymap)) | ||
| 303 | (set-keymap-parent custom-mode-map widget-keymap) | ||
| 304 | (define-key custom-mode-map "q" 'bury-buffer)) | ||
| 305 | |||
| 306 | (easy-menu-define custom-mode-menu | ||
| 307 | custom-mode-map | ||
| 308 | "Menu used in customization buffers." | ||
| 309 | '("Custom" | ||
| 310 | ["Set" custom-set t] | ||
| 311 | ["Save" custom-save t] | ||
| 312 | ["Reset to Current" custom-reset-current t] | ||
| 313 | ["Reset to Saved" custom-reset-saved t] | ||
| 314 | ["Reset to Factory Settings" custom-reset-factory t] | ||
| 315 | ["Info" (Info-goto-node "(custom)The Customization Buffer") t])) | ||
| 316 | |||
| 317 | (defcustom custom-mode-hook nil | ||
| 318 | "Hook called when entering custom-mode." | ||
| 319 | :type 'hook | ||
| 320 | :group 'customize) | ||
| 321 | |||
| 322 | (defun custom-mode () | ||
| 323 | "Major mode for editing customization buffers. | ||
| 324 | |||
| 325 | The following commands are available: | ||
| 326 | |||
| 327 | \\[widget-forward] Move to next button or editable field. | ||
| 328 | \\[widget-backward] Move to previous button or editable field. | ||
| 329 | \\[widget-button-click] Activate button under the mouse pointer. | ||
| 330 | \\[widget-button-press] Activate button under point. | ||
| 331 | \\[custom-set] Set all modifications. | ||
| 332 | \\[custom-save] Make all modifications default. | ||
| 333 | \\[custom-reset-current] Reset all modified options. | ||
| 334 | \\[custom-reset-saved] Reset all modified or set options. | ||
| 335 | \\[custom-reset-factory] Reset all options. | ||
| 336 | |||
| 337 | Entry to this mode calls the value of `custom-mode-hook' | ||
| 338 | if that value is non-nil." | ||
| 339 | (kill-all-local-variables) | ||
| 340 | (setq major-mode 'custom-mode | ||
| 341 | mode-name "Custom") | ||
| 342 | (use-local-map custom-mode-map) | ||
| 343 | (easy-menu-add custom-mode-menu) | ||
| 344 | (make-local-variable 'custom-options) | ||
| 345 | (run-hooks 'custom-mode-hook)) | ||
| 346 | |||
| 347 | ;;; Custom Mode Commands. | ||
| 348 | |||
| 349 | (defun custom-set () | ||
| 350 | "Set changes in all modified options." | ||
| 351 | (interactive) | ||
| 352 | (let ((children custom-options)) | ||
| 353 | (mapcar (lambda (child) | ||
| 354 | (when (eq (widget-get child :custom-state) 'modified) | ||
| 355 | (widget-apply child :custom-set))) | ||
| 356 | children))) | ||
| 357 | |||
| 358 | (defun custom-save () | ||
| 359 | "Set all modified group members and save them." | ||
| 360 | (interactive) | ||
| 361 | (let ((children custom-options)) | ||
| 362 | (mapcar (lambda (child) | ||
| 363 | (when (memq (widget-get child :custom-state) '(modified set)) | ||
| 364 | (widget-apply child :custom-save))) | ||
| 365 | children)) | ||
| 366 | (custom-save-all)) | ||
| 367 | |||
| 368 | (defvar custom-reset-menu | ||
| 369 | '(("Current" . custom-reset-current) | ||
| 370 | ("Saved" . custom-reset-saved) | ||
| 371 | ("Factory Settings" . custom-reset-factory)) | ||
| 372 | "Alist of actions for the `Reset' button. | ||
| 373 | The key is a string containing the name of the action, the value is a | ||
| 374 | lisp function taking the widget as an element which will be called | ||
| 375 | when the action is chosen.") | ||
| 376 | |||
| 377 | (defun custom-reset (event) | ||
| 378 | "Select item from reset menu." | ||
| 379 | (let* ((completion-ignore-case t) | ||
| 380 | (answer (widget-choose "Reset to" | ||
| 381 | custom-reset-menu | ||
| 382 | event))) | ||
| 383 | (if answer | ||
| 384 | (funcall answer)))) | ||
| 385 | |||
| 386 | (defun custom-reset-current () | ||
| 387 | "Reset all modified group members to their current value." | ||
| 388 | (interactive) | ||
| 389 | (let ((children custom-options)) | ||
| 390 | (mapcar (lambda (child) | ||
| 391 | (when (eq (widget-get child :custom-state) 'modified) | ||
| 392 | (widget-apply child :custom-reset-current))) | ||
| 393 | children))) | ||
| 394 | |||
| 395 | (defun custom-reset-saved () | ||
| 396 | "Reset all modified or set group members to their saved value." | ||
| 397 | (interactive) | ||
| 398 | (let ((children custom-options)) | ||
| 399 | (mapcar (lambda (child) | ||
| 400 | (when (eq (widget-get child :custom-state) 'modified) | ||
| 401 | (widget-apply child :custom-reset-current))) | ||
| 402 | children))) | ||
| 403 | |||
| 404 | (defun custom-reset-factory () | ||
| 405 | "Reset all modified, set, or saved group members to their factory settings." | ||
| 406 | (interactive) | ||
| 407 | (let ((children custom-options)) | ||
| 408 | (mapcar (lambda (child) | ||
| 409 | (when (eq (widget-get child :custom-state) 'modified) | ||
| 410 | (widget-apply child :custom-reset-current))) | ||
| 411 | children))) | ||
| 412 | |||
| 413 | ;;; The Customize Commands | ||
| 414 | |||
| 415 | ;;;###autoload | ||
| 416 | (defun customize (symbol) | ||
| 417 | "Customize SYMBOL, which must be a customization group." | ||
| 418 | (interactive (list (completing-read "Customize group: (default emacs) " | ||
| 419 | obarray | ||
| 420 | (lambda (symbol) | ||
| 421 | (get symbol 'custom-group)) | ||
| 422 | t))) | ||
| 423 | |||
| 424 | (when (stringp symbol) | ||
| 425 | (if (string-equal "" symbol) | ||
| 426 | (setq symbol 'emacs) | ||
| 427 | (setq symbol (intern symbol)))) | ||
| 428 | (custom-buffer-create (list (list symbol 'custom-group)))) | ||
| 429 | |||
| 430 | ;;;###autoload | ||
| 431 | (defun customize-variable (symbol) | ||
| 432 | "Customize SYMBOL, which must be a variable." | ||
| 433 | (interactive | ||
| 434 | ;; Code stolen from `help.el'. | ||
| 435 | (let ((v (variable-at-point)) | ||
| 436 | (enable-recursive-minibuffers t) | ||
| 437 | val) | ||
| 438 | (setq val (completing-read | ||
| 439 | (if v | ||
| 440 | (format "Customize variable (default %s): " v) | ||
| 441 | "Customize variable: ") | ||
| 442 | obarray 'boundp t)) | ||
| 443 | (list (if (equal val "") | ||
| 444 | v (intern val))))) | ||
| 445 | (custom-buffer-create (list (list symbol 'custom-variable)))) | ||
| 446 | |||
| 447 | ;;;###autoload | ||
| 448 | (defun customize-face (&optional symbol) | ||
| 449 | "Customize SYMBOL, which should be a face name or nil. | ||
| 450 | If SYMBOL is nil, customize all faces." | ||
| 451 | (interactive (list (completing-read "Customize face: (default all) " | ||
| 452 | obarray 'custom-facep))) | ||
| 453 | (if (or (null symbol) (and (stringp symbol) (zerop (length symbol)))) | ||
| 454 | (let ((found nil)) | ||
| 455 | (message "Looking for faces...") | ||
| 456 | (mapcar (lambda (symbol) | ||
| 457 | (setq found (cons (list symbol 'custom-face) found))) | ||
| 458 | (face-list)) | ||
| 459 | (custom-buffer-create found)) | ||
| 460 | (if (stringp symbol) | ||
| 461 | (setq symbol (intern symbol))) | ||
| 462 | (unless (symbolp symbol) | ||
| 463 | (error "Should be a symbol %S" symbol)) | ||
| 464 | (custom-buffer-create (list (list symbol 'custom-face))))) | ||
| 465 | |||
| 466 | ;;;###autoload | ||
| 467 | (defun customize-customized () | ||
| 468 | "Customize all already customized user options." | ||
| 469 | (interactive) | ||
| 470 | (let ((found nil)) | ||
| 471 | (mapatoms (lambda (symbol) | ||
| 472 | (and (get symbol 'saved-face) | ||
| 473 | (custom-facep symbol) | ||
| 474 | (setq found (cons (list symbol 'custom-face) found))) | ||
| 475 | (and (get symbol 'saved-value) | ||
| 476 | (boundp symbol) | ||
| 477 | (setq found | ||
| 478 | (cons (list symbol 'custom-variable) found))))) | ||
| 479 | (if found | ||
| 480 | (custom-buffer-create found) | ||
| 481 | (error "No customized user options")))) | ||
| 482 | |||
| 483 | ;;;###autoload | ||
| 484 | (defun customize-apropos (regexp &optional all) | ||
| 485 | "Customize all user options matching REGEXP. | ||
| 486 | If ALL (e.g., started with a prefix key), include options which are not | ||
| 487 | user-settable." | ||
| 488 | (interactive "sCustomize regexp: \nP") | ||
| 489 | (let ((found nil)) | ||
| 490 | (mapatoms (lambda (symbol) | ||
| 491 | (when (string-match regexp (symbol-name symbol)) | ||
| 492 | (when (get symbol 'custom-group) | ||
| 493 | (setq found (cons (list symbol 'custom-group) found))) | ||
| 494 | (when (custom-facep symbol) | ||
| 495 | (setq found (cons (list symbol 'custom-face) found))) | ||
| 496 | (when (and (boundp symbol) | ||
| 497 | (or (get symbol 'saved-value) | ||
| 498 | (get symbol 'factory-value) | ||
| 499 | (if all | ||
| 500 | (get symbol 'variable-documentation) | ||
| 501 | (user-variable-p symbol)))) | ||
| 502 | (setq found | ||
| 503 | (cons (list symbol 'custom-variable) found)))))) | ||
| 504 | (if found | ||
| 505 | (custom-buffer-create found) | ||
| 506 | (error "No matches")))) | ||
| 507 | |||
| 508 | ;;;###autoload | ||
| 509 | (defun custom-buffer-create (options) | ||
| 510 | "Create a buffer containing OPTIONS. | ||
| 511 | OPTIONS should be an alist of the form ((SYMBOL WIDGET)...), where | ||
| 512 | SYMBOL is a customization option, and WIDGET is a widget for editing | ||
| 513 | that option." | ||
| 514 | (message "Creating customization buffer...") | ||
| 515 | (kill-buffer (get-buffer-create "*Customization*")) | ||
| 516 | (switch-to-buffer (get-buffer-create "*Customization*")) | ||
| 517 | (custom-mode) | ||
| 518 | (widget-insert "This is a customization buffer. | ||
| 519 | Push RET or click mouse-2 on the word ") | ||
| 520 | ;; (put-text-property 1 2 'start-open nil) | ||
| 521 | (widget-create 'info-link | ||
| 522 | :tag "help" | ||
| 523 | :help-echo "Read the online help." | ||
| 524 | "(custom)The Customization Buffer") | ||
| 525 | (widget-insert " for more information.\n\n") | ||
| 526 | (setq custom-options | ||
| 527 | (if (= (length options) 1) | ||
| 528 | (mapcar (lambda (entry) | ||
| 529 | (widget-create (nth 1 entry) | ||
| 530 | :custom-state 'unknown | ||
| 531 | :tag (custom-unlispify-tag-name | ||
| 532 | (nth 0 entry)) | ||
| 533 | :value (nth 0 entry))) | ||
| 534 | options) | ||
| 535 | (let ((count 0) | ||
| 536 | (length (length options))) | ||
| 537 | (mapcar (lambda (entry) | ||
| 538 | (prog2 | ||
| 539 | (message "Creating customization items %2d%%..." | ||
| 540 | (/ (* 100.0 count) length)) | ||
| 541 | (widget-create (nth 1 entry) | ||
| 542 | :tag (custom-unlispify-tag-name | ||
| 543 | (nth 0 entry)) | ||
| 544 | :value (nth 0 entry)) | ||
| 545 | (setq count (1+ count)) | ||
| 546 | (unless (eq (preceding-char) ?\n) | ||
| 547 | (widget-insert "\n")) | ||
| 548 | (widget-insert "\n"))) | ||
| 549 | options)))) | ||
| 550 | (unless (eq (preceding-char) ?\n) | ||
| 551 | (widget-insert "\n")) | ||
| 552 | (widget-insert "\n") | ||
| 553 | (message "Creating customization magic...") | ||
| 554 | (mapcar 'custom-magic-reset custom-options) | ||
| 555 | (message "Creating customization buttons...") | ||
| 556 | (widget-create 'push-button | ||
| 557 | :tag "Set" | ||
| 558 | :help-echo "Set all modifications for this session." | ||
| 559 | :action (lambda (widget &optional event) | ||
| 560 | (custom-set))) | ||
| 561 | (widget-insert " ") | ||
| 562 | (widget-create 'push-button | ||
| 563 | :tag "Save" | ||
| 564 | :help-echo "\ | ||
| 565 | Make the modifications default for future sessions." | ||
| 566 | :action (lambda (widget &optional event) | ||
| 567 | (custom-save))) | ||
| 568 | (widget-insert " ") | ||
| 569 | (widget-create 'push-button | ||
| 570 | :tag "Reset" | ||
| 571 | :help-echo "Undo all modifications." | ||
| 572 | :action (lambda (widget &optional event) | ||
| 573 | (custom-reset event))) | ||
| 574 | (widget-insert " ") | ||
| 575 | (widget-create 'push-button | ||
| 576 | :tag "Done" | ||
| 577 | :help-echo "Bury the buffer." | ||
| 578 | :action (lambda (widget &optional event) | ||
| 579 | (bury-buffer) | ||
| 580 | ;; Steal button release event. | ||
| 581 | (if (and (fboundp 'button-press-event-p) | ||
| 582 | (fboundp 'next-command-event)) | ||
| 583 | ;; XEmacs | ||
| 584 | (and event | ||
| 585 | (button-press-event-p event) | ||
| 586 | (next-command-event)) | ||
| 587 | ;; Emacs | ||
| 588 | (when (memq 'down (event-modifiers event)) | ||
| 589 | (read-event))))) | ||
| 590 | (widget-insert "\n") | ||
| 591 | (message "Creating customization setup...") | ||
| 592 | (widget-setup) | ||
| 593 | (goto-char (point-min)) | ||
| 594 | (message "Creating customization buffer...done")) | ||
| 595 | |||
| 596 | ;;; Modification of Basic Widgets. | ||
| 597 | ;; | ||
| 598 | ;; We add extra properties to the basic widgets needed here. This is | ||
| 599 | ;; fine, as long as we are careful to stay within out own namespace. | ||
| 600 | ;; | ||
| 601 | ;; We want simple widgets to be displayed by default, but complex | ||
| 602 | ;; widgets to be hidden. | ||
| 603 | |||
| 604 | (widget-put (get 'item 'widget-type) :custom-show t) | ||
| 605 | (widget-put (get 'editable-field 'widget-type) | ||
| 606 | :custom-show (lambda (widget value) | ||
| 607 | (let ((pp (pp-to-string value))) | ||
| 608 | (cond ((string-match "\n" pp) | ||
| 609 | nil) | ||
| 610 | ((> (length pp) 40) | ||
| 611 | nil) | ||
| 612 | (t t))))) | ||
| 613 | (widget-put (get 'menu-choice 'widget-type) :custom-show t) | ||
| 614 | |||
| 615 | ;;; The `custom-manual' Widget. | ||
| 616 | |||
| 617 | (define-widget 'custom-manual 'info-link | ||
| 618 | "Link to the manual entry for this customization option." | ||
| 619 | :help-echo "Read the manual entry for this option." | ||
| 620 | :tag "Manual") | ||
| 621 | |||
| 622 | ;;; The `custom-magic' Widget. | ||
| 623 | |||
| 624 | (defface custom-invalid-face '((((class color)) | ||
| 625 | (:foreground "yellow" :background "red")) | ||
| 626 | (t | ||
| 627 | (:bold t :italic t :underline t))) | ||
| 628 | "Face used when the customize item is invalid.") | ||
| 629 | |||
| 630 | (defface custom-rogue-face '((((class color)) | ||
| 631 | (:foreground "pink" :background "black")) | ||
| 632 | (t | ||
| 633 | (:underline t))) | ||
| 634 | "Face used when the customize item is not defined for customization.") | ||
| 635 | |||
| 636 | (defface custom-modified-face '((((class color)) | ||
| 637 | (:foreground "white" :background "blue")) | ||
| 638 | (t | ||
| 639 | (:italic t :bold))) | ||
| 640 | "Face used when the customize item has been modified.") | ||
| 641 | |||
| 642 | (defface custom-set-face '((((class color)) | ||
| 643 | (:foreground "blue" :background "white")) | ||
| 644 | (t | ||
| 645 | (:italic t))) | ||
| 646 | "Face used when the customize item has been set.") | ||
| 647 | |||
| 648 | (defface custom-changed-face '((((class color)) | ||
| 649 | (:foreground "white" :background "blue")) | ||
| 650 | (t | ||
| 651 | (:italic t))) | ||
| 652 | "Face used when the customize item has been changed.") | ||
| 653 | |||
| 654 | (defface custom-saved-face '((t (:underline t))) | ||
| 655 | "Face used when the customize item has been saved.") | ||
| 656 | |||
| 657 | (defcustom custom-magic-alist '((nil "#" underline "\ | ||
| 658 | uninitialized, you should not see this.") | ||
| 659 | (unknown "?" italic "\ | ||
| 660 | unknown, you should not see this.") | ||
| 661 | (hidden "-" default "\ | ||
| 662 | hidden, press the state button to show.") | ||
| 663 | (invalid "x" custom-invalid-face "\ | ||
| 664 | the value displayed for this item is invalid and cannot be set.") | ||
| 665 | (modified "*" custom-modified-face "\ | ||
| 666 | you have edited the item, and can now set it.") | ||
| 667 | (set "+" custom-set-face "\ | ||
| 668 | you have set this item, but not saved it.") | ||
| 669 | (changed ":" custom-changed-face "\ | ||
| 670 | this item has been changed outside customize.") | ||
| 671 | (saved "!" custom-saved-face "\ | ||
| 672 | this item has been saved.") | ||
| 673 | (rogue "@" custom-rogue-face "\ | ||
| 674 | this item is not prepared for customization.") | ||
| 675 | (factory " " nil "\ | ||
| 676 | this item is unchanged from its factory setting.")) | ||
| 677 | "Alist of customize option states. | ||
| 678 | Each entry is of the form (STATE MAGIC FACE DESCRIPTION), where | ||
| 679 | |||
| 680 | STATE is one of the following symbols: | ||
| 681 | |||
| 682 | `nil' | ||
| 683 | For internal use, should never occur. | ||
| 684 | `unknown' | ||
| 685 | For internal use, should never occur. | ||
| 686 | `hidden' | ||
| 687 | This item is not being displayed. | ||
| 688 | `invalid' | ||
| 689 | This item is modified, but has an invalid form. | ||
| 690 | `modified' | ||
| 691 | This item is modified, and has a valid form. | ||
| 692 | `set' | ||
| 693 | This item has been set but not saved. | ||
| 694 | `changed' | ||
| 695 | The current value of this item has been changed temporarily. | ||
| 696 | `saved' | ||
| 697 | This item is marked for saving. | ||
| 698 | `rogue' | ||
| 699 | This item has no customization information. | ||
| 700 | `factory' | ||
| 701 | This item is unchanged from the factory default. | ||
| 702 | |||
| 703 | MAGIC is a string used to present that state. | ||
| 704 | |||
| 705 | FACE is a face used to present the state. | ||
| 706 | |||
| 707 | DESCRIPTION is a string describing the state. | ||
| 708 | |||
| 709 | The list should be sorted most significant first." | ||
| 710 | :type '(list (checklist :inline t | ||
| 711 | (group (const nil) | ||
| 712 | (string :tag "Magic") | ||
| 713 | face | ||
| 714 | (string :tag "Description")) | ||
| 715 | (group (const unknown) | ||
| 716 | (string :tag "Magic") | ||
| 717 | face | ||
| 718 | (string :tag "Description")) | ||
| 719 | (group (const hidden) | ||
| 720 | (string :tag "Magic") | ||
| 721 | face | ||
| 722 | (string :tag "Description")) | ||
| 723 | (group (const invalid) | ||
| 724 | (string :tag "Magic") | ||
| 725 | face | ||
| 726 | (string :tag "Description")) | ||
| 727 | (group (const modified) | ||
| 728 | (string :tag "Magic") | ||
| 729 | face | ||
| 730 | (string :tag "Description")) | ||
| 731 | (group (const set) | ||
| 732 | (string :tag "Magic") | ||
| 733 | face | ||
| 734 | (string :tag "Description")) | ||
| 735 | (group (const changed) | ||
| 736 | (string :tag "Magic") | ||
| 737 | face | ||
| 738 | (string :tag "Description")) | ||
| 739 | (group (const saved) | ||
| 740 | (string :tag "Magic") | ||
| 741 | face | ||
| 742 | (string :tag "Description")) | ||
| 743 | (group (const rogue) | ||
| 744 | (string :tag "Magic") | ||
| 745 | face | ||
| 746 | (string :tag "Description")) | ||
| 747 | (group (const factory) | ||
| 748 | (string :tag "Magic") | ||
| 749 | face | ||
| 750 | (string :tag "Description"))) | ||
| 751 | (editable-list :inline t | ||
| 752 | (group symbol | ||
| 753 | (string :tag "Magic") | ||
| 754 | face | ||
| 755 | (string :tag "Description")))) | ||
| 756 | :group 'customize) | ||
| 757 | |||
| 758 | (defcustom custom-magic-show 'long | ||
| 759 | "Show long description of the state of each customization option." | ||
| 760 | :type '(choice (const :tag "no" nil) | ||
| 761 | (const short) | ||
| 762 | (const long)) | ||
| 763 | :group 'customize) | ||
| 764 | |||
| 765 | (defcustom custom-magic-show-button t | ||
| 766 | "Show a magic button indicating the state of each customization option." | ||
| 767 | :type 'boolean | ||
| 768 | :group 'customize) | ||
| 769 | |||
| 770 | (define-widget 'custom-magic 'default | ||
| 771 | "Show and manipulate state for a customization option." | ||
| 772 | :format "%v" | ||
| 773 | :action 'widget-choice-item-action | ||
| 774 | :value-get 'ignore | ||
| 775 | :value-create 'custom-magic-value-create | ||
| 776 | :value-delete 'widget-children-value-delete) | ||
| 777 | |||
| 778 | (defun custom-magic-value-create (widget) | ||
| 779 | ;; Create compact status report for WIDGET. | ||
| 780 | (let* ((parent (widget-get widget :parent)) | ||
| 781 | (state (widget-get parent :custom-state)) | ||
| 782 | (entry (assq state custom-magic-alist)) | ||
| 783 | (magic (nth 1 entry)) | ||
| 784 | (face (nth 2 entry)) | ||
| 785 | (text (nth 3 entry)) | ||
| 786 | (lisp (eq (widget-get parent :custom-form) 'lisp)) | ||
| 787 | children) | ||
| 788 | (when custom-magic-show | ||
| 789 | (push (widget-create-child-and-convert widget 'choice-item | ||
| 790 | :help-echo "\ | ||
| 791 | Change the state of this item." | ||
| 792 | :format "%[%t%]" | ||
| 793 | :tag "State") | ||
| 794 | children) | ||
| 795 | (insert ": ") | ||
| 796 | (if (eq custom-magic-show 'long) | ||
| 797 | (insert text) | ||
| 798 | (insert (symbol-name state))) | ||
| 799 | (when lisp | ||
| 800 | (insert " (lisp)")) | ||
| 801 | (insert "\n")) | ||
| 802 | (when custom-magic-show-button | ||
| 803 | (when custom-magic-show | ||
| 804 | (let ((indent (widget-get parent :indent))) | ||
| 805 | (when indent | ||
| 806 | (insert-char ? indent)))) | ||
| 807 | (push (widget-create-child-and-convert widget 'choice-item | ||
| 808 | :button-face face | ||
| 809 | :help-echo "Change the state." | ||
| 810 | :format "%[%t%]" | ||
| 811 | :tag (if lisp | ||
| 812 | (concat "(" magic ")") | ||
| 813 | (concat "[" magic "]"))) | ||
| 814 | children) | ||
| 815 | (insert " ")) | ||
| 816 | (widget-put widget :children children))) | ||
| 817 | |||
| 818 | (defun custom-magic-reset (widget) | ||
| 819 | "Redraw the :custom-magic property of WIDGET." | ||
| 820 | (let ((magic (widget-get widget :custom-magic))) | ||
| 821 | (widget-value-set magic (widget-value magic)))) | ||
| 822 | |||
| 823 | ;;; The `custom-level' Widget. | ||
| 824 | |||
| 825 | (define-widget 'custom-level 'item | ||
| 826 | "The custom level buttons." | ||
| 827 | :format "%[%t%]" | ||
| 828 | :help-echo "Expand or collapse this item." | ||
| 829 | :action 'custom-level-action) | ||
| 830 | |||
| 831 | (defun custom-level-action (widget &optional event) | ||
| 832 | "Toggle visibility for parent to WIDGET." | ||
| 833 | (let* ((parent (widget-get widget :parent)) | ||
| 834 | (state (widget-get parent :custom-state))) | ||
| 835 | (cond ((memq state '(invalid modified)) | ||
| 836 | (error "There are unset changes")) | ||
| 837 | ((eq state 'hidden) | ||
| 838 | (widget-put parent :custom-state 'unknown)) | ||
| 839 | (t | ||
| 840 | (widget-put parent :custom-state 'hidden))) | ||
| 841 | (custom-redraw parent))) | ||
| 842 | |||
| 843 | ;;; The `custom' Widget. | ||
| 844 | |||
| 845 | (define-widget 'custom 'default | ||
| 846 | "Customize a user option." | ||
| 847 | :convert-widget 'custom-convert-widget | ||
| 848 | :format "%l%[%t%]: %v%m%h%a" | ||
| 849 | :format-handler 'custom-format-handler | ||
| 850 | :notify 'custom-notify | ||
| 851 | :custom-level 1 | ||
| 852 | :custom-state 'hidden | ||
| 853 | :documentation-property 'widget-subclass-responsibility | ||
| 854 | :value-create 'widget-subclass-responsibility | ||
| 855 | :value-delete 'widget-children-value-delete | ||
| 856 | :value-get 'widget-item-value-get | ||
| 857 | :validate 'widget-editable-list-validate | ||
| 858 | :match (lambda (widget value) (symbolp value))) | ||
| 859 | |||
| 860 | (defun custom-convert-widget (widget) | ||
| 861 | ;; Initialize :value and :tag from :args in WIDGET. | ||
| 862 | (let ((args (widget-get widget :args))) | ||
| 863 | (when args | ||
| 864 | (widget-put widget :value (widget-apply widget | ||
| 865 | :value-to-internal (car args))) | ||
| 866 | (widget-put widget :tag (custom-unlispify-tag-name (car args))) | ||
| 867 | (widget-put widget :args nil))) | ||
| 868 | widget) | ||
| 869 | |||
| 870 | (defun custom-format-handler (widget escape) | ||
| 871 | ;; We recognize extra escape sequences. | ||
| 872 | (let* ((buttons (widget-get widget :buttons)) | ||
| 873 | (state (widget-get widget :custom-state)) | ||
| 874 | (level (widget-get widget :custom-level))) | ||
| 875 | (cond ((eq escape ?l) | ||
| 876 | (when level | ||
| 877 | (push (widget-create-child-and-convert | ||
| 878 | widget 'custom-level (make-string level ?*)) | ||
| 879 | buttons) | ||
| 880 | (widget-insert " ") | ||
| 881 | (widget-put widget :buttons buttons))) | ||
| 882 | ((eq escape ?L) | ||
| 883 | (when (eq state 'hidden) | ||
| 884 | (widget-insert " ..."))) | ||
| 885 | ((eq escape ?m) | ||
| 886 | (and (eq (preceding-char) ?\n) | ||
| 887 | (widget-get widget :indent) | ||
| 888 | (insert-char ? (widget-get widget :indent))) | ||
| 889 | (let ((magic (widget-create-child-and-convert | ||
| 890 | widget 'custom-magic nil))) | ||
| 891 | (widget-put widget :custom-magic magic) | ||
| 892 | (push magic buttons) | ||
| 893 | (widget-put widget :buttons buttons))) | ||
| 894 | ((eq escape ?a) | ||
| 895 | (let* ((symbol (widget-get widget :value)) | ||
| 896 | (links (get symbol 'custom-links)) | ||
| 897 | (many (> (length links) 2))) | ||
| 898 | (when links | ||
| 899 | (and (eq (preceding-char) ?\n) | ||
| 900 | (widget-get widget :indent) | ||
| 901 | (insert-char ? (widget-get widget :indent))) | ||
| 902 | (insert "See also ") | ||
| 903 | (while links | ||
| 904 | (push (widget-create-child-and-convert widget (car links)) | ||
| 905 | buttons) | ||
| 906 | (setq links (cdr links)) | ||
| 907 | (cond ((null links) | ||
| 908 | (insert ".\n")) | ||
| 909 | ((null (cdr links)) | ||
| 910 | (if many | ||
| 911 | (insert ", and ") | ||
| 912 | (insert " and "))) | ||
| 913 | (t | ||
| 914 | (insert ", ")))) | ||
| 915 | (widget-put widget :buttons buttons)))) | ||
| 916 | (t | ||
| 917 | (widget-default-format-handler widget escape))))) | ||
| 918 | |||
| 919 | (defun custom-notify (widget &rest args) | ||
| 920 | "Keep track of changes." | ||
| 921 | (unless (memq (widget-get widget :custom-state) '(nil unknown hidden)) | ||
| 922 | (widget-put widget :custom-state 'modified)) | ||
| 923 | (let ((buffer-undo-list t)) | ||
| 924 | (custom-magic-reset widget)) | ||
| 925 | (apply 'widget-default-notify widget args)) | ||
| 926 | |||
| 927 | (defun custom-redraw (widget) | ||
| 928 | "Redraw WIDGET with current settings." | ||
| 929 | (let ((pos (point)) | ||
| 930 | (from (marker-position (widget-get widget :from))) | ||
| 931 | (to (marker-position (widget-get widget :to)))) | ||
| 932 | (save-excursion | ||
| 933 | (widget-value-set widget (widget-value widget)) | ||
| 934 | (custom-redraw-magic widget)) | ||
| 935 | (when (and (>= pos from) (<= pos to)) | ||
| 936 | (goto-char pos)))) | ||
| 937 | |||
| 938 | (defun custom-redraw-magic (widget) | ||
| 939 | "Redraw WIDGET state with current settings." | ||
| 940 | (while widget | ||
| 941 | (let ((magic (widget-get widget :custom-magic))) | ||
| 942 | (unless magic | ||
| 943 | (debug)) | ||
| 944 | (widget-value-set magic (widget-value magic)) | ||
| 945 | (when (setq widget (widget-get widget :group)) | ||
| 946 | (custom-group-state-update widget)))) | ||
| 947 | (widget-setup)) | ||
| 948 | |||
| 949 | (defun custom-show (widget value) | ||
| 950 | "Non-nil if WIDGET should be shown with VALUE by default." | ||
| 951 | (let ((show (widget-get widget :custom-show))) | ||
| 952 | (cond ((null show) | ||
| 953 | nil) | ||
| 954 | ((eq t show) | ||
| 955 | t) | ||
| 956 | (t | ||
| 957 | (funcall show widget value))))) | ||
| 958 | |||
| 959 | (defun custom-load-symbol (symbol) | ||
| 960 | "Load all dependencies for SYMBOL." | ||
| 961 | (let ((loads (get symbol 'custom-loads)) | ||
| 962 | load) | ||
| 963 | (while loads | ||
| 964 | (setq load (car loads) | ||
| 965 | loads (cdr loads)) | ||
| 966 | (cond ((symbolp load) | ||
| 967 | (condition-case nil | ||
| 968 | (require load) | ||
| 969 | (error nil))) | ||
| 970 | ((assoc load load-history)) | ||
| 971 | (t | ||
| 972 | (condition-case nil | ||
| 973 | (load-library load) | ||
| 974 | (error nil))))))) | ||
| 975 | |||
| 976 | (defun custom-load-widget (widget) | ||
| 977 | "Load all dependencies for WIDGET." | ||
| 978 | (custom-load-symbol (widget-value widget))) | ||
| 979 | |||
| 980 | ;;; The `custom-variable' Widget. | ||
| 981 | |||
| 982 | (defface custom-variable-sample-face '((t (:underline t))) | ||
| 983 | "Face used for unpushable variable tags." | ||
| 984 | :group 'customize) | ||
| 985 | |||
| 986 | (defface custom-variable-button-face '((t (:underline t :bold t))) | ||
| 987 | "Face used for pushable variable tags." | ||
| 988 | :group 'customize) | ||
| 989 | |||
| 990 | (define-widget 'custom-variable 'custom | ||
| 991 | "Customize variable." | ||
| 992 | :format "%l%v%m%h%a" | ||
| 993 | :help-echo "Set or reset this variable." | ||
| 994 | :documentation-property 'variable-documentation | ||
| 995 | :custom-state nil | ||
| 996 | :custom-menu 'custom-variable-menu-create | ||
| 997 | :custom-form 'edit | ||
| 998 | :value-create 'custom-variable-value-create | ||
| 999 | :action 'custom-variable-action | ||
| 1000 | :custom-set 'custom-variable-set | ||
| 1001 | :custom-save 'custom-variable-save | ||
| 1002 | :custom-reset-current 'custom-redraw | ||
| 1003 | :custom-reset-saved 'custom-variable-reset-saved | ||
| 1004 | :custom-reset-factory 'custom-variable-reset-factory) | ||
| 1005 | |||
| 1006 | (defun custom-variable-value-create (widget) | ||
| 1007 | "Here is where you edit the variables value." | ||
| 1008 | (custom-load-widget widget) | ||
| 1009 | (let* ((buttons (widget-get widget :buttons)) | ||
| 1010 | (children (widget-get widget :children)) | ||
| 1011 | (form (widget-get widget :custom-form)) | ||
| 1012 | (state (widget-get widget :custom-state)) | ||
| 1013 | (symbol (widget-get widget :value)) | ||
| 1014 | (options (get symbol 'custom-options)) | ||
| 1015 | (child-type (or (get symbol 'custom-type) 'sexp)) | ||
| 1016 | (tag (widget-get widget :tag)) | ||
| 1017 | (type (let ((tmp (if (listp child-type) | ||
| 1018 | (copy-list child-type) | ||
| 1019 | (list child-type)))) | ||
| 1020 | (when options | ||
| 1021 | (widget-put tmp :options options)) | ||
| 1022 | tmp)) | ||
| 1023 | (conv (widget-convert type)) | ||
| 1024 | (value (if (default-boundp symbol) | ||
| 1025 | (default-value symbol) | ||
| 1026 | (widget-get conv :value)))) | ||
| 1027 | ;; If the widget is new, the child determine whether it is hidden. | ||
| 1028 | (cond (state) | ||
| 1029 | ((custom-show type value) | ||
| 1030 | (setq state 'unknown)) | ||
| 1031 | (t | ||
| 1032 | (setq state 'hidden))) | ||
| 1033 | ;; If we don't know the state, see if we need to edit it in lisp form. | ||
| 1034 | (when (eq state 'unknown) | ||
| 1035 | (unless (widget-apply conv :match value) | ||
| 1036 | ;; (widget-apply (widget-convert type) :match value) | ||
| 1037 | (setq form 'lisp))) | ||
| 1038 | ;; Now we can create the child widget. | ||
| 1039 | (cond ((eq state 'hidden) | ||
| 1040 | ;; Indicate hidden value. | ||
| 1041 | (push (widget-create-child-and-convert | ||
| 1042 | widget 'item | ||
| 1043 | :format "%{%t%}: ..." | ||
| 1044 | :sample-face 'custom-variable-sample-face | ||
| 1045 | :tag tag | ||
| 1046 | :parent widget) | ||
| 1047 | children)) | ||
| 1048 | ((eq form 'lisp) | ||
| 1049 | ;; In lisp mode edit the saved value when possible. | ||
| 1050 | (let* ((value (cond ((get symbol 'saved-value) | ||
| 1051 | (car (get symbol 'saved-value))) | ||
| 1052 | ((get symbol 'factory-value) | ||
| 1053 | (car (get symbol 'factory-value))) | ||
| 1054 | ((default-boundp symbol) | ||
| 1055 | (custom-quote (default-value symbol))) | ||
| 1056 | (t | ||
| 1057 | (custom-quote (widget-get conv :value)))))) | ||
| 1058 | (push (widget-create-child-and-convert | ||
| 1059 | widget 'sexp | ||
| 1060 | :button-face 'custom-variable-button-face | ||
| 1061 | :tag (symbol-name symbol) | ||
| 1062 | :parent widget | ||
| 1063 | :value value) | ||
| 1064 | children))) | ||
| 1065 | (t | ||
| 1066 | ;; Edit mode. | ||
| 1067 | (push (widget-create-child-and-convert | ||
| 1068 | widget type | ||
| 1069 | :tag tag | ||
| 1070 | :button-face 'custom-variable-button-face | ||
| 1071 | :sample-face 'custom-variable-sample-face | ||
| 1072 | :value value) | ||
| 1073 | children))) | ||
| 1074 | ;; Now update the state. | ||
| 1075 | (unless (eq (preceding-char) ?\n) | ||
| 1076 | (widget-insert "\n")) | ||
| 1077 | (if (eq state 'hidden) | ||
| 1078 | (widget-put widget :custom-state state) | ||
| 1079 | (custom-variable-state-set widget)) | ||
| 1080 | (widget-put widget :custom-form form) | ||
| 1081 | (widget-put widget :buttons buttons) | ||
| 1082 | (widget-put widget :children children))) | ||
| 1083 | |||
| 1084 | (defun custom-variable-state-set (widget) | ||
| 1085 | "Set the state of WIDGET." | ||
| 1086 | (let* ((symbol (widget-value widget)) | ||
| 1087 | (value (if (default-boundp symbol) | ||
| 1088 | (default-value symbol) | ||
| 1089 | (widget-get widget :value))) | ||
| 1090 | tmp | ||
| 1091 | (state (cond ((setq tmp (get symbol 'customized-value)) | ||
| 1092 | (if (condition-case nil | ||
| 1093 | (equal value (eval (car tmp))) | ||
| 1094 | (error nil)) | ||
| 1095 | 'set | ||
| 1096 | 'changed)) | ||
| 1097 | ((setq tmp (get symbol 'saved-value)) | ||
| 1098 | (if (condition-case nil | ||
| 1099 | (equal value (eval (car tmp))) | ||
| 1100 | (error nil)) | ||
| 1101 | 'saved | ||
| 1102 | 'changed)) | ||
| 1103 | ((setq tmp (get symbol 'factory-value)) | ||
| 1104 | (if (condition-case nil | ||
| 1105 | (equal value (eval (car tmp))) | ||
| 1106 | (error nil)) | ||
| 1107 | 'factory | ||
| 1108 | 'changed)) | ||
| 1109 | (t 'rogue)))) | ||
| 1110 | (widget-put widget :custom-state state))) | ||
| 1111 | |||
| 1112 | (defvar custom-variable-menu | ||
| 1113 | '(("Edit" . custom-variable-edit) | ||
| 1114 | ("Edit Lisp" . custom-variable-edit-lisp) | ||
| 1115 | ("Set" . custom-variable-set) | ||
| 1116 | ("Save" . custom-variable-save) | ||
| 1117 | ("Reset to Current" . custom-redraw) | ||
| 1118 | ("Reset to Saved" . custom-variable-reset-saved) | ||
| 1119 | ("Reset to Factory Settings" . custom-variable-reset-factory)) | ||
| 1120 | "Alist of actions for the `custom-variable' widget. | ||
| 1121 | The key is a string containing the name of the action, the value is a | ||
| 1122 | lisp function taking the widget as an element which will be called | ||
| 1123 | when the action is chosen.") | ||
| 1124 | |||
| 1125 | (defun custom-variable-action (widget &optional event) | ||
| 1126 | "Show the menu for `custom-variable' WIDGET. | ||
| 1127 | Optional EVENT is the location for the menu." | ||
| 1128 | (if (eq (widget-get widget :custom-state) 'hidden) | ||
| 1129 | (progn | ||
| 1130 | (widget-put widget :custom-state 'unknown) | ||
| 1131 | (custom-redraw widget)) | ||
| 1132 | (let* ((completion-ignore-case t) | ||
| 1133 | (answer (widget-choose (custom-unlispify-tag-name | ||
| 1134 | (widget-get widget :value)) | ||
| 1135 | custom-variable-menu | ||
| 1136 | event))) | ||
| 1137 | (if answer | ||
| 1138 | (funcall answer widget))))) | ||
| 1139 | |||
| 1140 | (defun custom-variable-edit (widget) | ||
| 1141 | "Edit value of WIDGET." | ||
| 1142 | (widget-put widget :custom-state 'unknown) | ||
| 1143 | (widget-put widget :custom-form 'edit) | ||
| 1144 | (custom-redraw widget)) | ||
| 1145 | |||
| 1146 | (defun custom-variable-edit-lisp (widget) | ||
| 1147 | "Edit the lisp representation of the value of WIDGET." | ||
| 1148 | (widget-put widget :custom-state 'unknown) | ||
| 1149 | (widget-put widget :custom-form 'lisp) | ||
| 1150 | (custom-redraw widget)) | ||
| 1151 | |||
| 1152 | (defun custom-variable-set (widget) | ||
| 1153 | "Set the current value for the variable being edited by WIDGET." | ||
| 1154 | (let ((form (widget-get widget :custom-form)) | ||
| 1155 | (state (widget-get widget :custom-state)) | ||
| 1156 | (child (car (widget-get widget :children))) | ||
| 1157 | (symbol (widget-value widget)) | ||
| 1158 | val) | ||
| 1159 | (cond ((eq state 'hidden) | ||
| 1160 | (error "Cannot set hidden variable.")) | ||
| 1161 | ((setq val (widget-apply child :validate)) | ||
| 1162 | (goto-char (widget-get val :from)) | ||
| 1163 | (error "%s" (widget-get val :error))) | ||
| 1164 | ((eq form 'lisp) | ||
| 1165 | (set symbol (eval (setq val (widget-value child)))) | ||
| 1166 | (put symbol 'customized-value (list val))) | ||
| 1167 | (t | ||
| 1168 | (set symbol (setq val (widget-value child))) | ||
| 1169 | (put symbol 'customized-value (list (custom-quote val))))) | ||
| 1170 | (custom-variable-state-set widget) | ||
| 1171 | (custom-redraw-magic widget))) | ||
| 1172 | |||
| 1173 | (defun custom-variable-save (widget) | ||
| 1174 | "Set the default value for the variable being edited by WIDGET." | ||
| 1175 | (let ((form (widget-get widget :custom-form)) | ||
| 1176 | (state (widget-get widget :custom-state)) | ||
| 1177 | (child (car (widget-get widget :children))) | ||
| 1178 | (symbol (widget-value widget)) | ||
| 1179 | val) | ||
| 1180 | (cond ((eq state 'hidden) | ||
| 1181 | (error "Cannot set hidden variable.")) | ||
| 1182 | ((setq val (widget-apply child :validate)) | ||
| 1183 | (goto-char (widget-get val :from)) | ||
| 1184 | (error "%s" (widget-get val :error))) | ||
| 1185 | ((eq form 'lisp) | ||
| 1186 | (put symbol 'saved-value (list (widget-value child))) | ||
| 1187 | (set symbol (eval (widget-value child)))) | ||
| 1188 | (t | ||
| 1189 | (put symbol | ||
| 1190 | 'saved-value (list (custom-quote (widget-value | ||
| 1191 | child)))) | ||
| 1192 | (set symbol (widget-value child)))) | ||
| 1193 | (put symbol 'customized-value nil) | ||
| 1194 | (custom-save-all) | ||
| 1195 | (custom-variable-state-set widget) | ||
| 1196 | (custom-redraw-magic widget))) | ||
| 1197 | |||
| 1198 | (defun custom-variable-reset-saved (widget) | ||
| 1199 | "Restore the saved value for the variable being edited by WIDGET." | ||
| 1200 | (let ((symbol (widget-value widget))) | ||
| 1201 | (if (get symbol 'saved-value) | ||
| 1202 | (condition-case nil | ||
| 1203 | (set symbol (eval (car (get symbol 'saved-value)))) | ||
| 1204 | (error nil)) | ||
| 1205 | (error "No saved value for %s" symbol)) | ||
| 1206 | (put symbol 'customized-value nil) | ||
| 1207 | (widget-put widget :custom-state 'unknown) | ||
| 1208 | (custom-redraw widget))) | ||
| 1209 | |||
| 1210 | (defun custom-variable-reset-factory (widget) | ||
| 1211 | "Restore the factory setting for the variable being edited by WIDGET." | ||
| 1212 | (let ((symbol (widget-value widget))) | ||
| 1213 | (if (get symbol 'factory-value) | ||
| 1214 | (set symbol (eval (car (get symbol 'factory-value)))) | ||
| 1215 | (error "No factory default for %S" symbol)) | ||
| 1216 | (put symbol 'customized-value nil) | ||
| 1217 | (when (get symbol 'saved-value) | ||
| 1218 | (put symbol 'saved-value nil) | ||
| 1219 | (custom-save-all)) | ||
| 1220 | (widget-put widget :custom-state 'unknown) | ||
| 1221 | (custom-redraw widget))) | ||
| 1222 | |||
| 1223 | ;;; The `custom-face-edit' Widget. | ||
| 1224 | |||
| 1225 | (define-widget 'custom-face-edit 'checklist | ||
| 1226 | "Edit face attributes." | ||
| 1227 | :format "%t: %v" | ||
| 1228 | :tag "Attributes" | ||
| 1229 | :extra-offset 12 | ||
| 1230 | :button-args '(:help-echo "Control whether this attribute have any effect.") | ||
| 1231 | :args (mapcar (lambda (att) | ||
| 1232 | (list 'group | ||
| 1233 | :inline t | ||
| 1234 | :sibling-args (widget-get (nth 1 att) :sibling-args) | ||
| 1235 | (list 'const :format "" :value (nth 0 att)) | ||
| 1236 | (nth 1 att))) | ||
| 1237 | custom-face-attributes)) | ||
| 1238 | |||
| 1239 | ;;; The `custom-display' Widget. | ||
| 1240 | |||
| 1241 | (define-widget 'custom-display 'menu-choice | ||
| 1242 | "Select a display type." | ||
| 1243 | :tag "Display" | ||
| 1244 | :value t | ||
| 1245 | :help-echo "Specify frames where the face attributes should be used." | ||
| 1246 | :args '((const :tag "all" t) | ||
| 1247 | (checklist | ||
| 1248 | :offset 0 | ||
| 1249 | :extra-offset 9 | ||
| 1250 | :args ((group :sibling-args (:help-echo "\ | ||
| 1251 | Only match the specified window systems.") | ||
| 1252 | (const :format "Type: " | ||
| 1253 | type) | ||
| 1254 | (checklist :inline t | ||
| 1255 | :offset 0 | ||
| 1256 | (const :format "X " | ||
| 1257 | :sibling-args (:help-echo "\ | ||
| 1258 | The X11 Window System.") | ||
| 1259 | x) | ||
| 1260 | (const :format "PM " | ||
| 1261 | :sibling-args (:help-echo "\ | ||
| 1262 | OS/2 Presentation Manager.") | ||
| 1263 | pm) | ||
| 1264 | (const :format "Win32 " | ||
| 1265 | :sibling-args (:help-echo "\ | ||
| 1266 | Windows NT/95/97.") | ||
| 1267 | win32) | ||
| 1268 | (const :format "DOS " | ||
| 1269 | :sibling-args (:help-echo "\ | ||
| 1270 | Plain MS-DOS.") | ||
| 1271 | pc) | ||
| 1272 | (const :format "TTY%n" | ||
| 1273 | :sibling-args (:help-echo "\ | ||
| 1274 | Plain text terminals.") | ||
| 1275 | tty))) | ||
| 1276 | (group :sibling-args (:help-echo "\ | ||
| 1277 | Only match the frames with the specified color support.") | ||
| 1278 | (const :format "Class: " | ||
| 1279 | class) | ||
| 1280 | (checklist :inline t | ||
| 1281 | :offset 0 | ||
| 1282 | (const :format "Color " | ||
| 1283 | :sibling-args (:help-echo "\ | ||
| 1284 | Match color frames.") | ||
| 1285 | color) | ||
| 1286 | (const :format "Grayscale " | ||
| 1287 | :sibling-args (:help-echo "\ | ||
| 1288 | Match grayscale frames.") | ||
| 1289 | grayscale) | ||
| 1290 | (const :format "Monochrome%n" | ||
| 1291 | :sibling-args (:help-echo "\ | ||
| 1292 | Match frames with no color support.") | ||
| 1293 | mono))) | ||
| 1294 | (group :sibling-args (:help-echo "\ | ||
| 1295 | Only match frames with the specified intensity.") | ||
| 1296 | (const :format "\ | ||
| 1297 | Background brightness: " | ||
| 1298 | background) | ||
| 1299 | (checklist :inline t | ||
| 1300 | :offset 0 | ||
| 1301 | (const :format "Light " | ||
| 1302 | :sibling-args (:help-echo "\ | ||
| 1303 | Match frames with light backgrounds.") | ||
| 1304 | light) | ||
| 1305 | (const :format "Dark\n" | ||
| 1306 | :sibling-args (:help-echo "\ | ||
| 1307 | Match frames with dark backgrounds.") | ||
| 1308 | dark))))))) | ||
| 1309 | |||
| 1310 | ;;; The `custom-face' Widget. | ||
| 1311 | |||
| 1312 | (defface custom-face-tag-face '((t (:underline t))) | ||
| 1313 | "Face used for face tags." | ||
| 1314 | :group 'customize) | ||
| 1315 | |||
| 1316 | (define-widget 'custom-face 'custom | ||
| 1317 | "Customize face." | ||
| 1318 | :format "%l%{%t%}: %s%m%h%a%v" | ||
| 1319 | :format-handler 'custom-face-format-handler | ||
| 1320 | :sample-face 'custom-face-tag-face | ||
| 1321 | :help-echo "Set or reset this face." | ||
| 1322 | :documentation-property '(lambda (face) | ||
| 1323 | (face-doc-string face)) | ||
| 1324 | :value-create 'custom-face-value-create | ||
| 1325 | :action 'custom-face-action | ||
| 1326 | :custom-form 'selected | ||
| 1327 | :custom-set 'custom-face-set | ||
| 1328 | :custom-save 'custom-face-save | ||
| 1329 | :custom-reset-current 'custom-redraw | ||
| 1330 | :custom-reset-saved 'custom-face-reset-saved | ||
| 1331 | :custom-reset-factory 'custom-face-reset-factory | ||
| 1332 | :custom-menu 'custom-face-menu-create) | ||
| 1333 | |||
| 1334 | (defun custom-face-format-handler (widget escape) | ||
| 1335 | ;; We recognize extra escape sequences. | ||
| 1336 | (let (child | ||
| 1337 | (symbol (widget-get widget :value))) | ||
| 1338 | (cond ((eq escape ?s) | ||
| 1339 | (and (string-match "XEmacs" emacs-version) | ||
| 1340 | ;; XEmacs cannot display initialized faces. | ||
| 1341 | (not (custom-facep symbol)) | ||
| 1342 | (copy-face 'custom-face-empty symbol)) | ||
| 1343 | (setq child (widget-create-child-and-convert | ||
| 1344 | widget 'item | ||
| 1345 | :format "(%{%t%})\n" | ||
| 1346 | :sample-face symbol | ||
| 1347 | :tag "sample"))) | ||
| 1348 | (t | ||
| 1349 | (custom-format-handler widget escape))) | ||
| 1350 | (when child | ||
| 1351 | (widget-put widget | ||
| 1352 | :buttons (cons child (widget-get widget :buttons)))))) | ||
| 1353 | |||
| 1354 | (define-widget 'custom-face-all 'editable-list | ||
| 1355 | "An editable list of display specifications and attributes." | ||
| 1356 | :entry-format "%i %d %v" | ||
| 1357 | :insert-button-args '(:help-echo "Insert new display specification here.") | ||
| 1358 | :append-button-args '(:help-echo "Append new display specification here.") | ||
| 1359 | :delete-button-args '(:help-echo "Delete this display specification.") | ||
| 1360 | :args '((group :format "%v" custom-display custom-face-edit))) | ||
| 1361 | |||
| 1362 | (defconst custom-face-all (widget-convert 'custom-face-all) | ||
| 1363 | "Converted version of the `custom-face-all' widget.") | ||
| 1364 | |||
| 1365 | (define-widget 'custom-display-unselected 'item | ||
| 1366 | "A display specification that doesn't match the selected display." | ||
| 1367 | :match 'custom-display-unselected-match) | ||
| 1368 | |||
| 1369 | (defun custom-display-unselected-match (widget value) | ||
| 1370 | "Non-nil if VALUE is an unselected display specification." | ||
| 1371 | (and (listp value) | ||
| 1372 | (eq (length value) 2) | ||
| 1373 | (not (custom-display-match-frame value (selected-frame))))) | ||
| 1374 | |||
| 1375 | (define-widget 'custom-face-selected 'group | ||
| 1376 | "Edit the attributes of the selected display in a face specification." | ||
| 1377 | :args '((repeat :format "" | ||
| 1378 | :inline t | ||
| 1379 | (group custom-display-unselected sexp)) | ||
| 1380 | (group (sexp :format "") custom-face-edit) | ||
| 1381 | (repeat :format "" | ||
| 1382 | :inline t | ||
| 1383 | sexp))) | ||
| 1384 | |||
| 1385 | (defconst custom-face-selected (widget-convert 'custom-face-selected) | ||
| 1386 | "Converted version of the `custom-face-selected' widget.") | ||
| 1387 | |||
| 1388 | (defun custom-face-value-create (widget) | ||
| 1389 | ;; Create a list of the display specifications. | ||
| 1390 | (unless (eq (preceding-char) ?\n) | ||
| 1391 | (insert "\n")) | ||
| 1392 | (when (not (eq (widget-get widget :custom-state) 'hidden)) | ||
| 1393 | (message "Creating face editor...") | ||
| 1394 | (custom-load-widget widget) | ||
| 1395 | (let* ((symbol (widget-value widget)) | ||
| 1396 | (spec (or (get symbol 'saved-face) | ||
| 1397 | (get symbol 'factory-face) | ||
| 1398 | ;; Attempt to construct it. | ||
| 1399 | (list (list t (custom-face-attributes-get | ||
| 1400 | symbol (selected-frame)))))) | ||
| 1401 | (form (widget-get widget :custom-form)) | ||
| 1402 | (indent (widget-get widget :indent)) | ||
| 1403 | (edit (widget-create-child-and-convert | ||
| 1404 | widget | ||
| 1405 | (cond ((and (eq form 'selected) | ||
| 1406 | (widget-apply custom-face-selected :match spec)) | ||
| 1407 | (when indent (insert-char ?\ indent)) | ||
| 1408 | 'custom-face-selected) | ||
| 1409 | ((and (not (eq form 'lisp)) | ||
| 1410 | (widget-apply custom-face-all :match spec)) | ||
| 1411 | 'custom-face-all) | ||
| 1412 | (t | ||
| 1413 | (when indent (insert-char ?\ indent)) | ||
| 1414 | 'sexp)) | ||
| 1415 | :value spec))) | ||
| 1416 | (custom-face-state-set widget) | ||
| 1417 | (widget-put widget :children (list edit))) | ||
| 1418 | (message "Creating face editor...done"))) | ||
| 1419 | |||
| 1420 | (defvar custom-face-menu | ||
| 1421 | '(("Edit Selected" . custom-face-edit-selected) | ||
| 1422 | ("Edit All" . custom-face-edit-all) | ||
| 1423 | ("Edit Lisp" . custom-face-edit-lisp) | ||
| 1424 | ("Set" . custom-face-set) | ||
| 1425 | ("Save" . custom-face-save) | ||
| 1426 | ("Reset to Saved" . custom-face-reset-saved) | ||
| 1427 | ("Reset to Factory Setting" . custom-face-reset-factory)) | ||
| 1428 | "Alist of actions for the `custom-face' widget. | ||
| 1429 | The key is a string containing the name of the action, the value is a | ||
| 1430 | lisp function taking the widget as an element which will be called | ||
| 1431 | when the action is chosen.") | ||
| 1432 | |||
| 1433 | (defun custom-face-edit-selected (widget) | ||
| 1434 | "Edit selected attributes of the value of WIDGET." | ||
| 1435 | (widget-put widget :custom-state 'unknown) | ||
| 1436 | (widget-put widget :custom-form 'selected) | ||
| 1437 | (custom-redraw widget)) | ||
| 1438 | |||
| 1439 | (defun custom-face-edit-all (widget) | ||
| 1440 | "Edit all attributes of the value of WIDGET." | ||
| 1441 | (widget-put widget :custom-state 'unknown) | ||
| 1442 | (widget-put widget :custom-form 'all) | ||
| 1443 | (custom-redraw widget)) | ||
| 1444 | |||
| 1445 | (defun custom-face-edit-lisp (widget) | ||
| 1446 | "Edit the lisp representation of the value of WIDGET." | ||
| 1447 | (widget-put widget :custom-state 'unknown) | ||
| 1448 | (widget-put widget :custom-form 'lisp) | ||
| 1449 | (custom-redraw widget)) | ||
| 1450 | |||
| 1451 | (defun custom-face-state-set (widget) | ||
| 1452 | "Set the state of WIDGET." | ||
| 1453 | (let ((symbol (widget-value widget))) | ||
| 1454 | (widget-put widget :custom-state (cond ((get symbol 'customized-face) | ||
| 1455 | 'set) | ||
| 1456 | ((get symbol 'saved-face) | ||
| 1457 | 'saved) | ||
| 1458 | ((get symbol 'factory-face) | ||
| 1459 | 'factory) | ||
| 1460 | (t | ||
| 1461 | 'rogue))))) | ||
| 1462 | |||
| 1463 | (defun custom-face-action (widget &optional event) | ||
| 1464 | "Show the menu for `custom-face' WIDGET. | ||
| 1465 | Optional EVENT is the location for the menu." | ||
| 1466 | (if (eq (widget-get widget :custom-state) 'hidden) | ||
| 1467 | (progn | ||
| 1468 | (widget-put widget :custom-state 'unknown) | ||
| 1469 | (custom-redraw widget)) | ||
| 1470 | (let* ((completion-ignore-case t) | ||
| 1471 | (symbol (widget-get widget :value)) | ||
| 1472 | (answer (widget-choose (custom-unlispify-tag-name symbol) | ||
| 1473 | custom-face-menu event))) | ||
| 1474 | (if answer | ||
| 1475 | (funcall answer widget))))) | ||
| 1476 | |||
| 1477 | (defun custom-face-set (widget) | ||
| 1478 | "Make the face attributes in WIDGET take effect." | ||
| 1479 | (let* ((symbol (widget-value widget)) | ||
| 1480 | (child (car (widget-get widget :children))) | ||
| 1481 | (value (widget-value child))) | ||
| 1482 | (put symbol 'customized-face value) | ||
| 1483 | (when (fboundp 'copy-face) | ||
| 1484 | (copy-face 'custom-face-empty symbol)) | ||
| 1485 | (custom-face-display-set symbol value) | ||
| 1486 | (custom-face-state-set widget) | ||
| 1487 | (custom-redraw-magic widget))) | ||
| 1488 | |||
| 1489 | (defun custom-face-save (widget) | ||
| 1490 | "Make the face attributes in WIDGET default." | ||
| 1491 | (let* ((symbol (widget-value widget)) | ||
| 1492 | (child (car (widget-get widget :children))) | ||
| 1493 | (value (widget-value child))) | ||
| 1494 | (when (fboundp 'copy-face) | ||
| 1495 | (copy-face 'custom-face-empty symbol)) | ||
| 1496 | (custom-face-display-set symbol value) | ||
| 1497 | (put symbol 'saved-face value) | ||
| 1498 | (put symbol 'customized-face nil) | ||
| 1499 | (custom-face-state-set widget) | ||
| 1500 | (custom-redraw-magic widget))) | ||
| 1501 | |||
| 1502 | (defun custom-face-reset-saved (widget) | ||
| 1503 | "Restore WIDGET to the face's default attributes." | ||
| 1504 | (let* ((symbol (widget-value widget)) | ||
| 1505 | (child (car (widget-get widget :children))) | ||
| 1506 | (value (get symbol 'saved-face))) | ||
| 1507 | (unless value | ||
| 1508 | (error "No saved value for this face")) | ||
| 1509 | (put symbol 'customized-face nil) | ||
| 1510 | (when (fboundp 'copy-face) | ||
| 1511 | (copy-face 'custom-face-empty symbol)) | ||
| 1512 | (custom-face-display-set symbol value) | ||
| 1513 | (widget-value-set child value) | ||
| 1514 | (custom-face-state-set widget) | ||
| 1515 | (custom-redraw-magic widget))) | ||
| 1516 | |||
| 1517 | (defun custom-face-reset-factory (widget) | ||
| 1518 | "Restore WIDGET to the face's factory settings." | ||
| 1519 | (let* ((symbol (widget-value widget)) | ||
| 1520 | (child (car (widget-get widget :children))) | ||
| 1521 | (value (get symbol 'factory-face))) | ||
| 1522 | (unless value | ||
| 1523 | (error "No factory default for this face")) | ||
| 1524 | (put symbol 'customized-face nil) | ||
| 1525 | (when (get symbol 'saved-face) | ||
| 1526 | (put symbol 'saved-face nil) | ||
| 1527 | (custom-save-all)) | ||
| 1528 | (when (fboundp 'copy-face) | ||
| 1529 | (copy-face 'custom-face-empty symbol)) | ||
| 1530 | (custom-face-display-set symbol value) | ||
| 1531 | (widget-value-set child value) | ||
| 1532 | (custom-face-state-set widget) | ||
| 1533 | (custom-redraw-magic widget))) | ||
| 1534 | |||
| 1535 | ;;; The `face' Widget. | ||
| 1536 | |||
| 1537 | (define-widget 'face 'default | ||
| 1538 | "Select and customize a face." | ||
| 1539 | :convert-widget 'widget-item-convert-widget | ||
| 1540 | :format "%[%t%]: %v" | ||
| 1541 | :tag "Face" | ||
| 1542 | :value 'default | ||
| 1543 | :value-create 'widget-face-value-create | ||
| 1544 | :value-delete 'widget-face-value-delete | ||
| 1545 | :value-get 'widget-item-value-get | ||
| 1546 | :validate 'widget-editable-list-validate | ||
| 1547 | :action 'widget-face-action | ||
| 1548 | :match '(lambda (widget value) (symbolp value))) | ||
| 1549 | |||
| 1550 | (defun widget-face-value-create (widget) | ||
| 1551 | ;; Create a `custom-face' child. | ||
| 1552 | (let* ((symbol (widget-value widget)) | ||
| 1553 | (child (widget-create-child-and-convert | ||
| 1554 | widget 'custom-face | ||
| 1555 | :format "%t %s%m%h%v" | ||
| 1556 | :custom-level nil | ||
| 1557 | :value symbol))) | ||
| 1558 | (custom-magic-reset child) | ||
| 1559 | (setq custom-options (cons child custom-options)) | ||
| 1560 | (widget-put widget :children (list child)))) | ||
| 1561 | |||
| 1562 | (defun widget-face-value-delete (widget) | ||
| 1563 | ;; Remove the child from the options. | ||
| 1564 | (let ((child (car (widget-get widget :children)))) | ||
| 1565 | (setq custom-options (delq child custom-options)) | ||
| 1566 | (widget-children-value-delete widget))) | ||
| 1567 | |||
| 1568 | (defvar face-history nil | ||
| 1569 | "History of entered face names.") | ||
| 1570 | |||
| 1571 | (defun widget-face-action (widget &optional event) | ||
| 1572 | "Prompt for a face." | ||
| 1573 | (let ((answer (completing-read "Face: " | ||
| 1574 | (mapcar (lambda (face) | ||
| 1575 | (list (symbol-name face))) | ||
| 1576 | (face-list)) | ||
| 1577 | nil nil nil | ||
| 1578 | 'face-history))) | ||
| 1579 | (unless (zerop (length answer)) | ||
| 1580 | (widget-value-set widget (intern answer)) | ||
| 1581 | (widget-apply widget :notify widget event) | ||
| 1582 | (widget-setup)))) | ||
| 1583 | |||
| 1584 | ;;; The `hook' Widget. | ||
| 1585 | |||
| 1586 | (define-widget 'hook 'list | ||
| 1587 | "A emacs lisp hook" | ||
| 1588 | :convert-widget 'custom-hook-convert-widget | ||
| 1589 | :tag "Hook") | ||
| 1590 | |||
| 1591 | (defun custom-hook-convert-widget (widget) | ||
| 1592 | ;; Handle `:custom-options'. | ||
| 1593 | (let* ((options (widget-get widget :options)) | ||
| 1594 | (other `(editable-list :inline t | ||
| 1595 | :entry-format "%i %d%v" | ||
| 1596 | (function :format " %v"))) | ||
| 1597 | (args (if options | ||
| 1598 | (list `(checklist :inline t | ||
| 1599 | ,@(mapcar (lambda (entry) | ||
| 1600 | `(function-item ,entry)) | ||
| 1601 | options)) | ||
| 1602 | other) | ||
| 1603 | (list other)))) | ||
| 1604 | (widget-put widget :args args) | ||
| 1605 | widget)) | ||
| 1606 | |||
| 1607 | ;;; The `custom-group' Widget. | ||
| 1608 | |||
| 1609 | (defcustom custom-group-tag-faces '(custom-group-tag-face-1) | ||
| 1610 | ;; In XEmacs, this ought to play games with font size. | ||
| 1611 | "Face used for group tags. | ||
| 1612 | The first member is used for level 1 groups, the second for level 2, | ||
| 1613 | and so forth. The remaining group tags are shown with | ||
| 1614 | `custom-group-tag-face'." | ||
| 1615 | :type '(repeat face) | ||
| 1616 | :group 'customize) | ||
| 1617 | |||
| 1618 | (defface custom-group-tag-face-1 '((((class color) | ||
| 1619 | (background dark)) | ||
| 1620 | (:foreground "pink" :underline t)) | ||
| 1621 | (((class color) | ||
| 1622 | (background light)) | ||
| 1623 | (:foreground "red" :underline t)) | ||
| 1624 | (t (:underline t))) | ||
| 1625 | "Face used for group tags.") | ||
| 1626 | |||
| 1627 | (defface custom-group-tag-face '((((class color) | ||
| 1628 | (background dark)) | ||
| 1629 | (:foreground "light blue" :underline t)) | ||
| 1630 | (((class color) | ||
| 1631 | (background light)) | ||
| 1632 | (:foreground "blue" :underline t)) | ||
| 1633 | (t (:underline t))) | ||
| 1634 | "Face used for low level group tags." | ||
| 1635 | :group 'customize) | ||
| 1636 | |||
| 1637 | (define-widget 'custom-group 'custom | ||
| 1638 | "Customize group." | ||
| 1639 | :format "%l%{%t%}:%L\n%m%h%a%v" | ||
| 1640 | :sample-face-get 'custom-group-sample-face-get | ||
| 1641 | :documentation-property 'group-documentation | ||
| 1642 | :help-echo "Set or reset all members of this group." | ||
| 1643 | :value-create 'custom-group-value-create | ||
| 1644 | :action 'custom-group-action | ||
| 1645 | :custom-set 'custom-group-set | ||
| 1646 | :custom-save 'custom-group-save | ||
| 1647 | :custom-reset-current 'custom-group-reset-current | ||
| 1648 | :custom-reset-saved 'custom-group-reset-saved | ||
| 1649 | :custom-reset-factory 'custom-group-reset-factory | ||
| 1650 | :custom-menu 'custom-group-menu-create) | ||
| 1651 | |||
| 1652 | (defun custom-group-sample-face-get (widget) | ||
| 1653 | ;; Use :sample-face. | ||
| 1654 | (or (nth (1- (widget-get widget :custom-level)) custom-group-tag-faces) | ||
| 1655 | 'custom-group-tag-face)) | ||
| 1656 | |||
| 1657 | (defun custom-group-value-create (widget) | ||
| 1658 | (let ((state (widget-get widget :custom-state))) | ||
| 1659 | (unless (eq state 'hidden) | ||
| 1660 | (message "Creating group...") | ||
| 1661 | (custom-load-widget widget) | ||
| 1662 | (let* ((level (widget-get widget :custom-level)) | ||
| 1663 | (symbol (widget-value widget)) | ||
| 1664 | (members (get symbol 'custom-group)) | ||
| 1665 | (prefixes (widget-get widget :custom-prefixes)) | ||
| 1666 | (custom-prefix-list (custom-prefix-add symbol prefixes)) | ||
| 1667 | (length (length members)) | ||
| 1668 | (count 0) | ||
| 1669 | (children (mapcar (lambda (entry) | ||
| 1670 | (widget-insert "\n") | ||
| 1671 | (message "Creating group members... %2d%%" | ||
| 1672 | (/ (* 100.0 count) length)) | ||
| 1673 | (setq count (1+ count)) | ||
| 1674 | (prog1 | ||
| 1675 | (widget-create-child-and-convert | ||
| 1676 | widget (nth 1 entry) | ||
| 1677 | :group widget | ||
| 1678 | :tag (custom-unlispify-tag-name | ||
| 1679 | (nth 0 entry)) | ||
| 1680 | :custom-prefixes custom-prefix-list | ||
| 1681 | :custom-level (1+ level) | ||
| 1682 | :value (nth 0 entry)) | ||
| 1683 | (unless (eq (preceding-char) ?\n) | ||
| 1684 | (widget-insert "\n")))) | ||
| 1685 | members))) | ||
| 1686 | (message "Creating group magic...") | ||
| 1687 | (mapcar 'custom-magic-reset children) | ||
| 1688 | (message "Creating group state...") | ||
| 1689 | (widget-put widget :children children) | ||
| 1690 | (custom-group-state-update widget) | ||
| 1691 | (message "Creating group... done"))))) | ||
| 1692 | |||
| 1693 | (defvar custom-group-menu | ||
| 1694 | '(("Set" . custom-group-set) | ||
| 1695 | ("Save" . custom-group-save) | ||
| 1696 | ("Reset to Current" . custom-group-reset-current) | ||
| 1697 | ("Reset to Saved" . custom-group-reset-saved) | ||
| 1698 | ("Reset to Factory" . custom-group-reset-factory)) | ||
| 1699 | "Alist of actions for the `custom-group' widget. | ||
| 1700 | The key is a string containing the name of the action, the value is a | ||
| 1701 | lisp function taking the widget as an element which will be called | ||
| 1702 | when the action is chosen.") | ||
| 1703 | |||
| 1704 | (defun custom-group-action (widget &optional event) | ||
| 1705 | "Show the menu for `custom-group' WIDGET. | ||
| 1706 | Optional EVENT is the location for the menu." | ||
| 1707 | (if (eq (widget-get widget :custom-state) 'hidden) | ||
| 1708 | (progn | ||
| 1709 | (widget-put widget :custom-state 'unknown) | ||
| 1710 | (custom-redraw widget)) | ||
| 1711 | (let* ((completion-ignore-case t) | ||
| 1712 | (answer (widget-choose (custom-unlispify-tag-name | ||
| 1713 | (widget-get widget :value)) | ||
| 1714 | custom-group-menu | ||
| 1715 | event))) | ||
| 1716 | (if answer | ||
| 1717 | (funcall answer widget))))) | ||
| 1718 | |||
| 1719 | (defun custom-group-set (widget) | ||
| 1720 | "Set changes in all modified group members." | ||
| 1721 | (let ((children (widget-get widget :children))) | ||
| 1722 | (mapcar (lambda (child) | ||
| 1723 | (when (eq (widget-get child :custom-state) 'modified) | ||
| 1724 | (widget-apply child :custom-set))) | ||
| 1725 | children ))) | ||
| 1726 | |||
| 1727 | (defun custom-group-save (widget) | ||
| 1728 | "Save all modified group members." | ||
| 1729 | (let ((children (widget-get widget :children))) | ||
| 1730 | (mapcar (lambda (child) | ||
| 1731 | (when (memq (widget-get child :custom-state) '(modified set)) | ||
| 1732 | (widget-apply child :custom-save))) | ||
| 1733 | children ))) | ||
| 1734 | |||
| 1735 | (defun custom-group-reset-current (widget) | ||
| 1736 | "Reset all modified group members." | ||
| 1737 | (let ((children (widget-get widget :children))) | ||
| 1738 | (mapcar (lambda (child) | ||
| 1739 | (when (eq (widget-get child :custom-state) 'modified) | ||
| 1740 | (widget-apply child :custom-reset-current))) | ||
| 1741 | children ))) | ||
| 1742 | |||
| 1743 | (defun custom-group-reset-saved (widget) | ||
| 1744 | "Reset all modified or set group members." | ||
| 1745 | (let ((children (widget-get widget :children))) | ||
| 1746 | (mapcar (lambda (child) | ||
| 1747 | (when (memq (widget-get child :custom-state) '(modified set)) | ||
| 1748 | (widget-apply child :custom-reset-saved))) | ||
| 1749 | children ))) | ||
| 1750 | |||
| 1751 | (defun custom-group-reset-factory (widget) | ||
| 1752 | "Reset all modified, set, or saved group members." | ||
| 1753 | (let ((children (widget-get widget :children))) | ||
| 1754 | (mapcar (lambda (child) | ||
| 1755 | (when (memq (widget-get child :custom-state) | ||
| 1756 | '(modified set saved)) | ||
| 1757 | (widget-apply child :custom-reset-factory))) | ||
| 1758 | children ))) | ||
| 1759 | |||
| 1760 | (defun custom-group-state-update (widget) | ||
| 1761 | "Update magic." | ||
| 1762 | (unless (eq (widget-get widget :custom-state) 'hidden) | ||
| 1763 | (let* ((children (widget-get widget :children)) | ||
| 1764 | (states (mapcar (lambda (child) | ||
| 1765 | (widget-get child :custom-state)) | ||
| 1766 | children)) | ||
| 1767 | (magics custom-magic-alist) | ||
| 1768 | (found 'factory)) | ||
| 1769 | (while magics | ||
| 1770 | (let ((magic (car (car magics)))) | ||
| 1771 | (if (and (not (eq magic 'hidden)) | ||
| 1772 | (memq magic states)) | ||
| 1773 | (setq found magic | ||
| 1774 | magics nil) | ||
| 1775 | (setq magics (cdr magics))))) | ||
| 1776 | (widget-put widget :custom-state found))) | ||
| 1777 | (custom-magic-reset widget)) | ||
| 1778 | |||
| 1779 | ;;; The `custom-save-all' Function. | ||
| 1780 | |||
| 1781 | (defcustom custom-file "~/.emacs" | ||
| 1782 | "File used for storing customization information. | ||
| 1783 | If you change this from the default \"~/.emacs\" you need to | ||
| 1784 | explicitly load that file for the settings to take effect." | ||
| 1785 | :type 'file | ||
| 1786 | :group 'customize) | ||
| 1787 | |||
| 1788 | (defun custom-save-delete (symbol) | ||
| 1789 | "Delete the call to SYMBOL form `custom-file'. | ||
| 1790 | Leave point at the location of the call, or after the last expression." | ||
| 1791 | (set-buffer (find-file-noselect custom-file)) | ||
| 1792 | (goto-char (point-min)) | ||
| 1793 | (catch 'found | ||
| 1794 | (while t | ||
| 1795 | (let ((sexp (condition-case nil | ||
| 1796 | (read (current-buffer)) | ||
| 1797 | (end-of-file (throw 'found nil))))) | ||
| 1798 | (when (and (listp sexp) | ||
| 1799 | (eq (car sexp) symbol)) | ||
| 1800 | (delete-region (save-excursion | ||
| 1801 | (backward-sexp) | ||
| 1802 | (point)) | ||
| 1803 | (point)) | ||
| 1804 | (throw 'found nil)))))) | ||
| 1805 | |||
| 1806 | (defun custom-save-variables () | ||
| 1807 | "Save all customized variables in `custom-file'." | ||
| 1808 | (save-excursion | ||
| 1809 | (custom-save-delete 'custom-set-variables) | ||
| 1810 | (let ((standard-output (current-buffer))) | ||
| 1811 | (unless (bolp) | ||
| 1812 | (princ "\n")) | ||
| 1813 | (princ "(custom-set-variables") | ||
| 1814 | (mapatoms (lambda (symbol) | ||
| 1815 | (let ((value (get symbol 'saved-value))) | ||
| 1816 | (when value | ||
| 1817 | (princ "\n '(") | ||
| 1818 | (princ symbol) | ||
| 1819 | (princ " ") | ||
| 1820 | (prin1 (car value)) | ||
| 1821 | (if (or (get symbol 'factory-value) | ||
| 1822 | (and (not (boundp symbol)) | ||
| 1823 | (not (get symbol 'force-value)))) | ||
| 1824 | (princ ")") | ||
| 1825 | (princ " t)")))))) | ||
| 1826 | (princ ")") | ||
| 1827 | (unless (looking-at "\n") | ||
| 1828 | (princ "\n"))))) | ||
| 1829 | |||
| 1830 | (defun custom-save-faces () | ||
| 1831 | "Save all customized faces in `custom-file'." | ||
| 1832 | (save-excursion | ||
| 1833 | (custom-save-delete 'custom-set-faces) | ||
| 1834 | (let ((standard-output (current-buffer))) | ||
| 1835 | (unless (bolp) | ||
| 1836 | (princ "\n")) | ||
| 1837 | (princ "(custom-set-faces") | ||
| 1838 | (mapatoms (lambda (symbol) | ||
| 1839 | (let ((value (get symbol 'saved-face))) | ||
| 1840 | (when value | ||
| 1841 | (princ "\n '(") | ||
| 1842 | (princ symbol) | ||
| 1843 | (princ " ") | ||
| 1844 | (prin1 value) | ||
| 1845 | (if (or (get symbol 'factory-face) | ||
| 1846 | (and (not (custom-facep symbol)) | ||
| 1847 | (not (get symbol 'force-face)))) | ||
| 1848 | (princ ")") | ||
| 1849 | (princ " t)")))))) | ||
| 1850 | (princ ")") | ||
| 1851 | (unless (looking-at "\n") | ||
| 1852 | (princ "\n"))))) | ||
| 1853 | |||
| 1854 | ;;;###autoload | ||
| 1855 | (defun custom-save-all () | ||
| 1856 | "Save all customizations in `custom-file'." | ||
| 1857 | (custom-save-variables) | ||
| 1858 | (custom-save-faces) | ||
| 1859 | (save-excursion | ||
| 1860 | (set-buffer (find-file-noselect custom-file)) | ||
| 1861 | (save-buffer))) | ||
| 1862 | |||
| 1863 | ;;; The Customize Menu. | ||
| 1864 | |||
| 1865 | (defcustom custom-menu-nesting 2 | ||
| 1866 | "Maximum nesting in custom menus." | ||
| 1867 | :type 'integer | ||
| 1868 | :group 'customize) | ||
| 1869 | |||
| 1870 | (defun custom-face-menu-create (widget symbol) | ||
| 1871 | "Ignoring WIDGET, create a menu entry for customization face SYMBOL." | ||
| 1872 | (vector (custom-unlispify-menu-entry symbol) | ||
| 1873 | `(custom-buffer-create '((,symbol custom-face))) | ||
| 1874 | t)) | ||
| 1875 | |||
| 1876 | (defun custom-variable-menu-create (widget symbol) | ||
| 1877 | "Ignoring WIDGET, create a menu entry for customization variable SYMBOL." | ||
| 1878 | (let ((type (get symbol 'custom-type))) | ||
| 1879 | (unless (listp type) | ||
| 1880 | (setq type (list type))) | ||
| 1881 | (if (and type (widget-get type :custom-menu)) | ||
| 1882 | (widget-apply type :custom-menu symbol) | ||
| 1883 | (vector (custom-unlispify-menu-entry symbol) | ||
| 1884 | `(custom-buffer-create '((,symbol custom-variable))) | ||
| 1885 | t)))) | ||
| 1886 | |||
| 1887 | (widget-put (get 'boolean 'widget-type) | ||
| 1888 | :custom-menu (lambda (widget symbol) | ||
| 1889 | (vector (custom-unlispify-menu-entry symbol) | ||
| 1890 | `(custom-buffer-create | ||
| 1891 | '((,symbol custom-variable))) | ||
| 1892 | ':style 'toggle | ||
| 1893 | ':selected symbol))) | ||
| 1894 | |||
| 1895 | (if (string-match "XEmacs" emacs-version) | ||
| 1896 | ;; XEmacs can create menus dynamically. | ||
| 1897 | (defun custom-group-menu-create (widget symbol) | ||
| 1898 | "Ignoring WIDGET, create a menu entry for customization group SYMBOL." | ||
| 1899 | `( ,(custom-unlispify-menu-entry symbol t) | ||
| 1900 | :filter (lambda (&rest junk) | ||
| 1901 | (cdr (custom-menu-create ',symbol))))) | ||
| 1902 | ;; But emacs can't. | ||
| 1903 | (defun custom-group-menu-create (widget symbol) | ||
| 1904 | "Ignoring WIDGET, create a menu entry for customization group SYMBOL." | ||
| 1905 | ;; Limit the nesting. | ||
| 1906 | (let ((custom-menu-nesting (1- custom-menu-nesting))) | ||
| 1907 | (custom-menu-create symbol)))) | ||
| 1908 | |||
| 1909 | (defun custom-menu-create (symbol &optional name) | ||
| 1910 | "Create menu for customization group SYMBOL. | ||
| 1911 | If optional NAME is given, use that as the name of the menu. | ||
| 1912 | Otherwise make up a name from SYMBOL. | ||
| 1913 | The menu is in a format applicable to `easy-menu-define'." | ||
| 1914 | (unless name | ||
| 1915 | (setq name (custom-unlispify-menu-entry symbol))) | ||
| 1916 | (let ((item (vector name | ||
| 1917 | `(custom-buffer-create '((,symbol custom-group))) | ||
| 1918 | t))) | ||
| 1919 | (if (and (>= custom-menu-nesting 0) | ||
| 1920 | (< (length (get symbol 'custom-group)) widget-menu-max-size)) | ||
| 1921 | (let ((custom-prefix-list (custom-prefix-add symbol | ||
| 1922 | custom-prefix-list))) | ||
| 1923 | (custom-load-symbol symbol) | ||
| 1924 | `(,(custom-unlispify-menu-entry symbol t) | ||
| 1925 | ,item | ||
| 1926 | "--" | ||
| 1927 | ,@(mapcar (lambda (entry) | ||
| 1928 | (widget-apply (if (listp (nth 1 entry)) | ||
| 1929 | (nth 1 entry) | ||
| 1930 | (list (nth 1 entry))) | ||
| 1931 | :custom-menu (nth 0 entry))) | ||
| 1932 | (get symbol 'custom-group)))) | ||
| 1933 | item))) | ||
| 1934 | |||
| 1935 | ;;;###autoload | ||
| 1936 | (defun custom-menu-update (event) | ||
| 1937 | "Update customize menu." | ||
| 1938 | (interactive "e") | ||
| 1939 | (add-hook 'custom-define-hook 'custom-menu-reset) | ||
| 1940 | (let* ((emacs (widget-apply '(custom-group) :custom-menu 'emacs)) | ||
| 1941 | (menu `(,(car custom-help-menu) | ||
| 1942 | ,emacs | ||
| 1943 | ,@(cdr (cdr custom-help-menu))))) | ||
| 1944 | (let ((map (easy-menu-create-keymaps (car menu) (cdr menu)))) | ||
| 1945 | (define-key global-map [menu-bar help-menu customize-menu] | ||
| 1946 | (cons (car menu) map))))) | ||
| 1947 | |||
| 1948 | ;;; Dependencies. | ||
| 1949 | |||
| 1950 | ;;;###autoload | ||
| 1951 | (defun custom-make-dependencies () | ||
| 1952 | "Batch function to extract custom dependencies from .el files. | ||
| 1953 | Usage: emacs -batch *.el -f custom-make-dependencies > deps.el" | ||
| 1954 | (let ((buffers (buffer-list))) | ||
| 1955 | (while buffers | ||
| 1956 | (set-buffer (car buffers)) | ||
| 1957 | (setq buffers (cdr buffers)) | ||
| 1958 | (let ((file (buffer-file-name))) | ||
| 1959 | (when (and file (string-match "\\`\\(.*\\)\\.el\\'" file)) | ||
| 1960 | (goto-char (point-min)) | ||
| 1961 | (condition-case nil | ||
| 1962 | (let ((name (file-name-nondirectory (match-string 1 file)))) | ||
| 1963 | (while t | ||
| 1964 | (let ((expr (read (current-buffer)))) | ||
| 1965 | (when (and (listp expr) | ||
| 1966 | (memq (car expr) '(defcustom defface defgroup))) | ||
| 1967 | (eval expr) | ||
| 1968 | (put (nth 1 expr) 'custom-where name))))) | ||
| 1969 | (error nil)))))) | ||
| 1970 | (mapatoms (lambda (symbol) | ||
| 1971 | (let ((members (get symbol 'custom-group)) | ||
| 1972 | item where found) | ||
| 1973 | (when members | ||
| 1974 | (princ "(put '") | ||
| 1975 | (princ symbol) | ||
| 1976 | (princ " 'custom-loads '(") | ||
| 1977 | (while members | ||
| 1978 | (setq item (car (car members)) | ||
| 1979 | members (cdr members) | ||
| 1980 | where (get item 'custom-where)) | ||
| 1981 | (unless (or (null where) | ||
| 1982 | (member where found)) | ||
| 1983 | (when found | ||
| 1984 | (princ " ")) | ||
| 1985 | (prin1 where) | ||
| 1986 | (push where found))) | ||
| 1987 | (princ "))\n")))))) | ||
| 1988 | |||
| 1989 | ;;; The End. | ||
| 1990 | |||
| 1991 | (provide 'cus-edit) | ||
| 1992 | |||
| 1993 | ;; cus-edit.el ends here | ||
diff --git a/lisp/cus-face.el b/lisp/cus-face.el new file mode 100644 index 00000000000..ae8e60b499f --- /dev/null +++ b/lisp/cus-face.el | |||
| @@ -0,0 +1,590 @@ | |||
| 1 | ;;; cus-face.el -- XEmacs specific custom support. | ||
| 2 | ;; | ||
| 3 | ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc. | ||
| 4 | ;; | ||
| 5 | ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> | ||
| 6 | ;; Keywords: help, faces | ||
| 7 | ;; Version: 1.71 | ||
| 8 | ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ | ||
| 9 | |||
| 10 | ;;; Commentary: | ||
| 11 | ;; | ||
| 12 | ;; See `custom.el'. | ||
| 13 | |||
| 14 | ;;; Code: | ||
| 15 | |||
| 16 | (require 'custom) | ||
| 17 | |||
| 18 | (eval-and-compile (require 'cl)) | ||
| 19 | |||
| 20 | ;;; Compatibility. | ||
| 21 | |||
| 22 | (if (string-match "XEmacs" emacs-version) | ||
| 23 | (defun custom-face-background (face &optional frame) | ||
| 24 | ;; Specifiers suck! | ||
| 25 | "Return the background color name of face FACE, or nil if unspecified." | ||
| 26 | (color-instance-name (specifier-instance (face-background face) frame))) | ||
| 27 | (defalias 'custom-face-background 'face-background)) | ||
| 28 | |||
| 29 | (if (string-match "XEmacs" emacs-version) | ||
| 30 | (defun custom-face-foreground (face &optional frame) | ||
| 31 | ;; Specifiers suck! | ||
| 32 | "Return the background color name of face FACE, or nil if unspecified." | ||
| 33 | (color-instance-name (specifier-instance (face-foreground face) frame))) | ||
| 34 | (defalias 'custom-face-foreground 'face-foreground)) | ||
| 35 | |||
| 36 | (defalias 'custom-face-font-name (if (string-match "XEmacs" emacs-version) | ||
| 37 | 'face-font-name | ||
| 38 | 'face-font)) | ||
| 39 | |||
| 40 | (eval-and-compile | ||
| 41 | (unless (fboundp 'frame-property) | ||
| 42 | ;; XEmacs function missing in Emacs 19.34. | ||
| 43 | (defun frame-property (frame property &optional default) | ||
| 44 | "Return FRAME's value for property PROPERTY." | ||
| 45 | (or (cdr (assq property (frame-parameters frame))) | ||
| 46 | default))) | ||
| 47 | |||
| 48 | (unless (fboundp 'face-doc-string) | ||
| 49 | ;; XEmacs function missing in Emacs. | ||
| 50 | (defun face-doc-string (face) | ||
| 51 | "Get the documentation string for FACE." | ||
| 52 | (get face 'face-doc-string))) | ||
| 53 | |||
| 54 | (unless (fboundp 'set-face-doc-string) | ||
| 55 | ;; XEmacs function missing in Emacs. | ||
| 56 | (defun set-face-doc-string (face string) | ||
| 57 | "Set the documentation string for FACE to STRING." | ||
| 58 | (put face 'face-doc-string string))) | ||
| 59 | |||
| 60 | (when (and (not (fboundp 'set-face-stipple)) | ||
| 61 | (fboundp 'set-face-background-pixmap)) | ||
| 62 | ;; Emacs function missing in XEmacs 19.15. | ||
| 63 | (defun set-face-stipple (face pixmap &optional frame) | ||
| 64 | ;; Written by Kyle Jones. | ||
| 65 | "Change the stipple pixmap of face FACE to PIXMAP. | ||
| 66 | PIXMAP should be a string, the name of a file of pixmap data. | ||
| 67 | The directories listed in the `x-bitmap-file-path' variable are searched. | ||
| 68 | |||
| 69 | Alternatively, PIXMAP may be a list of the form (WIDTH HEIGHT DATA) | ||
| 70 | where WIDTH and HEIGHT are the size in pixels, | ||
| 71 | and DATA is a string, containing the raw bits of the bitmap. | ||
| 72 | |||
| 73 | If the optional FRAME argument is provided, change only | ||
| 74 | in that frame; otherwise change each frame." | ||
| 75 | (while (not (find-face face)) | ||
| 76 | (setq face (signal 'wrong-type-argument (list 'facep face)))) | ||
| 77 | (while (cond ((stringp pixmap) | ||
| 78 | (unless (file-readable-p pixmap) | ||
| 79 | (setq pixmap (vector 'xbm ':file pixmap))) | ||
| 80 | nil) | ||
| 81 | ((and (consp pixmap) (= (length pixmap) 3)) | ||
| 82 | (setq pixmap (vector 'xbm ':data pixmap)) | ||
| 83 | nil) | ||
| 84 | (t t)) | ||
| 85 | (setq pixmap (signal 'wrong-type-argument | ||
| 86 | (list 'stipple-pixmap-p pixmap)))) | ||
| 87 | (while (and frame (not (framep frame))) | ||
| 88 | (setq frame (signal 'wrong-type-argument (list 'framep frame)))) | ||
| 89 | (set-face-background-pixmap face pixmap frame)))) | ||
| 90 | |||
| 91 | (unless (fboundp 'x-color-values) | ||
| 92 | ;; Emacs function missing in XEmacs 19.14. | ||
| 93 | (defun x-color-values (color &optional frame) | ||
| 94 | "Return a description of the color named COLOR on frame FRAME. | ||
| 95 | The value is a list of integer RGB values--(RED GREEN BLUE). | ||
| 96 | These values appear to range from 0 to 65280 or 65535, depending | ||
| 97 | on the system; white is (65280 65280 65280) or (65535 65535 65535). | ||
| 98 | If FRAME is omitted or nil, use the selected frame." | ||
| 99 | (color-instance-rgb-components (make-color-instance color)))) | ||
| 100 | |||
| 101 | ;; XEmacs and Emacs have different definitions of `facep'. | ||
| 102 | ;; The Emacs definition is the useful one, so emulate that. | ||
| 103 | (cond ((not (fboundp 'facep)) | ||
| 104 | (defun custom-facep (face) | ||
| 105 | "No faces" | ||
| 106 | nil)) | ||
| 107 | ((string-match "XEmacs" emacs-version) | ||
| 108 | (defalias 'custom-facep 'find-face)) | ||
| 109 | (t | ||
| 110 | (defalias 'custom-facep 'facep))) | ||
| 111 | |||
| 112 | (unless (fboundp 'make-empty-face) | ||
| 113 | ;; This should be moved to `faces.el'. | ||
| 114 | (if (string-match "XEmacs" emacs-version) | ||
| 115 | ;; Give up for old XEmacs pre 19.15/20.1. | ||
| 116 | (defalias 'make-empty-face 'make-face) | ||
| 117 | ;; Define for Emacs pre 19.35. | ||
| 118 | (defun make-empty-face (name) | ||
| 119 | "Define a new FACE on all frames, ignoring X resources." | ||
| 120 | (interactive "SMake face: ") | ||
| 121 | (or (internal-find-face name) | ||
| 122 | (let ((face (make-vector 8 nil))) | ||
| 123 | (aset face 0 'face) | ||
| 124 | (aset face 1 name) | ||
| 125 | (let* ((frames (frame-list)) | ||
| 126 | (inhibit-quit t) | ||
| 127 | (id (internal-next-face-id))) | ||
| 128 | (make-face-internal id) | ||
| 129 | (aset face 2 id) | ||
| 130 | (while frames | ||
| 131 | (set-frame-face-alist (car frames) | ||
| 132 | (cons (cons name (copy-sequence face)) | ||
| 133 | (frame-face-alist (car frames)))) | ||
| 134 | (setq frames (cdr frames))) | ||
| 135 | (setq global-face-data (cons (cons name face) global-face-data))) | ||
| 136 | ;; add to menu | ||
| 137 | (if (fboundp 'facemenu-add-new-face) | ||
| 138 | (facemenu-add-new-face name)) | ||
| 139 | face)) | ||
| 140 | name))) | ||
| 141 | |||
| 142 | (defcustom initialize-face-resources t | ||
| 143 | "If non nil, allow X resources to initialize face properties. | ||
| 144 | This only affects faces declared with `defface', and only NT or X11 frames." | ||
| 145 | :group 'customize | ||
| 146 | :type 'boolean) | ||
| 147 | |||
| 148 | (cond ((fboundp 'initialize-face-resources) | ||
| 149 | ;; Already bound, do nothing. | ||
| 150 | ) | ||
| 151 | ((fboundp 'make-face-x-resource-internal) | ||
| 152 | ;; Emacs or new XEmacs. | ||
| 153 | (defun initialize-face-resources (face &optional frame) | ||
| 154 | "Initialize face according to the X11 resources. | ||
| 155 | This might overwrite existing face properties. | ||
| 156 | Does nothing when the variable initialize-face-resources is nil." | ||
| 157 | (when initialize-face-resources | ||
| 158 | (make-face-x-resource-internal face frame t)))) | ||
| 159 | (t | ||
| 160 | ;; Too hard to do right on XEmacs. | ||
| 161 | (defalias 'initialize-face-resources 'ignore))) | ||
| 162 | |||
| 163 | ;;(if (string-match "XEmacs" emacs-version) | ||
| 164 | ;; ;; Xemacs. | ||
| 165 | ;; (defun custom-invert-face (face &optional frame) | ||
| 166 | ;; "Swap the foreground and background colors of face FACE. | ||
| 167 | ;;If the colors are not specified in the face, use the default colors." | ||
| 168 | ;; (interactive (list (read-face-name "Reverse face: "))) | ||
| 169 | ;; (let ((fg (color-name (face-foreground face frame) frame)) | ||
| 170 | ;; (bg (color-name (face-background face frame) frame))) | ||
| 171 | ;; (set-face-foreground face bg frame) | ||
| 172 | ;; (set-face-background face fg frame))) | ||
| 173 | ;; ;; Emacs. | ||
| 174 | ;; (defun custom-invert-face (face &optional frame) | ||
| 175 | ;; "Swap the foreground and background colors of face FACE. | ||
| 176 | ;;If the colors are not specified in the face, use the default colors." | ||
| 177 | ;; (interactive (list (read-face-name "Reverse face: "))) | ||
| 178 | ;; (let ((fg (or (face-foreground face frame) | ||
| 179 | ;; (face-foreground 'default frame) | ||
| 180 | ;; (frame-property (or frame (selected-frame)) | ||
| 181 | ;; 'foreground-color) | ||
| 182 | ;; "black")) | ||
| 183 | ;; (bg (or (face-background face frame) | ||
| 184 | ;; (face-background 'default frame) | ||
| 185 | ;; (frame-property (or frame (selected-frame)) | ||
| 186 | ;; 'background-color) | ||
| 187 | ;; "white"))) | ||
| 188 | ;; (set-face-foreground face bg frame) | ||
| 189 | ;; (set-face-background face fg frame)))) | ||
| 190 | |||
| 191 | (defcustom custom-background-mode nil | ||
| 192 | "The brightness of the background. | ||
| 193 | Set this to the symbol dark if your background color is dark, light if | ||
| 194 | your background is light, or nil (default) if you want Emacs to | ||
| 195 | examine the brightness for you." | ||
| 196 | :group 'customize | ||
| 197 | :type '(choice (choice-item dark) | ||
| 198 | (choice-item light) | ||
| 199 | (choice-item :tag "default" nil))) | ||
| 200 | |||
| 201 | (defun custom-background-mode (frame) | ||
| 202 | "Kludge to detect background mode for FRAME." | ||
| 203 | (let* ((bg-resource | ||
| 204 | (condition-case () | ||
| 205 | (x-get-resource ".backgroundMode" "BackgroundMode" 'string) | ||
| 206 | (error nil))) | ||
| 207 | color | ||
| 208 | (mode (cond (bg-resource | ||
| 209 | (intern (downcase bg-resource))) | ||
| 210 | ((and (setq color (condition-case () | ||
| 211 | (or (frame-property | ||
| 212 | frame | ||
| 213 | 'background-color) | ||
| 214 | (custom-face-background | ||
| 215 | 'default)) | ||
| 216 | (error nil))) | ||
| 217 | (or (string-match "XEmacs" emacs-version) | ||
| 218 | window-system) | ||
| 219 | (< (apply '+ (x-color-values color)) | ||
| 220 | (/ (apply '+ (x-color-values "white")) | ||
| 221 | 3))) | ||
| 222 | 'dark) | ||
| 223 | (t 'light)))) | ||
| 224 | (modify-frame-parameters frame (list (cons 'background-mode mode))) | ||
| 225 | mode)) | ||
| 226 | |||
| 227 | (eval-and-compile | ||
| 228 | (if (string-match "XEmacs" emacs-version) | ||
| 229 | ;; XEmacs. | ||
| 230 | (defun custom-extract-frame-properties (frame) | ||
| 231 | "Return a plist with the frame properties of FRAME used by custom." | ||
| 232 | (list 'type (device-type (frame-device frame)) | ||
| 233 | 'class (device-class (frame-device frame)) | ||
| 234 | 'background (or custom-background-mode | ||
| 235 | (frame-property frame | ||
| 236 | 'background-mode) | ||
| 237 | (custom-background-mode frame)))) | ||
| 238 | ;; Emacs. | ||
| 239 | (defun custom-extract-frame-properties (frame) | ||
| 240 | "Return a plist with the frame properties of FRAME used by custom." | ||
| 241 | (list 'type window-system | ||
| 242 | 'class (frame-property frame 'display-type) | ||
| 243 | 'background (or custom-background-mode | ||
| 244 | (frame-property frame 'background-mode) | ||
| 245 | (custom-background-mode frame)))))) | ||
| 246 | |||
| 247 | ;;; Declaring a face. | ||
| 248 | |||
| 249 | ;;;###autoload | ||
| 250 | (defun custom-declare-face (face spec doc &rest args) | ||
| 251 | "Like `defface', but FACE is evaluated as a normal argument." | ||
| 252 | (when (fboundp 'load-gc) | ||
| 253 | ;; This should be allowed, somehow. | ||
| 254 | (error "Attempt to declare a face during dump")) | ||
| 255 | (unless (get face 'factory-face) | ||
| 256 | (put face 'factory-face spec) | ||
| 257 | (when (fboundp 'facep) | ||
| 258 | (unless (custom-facep face) | ||
| 259 | ;; If the user has already created the face, respect that. | ||
| 260 | (let ((value (or (get face 'saved-face) spec)) | ||
| 261 | (frames (custom-relevant-frames)) | ||
| 262 | frame) | ||
| 263 | ;; Create global face. | ||
| 264 | (make-empty-face face) | ||
| 265 | (custom-face-display-set face value) | ||
| 266 | ;; Create frame local faces | ||
| 267 | (while frames | ||
| 268 | (setq frame (car frames) | ||
| 269 | frames (cdr frames)) | ||
| 270 | (custom-face-display-set face value frame)) | ||
| 271 | (initialize-face-resources face)))) | ||
| 272 | (when (and doc (null (face-doc-string face))) | ||
| 273 | (set-face-doc-string face doc)) | ||
| 274 | (custom-handle-all-keywords face args 'custom-face) | ||
| 275 | (run-hooks 'custom-define-hook)) | ||
| 276 | face) | ||
| 277 | |||
| 278 | ;;; Font Attributes. | ||
| 279 | |||
| 280 | (defconst custom-face-attributes | ||
| 281 | '((:bold (toggle :format "Bold: %[%v%]\n" | ||
| 282 | :help-echo "Control whether a bold font should be used.") | ||
| 283 | custom-set-face-bold | ||
| 284 | custom-face-bold) | ||
| 285 | (:italic (toggle :format "Italic: %[%v%]\n" | ||
| 286 | :help-echo "\ | ||
| 287 | Control whether an italic font should be used.") | ||
| 288 | custom-set-face-italic | ||
| 289 | custom-face-italic) | ||
| 290 | (:underline (toggle :format "Underline: %[%v%]\n" | ||
| 291 | :help-echo "\ | ||
| 292 | Control whether the text should be underlined.") | ||
| 293 | set-face-underline-p | ||
| 294 | face-underline-p) | ||
| 295 | (:foreground (color :tag "Foreground" | ||
| 296 | :value "black" | ||
| 297 | :help-echo "Set foreground color.") | ||
| 298 | set-face-foreground | ||
| 299 | custom-face-foreground) | ||
| 300 | (:background (color :tag "Background" | ||
| 301 | :value "white" | ||
| 302 | :help-echo "Set background color.") | ||
| 303 | set-face-background | ||
| 304 | custom-face-background) | ||
| 305 | ;; (:invert (const :format "Invert Face\n" | ||
| 306 | ;; :sibling-args (:help-echo " | ||
| 307 | ;;Reverse the foreground and background color. | ||
| 308 | ;;If you haven't specified them for the face, the default colors will be used.") | ||
| 309 | ;; t) | ||
| 310 | ;; (lambda (face value &optional frame) | ||
| 311 | ;; ;; We don't use VALUE. | ||
| 312 | ;; (custom-invert-face face frame))) | ||
| 313 | (:stipple (editable-field :format "Stipple: %v" | ||
| 314 | :help-echo "Name of background bitmap file.") | ||
| 315 | set-face-stipple custom-face-stipple)) | ||
| 316 | "Alist of face attributes. | ||
| 317 | |||
| 318 | The elements are of the form (KEY TYPE SET GET) where KEY is a symbol | ||
| 319 | identifying the attribute, TYPE is a widget type for editing the | ||
| 320 | attibute, SET is a function for setting the attribute value, and GET is a function for getiing the attribute value. | ||
| 321 | |||
| 322 | The SET function should take three arguments, the face to modify, the | ||
| 323 | value of the attribute, and optionally the frame where the face should | ||
| 324 | be changed. | ||
| 325 | |||
| 326 | The GET function should take two arguments, the face to examine, and | ||
| 327 | optonally the frame where the face should be examined.") | ||
| 328 | |||
| 329 | (defun custom-face-attributes-set (face frame &rest atts) | ||
| 330 | "For FACE on FRAME set the attributes [KEYWORD VALUE].... | ||
| 331 | Each keyword should be listed in `custom-face-attributes'. | ||
| 332 | |||
| 333 | If FRAME is nil, set the default face." | ||
| 334 | (while atts | ||
| 335 | (let* ((name (nth 0 atts)) | ||
| 336 | (value (nth 1 atts)) | ||
| 337 | (fun (nth 2 (assq name custom-face-attributes)))) | ||
| 338 | (setq atts (cdr (cdr atts))) | ||
| 339 | (condition-case nil | ||
| 340 | (funcall fun face value frame) | ||
| 341 | (error nil))))) | ||
| 342 | |||
| 343 | (defun custom-face-attributes-get (face frame) | ||
| 344 | "For FACE on FRAME get the attributes [KEYWORD VALUE].... | ||
| 345 | Each keyword should be listed in `custom-face-attributes'. | ||
| 346 | |||
| 347 | If FRAME is nil, use the default face." | ||
| 348 | (condition-case nil | ||
| 349 | ;; Attempt to get `font.el' from w3. | ||
| 350 | (require 'font) | ||
| 351 | (error nil)) | ||
| 352 | (let ((atts custom-face-attributes) | ||
| 353 | att result get) | ||
| 354 | (while atts | ||
| 355 | (setq att (car atts) | ||
| 356 | atts (cdr atts) | ||
| 357 | get (nth 3 att)) | ||
| 358 | (when get | ||
| 359 | (let ((answer (funcall get face frame))) | ||
| 360 | (unless (equal answer (funcall get 'default frame)) | ||
| 361 | (when (widget-apply (nth 1 att) :match answer) | ||
| 362 | (setq result (cons (nth 0 att) (cons answer result)))))))) | ||
| 363 | result)) | ||
| 364 | |||
| 365 | (defun custom-set-face-bold (face value &optional frame) | ||
| 366 | "Set the bold property of FACE to VALUE." | ||
| 367 | (if value | ||
| 368 | (make-face-bold face frame) | ||
| 369 | (make-face-unbold face frame))) | ||
| 370 | |||
| 371 | (defun custom-face-bold (face &rest args) | ||
| 372 | "Return non-nil if the font of FACE is bold." | ||
| 373 | (let* ((font (apply 'custom-face-font-name face args)) | ||
| 374 | (fontobj (font-create-object font))) | ||
| 375 | (font-bold-p fontobj))) | ||
| 376 | |||
| 377 | (defun custom-set-face-italic (face value &optional frame) | ||
| 378 | "Set the italic property of FACE to VALUE." | ||
| 379 | (if value | ||
| 380 | (make-face-italic face frame) | ||
| 381 | (make-face-unitalic face frame))) | ||
| 382 | |||
| 383 | (defun custom-face-italic (face &rest args) | ||
| 384 | "Return non-nil if the font of FACE is italic." | ||
| 385 | (let* ((font (apply 'custom-face-font-name face args)) | ||
| 386 | (fontobj (font-create-object font))) | ||
| 387 | (font-italic-p fontobj))) | ||
| 388 | |||
| 389 | (defun custom-face-stipple (face &rest args) | ||
| 390 | "Return the name of the stipple file used for FACE." | ||
| 391 | (if (string-match "XEmacs" emacs-version) | ||
| 392 | (let ((image (apply 'specifier-instance | ||
| 393 | (face-background-pixmap face) args))) | ||
| 394 | (when image | ||
| 395 | (image-instance-file-name image))) | ||
| 396 | (apply 'face-stipple face args))) | ||
| 397 | |||
| 398 | (when (string-match "XEmacs" emacs-version) | ||
| 399 | ;; Support for special XEmacs font attributes. | ||
| 400 | (autoload 'font-create-object "font" nil) | ||
| 401 | |||
| 402 | (defun custom-set-face-font-size (face size &rest args) | ||
| 403 | "Set the font of FACE to SIZE" | ||
| 404 | (let* ((font (apply 'custom-face-font-name face args)) | ||
| 405 | (fontobj (font-create-object font))) | ||
| 406 | (set-font-size fontobj size) | ||
| 407 | (apply 'font-set-face-font face fontobj args))) | ||
| 408 | |||
| 409 | (defun custom-face-font-size (face &rest args) | ||
| 410 | "Return the size of the font of FACE as a string." | ||
| 411 | (let* ((font (apply 'custom-face-font-name face args)) | ||
| 412 | (fontobj (font-create-object font))) | ||
| 413 | (format "%d" (font-size fontobj)))) | ||
| 414 | |||
| 415 | (defun custom-set-face-font-family (face family &rest args) | ||
| 416 | "Set the font of FACE to FAMILY." | ||
| 417 | (let* ((font (apply 'custom-face-font-name face args)) | ||
| 418 | (fontobj (font-create-object font))) | ||
| 419 | (set-font-family fontobj family) | ||
| 420 | (apply 'font-set-face-font face fontobj args))) | ||
| 421 | |||
| 422 | (defun custom-face-font-family (face &rest args) | ||
| 423 | "Return the name of the font family of FACE." | ||
| 424 | (let* ((font (apply 'custom-face-font-name face args)) | ||
| 425 | (fontobj (font-create-object font))) | ||
| 426 | (font-family fontobj))) | ||
| 427 | |||
| 428 | (nconc custom-face-attributes | ||
| 429 | '((:family (editable-field :format "Font Family: %v" | ||
| 430 | :help-echo "\ | ||
| 431 | Name of font family to use (e.g. times).") | ||
| 432 | custom-set-face-font-family | ||
| 433 | custom-face-font-family) | ||
| 434 | (:size (editable-field :format "Size: %v" | ||
| 435 | :help-echo "\ | ||
| 436 | Text size (e.g. 9pt or 2mm).") | ||
| 437 | custom-set-face-font-size | ||
| 438 | custom-face-font-size)))) | ||
| 439 | |||
| 440 | ;;; Frames. | ||
| 441 | |||
| 442 | (defun custom-face-display-set (face spec &optional frame) | ||
| 443 | "Set FACE to the attributes to the first matching entry in SPEC. | ||
| 444 | Iff optional FRAME is non-nil, set it for that frame only. | ||
| 445 | See `defface' for information about SPEC." | ||
| 446 | (when (fboundp 'make-face) | ||
| 447 | (while spec | ||
| 448 | (let* ((entry (car spec)) | ||
| 449 | (display (nth 0 entry)) | ||
| 450 | (atts (nth 1 entry))) | ||
| 451 | (setq spec (cdr spec)) | ||
| 452 | (when (custom-display-match-frame display frame) | ||
| 453 | ;; Avoid creating frame local duplicates of the global face. | ||
| 454 | (unless (and frame (eq display (get face 'custom-face-display))) | ||
| 455 | (apply 'custom-face-attributes-set face frame atts)) | ||
| 456 | (unless frame | ||
| 457 | (put face 'custom-face-display display)) | ||
| 458 | (setq spec nil)))))) | ||
| 459 | |||
| 460 | (defvar custom-default-frame-properties nil | ||
| 461 | "The frame properties used for the global faces. | ||
| 462 | Frames who doesn't match these propertiess should have frame local faces. | ||
| 463 | The value should be nil, if uninitialized, or a plist otherwise. | ||
| 464 | See `defface' for a list of valid keys and values for the plist.") | ||
| 465 | |||
| 466 | (defun custom-get-frame-properties (&optional frame) | ||
| 467 | "Return a plist with the frame properties of FRAME used by custom. | ||
| 468 | If FRAME is nil, return the default frame properties." | ||
| 469 | (cond (frame | ||
| 470 | ;; Try to get from cache. | ||
| 471 | (let ((cache (frame-property frame 'custom-properties))) | ||
| 472 | (unless cache | ||
| 473 | ;; Oh well, get it then. | ||
| 474 | (setq cache (custom-extract-frame-properties frame)) | ||
| 475 | ;; and cache it... | ||
| 476 | (modify-frame-parameters frame | ||
| 477 | (list (cons 'custom-properties cache)))) | ||
| 478 | cache)) | ||
| 479 | (custom-default-frame-properties) | ||
| 480 | (t | ||
| 481 | (setq custom-default-frame-properties | ||
| 482 | (custom-extract-frame-properties (selected-frame)))))) | ||
| 483 | |||
| 484 | (defun custom-display-match-frame (display frame) | ||
| 485 | "Non-nil iff DISPLAY matches FRAME. | ||
| 486 | If FRAME is nil, the current FRAME is used." | ||
| 487 | ;; This is a kludge to get started, we really should use specifiers! | ||
| 488 | (if (eq display t) | ||
| 489 | t | ||
| 490 | (let* ((props (custom-get-frame-properties frame)) | ||
| 491 | (type (plist-get props 'type)) | ||
| 492 | (class (plist-get props 'class)) | ||
| 493 | (background (plist-get props 'background)) | ||
| 494 | (match t) | ||
| 495 | (entries display) | ||
| 496 | entry req options) | ||
| 497 | (while (and entries match) | ||
| 498 | (setq entry (car entries) | ||
| 499 | entries (cdr entries) | ||
| 500 | req (car entry) | ||
| 501 | options (cdr entry) | ||
| 502 | match (cond ((eq req 'type) | ||
| 503 | (memq type options)) | ||
| 504 | ((eq req 'class) | ||
| 505 | (memq class options)) | ||
| 506 | ((eq req 'background) | ||
| 507 | (memq background options)) | ||
| 508 | (t | ||
| 509 | (error "Unknown req `%S' with options `%S'" | ||
| 510 | req options))))) | ||
| 511 | match))) | ||
| 512 | |||
| 513 | (defun custom-relevant-frames () | ||
| 514 | "List of frames whose custom properties differ from the default." | ||
| 515 | (let ((relevant nil) | ||
| 516 | (default (custom-get-frame-properties)) | ||
| 517 | (frames (frame-list)) | ||
| 518 | frame) | ||
| 519 | (while frames | ||
| 520 | (setq frame (car frames) | ||
| 521 | frames (cdr frames)) | ||
| 522 | (unless (equal default (custom-get-frame-properties frame)) | ||
| 523 | (push frame relevant))) | ||
| 524 | relevant)) | ||
| 525 | |||
| 526 | (defun custom-initialize-faces (&optional frame) | ||
| 527 | "Initialize all custom faces for FRAME. | ||
| 528 | If FRAME is nil or omitted, initialize them for all frames." | ||
| 529 | (mapcar (lambda (symbol) | ||
| 530 | (let ((spec (or (get symbol 'saved-face) | ||
| 531 | (get symbol 'factory-face)))) | ||
| 532 | (when spec | ||
| 533 | (custom-face-display-set symbol spec frame) | ||
| 534 | (initialize-face-resources symbol frame)))) | ||
| 535 | (face-list))) | ||
| 536 | |||
| 537 | (defun custom-initialize-frame (&optional frame) | ||
| 538 | "Initialize local faces for FRAME if necessary. | ||
| 539 | If FRAME is missing or nil, the first member of (frame-list) is used." | ||
| 540 | (unless frame | ||
| 541 | (setq frame (car (frame-list)))) | ||
| 542 | (unless (equal (custom-get-frame-properties) | ||
| 543 | (custom-get-frame-properties frame)) | ||
| 544 | (custom-initialize-faces frame))) | ||
| 545 | |||
| 546 | ;; Enable. This should go away when bundled with Emacs. | ||
| 547 | (unless (string-match "XEmacs" emacs-version) | ||
| 548 | (add-hook 'after-make-frame-hook 'custom-initialize-frame)) | ||
| 549 | |||
| 550 | ;;; Initializing. | ||
| 551 | |||
| 552 | (and (fboundp 'make-face) | ||
| 553 | (make-face 'custom-face-empty)) | ||
| 554 | |||
| 555 | ;;;###autoload | ||
| 556 | (defun custom-set-faces (&rest args) | ||
| 557 | "Initialize faces according to user preferences. | ||
| 558 | The arguments should be a list where each entry has the form: | ||
| 559 | |||
| 560 | (FACE SPEC [NOW]) | ||
| 561 | |||
| 562 | SPEC will be stored as the saved value for FACE. If NOW is present | ||
| 563 | and non-nil, FACE will also be created according to SPEC. | ||
| 564 | |||
| 565 | See `defface' for the format of SPEC." | ||
| 566 | (while args | ||
| 567 | (let ((entry (car args))) | ||
| 568 | (if (listp entry) | ||
| 569 | (let ((face (nth 0 entry)) | ||
| 570 | (spec (nth 1 entry)) | ||
| 571 | (now (nth 2 entry))) | ||
| 572 | (put face 'saved-face spec) | ||
| 573 | (when now | ||
| 574 | (put face 'force-face t)) | ||
| 575 | (when (or now (custom-facep face)) | ||
| 576 | (when (fboundp 'copy-face) | ||
| 577 | (copy-face 'custom-face-empty face)) | ||
| 578 | (custom-face-display-set face spec)) | ||
| 579 | (setq args (cdr args))) | ||
| 580 | ;; Old format, a plist of FACE SPEC pairs. | ||
| 581 | (let ((face (nth 0 args)) | ||
| 582 | (spec (nth 1 args))) | ||
| 583 | (put face 'saved-face spec)) | ||
| 584 | (setq args (cdr (cdr args))))))) | ||
| 585 | |||
| 586 | ;;; The End. | ||
| 587 | |||
| 588 | (provide 'cus-face) | ||
| 589 | |||
| 590 | ;; cus-face.el ends here | ||
diff --git a/lisp/custom.el b/lisp/custom.el index e747264583c..6d247ebb379 100644 --- a/lisp/custom.el +++ b/lisp/custom.el | |||
| @@ -1,2472 +1,332 @@ | |||
| 1 | ;;; custom.el --- User friendly customization support. | 1 | ;;; custom.el -- Tools for declaring and initializing options. |
| 2 | |||
| 3 | ;; Copyright (C) 1995, 1996 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Per Abrahamsen <abraham@iesd.auc.dk> | ||
| 6 | ;; Keywords: help | ||
| 7 | ;; Version: 0.5 | ||
| 8 | |||
| 9 | ;; This file is part of GNU Emacs. | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 14 | ;; any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | ||
| 23 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 24 | ;; Boston, MA 02111-1307, USA. | ||
| 25 | |||
| 26 | ;;; Commentary: | ||
| 27 | |||
| 28 | ;; WARNING: This package is still under construction and not all of | ||
| 29 | ;; the features below are implemented. | ||
| 30 | ;; | 2 | ;; |
| 31 | ;; This package provides a framework for adding user friendly | 3 | ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc. |
| 32 | ;; customization support to Emacs. Having to do customization by | ||
| 33 | ;; editing a text file in some arcane syntax is user hostile in the | ||
| 34 | ;; extreme, and to most users emacs lisp definitely count as arcane. | ||
| 35 | ;; | 4 | ;; |
| 36 | ;; The intent is that authors of emacs lisp packages declare the | 5 | ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> |
| 37 | ;; variables intended for user customization with `custom-declare'. | 6 | ;; Keywords: help, faces |
| 38 | ;; Custom can then automatically generate a customization buffer with | 7 | ;; Version: 1.71 |
| 39 | ;; `custom-buffer-create' where the user can edit the package | 8 | ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ |
| 40 | ;; variables in a simple and intuitive way, as well as a menu with | 9 | |
| 41 | ;; `custom-menu-create' where he can set the more commonly used | 10 | ;;; Commentary: |
| 42 | ;; variables interactively. | ||
| 43 | ;; | 11 | ;; |
| 44 | ;; It is also possible to use custom for modifying the properties of | 12 | ;; If you want to use this code, please visit the URL above. |
| 45 | ;; other objects than the package itself, by specifying extra optional | ||
| 46 | ;; arguments to `custom-buffer-create'. | ||
| 47 | ;; | 13 | ;; |
| 48 | ;; Custom is inspired by OPEN LOOK property windows. | 14 | ;; This file only contain the code needed to declare and initialize |
| 15 | ;; user options. The code to customize options is autoloaded from | ||
| 16 | ;; `cus-edit.el'. | ||
| 49 | 17 | ||
| 50 | ;;; Todo: | 18 | ;; The code implementing face declarations is in `cus-face.el' |
| 51 | ;; | ||
| 52 | ;; - Toggle documentation in three states `none', `one-line', `full'. | ||
| 53 | ;; - Function to generate an XEmacs menu from a CUSTOM. | ||
| 54 | ;; - Write TeXinfo documentation. | ||
| 55 | ;; - Make it possible to hide sections by clicking at the level. | ||
| 56 | ;; - Declare AUC TeX variables. | ||
| 57 | ;; - Declare (ding) Gnus variables. | ||
| 58 | ;; - Declare Emacs variables. | ||
| 59 | ;; - Implement remaining types. | ||
| 60 | ;; - XEmacs port. | ||
| 61 | ;; - Allow `URL', `info', and internal hypertext buttons. | ||
| 62 | ;; - Support meta-variables and goal directed customization. | ||
| 63 | ;; - Make it easy to declare custom types independently. | ||
| 64 | ;; - Make it possible to declare default value and type for a single | ||
| 65 | ;; variable, storing the data in a symbol property. | ||
| 66 | ;; - Syntactic sugar for CUSTOM declarations. | ||
| 67 | ;; - Use W3 for variable documentation. | ||
| 68 | 19 | ||
| 69 | ;;; Code: | 20 | ;;; Code: |
| 70 | 21 | ||
| 71 | (eval-when-compile | 22 | (require 'widget) |
| 72 | (require 'cl)) | 23 | |
| 73 | 24 | (define-widget-keywords :prefix :tag :load :link :options :type :group) | |
| 74 | ;;; Compatibility: | 25 | |
| 75 | 26 | ;; These autoloads should be deleted when the file is added to Emacs | |
| 76 | (defun custom-xmas-add-text-properties (start end props &optional object) | 27 | |
| 77 | (add-text-properties start end props object) | 28 | (unless (fboundp 'load-gc) |
| 78 | (put-text-property start end 'start-open t object) | 29 | ;; From cus-edit.el |
| 79 | (put-text-property start end 'end-open t object)) | 30 | (autoload 'customize "cus-edit" nil t) |
| 80 | 31 | (autoload 'customize-variable "cus-edit" nil t) | |
| 81 | (defun custom-xmas-put-text-property (start end prop value &optional object) | 32 | (autoload 'customize-face "cus-edit" nil t) |
| 82 | (put-text-property start end prop value object) | 33 | (autoload 'customize-apropos "cus-edit" nil t) |
| 83 | (put-text-property start end 'start-open t object) | 34 | (autoload 'customize-customized "cus-edit" nil t) |
| 84 | (put-text-property start end 'end-open t object)) | 35 | (autoload 'custom-buffer-create "cus-edit") |
| 85 | 36 | (autoload 'custom-menu-update "cus-edit") | |
| 86 | (defun custom-xmas-extent-start-open () | 37 | (autoload 'custom-make-dependencies "cus-edit") |
| 87 | (map-extents (lambda (extent arg) | 38 | ;; From cus-face.el |
| 88 | (set-extent-property extent 'start-open t)) | 39 | (autoload 'custom-declare-face "cus-face") |
| 89 | nil (point) (min (1+ (point)) (point-max)))) | 40 | (autoload 'custom-set-faces "cus-face")) |
| 90 | 41 | ||
| 91 | (if (string-match "XEmacs\\|Lucid" emacs-version) | 42 | ;;; The `defcustom' Macro. |
| 92 | (progn | 43 | |
| 93 | (fset 'custom-add-text-properties 'custom-xmas-add-text-properties) | 44 | (defun custom-declare-variable (symbol value doc &rest args) |
| 94 | (fset 'custom-put-text-property 'custom-xmas-put-text-property) | 45 | "Like `defcustom', but SYMBOL and VALUE are evaluated as normal arguments." |
| 95 | (fset 'custom-extent-start-open 'custom-xmas-extent-start-open) | 46 | (unless (and (default-boundp symbol) |
| 96 | (fset 'custom-set-text-properties | 47 | (not (get symbol 'saved-value))) |
| 97 | (if (fboundp 'set-text-properties) | 48 | (set-default symbol (if (get symbol 'saved-value) |
| 98 | 'set-text-properties)) | 49 | (eval (car (get symbol 'saved-value))) |
| 99 | (fset 'custom-buffer-substring-no-properties | 50 | (eval value)))) |
| 100 | (if (fboundp 'buffer-substring-no-properties) | 51 | (put symbol 'factory-value (list value)) |
| 101 | 'buffer-substring-no-properties | 52 | (when doc |
| 102 | 'custom-xmas-buffer-substring-no-properties))) | 53 | (put symbol 'variable-documentation doc)) |
| 103 | (fset 'custom-add-text-properties 'add-text-properties) | 54 | (while args |
| 104 | (fset 'custom-put-text-property 'put-text-property) | 55 | (let ((arg (car args))) |
| 105 | (fset 'custom-extent-start-open 'ignore) | 56 | (setq args (cdr args)) |
| 106 | (fset 'custom-set-text-properties 'set-text-properties) | 57 | (unless (symbolp arg) |
| 107 | (fset 'custom-buffer-substring-no-properties | 58 | (error "Junk in args %S" args)) |
| 108 | 'buffer-substring-no-properties)) | 59 | (let ((keyword arg) |
| 109 | 60 | (value (car args))) | |
| 110 | (defun custom-xmas-buffer-substring-no-properties (beg end) | 61 | (unless args |
| 111 | "Return the text from BEG to END, without text properties, as a string." | 62 | (error "Keyword %s is missing an argument" keyword)) |
| 112 | (let ((string (buffer-substring beg end))) | 63 | (setq args (cdr args)) |
| 113 | (custom-set-text-properties 0 (length string) nil string) | 64 | (cond ((eq keyword :type) |
| 114 | string)) | 65 | (put symbol 'custom-type value)) |
| 115 | 66 | ((eq keyword :options) | |
| 116 | (or (fboundp 'add-to-list) | 67 | (if (get symbol 'custom-options) |
| 117 | ;; Introduced in Emacs 19.29. | 68 | ;; Slow safe code to avoid duplicates. |
| 118 | (defun add-to-list (list-var element) | 69 | (mapcar (lambda (option) |
| 119 | "Add to the value of LIST-VAR the element ELEMENT if it isn't there yet. | 70 | (custom-add-option symbol option)) |
| 120 | If you want to use `add-to-list' on a variable that is not defined | 71 | value) |
| 121 | until a certain package is loaded, you should put the call to `add-to-list' | 72 | ;; Fast code for the common case. |
| 122 | into a hook function that will be run only after loading the package. | 73 | (put symbol 'custom-options (copy-list value)))) |
| 123 | `eval-after-load' provides one way to do this. In some cases | ||
| 124 | other hooks, such as major mode hooks, can do the job." | ||
| 125 | (or (member element (symbol-value list-var)) | ||
| 126 | (set list-var (cons element (symbol-value list-var)))))) | ||
| 127 | |||
| 128 | (or (fboundp 'plist-get) | ||
| 129 | ;; Introduced in Emacs 19.29. | ||
| 130 | (defun plist-get (plist prop) | ||
| 131 | "Extract a value from a property list. | ||
| 132 | PLIST is a property list, which is a list of the form | ||
| 133 | \(PROP1 VALUE1 PROP2 VALUE2...). This function returns the value | ||
| 134 | corresponding to the given PROP, or nil if PROP is not | ||
| 135 | one of the properties on the list." | ||
| 136 | (let (result) | ||
| 137 | (while plist | ||
| 138 | (if (eq (car plist) prop) | ||
| 139 | (setq result (car (cdr plist)) | ||
| 140 | plist nil) | ||
| 141 | (set plist (cdr (cdr plist))))) | ||
| 142 | result))) | ||
| 143 | |||
| 144 | (or (fboundp 'plist-put) | ||
| 145 | ;; Introduced in Emacs 19.29. | ||
| 146 | (defun plist-put (plist prop val) | ||
| 147 | "Change value in PLIST of PROP to VAL. | ||
| 148 | PLIST is a property list, which is a list of the form | ||
| 149 | \(PROP1 VALUE1 PROP2 VALUE2 ...). PROP is a symbol and VAL is any object. | ||
| 150 | If PROP is already a property on the list, its value is set to VAL, | ||
| 151 | otherwise the new PROP VAL pair is added. The new plist is returned; | ||
| 152 | use `(setq x (plist-put x prop val))' to be sure to use the new value. | ||
| 153 | The PLIST is modified by side effects." | ||
| 154 | (if (null plist) | ||
| 155 | (list prop val) | ||
| 156 | (let ((current plist)) | ||
| 157 | (while current | ||
| 158 | (cond ((eq (car current) prop) | ||
| 159 | (setcar (cdr current) val) | ||
| 160 | (setq current nil)) | ||
| 161 | ((null (cdr (cdr current))) | ||
| 162 | (setcdr (cdr current) (list prop val)) | ||
| 163 | (setq current nil)) | ||
| 164 | (t | ||
| 165 | (setq current (cdr (cdr current))))))) | ||
| 166 | plist))) | ||
| 167 | |||
| 168 | (or (fboundp 'match-string) | ||
| 169 | ;; Introduced in Emacs 19.29. | ||
| 170 | (defun match-string (num &optional string) | ||
| 171 | "Return string of text matched by last search. | ||
| 172 | NUM specifies which parenthesized expression in the last regexp. | ||
| 173 | Value is nil if NUMth pair didn't match, or there were less than NUM pairs. | ||
| 174 | Zero means the entire text matched by the whole regexp or whole string. | ||
| 175 | STRING should be given if the last search was by `string-match' on STRING." | ||
| 176 | (if (match-beginning num) | ||
| 177 | (if string | ||
| 178 | (substring string (match-beginning num) (match-end num)) | ||
| 179 | (buffer-substring (match-beginning num) (match-end num)))))) | ||
| 180 | |||
| 181 | (or (fboundp 'facep) | ||
| 182 | ;; Introduced in Emacs 19.29. | ||
| 183 | (defun facep (x) | ||
| 184 | "Return t if X is a face name or an internal face vector." | ||
| 185 | (and (or (and (fboundp 'internal-facep) (internal-facep x)) | ||
| 186 | (and | ||
| 187 | (symbolp x) | ||
| 188 | (assq x (and (boundp 'global-face-data) global-face-data)))) | ||
| 189 | t))) | ||
| 190 | |||
| 191 | ;; XEmacs and Emacs 19.29 facep does different things. | ||
| 192 | (if (fboundp 'find-face) | ||
| 193 | (fset 'custom-facep 'find-face) | ||
| 194 | (fset 'custom-facep 'facep)) | ||
| 195 | |||
| 196 | (if (custom-facep 'underline) | ||
| 197 | () | ||
| 198 | ;; No underline face in XEmacs 19.12. | ||
| 199 | (and (fboundp 'make-face) | ||
| 200 | (funcall (intern "make-face") 'underline)) | ||
| 201 | ;; Must avoid calling set-face-underline-p directly, because it | ||
| 202 | ;; is a defsubst in emacs19, and will make the .elc files non | ||
| 203 | ;; portable! | ||
| 204 | (or (and (fboundp 'face-differs-from-default-p) | ||
| 205 | (face-differs-from-default-p 'underline)) | ||
| 206 | (and (fboundp 'set-face-underline-p) | ||
| 207 | (funcall 'set-face-underline-p 'underline t)))) | ||
| 208 | |||
| 209 | (defun custom-xmas-set-text-properties (start end props &optional buffer) | ||
| 210 | (if (null buffer) | ||
| 211 | (if props | ||
| 212 | (while props | ||
| 213 | (custom-put-text-property | ||
| 214 | start end (car props) (nth 1 props) buffer) | ||
| 215 | (setq props (nthcdr 2 props))) | ||
| 216 | (remove-text-properties start end ())))) | ||
| 217 | |||
| 218 | (or (fboundp 'event-point) | ||
| 219 | ;; Missing in Emacs 19.29. | ||
| 220 | (defun event-point (event) | ||
| 221 | "Return the character position of the given mouse-motion, button-press, | ||
| 222 | or button-release event. If the event did not occur over a window, or did | ||
| 223 | not occur over text, then this returns nil. Otherwise, it returns an index | ||
| 224 | into the buffer visible in the event's window." | ||
| 225 | (posn-point (event-start event)))) | ||
| 226 | |||
| 227 | (eval-when-compile | ||
| 228 | (defvar x-colors nil) | ||
| 229 | (defvar custom-button-face nil) | ||
| 230 | (defvar custom-field-uninitialized-face nil) | ||
| 231 | (defvar custom-field-invalid-face nil) | ||
| 232 | (defvar custom-field-modified-face nil) | ||
| 233 | (defvar custom-field-face nil) | ||
| 234 | (defvar custom-mouse-face nil) | ||
| 235 | (defvar custom-field-active-face nil)) | ||
| 236 | |||
| 237 | ;; We can't easily check for a working intangible. | ||
| 238 | (defconst intangible (if (and (boundp 'emacs-minor-version) | ||
| 239 | (or (> emacs-major-version 19) | ||
| 240 | (and (> emacs-major-version 18) | ||
| 241 | (> emacs-minor-version 28)))) | ||
| 242 | (setq intangible 'intangible) | ||
| 243 | (setq intangible 'intangible-if-it-had-been-working)) | ||
| 244 | "The symbol making text intangible.") | ||
| 245 | |||
| 246 | (defconst rear-nonsticky (if (string-match "XEmacs" emacs-version) | ||
| 247 | 'end-open | ||
| 248 | 'rear-nonsticky) | ||
| 249 | "The symbol making text properties non-sticky in the rear end.") | ||
| 250 | |||
| 251 | (defconst front-sticky (if (string-match "XEmacs" emacs-version) | ||
| 252 | 'front-closed | ||
| 253 | 'front-sticky) | ||
| 254 | "The symbol making text properties sticky in the front.") | ||
| 255 | |||
| 256 | (defconst mouse-face (if (string-match "XEmacs" emacs-version) | ||
| 257 | 'highlight | ||
| 258 | 'mouse-face) | ||
| 259 | "Symbol used for highlighting text under mouse.") | ||
| 260 | |||
| 261 | ;; Put it in the Help menu, if possible. | ||
| 262 | (if (string-match "XEmacs" emacs-version) | ||
| 263 | (if (featurep 'menubar) | ||
| 264 | ;; XEmacs (disabled because it doesn't work) | ||
| 265 | (and current-menubar | ||
| 266 | (add-menu-item '("Help") "Customize..." 'customize t))) | ||
| 267 | ;; Emacs 19.28 and earlier | ||
| 268 | (global-set-key [ menu-bar help customize ] | ||
| 269 | '("Customize..." . customize)) | ||
| 270 | ;; Emacs 19.29 and later | ||
| 271 | (global-set-key [ menu-bar help-menu customize ] | ||
| 272 | '("Customize..." . customize))) | ||
| 273 | |||
| 274 | ;; XEmacs popup-menu stolen from w3.el. | ||
| 275 | (defun custom-x-really-popup-menu (pos title menudesc) | ||
| 276 | "My hacked up function to do a blocking popup menu..." | ||
| 277 | (let ((echo-keystrokes 0) | ||
| 278 | event menu) | ||
| 279 | (while menudesc | ||
| 280 | (setq menu (cons (vector (car (car menudesc)) | ||
| 281 | (list (car (car menudesc))) t) menu) | ||
| 282 | menudesc (cdr menudesc))) | ||
| 283 | (setq menu (cons title menu)) | ||
| 284 | (popup-menu menu) | ||
| 285 | (catch 'popup-done | ||
| 286 | (while t | ||
| 287 | (setq event (next-command-event event)) | ||
| 288 | (cond ((and (misc-user-event-p event) (stringp (car-safe (event-object event)))) | ||
| 289 | (throw 'popup-done (event-object event))) | ||
| 290 | ((and (misc-user-event-p event) | ||
| 291 | (or (eq (event-object event) 'abort) | ||
| 292 | (eq (event-object event) 'menu-no-selection-hook))) | ||
| 293 | nil) | ||
| 294 | ((not (popup-menu-up-p)) | ||
| 295 | (throw 'popup-done nil)) | ||
| 296 | ((button-release-event-p event);; don't beep twice | ||
| 297 | nil) | ||
| 298 | (t | 74 | (t |
| 299 | (beep) | 75 | (custom-handle-keyword symbol keyword value |
| 300 | (message "please make a choice from the menu."))))))) | 76 | 'custom-variable)))))) |
| 301 | 77 | (run-hooks 'custom-define-hook) | |
| 302 | ;;; Categories: | 78 | symbol) |
| 303 | ;; | ||
| 304 | ;; XEmacs use inheritable extents for the same purpose as Emacs uses | ||
| 305 | ;; the category text property. | ||
| 306 | |||
| 307 | (if (string-match "XEmacs" emacs-version) | ||
| 308 | (progn | ||
| 309 | ;; XEmacs categories. | ||
| 310 | (defun custom-category-create (name) | ||
| 311 | (set name (make-extent nil nil)) | ||
| 312 | "Create a text property category named NAME.") | ||
| 313 | |||
| 314 | (defun custom-category-put (name property value) | ||
| 315 | "In CATEGORY set PROPERTY to VALUE." | ||
| 316 | (set-extent-property (symbol-value name) property value)) | ||
| 317 | |||
| 318 | (defun custom-category-get (name property) | ||
| 319 | "In CATEGORY get PROPERTY." | ||
| 320 | (extent-property (symbol-value name) property)) | ||
| 321 | |||
| 322 | (defun custom-category-set (from to category) | ||
| 323 | "Make text between FROM and TWO have category CATEGORY." | ||
| 324 | (let ((extent (make-extent from to))) | ||
| 325 | (set-extent-parent extent (symbol-value category))))) | ||
| 326 | |||
| 327 | ;; Emacs categories. | ||
| 328 | (defun custom-category-create (name) | ||
| 329 | "Create a text property category named NAME." | ||
| 330 | (set name name)) | ||
| 331 | |||
| 332 | (defun custom-category-put (name property value) | ||
| 333 | "In CATEGORY set PROPERTY to VALUE." | ||
| 334 | (put name property value)) | ||
| 335 | |||
| 336 | (defun custom-category-get (name property) | ||
| 337 | "In CATEGORY get PROPERTY." | ||
| 338 | (get name property)) | ||
| 339 | |||
| 340 | (defun custom-category-set (from to category) | ||
| 341 | "Make text between FROM and TWO have category CATEGORY." | ||
| 342 | (custom-put-text-property from to 'category category))) | ||
| 343 | |||
| 344 | ;;; External Data: | ||
| 345 | ;; | ||
| 346 | ;; The following functions and variables defines the interface for | ||
| 347 | ;; connecting a CUSTOM with an external entity, by default an emacs | ||
| 348 | ;; lisp variable. | ||
| 349 | |||
| 350 | (defvar custom-external 'default-value | ||
| 351 | "Function returning the external value of NAME.") | ||
| 352 | |||
| 353 | (defvar custom-external-set 'set-default | ||
| 354 | "Function setting the external value of NAME to VALUE.") | ||
| 355 | |||
| 356 | (defun custom-external (name) | ||
| 357 | "Get the external value associated with NAME." | ||
| 358 | (funcall custom-external name)) | ||
| 359 | |||
| 360 | (defun custom-external-set (name value) | ||
| 361 | "Set the external value associated with NAME to VALUE." | ||
| 362 | (funcall custom-external-set name value)) | ||
| 363 | |||
| 364 | (defvar custom-name-fields nil | ||
| 365 | "Alist of custom names and their associated editing field.") | ||
| 366 | (make-variable-buffer-local 'custom-name-fields) | ||
| 367 | |||
| 368 | (defun custom-name-enter (name field) | ||
| 369 | "Associate NAME with FIELD." | ||
| 370 | (if (null name) | ||
| 371 | () | ||
| 372 | (custom-assert 'field) | ||
| 373 | (setq custom-name-fields (cons (cons name field) custom-name-fields)))) | ||
| 374 | |||
| 375 | (defun custom-name-field (name) | ||
| 376 | "The editing field associated with NAME." | ||
| 377 | (cdr (assq name custom-name-fields))) | ||
| 378 | |||
| 379 | (defun custom-name-value (name) | ||
| 380 | "The value currently displayed for NAME in the customization buffer." | ||
| 381 | (let* ((field (custom-name-field name)) | ||
| 382 | (custom (custom-field-custom field))) | ||
| 383 | (custom-field-parse field) | ||
| 384 | (funcall (custom-property custom 'export) custom | ||
| 385 | (car (custom-field-extract custom field))))) | ||
| 386 | |||
| 387 | (defvar custom-save 'custom-save | ||
| 388 | "Function that will save current customization buffer.") | ||
| 389 | |||
| 390 | ;;; Custom Functions: | ||
| 391 | ;; | ||
| 392 | ;; The following functions are part of the public interface to the | ||
| 393 | ;; CUSTOM datastructure. Each CUSTOM describes a group of variables, | ||
| 394 | ;; a single variable, or a component of a structured variable. The | ||
| 395 | ;; CUSTOM instances are part of two hierarchies, the first is the | ||
| 396 | ;; `part-of' hierarchy in which each CUSTOM is a component of another | ||
| 397 | ;; CUSTOM, except for the top level CUSTOM which is contained in | ||
| 398 | ;; `custom-data'. The second hierarchy is a `is-a' type hierarchy | ||
| 399 | ;; where each CUSTOM is a leaf in the hierarchy defined by the `type' | ||
| 400 | ;; property and `custom-type-properties'. | ||
| 401 | |||
| 402 | (defvar custom-file "~/.custom.el" | ||
| 403 | "Name of file with customization information.") | ||
| 404 | |||
| 405 | (defconst custom-data | ||
| 406 | '((tag . "Emacs") | ||
| 407 | (doc . "The extensible self-documenting text editor.") | ||
| 408 | (type . group) | ||
| 409 | (data "\n" | ||
| 410 | ((header . nil) | ||
| 411 | (compact . t) | ||
| 412 | (type . group) | ||
| 413 | (doc . "\ | ||
| 414 | Press [Save] to save any changes permanently after you are done editing. | ||
| 415 | You can load customization information from other files by editing the | ||
| 416 | `File' field and pressing the [Load] button. When you press [Save] the | ||
| 417 | customization information of all files you have loaded, plus any | ||
| 418 | changes you might have made manually, will be stored in the file | ||
| 419 | specified by the `File' field.") | ||
| 420 | (data ((tag . "Load") | ||
| 421 | (type . button) | ||
| 422 | (query . custom-load)) | ||
| 423 | ((tag . "Save") | ||
| 424 | (type . button) | ||
| 425 | (query . custom-save)) | ||
| 426 | ((name . custom-file) | ||
| 427 | (default . "~/.custom.el") | ||
| 428 | (doc . "Name of file with customization information.\n") | ||
| 429 | (tag . "File") | ||
| 430 | (type . file)))))) | ||
| 431 | "The global customization information. | ||
| 432 | A custom association list.") | ||
| 433 | |||
| 434 | (defun custom-declare (path custom) | ||
| 435 | "Declare variables for customization. | ||
| 436 | PATH is a list of tags leading to the place in the customization | ||
| 437 | hierarchy the new entry should be added. CUSTOM is the entry to add." | ||
| 438 | (custom-initialize custom) | ||
| 439 | (let ((current (custom-travel-path custom-data path))) | ||
| 440 | (or (member custom (custom-data current)) | ||
| 441 | (nconc (custom-data current) (list custom))))) | ||
| 442 | |||
| 443 | (put 'custom-declare 'lisp-indent-hook 1) | ||
| 444 | |||
| 445 | (defconst custom-type-properties | ||
| 446 | '((repeat (type . default) | ||
| 447 | ;; See `custom-match'. | ||
| 448 | (import . custom-repeat-import) | ||
| 449 | (eval . custom-repeat-eval) | ||
| 450 | (quote . custom-repeat-quote) | ||
| 451 | (accept . custom-repeat-accept) | ||
| 452 | (extract . custom-repeat-extract) | ||
| 453 | (validate . custom-repeat-validate) | ||
| 454 | (insert . custom-repeat-insert) | ||
| 455 | (match . custom-repeat-match) | ||
| 456 | (query . custom-repeat-query) | ||
| 457 | (prefix . "") | ||
| 458 | (del-tag . "[DEL]") | ||
| 459 | (add-tag . "[INS]")) | ||
| 460 | (pair (type . group) | ||
| 461 | ;; A cons-cell. | ||
| 462 | (accept . custom-pair-accept) | ||
| 463 | (eval . custom-pair-eval) | ||
| 464 | (import . custom-pair-import) | ||
| 465 | (quote . custom-pair-quote) | ||
| 466 | (valid . (lambda (c d) (consp d))) | ||
| 467 | (extract . custom-pair-extract)) | ||
| 468 | (list (type . group) | ||
| 469 | ;; A lisp list. | ||
| 470 | (quote . custom-list-quote) | ||
| 471 | (valid . (lambda (c d) | ||
| 472 | (listp d))) | ||
| 473 | (extract . custom-list-extract)) | ||
| 474 | (group (type . default) | ||
| 475 | ;; See `custom-match'. | ||
| 476 | (face-tag . nil) | ||
| 477 | (eval . custom-group-eval) | ||
| 478 | (import . custom-group-import) | ||
| 479 | (initialize . custom-group-initialize) | ||
| 480 | (apply . custom-group-apply) | ||
| 481 | (reset . custom-group-reset) | ||
| 482 | (factory-reset . custom-group-factory-reset) | ||
| 483 | (extract . nil) | ||
| 484 | (validate . custom-group-validate) | ||
| 485 | (query . custom-toggle-hide) | ||
| 486 | (accept . custom-group-accept) | ||
| 487 | (insert . custom-group-insert) | ||
| 488 | (find . custom-group-find)) | ||
| 489 | (toggle (type . choice) | ||
| 490 | ;; Booleans. | ||
| 491 | (data ((type . const) | ||
| 492 | (tag . "On ") | ||
| 493 | (default . t)) | ||
| 494 | ((type . const) | ||
| 495 | (tag . "Off") | ||
| 496 | (default . nil)))) | ||
| 497 | (triggle (type . choice) | ||
| 498 | ;; On/Off/Default. | ||
| 499 | (data ((type . const) | ||
| 500 | (tag . "On ") | ||
| 501 | (default . t)) | ||
| 502 | ((type . const) | ||
| 503 | (tag . "Off") | ||
| 504 | (default . nil)) | ||
| 505 | ((type . const) | ||
| 506 | (tag . "Def") | ||
| 507 | (default . custom:asis)))) | ||
| 508 | (choice (type . default) | ||
| 509 | ;; See `custom-match'. | ||
| 510 | (query . custom-choice-query) | ||
| 511 | (accept . custom-choice-accept) | ||
| 512 | (extract . custom-choice-extract) | ||
| 513 | (validate . custom-choice-validate) | ||
| 514 | (insert . custom-choice-insert) | ||
| 515 | (none (tag . "Unknown") | ||
| 516 | (default . __uninitialized__) | ||
| 517 | (type . const))) | ||
| 518 | (const (type . default) | ||
| 519 | ;; A `const' only matches a single lisp value. | ||
| 520 | (extract . (lambda (c f) (list (custom-default c)))) | ||
| 521 | (validate . (lambda (c f) nil)) | ||
| 522 | (valid . custom-const-valid) | ||
| 523 | (update . custom-const-update) | ||
| 524 | (insert . custom-const-insert)) | ||
| 525 | (face-doc (type . doc) | ||
| 526 | ;; A variable containing a face. | ||
| 527 | (doc . "\ | ||
| 528 | You can customize the look of Emacs by deciding which faces should be | ||
| 529 | used when. If you push one of the face buttons below, you will be | ||
| 530 | given a choice between a number of standard faces. The name of the | ||
| 531 | selected face is shown right after the face button, and it is | ||
| 532 | displayed its own face so you can see how it looks. If you know of | ||
| 533 | another standard face not listed and want to use it, you can select | ||
| 534 | `Other' and write the name in the editing field. | ||
| 535 | |||
| 536 | If none of the standard faces suits you, you can select `Customize' to | ||
| 537 | create your own face. This will make six fields appear under the face | ||
| 538 | button. The `Fg' and `Bg' fields are the foreground and background | ||
| 539 | colors for the face, respectively. You should type the name of the | ||
| 540 | color in the field. You can use any X11 color name. A list of X11 | ||
| 541 | color names may be available in the file `/usr/lib/X11/rgb.txt' on | ||
| 542 | your system. The special color name `default' means that the face | ||
| 543 | will not change the color of the text. The `Stipple' field is weird, | ||
| 544 | so just ignore it. The three remaining fields are toggles, which will | ||
| 545 | make the text `bold', `italic', or `underline' respectively. For some | ||
| 546 | fonts `bold' or `italic' will not make any visible change.")) | ||
| 547 | (face (type . choice) | ||
| 548 | (eval . custom-face-eval) | ||
| 549 | (import . custom-face-import) | ||
| 550 | (data ((tag . "None") | ||
| 551 | (default . nil) | ||
| 552 | (type . const)) | ||
| 553 | ((tag . "Default") | ||
| 554 | (default . default) | ||
| 555 | (face . custom-const-face) | ||
| 556 | (type . const)) | ||
| 557 | ((tag . "Bold") | ||
| 558 | (default . bold) | ||
| 559 | (face . custom-const-face) | ||
| 560 | (type . const)) | ||
| 561 | ((tag . "Bold-italic") | ||
| 562 | (default . bold-italic) | ||
| 563 | (face . custom-const-face) | ||
| 564 | (type . const)) | ||
| 565 | ((tag . "Italic") | ||
| 566 | (default . italic) | ||
| 567 | (face . custom-const-face) | ||
| 568 | (type . const)) | ||
| 569 | ((tag . "Underline") | ||
| 570 | (default . underline) | ||
| 571 | (face . custom-const-face) | ||
| 572 | (type . const)) | ||
| 573 | ((tag . "Highlight") | ||
| 574 | (default . highlight) | ||
| 575 | (face . custom-const-face) | ||
| 576 | (type . const)) | ||
| 577 | ((tag . "Modeline") | ||
| 578 | (default . modeline) | ||
| 579 | (face . custom-const-face) | ||
| 580 | (type . const)) | ||
| 581 | ((tag . "Region") | ||
| 582 | (default . region) | ||
| 583 | (face . custom-const-face) | ||
| 584 | (type . const)) | ||
| 585 | ((tag . "Secondary Selection") | ||
| 586 | (default . secondary-selection) | ||
| 587 | (face . custom-const-face) | ||
| 588 | (type . const)) | ||
| 589 | ((tag . "Customized") | ||
| 590 | (compact . t) | ||
| 591 | (face-tag . custom-face-hack) | ||
| 592 | (eval . custom-face-eval) | ||
| 593 | (data ((hidden . t) | ||
| 594 | (tag . "") | ||
| 595 | (doc . "\ | ||
| 596 | Select the properties you want this face to have.") | ||
| 597 | (default . custom-face-lookup) | ||
| 598 | (type . const)) | ||
| 599 | "\n" | ||
| 600 | ((tag . "Fg") | ||
| 601 | (hidden . t) | ||
| 602 | (default . "default") | ||
| 603 | (width . 20) | ||
| 604 | (type . string)) | ||
| 605 | ((tag . "Bg") | ||
| 606 | (default . "default") | ||
| 607 | (width . 20) | ||
| 608 | (type . string)) | ||
| 609 | ((tag . "Stipple") | ||
| 610 | (default . "default") | ||
| 611 | (width . 20) | ||
| 612 | (type . string)) | ||
| 613 | "\n" | ||
| 614 | ((tag . "Bold") | ||
| 615 | (default . custom:asis) | ||
| 616 | (type . triggle)) | ||
| 617 | " " | ||
| 618 | ((tag . "Italic") | ||
| 619 | (default . custom:asis) | ||
| 620 | (type . triggle)) | ||
| 621 | " " | ||
| 622 | ((tag . "Underline") | ||
| 623 | (hidden . t) | ||
| 624 | (default . custom:asis) | ||
| 625 | (type . triggle))) | ||
| 626 | (default . (custom-face-lookup "default" "default" "default" | ||
| 627 | nil nil nil)) | ||
| 628 | (type . list)) | ||
| 629 | ((prompt . "Other") | ||
| 630 | (face . custom-field-value) | ||
| 631 | (default . __uninitialized__) | ||
| 632 | (type . symbol)))) | ||
| 633 | (file (type . string) | ||
| 634 | ;; A string containing a file or directory name. | ||
| 635 | (directory . nil) | ||
| 636 | (default-file . nil) | ||
| 637 | (query . custom-file-query)) | ||
| 638 | (sexp (type . default) | ||
| 639 | ;; Any lisp expression. | ||
| 640 | (width . 40) | ||
| 641 | (default . (__uninitialized__ . "Uninitialized")) | ||
| 642 | (read . custom-sexp-read) | ||
| 643 | (write . custom-sexp-write)) | ||
| 644 | (symbol (type . sexp) | ||
| 645 | ;; A lisp symbol. | ||
| 646 | (width . 40) | ||
| 647 | (valid . (lambda (c d) (symbolp d)))) | ||
| 648 | (integer (type . sexp) | ||
| 649 | ;; A lisp integer. | ||
| 650 | (width . 10) | ||
| 651 | (valid . (lambda (c d) (integerp d)))) | ||
| 652 | (string (type . default) | ||
| 653 | ;; A lisp string. | ||
| 654 | (width . 40) | ||
| 655 | (valid . (lambda (c d) (stringp d))) | ||
| 656 | (read . custom-string-read) | ||
| 657 | (write . custom-string-write)) | ||
| 658 | (button (type . default) | ||
| 659 | ;; Push me. | ||
| 660 | (accept . ignore) | ||
| 661 | (extract . nil) | ||
| 662 | (validate . ignore) | ||
| 663 | (insert . custom-button-insert)) | ||
| 664 | (doc (type . default) | ||
| 665 | ;; A documentation only entry with no value. | ||
| 666 | (header . nil) | ||
| 667 | (reset . ignore) | ||
| 668 | (extract . nil) | ||
| 669 | (validate . ignore) | ||
| 670 | (insert . custom-documentation-insert)) | ||
| 671 | (default (width . 20) | ||
| 672 | (valid . (lambda (c v) t)) | ||
| 673 | (insert . custom-default-insert) | ||
| 674 | (update . custom-default-update) | ||
| 675 | (query . custom-default-query) | ||
| 676 | (tag . nil) | ||
| 677 | (prompt . nil) | ||
| 678 | (doc . nil) | ||
| 679 | (header . t) | ||
| 680 | (padding . ? ) | ||
| 681 | (quote . custom-default-quote) | ||
| 682 | (eval . (lambda (c v) nil)) | ||
| 683 | (export . custom-default-export) | ||
| 684 | (import . (lambda (c v) (list v))) | ||
| 685 | (synchronize . ignore) | ||
| 686 | (initialize . custom-default-initialize) | ||
| 687 | (extract . custom-default-extract) | ||
| 688 | (validate . custom-default-validate) | ||
| 689 | (apply . custom-default-apply) | ||
| 690 | (reset . custom-default-reset) | ||
| 691 | (factory-reset . custom-default-factory-reset) | ||
| 692 | (accept . custom-default-accept) | ||
| 693 | (match . custom-default-match) | ||
| 694 | (name . nil) | ||
| 695 | (compact . nil) | ||
| 696 | (hidden . nil) | ||
| 697 | (face . custom-default-face) | ||
| 698 | (data . nil) | ||
| 699 | (calculate . nil) | ||
| 700 | (default . __uninitialized__))) | ||
| 701 | "Alist of default properties for type symbols. | ||
| 702 | The format is `((SYMBOL (PROPERTY . VALUE)... )... )'.") | ||
| 703 | |||
| 704 | (defconst custom-local-type-properties nil | ||
| 705 | "Local type properties. | ||
| 706 | Entries in this list take precedence over `custom-type-properties'.") | ||
| 707 | |||
| 708 | (make-variable-buffer-local 'custom-local-type-properties) | ||
| 709 | |||
| 710 | (defconst custom-nil '__uninitialized__ | ||
| 711 | "Special value representing an uninitialized field.") | ||
| 712 | |||
| 713 | (defconst custom-invalid '__invalid__ | ||
| 714 | "Special value representing an invalid field.") | ||
| 715 | |||
| 716 | (defconst custom:asis 'custom:asis) | ||
| 717 | ;; Bad, ugly, and horrible kludge. | ||
| 718 | |||
| 719 | (defun custom-property (custom property) | ||
| 720 | "Extract from CUSTOM property PROPERTY." | ||
| 721 | (let ((entry (assq property custom))) | ||
| 722 | (while (null entry) | ||
| 723 | ;; Look in superclass. | ||
| 724 | (let ((type (custom-type custom))) | ||
| 725 | (setq custom (cdr (or (assq type custom-local-type-properties) | ||
| 726 | (assq type custom-type-properties))) | ||
| 727 | entry (assq property custom)) | ||
| 728 | (custom-assert 'custom))) | ||
| 729 | (cdr entry))) | ||
| 730 | |||
| 731 | (defun custom-super (custom property) | ||
| 732 | "Extract from CUSTOM property PROPERTY. Start with CUSTOM's superclass." | ||
| 733 | (let ((entry nil)) | ||
| 734 | (while (null entry) | ||
| 735 | ;; Look in superclass. | ||
| 736 | (let ((type (custom-type custom))) | ||
| 737 | (setq custom (cdr (or (assq type custom-local-type-properties) | ||
| 738 | (assq type custom-type-properties))) | ||
| 739 | entry (assq property custom)) | ||
| 740 | (custom-assert 'custom))) | ||
| 741 | (cdr entry))) | ||
| 742 | |||
| 743 | (defun custom-property-set (custom property value) | ||
| 744 | "Set CUSTOM PROPERTY to VALUE by side effect. | ||
| 745 | CUSTOM must have at least one property already." | ||
| 746 | (let ((entry (assq property custom))) | ||
| 747 | (if entry | ||
| 748 | (setcdr entry value) | ||
| 749 | (setcdr custom (cons (cons property value) (cdr custom)))))) | ||
| 750 | |||
| 751 | (defun custom-type (custom) | ||
| 752 | "Extract `type' from CUSTOM." | ||
| 753 | (cdr (assq 'type custom))) | ||
| 754 | |||
| 755 | (defun custom-name (custom) | ||
| 756 | "Extract `name' from CUSTOM." | ||
| 757 | (custom-property custom 'name)) | ||
| 758 | |||
| 759 | (defun custom-tag (custom) | ||
| 760 | "Extract `tag' from CUSTOM." | ||
| 761 | (custom-property custom 'tag)) | ||
| 762 | |||
| 763 | (defun custom-face-tag (custom) | ||
| 764 | "Extract `face-tag' from CUSTOM." | ||
| 765 | (custom-property custom 'face-tag)) | ||
| 766 | |||
| 767 | (defun custom-prompt (custom) | ||
| 768 | "Extract `prompt' from CUSTOM. | ||
| 769 | If none exist, default to `tag' or, failing that, `type'." | ||
| 770 | (or (custom-property custom 'prompt) | ||
| 771 | (custom-property custom 'tag) | ||
| 772 | (capitalize (symbol-name (custom-type custom))))) | ||
| 773 | |||
| 774 | (defun custom-default (custom) | ||
| 775 | "Extract `default' from CUSTOM." | ||
| 776 | (let ((value (custom-property custom 'calculate))) | ||
| 777 | (if value | ||
| 778 | (eval value) | ||
| 779 | (custom-property custom 'default)))) | ||
| 780 | |||
| 781 | (defun custom-data (custom) | ||
| 782 | "Extract the `data' from CUSTOM." | ||
| 783 | (custom-property custom 'data)) | ||
| 784 | |||
| 785 | (defun custom-documentation (custom) | ||
| 786 | "Extract `doc' from CUSTOM." | ||
| 787 | (custom-property custom 'doc)) | ||
| 788 | |||
| 789 | (defun custom-width (custom) | ||
| 790 | "Extract `width' from CUSTOM." | ||
| 791 | (custom-property custom 'width)) | ||
| 792 | |||
| 793 | (defun custom-compact (custom) | ||
| 794 | "Extract `compact' from CUSTOM." | ||
| 795 | (custom-property custom 'compact)) | ||
| 796 | |||
| 797 | (defun custom-padding (custom) | ||
| 798 | "Extract `padding' from CUSTOM." | ||
| 799 | (custom-property custom 'padding)) | ||
| 800 | |||
| 801 | (defun custom-valid (custom value) | ||
| 802 | "Non-nil if CUSTOM may validly be set to VALUE." | ||
| 803 | (and (not (and (listp value) (eq custom-invalid (car value)))) | ||
| 804 | (funcall (custom-property custom 'valid) custom value))) | ||
| 805 | |||
| 806 | (defun custom-import (custom value) | ||
| 807 | "Import CUSTOM VALUE from external variable. | ||
| 808 | |||
| 809 | This function change VALUE into a form that makes it easier to edit | ||
| 810 | internally. What the internal form is exactly depends on CUSTOM. | ||
| 811 | The internal form is returned." | ||
| 812 | (if (eq custom-nil value) | ||
| 813 | (list custom-nil) | ||
| 814 | (funcall (custom-property custom 'import) custom value))) | ||
| 815 | |||
| 816 | (defun custom-eval (custom value) | ||
| 817 | "Return non-nil if CUSTOM's VALUE needs to be evaluated." | ||
| 818 | (funcall (custom-property custom 'eval) custom value)) | ||
| 819 | |||
| 820 | (defun custom-quote (custom value) | ||
| 821 | "Quote CUSTOM's VALUE if necessary." | ||
| 822 | (funcall (custom-property custom 'quote) custom value)) | ||
| 823 | |||
| 824 | (defun custom-write (custom value) | ||
| 825 | "Convert CUSTOM VALUE to a string." | ||
| 826 | (cond ((eq value custom-nil) | ||
| 827 | "") | ||
| 828 | ((and (listp value) (eq (car value) custom-invalid)) | ||
| 829 | (cdr value)) | ||
| 830 | (t | ||
| 831 | (funcall (custom-property custom 'write) custom value)))) | ||
| 832 | |||
| 833 | (defun custom-read (custom string) | ||
| 834 | "Convert CUSTOM field content STRING into lisp." | ||
| 835 | (condition-case nil | ||
| 836 | (funcall (custom-property custom 'read) custom string) | ||
| 837 | (error (cons custom-invalid string)))) | ||
| 838 | |||
| 839 | (defun custom-match (custom values) | ||
| 840 | "Match CUSTOM with a list of VALUES. | ||
| 841 | |||
| 842 | Return a cons-cell where the car is the sublist of VALUES matching CUSTOM, | ||
| 843 | and the cdr is the remaining VALUES. | ||
| 844 | |||
| 845 | A CUSTOM is actually a regular expression over the alphabet of lisp | ||
| 846 | types. Most CUSTOM types are just doing a literal match, e.g. the | ||
| 847 | `symbol' type matches any lisp symbol. The exceptions are: | ||
| 848 | |||
| 849 | group: which corresponds to a `(' and `)' group in a regular expression. | ||
| 850 | choice: which corresponds to a group of `|' in a regular expression. | ||
| 851 | repeat: which corresponds to a `*' in a regular expression. | ||
| 852 | optional: which corresponds to a `?', and isn't implemented yet." | ||
| 853 | (if (memq values (list custom-nil nil)) | ||
| 854 | ;; Nothing matches the uninitialized or empty list. | ||
| 855 | (cons custom-nil nil) | ||
| 856 | (funcall (custom-property custom 'match) custom values))) | ||
| 857 | |||
| 858 | (defun custom-initialize (custom) | ||
| 859 | "Initialize `doc' and `default' attributes of CUSTOM." | ||
| 860 | (funcall (custom-property custom 'initialize) custom)) | ||
| 861 | |||
| 862 | (defun custom-find (custom tag) | ||
| 863 | "Find child in CUSTOM with `tag' TAG." | ||
| 864 | (funcall (custom-property custom 'find) custom tag)) | ||
| 865 | |||
| 866 | (defun custom-travel-path (custom path) | ||
| 867 | "Find decedent of CUSTOM by looking through PATH." | ||
| 868 | (if (null path) | ||
| 869 | custom | ||
| 870 | (custom-travel-path (custom-find custom (car path)) (cdr path)))) | ||
| 871 | |||
| 872 | (defun custom-field-extract (custom field) | ||
| 873 | "Extract CUSTOM's value in FIELD." | ||
| 874 | (if (stringp custom) | ||
| 875 | nil | ||
| 876 | (funcall (custom-property (custom-field-custom field) 'extract) | ||
| 877 | custom field))) | ||
| 878 | |||
| 879 | (defun custom-field-validate (custom field) | ||
| 880 | "Validate CUSTOM's value in FIELD. | ||
| 881 | Return nil if valid, otherwise return a cons-cell where the car is the | ||
| 882 | position of the error, and the cdr is a text describing the error." | ||
| 883 | (if (stringp custom) | ||
| 884 | nil | ||
| 885 | (funcall (custom-property custom 'validate) custom field))) | ||
| 886 | |||
| 887 | ;;; Field Functions: | ||
| 888 | ;; | ||
| 889 | ;; This section defines the public functions for manipulating the | ||
| 890 | ;; FIELD datatype. The FIELD instance hold information about a | ||
| 891 | ;; specific editing field in the customization buffer. | ||
| 892 | ;; | ||
| 893 | ;; Each FIELD can be seen as an instantiation of a CUSTOM. | ||
| 894 | |||
| 895 | (defvar custom-field-last nil) | ||
| 896 | ;; Last field containing point. | ||
| 897 | (make-variable-buffer-local 'custom-field-last) | ||
| 898 | |||
| 899 | (defvar custom-modified-list nil) | ||
| 900 | ;; List of modified fields. | ||
| 901 | (make-variable-buffer-local 'custom-modified-list) | ||
| 902 | |||
| 903 | (defun custom-field-create (custom value) | ||
| 904 | "Create a field structure of type CUSTOM containing VALUE. | ||
| 905 | |||
| 906 | A field structure is an array [ CUSTOM VALUE ORIGINAL START END ], where | ||
| 907 | CUSTOM defines the type of the field, | ||
| 908 | VALUE is the current value of the field, | ||
| 909 | ORIGINAL is the original value when created, and | ||
| 910 | START and END are markers to the start and end of the field." | ||
| 911 | (vector custom value custom-nil nil nil)) | ||
| 912 | |||
| 913 | (defun custom-field-custom (field) | ||
| 914 | "Return the `custom' attribute of FIELD." | ||
| 915 | (aref field 0)) | ||
| 916 | |||
| 917 | (defun custom-field-value (field) | ||
| 918 | "Return the `value' attribute of FIELD." | ||
| 919 | (aref field 1)) | ||
| 920 | |||
| 921 | (defun custom-field-original (field) | ||
| 922 | "Return the `original' attribute of FIELD." | ||
| 923 | (aref field 2)) | ||
| 924 | |||
| 925 | (defun custom-field-start (field) | ||
| 926 | "Return the `start' attribute of FIELD." | ||
| 927 | (aref field 3)) | ||
| 928 | |||
| 929 | (defun custom-field-end (field) | ||
| 930 | "Return the `end' attribute of FIELD." | ||
| 931 | (aref field 4)) | ||
| 932 | |||
| 933 | (defun custom-field-value-set (field value) | ||
| 934 | "Set the `value' attribute of FIELD to VALUE." | ||
| 935 | (aset field 1 value)) | ||
| 936 | |||
| 937 | (defun custom-field-original-set (field original) | ||
| 938 | "Set the `original' attribute of FIELD to ORIGINAL." | ||
| 939 | (aset field 2 original)) | ||
| 940 | |||
| 941 | (defun custom-field-move (field start end) | ||
| 942 | "Set the `start'and `end' attributes of FIELD to START and END." | ||
| 943 | (set-marker (or (aref field 3) (aset field 3 (make-marker))) start) | ||
| 944 | (set-marker (or (aref field 4) (aset field 4 (make-marker))) end)) | ||
| 945 | |||
| 946 | (defun custom-field-query (field) | ||
| 947 | "Query user for content of current field." | ||
| 948 | (funcall (custom-property (custom-field-custom field) 'query) field)) | ||
| 949 | 79 | ||
| 950 | (defun custom-field-accept (field value &optional original) | 80 | (defmacro defcustom (symbol value doc &rest args) |
| 951 | "Store a new value into field FIELD, taking it from VALUE. | 81 | "Declare SYMBOL as a customizable variable that defaults to VALUE. |
| 952 | If optional ORIGINAL is non-nil, consider VALUE for the original value." | 82 | DOC is the variable documentation. |
| 953 | (let ((inhibit-point-motion-hooks t)) | ||
| 954 | (funcall (custom-property (custom-field-custom field) 'accept) | ||
| 955 | field value original))) | ||
| 956 | 83 | ||
| 957 | (defun custom-field-face (field) | 84 | Neither SYMBOL nor VALUE needs to be quoted. |
| 958 | "The face used for highlighting FIELD." | 85 | If SYMBOL is not already bound, initialize it to VALUE. |
| 959 | (let ((custom (custom-field-custom field))) | 86 | The remaining arguments should have the form |
| 960 | (if (stringp custom) | ||
| 961 | nil | ||
| 962 | (let ((face (funcall (custom-property custom 'face) field))) | ||
| 963 | (if (custom-facep face) face nil))))) | ||
| 964 | 87 | ||
| 965 | (defun custom-field-update (field) | 88 | [KEYWORD VALUE]... |
| 966 | "Update the screen appearance of FIELD to correspond with the field's value." | ||
| 967 | (let ((custom (custom-field-custom field))) | ||
| 968 | (if (stringp custom) | ||
| 969 | nil | ||
| 970 | (funcall (custom-property custom 'update) field)))) | ||
| 971 | 89 | ||
| 972 | ;;; Types: | 90 | The following KEYWORD's are defined: |
| 973 | ;; | ||
| 974 | ;; The following functions defines type specific actions. | ||
| 975 | |||
| 976 | (defun custom-repeat-eval (custom value) | ||
| 977 | "Non-nil if CUSTOM's VALUE needs to be evaluated." | ||
| 978 | (if (eq value custom-nil) | ||
| 979 | nil | ||
| 980 | (let ((child (custom-data custom)) | ||
| 981 | (found nil)) | ||
| 982 | (mapcar (lambda (v) (if (custom-eval child v) (setq found t))) | ||
| 983 | value)))) | ||
| 984 | |||
| 985 | (defun custom-repeat-quote (custom value) | ||
| 986 | "A list of CUSTOM's VALUEs quoted." | ||
| 987 | (let ((child (custom-data custom))) | ||
| 988 | (apply 'append (mapcar (lambda (v) (custom-quote child v)) | ||
| 989 | value)))) | ||
| 990 | |||
| 991 | |||
| 992 | (defun custom-repeat-import (custom value) | ||
| 993 | "Modify CUSTOM's VALUE to match internal expectations." | ||
| 994 | (let ((child (custom-data custom))) | ||
| 995 | (apply 'append (mapcar (lambda (v) (custom-import child v)) | ||
| 996 | value)))) | ||
| 997 | |||
| 998 | (defun custom-repeat-accept (field value &optional original) | ||
| 999 | "Store a new value into field FIELD, taking it from VALUE." | ||
| 1000 | (let ((values (copy-sequence (custom-field-value field))) | ||
| 1001 | (all (custom-field-value field)) | ||
| 1002 | (start (custom-field-start field)) | ||
| 1003 | current new) | ||
| 1004 | (if original | ||
| 1005 | (custom-field-original-set field value)) | ||
| 1006 | (while (consp value) | ||
| 1007 | (setq new (car value) | ||
| 1008 | value (cdr value)) | ||
| 1009 | (if values | ||
| 1010 | ;; Change existing field. | ||
| 1011 | (setq current (car values) | ||
| 1012 | values (cdr values)) | ||
| 1013 | ;; Insert new field if series has grown. | ||
| 1014 | (goto-char start) | ||
| 1015 | (setq current (custom-repeat-insert-entry field)) | ||
| 1016 | (setq all (custom-insert-before all nil current)) | ||
| 1017 | (custom-field-value-set field all)) | ||
| 1018 | (custom-field-accept current new original)) | ||
| 1019 | (while (consp values) | ||
| 1020 | ;; Delete old field if series has scrunk. | ||
| 1021 | (setq current (car values) | ||
| 1022 | values (cdr values)) | ||
| 1023 | (let ((pos (custom-field-start current)) | ||
| 1024 | data) | ||
| 1025 | (while (not data) | ||
| 1026 | (setq pos (previous-single-property-change pos 'custom-data)) | ||
| 1027 | (custom-assert 'pos) | ||
| 1028 | (setq data (get-text-property pos 'custom-data)) | ||
| 1029 | (or (and (arrayp data) | ||
| 1030 | (> (length data) 1) | ||
| 1031 | (eq current (aref data 1))) | ||
| 1032 | (setq data nil))) | ||
| 1033 | (custom-repeat-delete data))))) | ||
| 1034 | |||
| 1035 | (defun custom-repeat-insert (custom level) | ||
| 1036 | "Insert field for CUSTOM at nesting LEVEL in customization buffer." | ||
| 1037 | (let* ((field (custom-field-create custom nil)) | ||
| 1038 | (add-tag (custom-property custom 'add-tag)) | ||
| 1039 | (start (make-marker)) | ||
| 1040 | (data (vector field nil start nil))) | ||
| 1041 | (custom-text-insert "\n") | ||
| 1042 | (let ((pos (point))) | ||
| 1043 | (custom-text-insert (custom-property custom 'prefix)) | ||
| 1044 | (custom-tag-insert add-tag 'custom-repeat-add data) | ||
| 1045 | (set-marker start pos)) | ||
| 1046 | (custom-field-move field start (point)) | ||
| 1047 | (custom-documentation-insert custom) | ||
| 1048 | field)) | ||
| 1049 | |||
| 1050 | (defun custom-repeat-insert-entry (repeat) | ||
| 1051 | "Insert entry at point in the REPEAT field." | ||
| 1052 | (let* ((inhibit-point-motion-hooks t) | ||
| 1053 | (inhibit-read-only t) | ||
| 1054 | (before-change-functions nil) | ||
| 1055 | (after-change-functions nil) | ||
| 1056 | (custom (custom-field-custom repeat)) | ||
| 1057 | (add-tag (custom-property custom 'add-tag)) | ||
| 1058 | (del-tag (custom-property custom 'del-tag)) | ||
| 1059 | (start (make-marker)) | ||
| 1060 | (end (make-marker)) | ||
| 1061 | (data (vector repeat nil start end)) | ||
| 1062 | field) | ||
| 1063 | (custom-extent-start-open) | ||
| 1064 | (insert-before-markers "\n") | ||
| 1065 | (backward-char 1) | ||
| 1066 | (set-marker start (point)) | ||
| 1067 | (custom-text-insert " ") | ||
| 1068 | (aset data 1 (setq field (custom-insert (custom-data custom) nil))) | ||
| 1069 | (custom-text-insert " ") | ||
| 1070 | (set-marker end (point)) | ||
| 1071 | (goto-char start) | ||
| 1072 | (custom-text-insert (custom-property custom 'prefix)) | ||
| 1073 | (custom-tag-insert add-tag 'custom-repeat-add data) | ||
| 1074 | (custom-text-insert " ") | ||
| 1075 | (custom-tag-insert del-tag 'custom-repeat-delete data) | ||
| 1076 | (forward-char 1) | ||
| 1077 | field)) | ||
| 1078 | |||
| 1079 | (defun custom-repeat-add (data) | ||
| 1080 | "Add list entry." | ||
| 1081 | (let ((parent (aref data 0)) | ||
| 1082 | (field (aref data 1)) | ||
| 1083 | (at (aref data 2)) | ||
| 1084 | new) | ||
| 1085 | (goto-char at) | ||
| 1086 | (setq new (custom-repeat-insert-entry parent)) | ||
| 1087 | (custom-field-value-set parent | ||
| 1088 | (custom-insert-before (custom-field-value parent) | ||
| 1089 | field new)))) | ||
| 1090 | |||
| 1091 | (defun custom-repeat-delete (data) | ||
| 1092 | "Delete list entry." | ||
| 1093 | (let ((inhibit-point-motion-hooks t) | ||
| 1094 | (inhibit-read-only t) | ||
| 1095 | (before-change-functions nil) | ||
| 1096 | (after-change-functions nil) | ||
| 1097 | (parent (aref data 0)) | ||
| 1098 | (field (aref data 1))) | ||
| 1099 | (delete-region (aref data 2) (1+ (aref data 3))) | ||
| 1100 | (custom-field-untouch (aref data 1)) | ||
| 1101 | (custom-field-value-set parent | ||
| 1102 | (delq field (custom-field-value parent))))) | ||
| 1103 | |||
| 1104 | (defun custom-repeat-match (custom values) | ||
| 1105 | "Match CUSTOM with VALUES." | ||
| 1106 | (let* ((child (custom-data custom)) | ||
| 1107 | (match (custom-match child values)) | ||
| 1108 | matches) | ||
| 1109 | (while (not (eq (car match) custom-nil)) | ||
| 1110 | (setq matches (cons (car match) matches) | ||
| 1111 | values (cdr match) | ||
| 1112 | match (custom-match child values))) | ||
| 1113 | (cons (nreverse matches) values))) | ||
| 1114 | |||
| 1115 | (defun custom-repeat-extract (custom field) | ||
| 1116 | "Extract list of children's values." | ||
| 1117 | (let ((values (custom-field-value field)) | ||
| 1118 | (data (custom-data custom)) | ||
| 1119 | result) | ||
| 1120 | (if (eq values custom-nil) | ||
| 1121 | () | ||
| 1122 | (while values | ||
| 1123 | (setq result (append result (custom-field-extract data (car values))) | ||
| 1124 | values (cdr values)))) | ||
| 1125 | result)) | ||
| 1126 | |||
| 1127 | (defun custom-repeat-validate (custom field) | ||
| 1128 | "Validate children." | ||
| 1129 | (let ((values (custom-field-value field)) | ||
| 1130 | (data (custom-data custom)) | ||
| 1131 | result) | ||
| 1132 | (if (eq values custom-nil) | ||
| 1133 | (setq result (cons (custom-field-start field) "Uninitialized list"))) | ||
| 1134 | (while (and values (not result)) | ||
| 1135 | (setq result (custom-field-validate data (car values)) | ||
| 1136 | values (cdr values))) | ||
| 1137 | result)) | ||
| 1138 | |||
| 1139 | (defun custom-pair-accept (field value &optional original) | ||
| 1140 | "Store a new value into field FIELD, taking it from VALUE." | ||
| 1141 | (custom-group-accept field (list (car value) (cdr value)) original)) | ||
| 1142 | |||
| 1143 | (defun custom-pair-eval (custom value) | ||
| 1144 | "Non-nil if CUSTOM's VALUE needs to be evaluated." | ||
| 1145 | (custom-group-eval custom (list (car value) (cdr value)))) | ||
| 1146 | |||
| 1147 | (defun custom-pair-import (custom value) | ||
| 1148 | "Modify CUSTOM's VALUE to match internal expectations." | ||
| 1149 | (let ((result (car (custom-group-import custom | ||
| 1150 | (list (car value) (cdr value)))))) | ||
| 1151 | (custom-assert '(eq (length result) 2)) | ||
| 1152 | (list (cons (nth 0 result) (nth 1 result))))) | ||
| 1153 | |||
| 1154 | (defun custom-pair-quote (custom value) | ||
| 1155 | "Quote CUSTOM's VALUE if necessary." | ||
| 1156 | (if (custom-eval custom value) | ||
| 1157 | (let ((v (car (custom-group-quote custom | ||
| 1158 | (list (car value) (cdr value)))))) | ||
| 1159 | (list (list 'cons (nth 0 v) (nth 1 v)))) | ||
| 1160 | (custom-default-quote custom value))) | ||
| 1161 | |||
| 1162 | (defun custom-pair-extract (custom field) | ||
| 1163 | "Extract cons of children's values." | ||
| 1164 | (let ((values (custom-field-value field)) | ||
| 1165 | (data (custom-data custom)) | ||
| 1166 | result) | ||
| 1167 | (custom-assert '(eq (length values) (length data))) | ||
| 1168 | (while values | ||
| 1169 | (setq result (append result | ||
| 1170 | (custom-field-extract (car data) (car values))) | ||
| 1171 | data (cdr data) | ||
| 1172 | values (cdr values))) | ||
| 1173 | (custom-assert '(null data)) | ||
| 1174 | (list (cons (nth 0 result) (nth 1 result))))) | ||
| 1175 | |||
| 1176 | (defun custom-list-quote (custom value) | ||
| 1177 | "Quote CUSTOM's VALUE if necessary." | ||
| 1178 | (if (custom-eval custom value) | ||
| 1179 | (let ((v (car (custom-group-quote custom value)))) | ||
| 1180 | (list (cons 'list v))) | ||
| 1181 | (custom-default-quote custom value))) | ||
| 1182 | |||
| 1183 | (defun custom-list-extract (custom field) | ||
| 1184 | "Extract list of children's values." | ||
| 1185 | (let ((values (custom-field-value field)) | ||
| 1186 | (data (custom-data custom)) | ||
| 1187 | result) | ||
| 1188 | (custom-assert '(eq (length values) (length data))) | ||
| 1189 | (while values | ||
| 1190 | (setq result (append result | ||
| 1191 | (custom-field-extract (car data) (car values))) | ||
| 1192 | data (cdr data) | ||
| 1193 | values (cdr values))) | ||
| 1194 | (custom-assert '(null data)) | ||
| 1195 | (list result))) | ||
| 1196 | |||
| 1197 | (defun custom-group-validate (custom field) | ||
| 1198 | "Validate children." | ||
| 1199 | (let ((values (custom-field-value field)) | ||
| 1200 | (data (custom-data custom)) | ||
| 1201 | result) | ||
| 1202 | (if (eq values custom-nil) | ||
| 1203 | (setq result (cons (custom-field-start field) "Uninitialized list")) | ||
| 1204 | (custom-assert '(eq (length values) (length data)))) | ||
| 1205 | (while (and values (not result)) | ||
| 1206 | (setq result (custom-field-validate (car data) (car values)) | ||
| 1207 | data (cdr data) | ||
| 1208 | values (cdr values))) | ||
| 1209 | result)) | ||
| 1210 | |||
| 1211 | (defun custom-group-eval (custom value) | ||
| 1212 | "Non-nil if CUSTOM's VALUE needs to be evaluated." | ||
| 1213 | (let ((found nil)) | ||
| 1214 | (mapcar (lambda (c) | ||
| 1215 | (or (stringp c) | ||
| 1216 | (let ((match (custom-match c value))) | ||
| 1217 | (if (custom-eval c (car match)) | ||
| 1218 | (setq found t)) | ||
| 1219 | (setq value (cdr match))))) | ||
| 1220 | (custom-data custom)) | ||
| 1221 | found)) | ||
| 1222 | |||
| 1223 | (defun custom-group-quote (custom value) | ||
| 1224 | "A list of CUSTOM's VALUE members, quoted." | ||
| 1225 | (list (apply 'append | ||
| 1226 | (mapcar (lambda (c) | ||
| 1227 | (if (stringp c) | ||
| 1228 | () | ||
| 1229 | (let ((match (custom-match c value))) | ||
| 1230 | (prog1 (custom-quote c (car match)) | ||
| 1231 | (setq value (cdr match)))))) | ||
| 1232 | (custom-data custom))))) | ||
| 1233 | |||
| 1234 | (defun custom-group-import (custom value) | ||
| 1235 | "Modify CUSTOM's VALUE to match internal expectations." | ||
| 1236 | (list (apply 'append | ||
| 1237 | (mapcar (lambda (c) | ||
| 1238 | (if (stringp c) | ||
| 1239 | () | ||
| 1240 | (let ((match (custom-match c value))) | ||
| 1241 | (prog1 (custom-import c (car match)) | ||
| 1242 | (setq value (cdr match)))))) | ||
| 1243 | (custom-data custom))))) | ||
| 1244 | |||
| 1245 | (defun custom-group-initialize (custom) | ||
| 1246 | "Initialize `doc' and `default' entries in CUSTOM." | ||
| 1247 | (if (custom-name custom) | ||
| 1248 | (custom-default-initialize custom) | ||
| 1249 | (mapcar 'custom-initialize (custom-data custom)))) | ||
| 1250 | |||
| 1251 | (defun custom-group-apply (field) | ||
| 1252 | "Reset `value' in FIELD to `original'." | ||
| 1253 | (let ((custom (custom-field-custom field)) | ||
| 1254 | (values (custom-field-value field))) | ||
| 1255 | (if (custom-name custom) | ||
| 1256 | (custom-default-apply field) | ||
| 1257 | (mapcar 'custom-field-apply values)))) | ||
| 1258 | |||
| 1259 | (defun custom-group-reset (field) | ||
| 1260 | "Reset `value' in FIELD to `original'." | ||
| 1261 | (let ((custom (custom-field-custom field)) | ||
| 1262 | (values (custom-field-value field))) | ||
| 1263 | (if (custom-name custom) | ||
| 1264 | (custom-default-reset field) | ||
| 1265 | (mapcar 'custom-field-reset values)))) | ||
| 1266 | |||
| 1267 | (defun custom-group-factory-reset (field) | ||
| 1268 | "Reset `value' in FIELD to `default'." | ||
| 1269 | (let ((custom (custom-field-custom field)) | ||
| 1270 | (values (custom-field-value field))) | ||
| 1271 | (if (custom-name custom) | ||
| 1272 | (custom-default-factory-reset field) | ||
| 1273 | (mapcar 'custom-field-factory-reset values)))) | ||
| 1274 | |||
| 1275 | (defun custom-group-find (custom tag) | ||
| 1276 | "Find child in CUSTOM with `tag' TAG." | ||
| 1277 | (let ((data (custom-data custom)) | ||
| 1278 | (result nil)) | ||
| 1279 | (while (not result) | ||
| 1280 | (custom-assert 'data) | ||
| 1281 | (if (equal (custom-tag (car data)) tag) | ||
| 1282 | (setq result (car data)) | ||
| 1283 | (setq data (cdr data)))))) | ||
| 1284 | 91 | ||
| 1285 | (defun custom-group-accept (field value &optional original) | 92 | :type VALUE should be a widget type. |
| 1286 | "Store a new value into field FIELD, taking it from VALUE." | 93 | :options VALUE should be a list of valid members of the widget type. |
| 1287 | (let* ((values (custom-field-value field)) | 94 | :group VALUE should be a customization group. |
| 1288 | (custom (custom-field-custom field)) | 95 | Add SYMBOL to that group. |
| 1289 | (from (custom-field-start field)) | ||
| 1290 | (face-tag (custom-face-tag custom)) | ||
| 1291 | current) | ||
| 1292 | (if face-tag | ||
| 1293 | (custom-put-text-property from (+ from (length (custom-tag custom))) | ||
| 1294 | 'face (funcall face-tag field value))) | ||
| 1295 | (if original | ||
| 1296 | (custom-field-original-set field value)) | ||
| 1297 | (while values | ||
| 1298 | (setq current (car values) | ||
| 1299 | values (cdr values)) | ||
| 1300 | (if current | ||
| 1301 | (let* ((custom (custom-field-custom current)) | ||
| 1302 | (match (custom-match custom value))) | ||
| 1303 | (setq value (cdr match)) | ||
| 1304 | (custom-field-accept current (car match) original)))))) | ||
| 1305 | 96 | ||
| 1306 | (defun custom-group-insert (custom level) | 97 | Read the section about customization in the emacs lisp manual for more |
| 1307 | "Insert field for CUSTOM at nesting LEVEL in customization buffer." | 98 | information." |
| 1308 | (let* ((field (custom-field-create custom nil)) | 99 | `(eval-and-compile |
| 1309 | fields hidden | 100 | (custom-declare-variable (quote ,symbol) (quote ,value) ,doc ,@args))) |
| 1310 | (from (point)) | ||
| 1311 | (compact (custom-compact custom)) | ||
| 1312 | (tag (custom-tag custom)) | ||
| 1313 | (face-tag (custom-face-tag custom))) | ||
| 1314 | (cond (face-tag (custom-text-insert tag)) | ||
| 1315 | (tag (custom-tag-insert tag field))) | ||
| 1316 | (or compact (custom-documentation-insert custom)) | ||
| 1317 | (or compact (custom-text-insert "\n")) | ||
| 1318 | (let ((data (custom-data custom))) | ||
| 1319 | (while data | ||
| 1320 | (setq fields (cons (custom-insert (car data) (if level (1+ level))) | ||
| 1321 | fields)) | ||
| 1322 | (setq hidden (or (stringp (car data)) | ||
| 1323 | (custom-property (car data) 'hidden))) | ||
| 1324 | (setq data (cdr data)) | ||
| 1325 | (if data (custom-text-insert (cond (hidden "") | ||
| 1326 | (compact " ") | ||
| 1327 | (t "\n")))))) | ||
| 1328 | (if compact (custom-documentation-insert custom)) | ||
| 1329 | (custom-field-value-set field (nreverse fields)) | ||
| 1330 | (custom-field-move field from (point)) | ||
| 1331 | field)) | ||
| 1332 | 101 | ||
| 1333 | (defun custom-choice-insert (custom level) | 102 | ;;; The `defface' Macro. |
| 1334 | "Insert field for CUSTOM at nesting LEVEL in customization buffer." | ||
| 1335 | (let* ((field (custom-field-create custom nil)) | ||
| 1336 | (from (point))) | ||
| 1337 | (custom-text-insert "lars er en nisse") | ||
| 1338 | (custom-field-move field from (point)) | ||
| 1339 | (custom-documentation-insert custom) | ||
| 1340 | (custom-field-reset field) | ||
| 1341 | field)) | ||
| 1342 | 103 | ||
| 1343 | (defun custom-choice-accept (field value &optional original) | 104 | (defmacro defface (face spec doc &rest args) |
| 1344 | "Store a new value into field FIELD, taking it from VALUE." | 105 | "Declare FACE as a customizable face that defaults to SPEC. |
| 1345 | (let ((custom (custom-field-custom field)) | 106 | FACE does not need to be quoted. |
| 1346 | (start (custom-field-start field)) | ||
| 1347 | (end (custom-field-end field)) | ||
| 1348 | (inhibit-read-only t) | ||
| 1349 | (before-change-functions nil) | ||
| 1350 | (after-change-functions nil) | ||
| 1351 | from) | ||
| 1352 | (cond (original | ||
| 1353 | (setq custom-modified-list (delq field custom-modified-list)) | ||
| 1354 | (custom-field-original-set field value)) | ||
| 1355 | ((equal value (custom-field-original field)) | ||
| 1356 | (setq custom-modified-list (delq field custom-modified-list))) | ||
| 1357 | (t | ||
| 1358 | (add-to-list 'custom-modified-list field))) | ||
| 1359 | (custom-field-untouch (custom-field-value field)) | ||
| 1360 | (delete-region start end) | ||
| 1361 | (goto-char start) | ||
| 1362 | (setq from (point)) | ||
| 1363 | (insert-before-markers " ") | ||
| 1364 | (backward-char 1) | ||
| 1365 | (custom-category-set (point) (1+ (point)) 'custom-hidden-properties) | ||
| 1366 | (custom-tag-insert (custom-tag custom) field) | ||
| 1367 | (custom-text-insert ": ") | ||
| 1368 | (let ((data (custom-data custom)) | ||
| 1369 | found begin) | ||
| 1370 | (while (and data (not found)) | ||
| 1371 | (if (not (custom-valid (car data) value)) | ||
| 1372 | (setq data (cdr data)) | ||
| 1373 | (setq found (custom-insert (car data) nil)) | ||
| 1374 | (setq data nil))) | ||
| 1375 | (if found | ||
| 1376 | () | ||
| 1377 | (setq begin (point) | ||
| 1378 | found (custom-insert (custom-property custom 'none) nil)) | ||
| 1379 | (custom-add-text-properties | ||
| 1380 | begin (point) | ||
| 1381 | (list rear-nonsticky t | ||
| 1382 | 'face custom-field-uninitialized-face))) | ||
| 1383 | (or original | ||
| 1384 | (custom-field-original-set found (custom-field-original field))) | ||
| 1385 | (custom-field-accept found value original) | ||
| 1386 | (custom-field-value-set field found) | ||
| 1387 | (custom-field-move field from end)))) | ||
| 1388 | 107 | ||
| 1389 | (defun custom-choice-extract (custom field) | 108 | Third argument DOC is the face documentation. |
| 1390 | "Extract child's value." | ||
| 1391 | (let ((value (custom-field-value field))) | ||
| 1392 | (custom-field-extract (custom-field-custom value) value))) | ||
| 1393 | 109 | ||
| 1394 | (defun custom-choice-validate (custom field) | 110 | If FACE has been set with `custom-set-face', set the face attributes |
| 1395 | "Validate child's value." | 111 | as specified by that function, otherwise set the face attributes |
| 1396 | (let ((value (custom-field-value field)) | 112 | according to SPEC. |
| 1397 | (custom (custom-field-custom field))) | ||
| 1398 | (if (or (eq value custom-nil) | ||
| 1399 | (eq (custom-field-custom value) (custom-property custom 'none))) | ||
| 1400 | (cons (custom-field-start field) "Make a choice") | ||
| 1401 | (custom-field-validate (custom-field-custom value) value)))) | ||
| 1402 | 113 | ||
| 1403 | (defun custom-choice-query (field) | 114 | The remaining arguments should have the form |
| 1404 | "Choose a child." | ||
| 1405 | (let* ((custom (custom-field-custom field)) | ||
| 1406 | (old (custom-field-custom (custom-field-value field))) | ||
| 1407 | (default (custom-prompt old)) | ||
| 1408 | (tag (custom-prompt custom)) | ||
| 1409 | (data (custom-data custom)) | ||
| 1410 | current alist) | ||
| 1411 | (if (eq (length data) 2) | ||
| 1412 | (custom-field-accept field (custom-default (if (eq (nth 0 data) old) | ||
| 1413 | (nth 1 data) | ||
| 1414 | (nth 0 data)))) | ||
| 1415 | (while data | ||
| 1416 | (setq current (car data) | ||
| 1417 | data (cdr data)) | ||
| 1418 | (setq alist (cons (cons (custom-prompt current) current) alist))) | ||
| 1419 | (let ((answer (cond ((and (fboundp 'button-press-event-p) | ||
| 1420 | (fboundp 'popup-menu) | ||
| 1421 | (button-press-event-p last-input-event)) | ||
| 1422 | (cdr (assoc (car (custom-x-really-popup-menu | ||
| 1423 | last-input-event tag | ||
| 1424 | (reverse alist))) | ||
| 1425 | alist))) | ||
| 1426 | ((listp last-input-event) | ||
| 1427 | (x-popup-menu last-input-event | ||
| 1428 | (list tag (cons "" (reverse alist))))) | ||
| 1429 | (t | ||
| 1430 | (let ((choice (completing-read (concat tag | ||
| 1431 | " (default " | ||
| 1432 | default | ||
| 1433 | "): ") | ||
| 1434 | alist nil t))) | ||
| 1435 | (if (or (null choice) (string-equal choice "")) | ||
| 1436 | (setq choice default)) | ||
| 1437 | (cdr (assoc choice alist))))))) | ||
| 1438 | (if answer | ||
| 1439 | (custom-field-accept field (custom-default answer))))))) | ||
| 1440 | 115 | ||
| 1441 | (defun custom-file-query (field) | 116 | [KEYWORD VALUE]... |
| 1442 | "Prompt for a file name" | ||
| 1443 | (let* ((value (custom-field-value field)) | ||
| 1444 | (custom (custom-field-custom field)) | ||
| 1445 | (valid (custom-valid custom value)) | ||
| 1446 | (directory (custom-property custom 'directory)) | ||
| 1447 | (default (and (not valid) | ||
| 1448 | (custom-property custom 'default-file))) | ||
| 1449 | (tag (custom-tag custom)) | ||
| 1450 | (prompt (if default | ||
| 1451 | (concat tag " (" default "): ") | ||
| 1452 | (concat tag ": ")))) | ||
| 1453 | (custom-field-accept field | ||
| 1454 | (if (custom-valid custom value) | ||
| 1455 | (read-file-name prompt | ||
| 1456 | (if (file-name-absolute-p value) | ||
| 1457 | "" | ||
| 1458 | directory) | ||
| 1459 | default nil value) | ||
| 1460 | (read-file-name prompt directory default))))) | ||
| 1461 | 117 | ||
| 1462 | (defun custom-face-eval (custom value) | 118 | The following KEYWORD's are defined: |
| 1463 | "Return non-nil if CUSTOM's VALUE needs to be evaluated." | ||
| 1464 | (not (symbolp value))) | ||
| 1465 | 119 | ||
| 1466 | (defun custom-face-import (custom value) | 120 | :group VALUE should be a customization group. |
| 1467 | "Modify CUSTOM's VALUE to match internal expectations." | 121 | Add FACE to that group. |
| 1468 | (let ((name (or (and (facep value) (symbol-name (face-name value))) | ||
| 1469 | (symbol-name value)))) | ||
| 1470 | (list (if (string-match "\ | ||
| 1471 | custom-face-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)" | ||
| 1472 | name) | ||
| 1473 | (list 'custom-face-lookup | ||
| 1474 | (match-string 1 name) | ||
| 1475 | (match-string 2 name) | ||
| 1476 | (match-string 3 name) | ||
| 1477 | (intern (match-string 4 name)) | ||
| 1478 | (intern (match-string 5 name)) | ||
| 1479 | (intern (match-string 6 name))) | ||
| 1480 | value)))) | ||
| 1481 | 122 | ||
| 1482 | (defun custom-face-lookup (&optional fg bg stipple bold italic underline) | 123 | SPEC should be an alist of the form ((DISPLAY ATTS)...). |
| 1483 | "Lookup or create a face with specified attributes." | ||
| 1484 | (let ((name (intern (format "custom-face-%s-%s-%s-%S-%S-%S" | ||
| 1485 | (or fg "default") | ||
| 1486 | (or bg "default") | ||
| 1487 | (or stipple "default") | ||
| 1488 | bold italic underline)))) | ||
| 1489 | (if (and (custom-facep name) | ||
| 1490 | (fboundp 'make-face)) | ||
| 1491 | () | ||
| 1492 | (copy-face 'default name) | ||
| 1493 | (when (and fg | ||
| 1494 | (not (string-equal fg "default"))) | ||
| 1495 | (condition-case () | ||
| 1496 | (set-face-foreground name fg) | ||
| 1497 | (error nil))) | ||
| 1498 | (when (and bg | ||
| 1499 | (not (string-equal bg "default"))) | ||
| 1500 | (condition-case () | ||
| 1501 | (set-face-background name bg) | ||
| 1502 | (error nil))) | ||
| 1503 | (when (and stipple | ||
| 1504 | (not (string-equal stipple "default")) | ||
| 1505 | (not (eq stipple 'custom:asis)) | ||
| 1506 | (fboundp 'set-face-stipple)) | ||
| 1507 | (set-face-stipple name stipple)) | ||
| 1508 | (when (and bold | ||
| 1509 | (not (eq bold 'custom:asis))) | ||
| 1510 | (condition-case () | ||
| 1511 | (make-face-bold name) | ||
| 1512 | (error nil))) | ||
| 1513 | (when (and italic | ||
| 1514 | (not (eq italic 'custom:asis))) | ||
| 1515 | (condition-case () | ||
| 1516 | (make-face-italic name) | ||
| 1517 | (error nil))) | ||
| 1518 | (when (and underline | ||
| 1519 | (not (eq underline 'custom:asis))) | ||
| 1520 | (condition-case () | ||
| 1521 | (set-face-underline-p name t) | ||
| 1522 | (error nil)))) | ||
| 1523 | name)) | ||
| 1524 | 124 | ||
| 1525 | (defun custom-face-hack (field value) | 125 | ATTS is a list of face attributes and their values. The possible |
| 1526 | "Face that should be used for highlighting FIELD containing VALUE." | 126 | attributes are defined in the variable `custom-face-attributes'. |
| 1527 | (let* ((custom (custom-field-custom field)) | 127 | Alternatively, ATTS can be a face in which case the attributes of that |
| 1528 | (form (funcall (custom-property custom 'export) custom value)) | 128 | face is used. |
| 1529 | (face (apply (car form) (cdr form)))) | ||
| 1530 | (if (custom-facep face) face nil))) | ||
| 1531 | 129 | ||
| 1532 | (defun custom-const-insert (custom level) | 130 | The ATTS of the first entry in SPEC where the DISPLAY matches the |
| 1533 | "Insert field for CUSTOM at nesting LEVEL in customization buffer." | 131 | frame should take effect in that frame. DISPLAY can either be the |
| 1534 | (let* ((field (custom-field-create custom custom-nil)) | 132 | symbol t, which will match all frames, or an alist of the form |
| 1535 | (face (custom-field-face field)) | 133 | \((REQ ITEM...)...) |
| 1536 | (from (point))) | ||
| 1537 | (custom-text-insert (custom-tag custom)) | ||
| 1538 | (custom-add-text-properties from (point) | ||
| 1539 | (list 'face face | ||
| 1540 | rear-nonsticky t)) | ||
| 1541 | (custom-documentation-insert custom) | ||
| 1542 | (custom-field-move field from (point)) | ||
| 1543 | field)) | ||
| 1544 | 134 | ||
| 1545 | (defun custom-const-update (field) | 135 | For the DISPLAY to match a FRAME, the REQ property of the frame must |
| 1546 | "Update face of FIELD." | 136 | match one of the ITEM. The following REQ are defined: |
| 1547 | (let ((from (custom-field-start field)) | ||
| 1548 | (custom (custom-field-custom field))) | ||
| 1549 | (custom-put-text-property from (+ from (length (custom-tag custom))) | ||
| 1550 | 'face (custom-field-face field)))) | ||
| 1551 | 137 | ||
| 1552 | (defun custom-const-valid (custom value) | 138 | `type' (the value of `window-system') |
| 1553 | "Non-nil if CUSTOM can validly have the value VALUE." | 139 | Should be one of `x' or `tty'. |
| 1554 | (equal (custom-default custom) value)) | ||
| 1555 | 140 | ||
| 1556 | (defun custom-const-face (field) | 141 | `class' (the frame's color support) |
| 1557 | "Face used for a FIELD." | 142 | Should be one of `color', `grayscale', or `mono'. |
| 1558 | (custom-default (custom-field-custom field))) | ||
| 1559 | 143 | ||
| 1560 | (defun custom-sexp-read (custom string) | 144 | `background' (what color is used for the background text) |
| 1561 | "Read from CUSTOM an STRING." | 145 | Should be one of `light' or `dark'. |
| 1562 | (save-match-data | ||
| 1563 | (save-excursion | ||
| 1564 | (set-buffer (get-buffer-create " *Custom Scratch*")) | ||
| 1565 | (erase-buffer) | ||
| 1566 | (insert string) | ||
| 1567 | (goto-char (point-min)) | ||
| 1568 | (prog1 (read (current-buffer)) | ||
| 1569 | (or (looking-at | ||
| 1570 | (concat (regexp-quote (char-to-string | ||
| 1571 | (custom-padding custom))) | ||
| 1572 | "*\\'")) | ||
| 1573 | (error "Junk at end of expression")))))) | ||
| 1574 | 146 | ||
| 1575 | (autoload 'pp-to-string "pp") | 147 | Read the section about customization in the emacs lisp manual for more |
| 148 | information." | ||
| 149 | `(custom-declare-face (quote ,face) ,spec ,doc ,@args)) | ||
| 1576 | 150 | ||
| 1577 | (defun custom-sexp-write (custom sexp) | 151 | ;;; The `defgroup' Macro. |
| 1578 | "Write CUSTOM SEXP as string." | ||
| 1579 | (let ((string (prin1-to-string sexp))) | ||
| 1580 | (if (<= (length string) (custom-width custom)) | ||
| 1581 | string | ||
| 1582 | (setq string (pp-to-string sexp)) | ||
| 1583 | (string-match "[ \t\n]*\\'" string) | ||
| 1584 | (concat "\n" (substring string 0 (match-beginning 0)))))) | ||
| 1585 | 152 | ||
| 1586 | (defun custom-string-read (custom string) | 153 | (defun custom-declare-group (symbol members doc &rest args) |
| 1587 | "Read string by ignoring trailing padding characters." | 154 | "Like `defgroup', but SYMBOL is evaluated as a normal argument." |
| 1588 | (let ((last (length string)) | 155 | (put symbol 'custom-group (nconc members (get symbol 'custom-group))) |
| 1589 | (padding (custom-padding custom))) | 156 | (when doc |
| 1590 | (while (and (> last 0) | 157 | (put symbol 'group-documentation doc)) |
| 1591 | (eq (aref string (1- last)) padding)) | 158 | (while args |
| 1592 | (setq last (1- last))) | 159 | (let ((arg (car args))) |
| 1593 | (substring string 0 last))) | 160 | (setq args (cdr args)) |
| 1594 | 161 | (unless (symbolp arg) | |
| 1595 | (defun custom-string-write (custom string) | 162 | (error "Junk in args %S" args)) |
| 1596 | "Write raw string." | 163 | (let ((keyword arg) |
| 1597 | string) | 164 | (value (car args))) |
| 1598 | 165 | (unless args | |
| 1599 | (defun custom-button-insert (custom level) | 166 | (error "Keyword %s is missing an argument" keyword)) |
| 1600 | "Insert field for CUSTOM at nesting LEVEL in customization buffer." | 167 | (setq args (cdr args)) |
| 1601 | (custom-tag-insert (concat "[" (custom-tag custom) "]") | 168 | (cond ((eq keyword :prefix) |
| 1602 | (custom-property custom 'query)) | 169 | (put symbol 'custom-prefix value)) |
| 1603 | (custom-documentation-insert custom) | 170 | (t |
| 1604 | nil) | 171 | (custom-handle-keyword symbol keyword value |
| 1605 | 172 | 'custom-group)))))) | |
| 1606 | (defun custom-default-export (custom value) | 173 | (run-hooks 'custom-define-hook) |
| 1607 | ;; Convert CUSTOM's VALUE to external representation. | 174 | symbol) |
| 1608 | ;; See `custom-import'. | 175 | |
| 1609 | (if (custom-eval custom value) | 176 | (defmacro defgroup (symbol members doc &rest args) |
| 1610 | (eval (car (custom-quote custom value))) | 177 | "Declare SYMBOL as a customization group containing MEMBERS. |
| 1611 | value)) | 178 | SYMBOL does not need to be quoted. |
| 1612 | 179 | ||
| 1613 | (defun custom-default-quote (custom value) | 180 | Third arg DOC is the group documentation. |
| 1614 | "Quote CUSTOM's VALUE if necessary." | 181 | |
| 1615 | (list (if (and (not (custom-eval custom value)) | 182 | MEMBERS should be an alist of the form ((NAME WIDGET)...) where |
| 1616 | (or (and (symbolp value) | 183 | NAME is a symbol and WIDGET is a widget is a widget for editing that |
| 1617 | value | 184 | symbol. Useful widgets are `custom-variable' for editing variables, |
| 1618 | (not (eq t value))) | 185 | `custom-face' for edit faces, and `custom-group' for editing groups. |
| 1619 | (and (listp value) | 186 | |
| 1620 | value | 187 | The remaining arguments should have the form |
| 1621 | (not (memq (car value) '(quote function lambda)))))) | 188 | |
| 1622 | (list 'quote value) | 189 | [KEYWORD VALUE]... |
| 1623 | value))) | 190 | |
| 1624 | 191 | The following KEYWORD's are defined: | |
| 1625 | (defun custom-default-initialize (custom) | 192 | |
| 1626 | "Initialize `doc' and `default' entries in CUSTOM." | 193 | :group VALUE should be a customization group. |
| 1627 | (let ((name (custom-name custom))) | 194 | Add SYMBOL to that group. |
| 1628 | (if (null name) | 195 | |
| 1629 | () | 196 | Read the section about customization in the emacs lisp manual for more |
| 1630 | (let ((default (custom-default custom)) | 197 | information." |
| 1631 | (doc (custom-documentation custom)) | 198 | `(custom-declare-group (quote ,symbol) ,members ,doc ,@args)) |
| 1632 | (vdoc (documentation-property name 'variable-documentation t))) | 199 | |
| 1633 | (if doc | 200 | (defun custom-add-to-group (group option widget) |
| 1634 | (or vdoc (put name 'variable-documentation doc)) | 201 | "To existing GROUP add a new OPTION of type WIDGET. |
| 1635 | (if vdoc (custom-property-set custom 'doc vdoc))) | 202 | If there already is an entry for that option, overwrite it." |
| 1636 | (if (eq default custom-nil) | 203 | (let* ((members (get group 'custom-group)) |
| 1637 | (if (boundp name) | 204 | (old (assq option members))) |
| 1638 | (custom-property-set custom 'default (symbol-value name))) | 205 | (if old |
| 1639 | (or (boundp name) | 206 | (setcar (cdr old) widget) |
| 1640 | (set name default))))))) | 207 | (put group 'custom-group (nconc members (list (list option widget))))))) |
| 1641 | 208 | ||
| 1642 | (defun custom-default-insert (custom level) | 209 | ;;; Properties. |
| 1643 | "Insert field for CUSTOM at nesting LEVEL in customization buffer." | 210 | |
| 1644 | (let ((field (custom-field-create custom custom-nil)) | 211 | (defun custom-handle-all-keywords (symbol args type) |
| 1645 | (tag (custom-tag custom))) | 212 | "For customization option SYMBOL, handle keyword arguments ARGS. |
| 1646 | (if (null tag) | 213 | Third argument TYPE is the custom option type." |
| 1647 | () | 214 | (while args |
| 1648 | (custom-tag-insert tag field) | 215 | (let ((arg (car args))) |
| 1649 | (custom-text-insert ": ")) | 216 | (setq args (cdr args)) |
| 1650 | (custom-field-insert field) | 217 | (unless (symbolp arg) |
| 1651 | (custom-documentation-insert custom) | 218 | (error "Junk in args %S" args)) |
| 1652 | field)) | 219 | (let ((keyword arg) |
| 1653 | 220 | (value (car args))) | |
| 1654 | (defun custom-default-accept (field value &optional original) | 221 | (unless args |
| 1655 | "Store a new value into field FIELD, taking it from VALUE." | 222 | (error "Keyword %s is missing an argument" keyword)) |
| 1656 | (if original | 223 | (setq args (cdr args)) |
| 1657 | (custom-field-original-set field value)) | 224 | (custom-handle-keyword symbol keyword value type))))) |
| 1658 | (custom-field-value-set field value) | 225 | |
| 1659 | (custom-field-update field)) | 226 | (defun custom-handle-keyword (symbol keyword value type) |
| 1660 | 227 | "For customization option SYMBOL, handle KEYWORD with VALUE. | |
| 1661 | (defun custom-default-apply (field) | 228 | Fourth argument TYPE is the custom option type." |
| 1662 | "Apply any changes in FIELD since the last apply." | 229 | (cond ((eq keyword :group) |
| 1663 | (let* ((custom (custom-field-custom field)) | 230 | (custom-add-to-group value symbol type)) |
| 1664 | (name (custom-name custom))) | 231 | ((eq keyword :link) |
| 1665 | (if (null name) | 232 | (custom-add-link symbol value)) |
| 1666 | (error "This field cannot be applied alone")) | 233 | ((eq keyword :load) |
| 1667 | (custom-external-set name (custom-name-value name)) | 234 | (custom-add-load symbol value)) |
| 1668 | (custom-field-reset field))) | 235 | ((eq keyword :tag) |
| 1669 | 236 | (put symbol 'custom-tag value)) | |
| 1670 | (defun custom-default-reset (field) | ||
| 1671 | "Reset content of editing FIELD to `original'." | ||
| 1672 | (custom-field-accept field (custom-field-original field) t)) | ||
| 1673 | |||
| 1674 | (defun custom-default-factory-reset (field) | ||
| 1675 | "Reset content of editing FIELD to `default'." | ||
| 1676 | (let* ((custom (custom-field-custom field)) | ||
| 1677 | (default (car (custom-import custom (custom-default custom))))) | ||
| 1678 | (or (eq default custom-nil) | ||
| 1679 | (custom-field-accept field default nil)))) | ||
| 1680 | |||
| 1681 | (defun custom-default-query (field) | ||
| 1682 | "Prompt for a FIELD" | ||
| 1683 | (let* ((custom (custom-field-custom field)) | ||
| 1684 | (value (custom-field-value field)) | ||
| 1685 | (initial (custom-write custom value)) | ||
| 1686 | (prompt (concat (custom-prompt custom) ": "))) | ||
| 1687 | (custom-field-accept field | ||
| 1688 | (custom-read custom | ||
| 1689 | (if (custom-valid custom value) | ||
| 1690 | (read-string prompt (cons initial 1)) | ||
| 1691 | (read-string prompt)))))) | ||
| 1692 | |||
| 1693 | (defun custom-default-match (custom values) | ||
| 1694 | "Match CUSTOM with VALUES." | ||
| 1695 | values) | ||
| 1696 | |||
| 1697 | (defun custom-default-extract (custom field) | ||
| 1698 | "Extract CUSTOM's content in FIELD." | ||
| 1699 | (list (custom-field-value field))) | ||
| 1700 | |||
| 1701 | (defun custom-default-validate (custom field) | ||
| 1702 | "Validate FIELD." | ||
| 1703 | (let ((value (custom-field-value field)) | ||
| 1704 | (start (custom-field-start field))) | ||
| 1705 | (cond ((eq value custom-nil) | ||
| 1706 | (cons start "Uninitialized field")) | ||
| 1707 | ((and (consp value) (eq (car value) custom-invalid)) | ||
| 1708 | (cons start "Unparsable field content")) | ||
| 1709 | ((custom-valid custom value) | ||
| 1710 | nil) | ||
| 1711 | (t | ||
| 1712 | (cons start "Wrong type of field content"))))) | ||
| 1713 | |||
| 1714 | (defun custom-default-face (field) | ||
| 1715 | "Face used for a FIELD." | ||
| 1716 | (let ((value (custom-field-value field))) | ||
| 1717 | (cond ((eq value custom-nil) | ||
| 1718 | custom-field-uninitialized-face) | ||
| 1719 | ((not (custom-valid (custom-field-custom field) value)) | ||
| 1720 | custom-field-invalid-face) | ||
| 1721 | ((not (equal (custom-field-original field) value)) | ||
| 1722 | custom-field-modified-face) | ||
| 1723 | (t | ||
| 1724 | custom-field-face)))) | ||
| 1725 | |||
| 1726 | (defun custom-default-update (field) | ||
| 1727 | "Update the content of FIELD." | ||
| 1728 | (let ((inhibit-point-motion-hooks t) | ||
| 1729 | (before-change-functions nil) | ||
| 1730 | (after-change-functions nil) | ||
| 1731 | (start (custom-field-start field)) | ||
| 1732 | (end (custom-field-end field)) | ||
| 1733 | (pos (point))) | ||
| 1734 | ;; Keep track of how many modified fields we have. | ||
| 1735 | (cond ((equal (custom-field-value field) (custom-field-original field)) | ||
| 1736 | (setq custom-modified-list (delq field custom-modified-list))) | ||
| 1737 | ((memq field custom-modified-list)) | ||
| 1738 | (t | ||
| 1739 | (setq custom-modified-list (cons field custom-modified-list)))) | ||
| 1740 | ;; Update the field. | ||
| 1741 | (goto-char end) | ||
| 1742 | (insert-before-markers " ") | ||
| 1743 | (delete-region start (1- end)) | ||
| 1744 | (goto-char start) | ||
| 1745 | (custom-field-insert field) | ||
| 1746 | (goto-char end) | ||
| 1747 | (delete-char 1) | ||
| 1748 | (goto-char pos) | ||
| 1749 | (and (<= start pos) | ||
| 1750 | (<= pos end) | ||
| 1751 | (custom-field-enter field)))) | ||
| 1752 | |||
| 1753 | ;;; Create Buffer: | ||
| 1754 | ;; | ||
| 1755 | ;; Public functions to create a customization buffer and to insert | ||
| 1756 | ;; various forms of text, fields, and buttons in it. | ||
| 1757 | |||
| 1758 | (defun customize () | ||
| 1759 | "Customize GNU Emacs. | ||
| 1760 | Create a *Customize* buffer with editable customization information | ||
| 1761 | about GNU Emacs." | ||
| 1762 | (interactive) | ||
| 1763 | (custom-buffer-create "*Customize*") | ||
| 1764 | (custom-reset-all)) | ||
| 1765 | |||
| 1766 | (defun custom-buffer-create (name &optional custom types set get save) | ||
| 1767 | "Create a customization buffer named NAME. | ||
| 1768 | If the optional argument CUSTOM is non-nil, use that as the custom declaration. | ||
| 1769 | If the optional argument TYPES is non-nil, use that as the local types. | ||
| 1770 | If the optional argument SET is non-nil, use that to set external data. | ||
| 1771 | If the optional argument GET is non-nil, use that to get external data. | ||
| 1772 | If the optional argument SAVE is non-nil, use that for saving changes." | ||
| 1773 | (switch-to-buffer name) | ||
| 1774 | (buffer-disable-undo (current-buffer)) | ||
| 1775 | (custom-mode) | ||
| 1776 | (setq custom-local-type-properties types) | ||
| 1777 | (if (null custom) | ||
| 1778 | () | ||
| 1779 | (make-local-variable 'custom-data) | ||
| 1780 | (setq custom-data custom)) | ||
| 1781 | (if (null set) | ||
| 1782 | () | ||
| 1783 | (make-local-variable 'custom-external-set) | ||
| 1784 | (setq custom-external-set set)) | ||
| 1785 | (if (null get) | ||
| 1786 | () | ||
| 1787 | (make-local-variable 'custom-external) | ||
| 1788 | (setq custom-external get)) | ||
| 1789 | (if (null save) | ||
| 1790 | () | ||
| 1791 | (make-local-variable 'custom-save) | ||
| 1792 | (setq custom-save save)) | ||
| 1793 | (let ((inhibit-point-motion-hooks t) | ||
| 1794 | (before-change-functions nil) | ||
| 1795 | (after-change-functions nil)) | ||
| 1796 | (erase-buffer) | ||
| 1797 | (insert "\n") | ||
| 1798 | (goto-char (point-min)) | ||
| 1799 | (custom-text-insert "This is a customization buffer.\n") | ||
| 1800 | (custom-help-insert "\n") | ||
| 1801 | (custom-help-button 'custom-forward-field) | ||
| 1802 | (custom-help-button 'custom-backward-field) | ||
| 1803 | (custom-help-button 'custom-enter-value) | ||
| 1804 | (custom-help-button 'custom-field-factory-reset) | ||
| 1805 | (custom-help-button 'custom-field-reset) | ||
| 1806 | (custom-help-button 'custom-field-apply) | ||
| 1807 | (custom-help-button 'custom-save-and-exit) | ||
| 1808 | (custom-help-button 'custom-toggle-documentation) | ||
| 1809 | (custom-help-insert "\nClick mouse-2 on any button to activate it.\n") | ||
| 1810 | (custom-text-insert "\n") | ||
| 1811 | (custom-insert custom-data 0) | ||
| 1812 | (goto-char (point-min)))) | ||
| 1813 | |||
| 1814 | (defun custom-insert (custom level) | ||
| 1815 | "Insert custom declaration CUSTOM in current buffer at level LEVEL." | ||
| 1816 | (if (stringp custom) | ||
| 1817 | (progn | ||
| 1818 | (custom-text-insert custom) | ||
| 1819 | nil) | ||
| 1820 | (and level (null (custom-property custom 'header)) | ||
| 1821 | (setq level nil)) | ||
| 1822 | (and level | ||
| 1823 | (> level 0) | ||
| 1824 | (custom-text-insert (concat "\n" (make-string level ?*) " "))) | ||
| 1825 | (let ((field (funcall (custom-property custom 'insert) custom level))) | ||
| 1826 | (custom-name-enter (custom-name custom) field) | ||
| 1827 | field))) | ||
| 1828 | |||
| 1829 | (defun custom-text-insert (text) | ||
| 1830 | "Insert TEXT in current buffer." | ||
| 1831 | (insert text)) | ||
| 1832 | |||
| 1833 | (defun custom-tag-insert (tag field &optional data) | ||
| 1834 | "Insert TAG for FIELD in current buffer." | ||
| 1835 | (let ((from (point))) | ||
| 1836 | (insert tag) | ||
| 1837 | (custom-category-set from (point) 'custom-button-properties) | ||
| 1838 | (custom-put-text-property from (point) 'custom-tag field) | ||
| 1839 | (if data | ||
| 1840 | (custom-add-text-properties from (point) (list 'custom-data data))))) | ||
| 1841 | |||
| 1842 | (defun custom-documentation-insert (custom &rest ignore) | ||
| 1843 | "Insert documentation from CUSTOM in current buffer." | ||
| 1844 | (let ((doc (custom-documentation custom))) | ||
| 1845 | (if (null doc) | ||
| 1846 | () | ||
| 1847 | (custom-help-insert "\n" doc)))) | ||
| 1848 | |||
| 1849 | (defun custom-help-insert (&rest args) | ||
| 1850 | "Insert ARGS as documentation text." | ||
| 1851 | (let ((from (point))) | ||
| 1852 | (apply 'insert args) | ||
| 1853 | (custom-category-set from (point) 'custom-documentation-properties))) | ||
| 1854 | |||
| 1855 | (defun custom-help-button (command) | ||
| 1856 | "Describe how to execute COMMAND." | ||
| 1857 | (let ((from (point))) | ||
| 1858 | (insert "`" (key-description (where-is-internal command nil t)) "'") | ||
| 1859 | (custom-set-text-properties from (point) | ||
| 1860 | (list 'face custom-button-face | ||
| 1861 | mouse-face custom-mouse-face | ||
| 1862 | 'custom-jump t ;Make TAB jump over it. | ||
| 1863 | 'custom-tag command | ||
| 1864 | 'start-open t | ||
| 1865 | 'end-open t)) | ||
| 1866 | (custom-category-set from (point) 'custom-documentation-properties)) | ||
| 1867 | (custom-help-insert ": " (custom-first-line (documentation command)) "\n")) | ||
| 1868 | |||
| 1869 | ;;; Mode: | ||
| 1870 | ;; | ||
| 1871 | ;; The Customization major mode and interactive commands. | ||
| 1872 | |||
| 1873 | (defvar custom-mode-map nil | ||
| 1874 | "Keymap for Custom Mode.") | ||
| 1875 | (if custom-mode-map | ||
| 1876 | nil | ||
| 1877 | (setq custom-mode-map (make-sparse-keymap)) | ||
| 1878 | (define-key custom-mode-map (if (string-match "XEmacs" emacs-version) [button2] [mouse-2]) 'custom-push-button) | ||
| 1879 | (define-key custom-mode-map "\t" 'custom-forward-field) | ||
| 1880 | (define-key custom-mode-map "\M-\t" 'custom-backward-field) | ||
| 1881 | (define-key custom-mode-map "\r" 'custom-enter-value) | ||
| 1882 | (define-key custom-mode-map "\C-k" 'custom-kill-line) | ||
| 1883 | (define-key custom-mode-map "\C-c\C-r" 'custom-field-reset) | ||
| 1884 | (define-key custom-mode-map "\C-c\M-\C-r" 'custom-reset-all) | ||
| 1885 | (define-key custom-mode-map "\C-c\C-z" 'custom-field-factory-reset) | ||
| 1886 | (define-key custom-mode-map "\C-c\M-\C-z" 'custom-factory-reset-all) | ||
| 1887 | (define-key custom-mode-map "\C-c\C-a" 'custom-field-apply) | ||
| 1888 | (define-key custom-mode-map "\C-c\M-\C-a" 'custom-apply-all) | ||
| 1889 | (define-key custom-mode-map "\C-c\C-c" 'custom-save-and-exit) | ||
| 1890 | (define-key custom-mode-map "\C-c\C-d" 'custom-toggle-documentation)) | ||
| 1891 | |||
| 1892 | ;; C-c keymap ideas: C-a field-beginning, C-e field-end, C-f | ||
| 1893 | ;; forward-field, C-b backward-field, C-n next-field, C-p | ||
| 1894 | ;; previous-field, ? describe-field. | ||
| 1895 | |||
| 1896 | (defun custom-mode () | ||
| 1897 | "Major mode for doing customizations. | ||
| 1898 | |||
| 1899 | \\{custom-mode-map}" | ||
| 1900 | (kill-all-local-variables) | ||
| 1901 | (setq major-mode 'custom-mode | ||
| 1902 | mode-name "Custom") | ||
| 1903 | (use-local-map custom-mode-map) | ||
| 1904 | (make-local-variable 'before-change-functions) | ||
| 1905 | (setq before-change-functions '(custom-before-change)) | ||
| 1906 | (make-local-variable 'after-change-functions) | ||
| 1907 | (setq after-change-functions '(custom-after-change)) | ||
| 1908 | (if (not (fboundp 'make-local-hook)) | ||
| 1909 | ;; Emacs 19.28 and earlier. | ||
| 1910 | (add-hook 'post-command-hook | ||
| 1911 | (lambda () | ||
| 1912 | (if (eq major-mode 'custom-mode) | ||
| 1913 | (custom-post-command)))) | ||
| 1914 | ;; Emacs 19.29. | ||
| 1915 | (make-local-hook 'post-command-hook) | ||
| 1916 | (add-hook 'post-command-hook 'custom-post-command nil t))) | ||
| 1917 | |||
| 1918 | (defun custom-forward-field (arg) | ||
| 1919 | "Move point to the next field or button. | ||
| 1920 | With optional ARG, move across that many fields." | ||
| 1921 | (interactive "p") | ||
| 1922 | (while (> arg 0) | ||
| 1923 | (let ((next (if (get-text-property (point) 'custom-tag) | ||
| 1924 | (next-single-property-change (point) 'custom-tag) | ||
| 1925 | (point)))) | ||
| 1926 | (setq next (or (next-single-property-change next 'custom-tag) | ||
| 1927 | (next-single-property-change (point-min) 'custom-tag))) | ||
| 1928 | (if next | ||
| 1929 | (goto-char next) | ||
| 1930 | (error "No customization fields in this buffer."))) | ||
| 1931 | (or (get-text-property (point) 'custom-jump) | ||
| 1932 | (setq arg (1- arg)))) | ||
| 1933 | (while (< arg 0) | ||
| 1934 | (let ((previous (if (get-text-property (1- (point)) 'custom-tag) | ||
| 1935 | (previous-single-property-change (point) 'custom-tag) | ||
| 1936 | (point)))) | ||
| 1937 | (setq previous | ||
| 1938 | (or (previous-single-property-change previous 'custom-tag) | ||
| 1939 | (previous-single-property-change (point-max) 'custom-tag))) | ||
| 1940 | (if previous | ||
| 1941 | (goto-char previous) | ||
| 1942 | (error "No customization fields in this buffer."))) | ||
| 1943 | (or (get-text-property (1- (point)) 'custom-jump) | ||
| 1944 | (setq arg (1+ arg))))) | ||
| 1945 | |||
| 1946 | (defun custom-backward-field (arg) | ||
| 1947 | "Move point to the previous field or button. | ||
| 1948 | With optional ARG, move across that many fields." | ||
| 1949 | (interactive "p") | ||
| 1950 | (custom-forward-field (- arg))) | ||
| 1951 | |||
| 1952 | (defun custom-toggle-documentation (&optional arg) | ||
| 1953 | "Toggle display of documentation text. | ||
| 1954 | If the optional argument is non-nil, show text iff the argument is positive." | ||
| 1955 | (interactive "P") | ||
| 1956 | (let ((hide (or (and (null arg) | ||
| 1957 | (null (custom-category-get | ||
| 1958 | 'custom-documentation-properties 'invisible))) | ||
| 1959 | (<= (prefix-numeric-value arg) 0)))) | ||
| 1960 | (custom-category-put 'custom-documentation-properties 'invisible hide) | ||
| 1961 | (custom-category-put 'custom-documentation-properties intangible hide)) | ||
| 1962 | (redraw-display)) | ||
| 1963 | |||
| 1964 | (defun custom-enter-value (field data) | ||
| 1965 | "Enter value for current customization field or push button." | ||
| 1966 | (interactive (list (get-text-property (point) 'custom-tag) | ||
| 1967 | (get-text-property (point) 'custom-data))) | ||
| 1968 | (cond (data | ||
| 1969 | (funcall field data)) | ||
| 1970 | ((eq field 'custom-enter-value) | ||
| 1971 | (error "Don't be silly")) | ||
| 1972 | ((and (symbolp field) (fboundp field)) | ||
| 1973 | (call-interactively field)) | ||
| 1974 | (field | ||
| 1975 | (custom-field-query field)) | ||
| 1976 | (t | ||
| 1977 | (message "Nothing to enter here")))) | ||
| 1978 | |||
| 1979 | (defun custom-kill-line () | ||
| 1980 | "Kill to end of field or end of line, whichever is first." | ||
| 1981 | (interactive) | ||
| 1982 | (let ((field (get-text-property (point) 'custom-field)) | ||
| 1983 | (newline (save-excursion (search-forward "\n"))) | ||
| 1984 | (next (next-single-property-change (point) 'custom-field))) | ||
| 1985 | (if (and field (> newline next)) | ||
| 1986 | (kill-region (point) next) | ||
| 1987 | (call-interactively 'kill-line)))) | ||
| 1988 | |||
| 1989 | (defun custom-push-button (event) | ||
| 1990 | "Activate button below mouse pointer." | ||
| 1991 | (interactive "@e") | ||
| 1992 | (let* ((pos (event-point event)) | ||
| 1993 | (field (get-text-property pos 'custom-field)) | ||
| 1994 | (tag (get-text-property pos 'custom-tag)) | ||
| 1995 | (data (get-text-property pos 'custom-data))) | ||
| 1996 | (cond (data | ||
| 1997 | (funcall tag data)) | ||
| 1998 | ((and (symbolp tag) (fboundp tag)) | ||
| 1999 | (call-interactively tag)) | ||
| 2000 | (field | ||
| 2001 | (call-interactively (lookup-key global-map (this-command-keys)))) | ||
| 2002 | (tag | ||
| 2003 | (custom-enter-value tag data)) | ||
| 2004 | (t | ||
| 2005 | (error "Nothing to click on here."))))) | ||
| 2006 | |||
| 2007 | (defun custom-reset-all () | ||
| 2008 | "Undo any changes since the last apply in all fields." | ||
| 2009 | (interactive (and custom-modified-list | ||
| 2010 | (not (y-or-n-p "Discard all changes? ")) | ||
| 2011 | (error "Reset aborted"))) | ||
| 2012 | (let ((all custom-name-fields) | ||
| 2013 | current field) | ||
| 2014 | (while all | ||
| 2015 | (setq current (car all) | ||
| 2016 | field (cdr current) | ||
| 2017 | all (cdr all)) | ||
| 2018 | (custom-field-reset field)))) | ||
| 2019 | |||
| 2020 | (defun custom-field-reset (field) | ||
| 2021 | "Undo any changes in FIELD since the last apply." | ||
| 2022 | (interactive (list (or (get-text-property (point) 'custom-field) | ||
| 2023 | (get-text-property (point) 'custom-tag)))) | ||
| 2024 | (if (arrayp field) | ||
| 2025 | (let* ((custom (custom-field-custom field)) | ||
| 2026 | (name (custom-name custom))) | ||
| 2027 | (save-excursion | ||
| 2028 | (if name | ||
| 2029 | (custom-field-original-set | ||
| 2030 | field (car (custom-import custom (custom-external name))))) | ||
| 2031 | (if (not (custom-valid custom (custom-field-original field))) | ||
| 2032 | (error "This field cannot be reset alone") | ||
| 2033 | (funcall (custom-property custom 'reset) field) | ||
| 2034 | (funcall (custom-property custom 'synchronize) field)))))) | ||
| 2035 | |||
| 2036 | (defun custom-factory-reset-all () | ||
| 2037 | "Reset all field to their default values." | ||
| 2038 | (interactive (and custom-modified-list | ||
| 2039 | (not (y-or-n-p "Discard all changes? ")) | ||
| 2040 | (error "Reset aborted"))) | ||
| 2041 | (let ((all custom-name-fields) | ||
| 2042 | field) | ||
| 2043 | (while all | ||
| 2044 | (setq field (cdr (car all)) | ||
| 2045 | all (cdr all)) | ||
| 2046 | (custom-field-factory-reset field)))) | ||
| 2047 | |||
| 2048 | (defun custom-field-factory-reset (field) | ||
| 2049 | "Reset FIELD to its default value." | ||
| 2050 | (interactive (list (or (get-text-property (point) 'custom-field) | ||
| 2051 | (get-text-property (point) 'custom-tag)))) | ||
| 2052 | (if (arrayp field) | ||
| 2053 | (save-excursion | ||
| 2054 | (funcall (custom-property (custom-field-custom field) 'factory-reset) | ||
| 2055 | field)))) | ||
| 2056 | |||
| 2057 | (defun custom-apply-all () | ||
| 2058 | "Apply any changes since the last reset in all fields." | ||
| 2059 | (interactive (if custom-modified-list | ||
| 2060 | nil | ||
| 2061 | (error "No changes to apply."))) | ||
| 2062 | (custom-field-parse custom-field-last) | ||
| 2063 | (let ((all custom-name-fields) | ||
| 2064 | field) | ||
| 2065 | (while all | ||
| 2066 | (setq field (cdr (car all)) | ||
| 2067 | all (cdr all)) | ||
| 2068 | (let ((error (custom-field-validate (custom-field-custom field) field))) | ||
| 2069 | (if (null error) | ||
| 2070 | () | ||
| 2071 | (goto-char (car error)) | ||
| 2072 | (error (cdr error)))))) | ||
| 2073 | (let ((all custom-name-fields) | ||
| 2074 | field) | ||
| 2075 | (while all | ||
| 2076 | (setq field (cdr (car all)) | ||
| 2077 | all (cdr all)) | ||
| 2078 | (custom-field-apply field)))) | ||
| 2079 | |||
| 2080 | (defun custom-field-apply (field) | ||
| 2081 | "Apply any changes in FIELD since the last apply." | ||
| 2082 | (interactive (list (or (get-text-property (point) 'custom-field) | ||
| 2083 | (get-text-property (point) 'custom-tag)))) | ||
| 2084 | (custom-field-parse custom-field-last) | ||
| 2085 | (if (arrayp field) | ||
| 2086 | (let* ((custom (custom-field-custom field)) | ||
| 2087 | (error (custom-field-validate custom field))) | ||
| 2088 | (if error | ||
| 2089 | (error (cdr error))) | ||
| 2090 | (funcall (custom-property custom 'apply) field)))) | ||
| 2091 | |||
| 2092 | (defun custom-toggle-hide (&rest ignore) | ||
| 2093 | "Hide or show entry." | ||
| 2094 | (interactive) | ||
| 2095 | (error "This button is not yet implemented")) | ||
| 2096 | |||
| 2097 | (defun custom-save-and-exit () | ||
| 2098 | "Save and exit customization buffer." | ||
| 2099 | (interactive "@") | ||
| 2100 | (save-excursion | ||
| 2101 | (funcall custom-save)) | ||
| 2102 | (kill-buffer (current-buffer))) | ||
| 2103 | |||
| 2104 | (defun custom-save () | ||
| 2105 | "Save customization information." | ||
| 2106 | (interactive) | ||
| 2107 | (custom-apply-all) | ||
| 2108 | (let ((new custom-name-fields)) | ||
| 2109 | (set-buffer (find-file-noselect custom-file)) | ||
| 2110 | (goto-char (point-min)) | ||
| 2111 | (save-excursion | ||
| 2112 | (let ((old (condition-case nil | ||
| 2113 | (read (current-buffer)) | ||
| 2114 | (end-of-file (append '(setq custom-dummy | ||
| 2115 | 'custom-dummy) ()))))) | ||
| 2116 | (or (eq (car old) 'setq) | ||
| 2117 | (error "Invalid customization file: %s" custom-file)) | ||
| 2118 | (while new | ||
| 2119 | (let* ((field (cdr (car new))) | ||
| 2120 | (custom (custom-field-custom field)) | ||
| 2121 | (value (custom-field-original field)) | ||
| 2122 | (default (car (custom-import custom (custom-default custom)))) | ||
| 2123 | (name (car (car new)))) | ||
| 2124 | (setq new (cdr new)) | ||
| 2125 | (custom-assert '(eq name (custom-name custom))) | ||
| 2126 | (if (equal default value) | ||
| 2127 | (setcdr old (custom-plist-delq name (cdr old))) | ||
| 2128 | (setcdr old (plist-put (cdr old) name | ||
| 2129 | (car (custom-quote custom value))))))) | ||
| 2130 | (erase-buffer) | ||
| 2131 | (insert ";; " custom-file "\ | ||
| 2132 | --- Automatically generated customization information. | ||
| 2133 | ;; | ||
| 2134 | ;; Feel free to edit by hand, but the entire content should consist of | ||
| 2135 | ;; a single setq. Any other lisp expressions will confuse the | ||
| 2136 | ;; automatic configuration engine. | ||
| 2137 | |||
| 2138 | \(setq ") | ||
| 2139 | (setq old (cdr old)) | ||
| 2140 | (while old | ||
| 2141 | (prin1 (car old) (current-buffer)) | ||
| 2142 | (setq old (cdr old)) | ||
| 2143 | (insert " ") | ||
| 2144 | (pp (car old) (current-buffer)) | ||
| 2145 | (setq old (cdr old)) | ||
| 2146 | (if old (insert "\n "))) | ||
| 2147 | (insert ")\n") | ||
| 2148 | (save-buffer) | ||
| 2149 | (kill-buffer (current-buffer)))))) | ||
| 2150 | |||
| 2151 | (defun custom-load () | ||
| 2152 | "Save customization information." | ||
| 2153 | (interactive (and custom-modified-list | ||
| 2154 | (not (equal (list (custom-name-field 'custom-file)) | ||
| 2155 | custom-modified-list)) | ||
| 2156 | (not (y-or-n-p "Discard all changes? ")) | ||
| 2157 | (error "Load aborted"))) | ||
| 2158 | (load-file (custom-name-value 'custom-file)) | ||
| 2159 | (custom-reset-all)) | ||
| 2160 | |||
| 2161 | ;;; Field Editing: | ||
| 2162 | ;; | ||
| 2163 | ;; Various internal functions for implementing the direct editing of | ||
| 2164 | ;; fields in the customization buffer. | ||
| 2165 | |||
| 2166 | (defun custom-field-untouch (field) | ||
| 2167 | ;; Remove FIELD and its children from `custom-modified-list'. | ||
| 2168 | (setq custom-modified-list (delq field custom-modified-list)) | ||
| 2169 | (if (arrayp field) | ||
| 2170 | (let ((value (custom-field-value field))) | ||
| 2171 | (cond ((null (custom-data (custom-field-custom field)))) | ||
| 2172 | ((arrayp value) | ||
| 2173 | (custom-field-untouch value)) | ||
| 2174 | ((listp value) | ||
| 2175 | (mapcar 'custom-field-untouch value)))))) | ||
| 2176 | |||
| 2177 | |||
| 2178 | (defun custom-field-insert (field) | ||
| 2179 | ;; Insert editing FIELD in current buffer. | ||
| 2180 | (let ((from (point)) | ||
| 2181 | (custom (custom-field-custom field)) | ||
| 2182 | (value (custom-field-value field))) | ||
| 2183 | (insert (custom-write custom value)) | ||
| 2184 | (insert-char (custom-padding custom) | ||
| 2185 | (- (custom-width custom) (- (point) from))) | ||
| 2186 | (custom-field-move field from (point)) | ||
| 2187 | (custom-set-text-properties | ||
| 2188 | from (point) | ||
| 2189 | (list 'custom-field field | ||
| 2190 | 'custom-tag field | ||
| 2191 | 'face (custom-field-face field) | ||
| 2192 | 'start-open t | ||
| 2193 | 'end-open t)))) | ||
| 2194 | |||
| 2195 | (defun custom-field-read (field) | ||
| 2196 | ;; Read the screen content of FIELD. | ||
| 2197 | (custom-read (custom-field-custom field) | ||
| 2198 | (custom-buffer-substring-no-properties (custom-field-start field) | ||
| 2199 | (custom-field-end field)))) | ||
| 2200 | |||
| 2201 | ;; Fields are shown in a special `active' face when point is inside | ||
| 2202 | ;; it. You activate the field by moving point inside (entering) it | ||
| 2203 | ;; and deactivate the field by moving point outside (leaving) it. | ||
| 2204 | |||
| 2205 | (defun custom-field-leave (field) | ||
| 2206 | ;; Deactivate FIELD. | ||
| 2207 | (let ((before-change-functions nil) | ||
| 2208 | (after-change-functions nil)) | ||
| 2209 | (custom-put-text-property (custom-field-start field) (custom-field-end field) | ||
| 2210 | 'face (custom-field-face field)))) | ||
| 2211 | |||
| 2212 | (defun custom-field-enter (field) | ||
| 2213 | ;; Activate FIELD. | ||
| 2214 | (let* ((start (custom-field-start field)) | ||
| 2215 | (end (custom-field-end field)) | ||
| 2216 | (custom (custom-field-custom field)) | ||
| 2217 | (padding (custom-padding custom)) | ||
| 2218 | (before-change-functions nil) | ||
| 2219 | (after-change-functions nil)) | ||
| 2220 | (or (eq this-command 'self-insert-command) | ||
| 2221 | (let ((pos end)) | ||
| 2222 | (while (and (< start pos) | ||
| 2223 | (eq (char-after (1- pos)) padding)) | ||
| 2224 | (setq pos (1- pos))) | ||
| 2225 | (if (< pos (point)) | ||
| 2226 | (goto-char pos)))) | ||
| 2227 | (custom-put-text-property start end 'face custom-field-active-face))) | ||
| 2228 | |||
| 2229 | (defun custom-field-resize (field) | ||
| 2230 | ;; Resize FIELD after change. | ||
| 2231 | (let* ((custom (custom-field-custom field)) | ||
| 2232 | (begin (custom-field-start field)) | ||
| 2233 | (end (custom-field-end field)) | ||
| 2234 | (pos (point)) | ||
| 2235 | (padding (custom-padding custom)) | ||
| 2236 | (width (custom-width custom)) | ||
| 2237 | (size (- end begin))) | ||
| 2238 | (cond ((< size width) | ||
| 2239 | (goto-char end) | ||
| 2240 | (if (fboundp 'insert-before-markers-and-inherit) | ||
| 2241 | ;; Emacs 19. | ||
| 2242 | (insert-before-markers-and-inherit | ||
| 2243 | (make-string (- width size) padding)) | ||
| 2244 | ;; XEmacs: BUG: Doesn't work! | ||
| 2245 | (insert-before-markers (make-string (- width size) padding))) | ||
| 2246 | (goto-char pos)) | ||
| 2247 | ((> size width) | ||
| 2248 | (let ((start (if (and (< (+ begin width) pos) (<= pos end)) | ||
| 2249 | pos | ||
| 2250 | (+ begin width)))) | ||
| 2251 | (goto-char end) | ||
| 2252 | (while (and (< start (point)) (= (preceding-char) padding)) | ||
| 2253 | (backward-delete-char 1)) | ||
| 2254 | (goto-char pos)))))) | ||
| 2255 | |||
| 2256 | (defvar custom-field-changed nil) | ||
| 2257 | ;; List of fields changed on the screen but whose VALUE attribute has | ||
| 2258 | ;; not yet been updated to reflect the new screen content. | ||
| 2259 | (make-variable-buffer-local 'custom-field-changed) | ||
| 2260 | |||
| 2261 | (defun custom-field-parse (field) | ||
| 2262 | ;; Parse FIELD content iff changed. | ||
| 2263 | (if (memq field custom-field-changed) | ||
| 2264 | (progn | ||
| 2265 | (setq custom-field-changed (delq field custom-field-changed)) | ||
| 2266 | (custom-field-value-set field (custom-field-read field)) | ||
| 2267 | (custom-field-update field)))) | ||
| 2268 | |||
| 2269 | (defun custom-post-command () | ||
| 2270 | ;; Keep track of their active field. | ||
| 2271 | (custom-assert '(eq major-mode 'custom-mode)) | ||
| 2272 | (let ((field (custom-field-property (point)))) | ||
| 2273 | (if (eq field custom-field-last) | ||
| 2274 | (if (memq field custom-field-changed) | ||
| 2275 | (custom-field-resize field)) | ||
| 2276 | (custom-field-parse custom-field-last) | ||
| 2277 | (if custom-field-last | ||
| 2278 | (custom-field-leave custom-field-last)) | ||
| 2279 | (if field | ||
| 2280 | (custom-field-enter field)) | ||
| 2281 | (setq custom-field-last field)) | ||
| 2282 | (set-buffer-modified-p (or custom-modified-list | ||
| 2283 | custom-field-changed)))) | ||
| 2284 | |||
| 2285 | (defvar custom-field-was nil) | ||
| 2286 | ;; The custom data before the change. | ||
| 2287 | (make-variable-buffer-local 'custom-field-was) | ||
| 2288 | |||
| 2289 | (defun custom-before-change (begin end) | ||
| 2290 | ;; Check that we the modification is allowed. | ||
| 2291 | (if (not (eq major-mode 'custom-mode)) | ||
| 2292 | (message "Aargh! Why is custom-before-change called here?") | ||
| 2293 | (let ((from (custom-field-property begin)) | ||
| 2294 | (to (custom-field-property end))) | ||
| 2295 | (cond ((or (null from) (null to)) | ||
| 2296 | (error "You can only modify the fields")) | ||
| 2297 | ((not (eq from to)) | ||
| 2298 | (error "Changes must be limited to a single field.")) | ||
| 2299 | (t | ||
| 2300 | (setq custom-field-was from)))))) | ||
| 2301 | |||
| 2302 | (defun custom-after-change (begin end length) | ||
| 2303 | ;; Keep track of field content. | ||
| 2304 | (if (not (eq major-mode 'custom-mode)) | ||
| 2305 | (message "Aargh! Why is custom-after-change called here?") | ||
| 2306 | (let ((field custom-field-was)) | ||
| 2307 | (custom-assert '(prog1 field (setq custom-field-was nil))) | ||
| 2308 | ;; Prevent mixing fields properties. | ||
| 2309 | (custom-put-text-property begin end 'custom-field field) | ||
| 2310 | ;; Update the field after modification. | ||
| 2311 | (if (eq (custom-field-property begin) field) | ||
| 2312 | (let ((field-end (custom-field-end field))) | ||
| 2313 | (if (> end field-end) | ||
| 2314 | (set-marker field-end end)) | ||
| 2315 | (add-to-list 'custom-field-changed field)) | ||
| 2316 | ;; We deleted the entire field, reinsert it. | ||
| 2317 | (custom-assert '(eq begin end)) | ||
| 2318 | (save-excursion | ||
| 2319 | (goto-char begin) | ||
| 2320 | (custom-field-value-set field | ||
| 2321 | (custom-read (custom-field-custom field) "")) | ||
| 2322 | (custom-field-insert field)))))) | ||
| 2323 | |||
| 2324 | (defun custom-field-property (pos) | ||
| 2325 | ;; The `custom-field' text property valid for POS. | ||
| 2326 | (or (get-text-property pos 'custom-field) | ||
| 2327 | (and (not (eq pos (point-min))) | ||
| 2328 | (get-text-property (1- pos) 'custom-field)))) | ||
| 2329 | |||
| 2330 | ;;; Generic Utilities: | ||
| 2331 | ;; | ||
| 2332 | ;; Some utility functions that are not really specific to custom. | ||
| 2333 | |||
| 2334 | (defun custom-assert (expr) | ||
| 2335 | "Assert that EXPR evaluates to non-nil at this point" | ||
| 2336 | (or (eval expr) | ||
| 2337 | (error "Assertion failed: %S" expr))) | ||
| 2338 | |||
| 2339 | (defun custom-first-line (string) | ||
| 2340 | "Return the part of STRING before the first newline." | ||
| 2341 | (let ((pos 0) | ||
| 2342 | (len (length string))) | ||
| 2343 | (while (and (< pos len) (not (eq (aref string pos) ?\n))) | ||
| 2344 | (setq pos (1+ pos))) | ||
| 2345 | (if (eq pos len) | ||
| 2346 | string | ||
| 2347 | (substring string 0 pos)))) | ||
| 2348 | |||
| 2349 | (defun custom-insert-before (list old new) | ||
| 2350 | "In LIST insert before OLD a NEW element." | ||
| 2351 | (cond ((null list) | ||
| 2352 | (list new)) | ||
| 2353 | ((null old) | ||
| 2354 | (nconc list (list new))) | ||
| 2355 | ((eq old (car list)) | ||
| 2356 | (cons new list)) | ||
| 2357 | (t | 237 | (t |
| 2358 | (let ((list list)) | 238 | (error "Unknown keyword %s" symbol)))) |
| 2359 | (while (not (eq old (car (cdr list)))) | 239 | |
| 2360 | (setq list (cdr list)) | 240 | (defun custom-add-option (symbol option) |
| 2361 | (custom-assert '(cdr list))) | 241 | "To the variable SYMBOL add OPTION. |
| 2362 | (setcdr list (cons new (cdr list)))) | 242 | |
| 2363 | list))) | 243 | If SYMBOL is a hook variable, OPTION should be a hook member. |
| 2364 | 244 | For other types variables, the effect is undefined." | |
| 2365 | (defun custom-strip-padding (string padding) | 245 | (let ((options (get symbol 'custom-options))) |
| 2366 | "Remove padding from STRING." | 246 | (unless (member option options) |
| 2367 | (let ((regexp (concat (regexp-quote (char-to-string padding)) "+"))) | 247 | (put symbol 'custom-options (cons option options))))) |
| 2368 | (while (string-match regexp string) | 248 | |
| 2369 | (setq string (concat (substring string 0 (match-beginning 0)) | 249 | (defun custom-add-link (symbol widget) |
| 2370 | (substring string (match-end 0)))))) | 250 | "To the custom option SYMBOL add the link WIDGET." |
| 2371 | string) | 251 | (let ((links (get symbol 'custom-links))) |
| 2372 | 252 | (unless (member widget links) | |
| 2373 | (defun custom-plist-memq (prop plist) | 253 | (put symbol 'custom-links (cons widget links))))) |
| 2374 | "Return non-nil if PROP is a property of PLIST. Comparison done with EQ." | 254 | |
| 2375 | (let (result) | 255 | (defun custom-add-load (symbol load) |
| 2376 | (while plist | 256 | "To the custom option SYMBOL add the dependency LOAD. |
| 2377 | (if (eq (car plist) prop) | 257 | LOAD should be either a library file name, or a feature name." |
| 2378 | (setq result plist | 258 | (let ((loads (get symbol 'custom-loads))) |
| 2379 | plist nil) | 259 | (unless (member load loads) |
| 2380 | (setq plist (cdr (cdr plist))))) | 260 | (put symbol 'custom-loads (cons load loads))))) |
| 2381 | result)) | 261 | |
| 2382 | 262 | ;;; Initializing. | |
| 2383 | (defun custom-plist-delq (prop plist) | 263 | |
| 2384 | "Delete property PROP from property list PLIST." | 264 | (defun custom-set-variables (&rest args) |
| 2385 | (while (eq (car plist) prop) | 265 | "Initialize variables according to user preferences. |
| 2386 | (setq plist (cdr (cdr plist)))) | 266 | |
| 2387 | (let ((list plist) | 267 | The arguments should be a list where each entry has the form: |
| 2388 | (next (cdr (cdr plist)))) | 268 | |
| 2389 | (while next | 269 | (SYMBOL VALUE [NOW]) |
| 2390 | (if (eq (car next) prop) | 270 | |
| 2391 | (progn | 271 | The unevaluated VALUE is stored as the saved value for SYMBOL. |
| 2392 | (setq next (cdr (cdr next))) | 272 | If NOW is present and non-nil, VALUE is also evaluated and bound as |
| 2393 | (setcdr (cdr list) next)) | 273 | the default value for the SYMBOL." |
| 2394 | (setq list next | 274 | (while args |
| 2395 | next (cdr (cdr next)))))) | 275 | (let ((entry (car args))) |
| 2396 | plist) | 276 | (if (listp entry) |
| 277 | (let ((symbol (nth 0 entry)) | ||
| 278 | (value (nth 1 entry)) | ||
| 279 | (now (nth 2 entry))) | ||
| 280 | (put symbol 'saved-value (list value)) | ||
| 281 | (when now | ||
| 282 | (put symbol 'force-value t) | ||
| 283 | (set-default symbol (eval value))) | ||
| 284 | (setq args (cdr args))) | ||
| 285 | ;; Old format, a plist of SYMBOL VALUE pairs. | ||
| 286 | (let ((symbol (nth 0 args)) | ||
| 287 | (value (nth 1 args))) | ||
| 288 | (put symbol 'saved-value (list value))) | ||
| 289 | (setq args (cdr (cdr args))))))) | ||
| 290 | |||
| 291 | ;;; Meta Customization | ||
| 292 | |||
| 293 | (defcustom custom-define-hook nil | ||
| 294 | "Hook called after defining each customize option." | ||
| 295 | :group 'customize | ||
| 296 | :type 'hook) | ||
| 297 | |||
| 298 | ;;; Menu support | ||
| 299 | |||
| 300 | (defconst custom-help-menu | ||
| 301 | `("Customize" | ||
| 302 | ,(if (string-match "XEmacs" emacs-version) | ||
| 303 | '("Emacs" :filter (lambda (&rest junk) | ||
| 304 | (cdr (custom-menu-create 'emacs)))) | ||
| 305 | ["Update menu..." custom-menu-update t]) | ||
| 306 | ["Group..." customize t] | ||
| 307 | ["Variable..." customize-variable t] | ||
| 308 | ["Face..." customize-face t] | ||
| 309 | ["Saved..." customize-customized t] | ||
| 310 | ["Apropos..." customize-apropos t]) | ||
| 311 | "Customize menu") | ||
| 312 | |||
| 313 | (defun custom-menu-reset () | ||
| 314 | "Reset customize menu." | ||
| 315 | (remove-hook 'custom-define-hook 'custom-menu-reset) | ||
| 316 | (if (string-match "XEmacs" emacs-version) | ||
| 317 | (when (fboundp 'add-submenu) | ||
| 318 | (add-submenu '("Options") custom-help-menu)) | ||
| 319 | (define-key global-map [menu-bar help-menu customize-menu] | ||
| 320 | (cons (car custom-help-menu) | ||
| 321 | (easy-menu-create-keymaps (car custom-help-menu) | ||
| 322 | (cdr custom-help-menu)))))) | ||
| 2397 | 323 | ||
| 2398 | ;;; Meta Customization: | 324 | (if (string-match "XEmacs" emacs-version) |
| 2399 | 325 | (autoload 'custom-menu-create "cus-edit") | |
| 2400 | (custom-declare '() | 326 | (custom-menu-reset)) |
| 2401 | '((tag . "Meta Customization") | ||
| 2402 | (doc . "Customization of the customization support.") | ||
| 2403 | (type . group) | ||
| 2404 | (data ((type . face-doc)) | ||
| 2405 | ((tag . "Button Face") | ||
| 2406 | (default . bold) | ||
| 2407 | (doc . "Face used for tags in customization buffers.") | ||
| 2408 | (name . custom-button-face) | ||
| 2409 | (synchronize . (lambda (f) | ||
| 2410 | (custom-category-put 'custom-button-properties | ||
| 2411 | 'face custom-button-face))) | ||
| 2412 | (type . face)) | ||
| 2413 | ((tag . "Mouse Face") | ||
| 2414 | (default . highlight) | ||
| 2415 | (doc . "\ | ||
| 2416 | Face used when mouse is above a button in customization buffers.") | ||
| 2417 | (name . custom-mouse-face) | ||
| 2418 | (synchronize . (lambda (f) | ||
| 2419 | (custom-category-put 'custom-button-properties | ||
| 2420 | mouse-face | ||
| 2421 | custom-mouse-face))) | ||
| 2422 | (type . face)) | ||
| 2423 | ((tag . "Field Face") | ||
| 2424 | (default . italic) | ||
| 2425 | (doc . "Face used for customization fields.") | ||
| 2426 | (name . custom-field-face) | ||
| 2427 | (type . face)) | ||
| 2428 | ((tag . "Uninitialized Face") | ||
| 2429 | (default . modeline) | ||
| 2430 | (doc . "Face used for uninitialized customization fields.") | ||
| 2431 | (name . custom-field-uninitialized-face) | ||
| 2432 | (type . face)) | ||
| 2433 | ((tag . "Invalid Face") | ||
| 2434 | (default . highlight) | ||
| 2435 | (doc . "\ | ||
| 2436 | Face used for customization fields containing invalid data.") | ||
| 2437 | (name . custom-field-invalid-face) | ||
| 2438 | (type . face)) | ||
| 2439 | ((tag . "Modified Face") | ||
| 2440 | (default . bold-italic) | ||
| 2441 | (doc . "Face used for modified customization fields.") | ||
| 2442 | (name . custom-field-modified-face) | ||
| 2443 | (type . face)) | ||
| 2444 | ((tag . "Active Face") | ||
| 2445 | (default . underline) | ||
| 2446 | (doc . "\ | ||
| 2447 | Face used for customization fields while they are being edited.") | ||
| 2448 | (name . custom-field-active-face) | ||
| 2449 | (type . face))))) | ||
| 2450 | |||
| 2451 | ;; custom.el uses two categories. | ||
| 2452 | |||
| 2453 | (custom-category-create 'custom-documentation-properties) | ||
| 2454 | (custom-category-put 'custom-documentation-properties rear-nonsticky t) | ||
| 2455 | |||
| 2456 | (custom-category-create 'custom-button-properties) | ||
| 2457 | (custom-category-put 'custom-button-properties 'face custom-button-face) | ||
| 2458 | (custom-category-put 'custom-button-properties mouse-face custom-mouse-face) | ||
| 2459 | (custom-category-put 'custom-button-properties rear-nonsticky t) | ||
| 2460 | |||
| 2461 | (custom-category-create 'custom-hidden-properties) | ||
| 2462 | (custom-category-put 'custom-hidden-properties 'invisible | ||
| 2463 | (not (string-match "XEmacs" emacs-version))) | ||
| 2464 | (custom-category-put 'custom-hidden-properties intangible t) | ||
| 2465 | 327 | ||
| 2466 | (and init-file-user ; Don't load any init file if -q was used. | 328 | ;;; The End. |
| 2467 | (file-readable-p custom-file) | ||
| 2468 | (load-file custom-file)) | ||
| 2469 | 329 | ||
| 2470 | (provide 'custom) | 330 | (provide 'custom) |
| 2471 | 331 | ||
| 2472 | ;;; custom.el ends here | 332 | ;; custom.el ends here |
diff --git a/lisp/wid-browse.el b/lisp/wid-browse.el new file mode 100644 index 00000000000..d90836c05c4 --- /dev/null +++ b/lisp/wid-browse.el | |||
| @@ -0,0 +1,232 @@ | |||
| 1 | ;;; wid-browse.el --- Functions for browsing widgets. | ||
| 2 | ;; | ||
| 3 | ;; Copyright (C) 1997 Free Software Foundation, Inc. | ||
| 4 | ;; | ||
| 5 | ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> | ||
| 6 | ;; Keywords: extensions | ||
| 7 | ;; Version: 1.71 | ||
| 8 | ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ | ||
| 9 | |||
| 10 | ;;; Commentary: | ||
| 11 | ;; | ||
| 12 | ;; Widget browser. See `widget.el'. | ||
| 13 | |||
| 14 | ;;; Code: | ||
| 15 | |||
| 16 | (require 'easymenu) | ||
| 17 | (require 'custom) | ||
| 18 | (require 'wid-edit) | ||
| 19 | (require 'cl) | ||
| 20 | |||
| 21 | (defgroup widget-browse nil | ||
| 22 | "Customization support for browsing widgets." | ||
| 23 | :group 'widgets) | ||
| 24 | |||
| 25 | ;;; The Mode. | ||
| 26 | |||
| 27 | (defvar widget-browse-mode-map nil | ||
| 28 | "Keymap for `widget-browse-mode'.") | ||
| 29 | |||
| 30 | (unless widget-browse-mode-map | ||
| 31 | (setq widget-browse-mode-map (make-sparse-keymap)) | ||
| 32 | (set-keymap-parent widget-browse-mode-map widget-keymap)) | ||
| 33 | |||
| 34 | (easy-menu-define widget-browse-mode-menu | ||
| 35 | widget-browse-mode-map | ||
| 36 | "Menu used in widget browser buffers." | ||
| 37 | '("Widget" | ||
| 38 | ["Browse" widget-browse t] | ||
| 39 | ["Browse At" widget-browse-at t])) | ||
| 40 | |||
| 41 | (defcustom widget-browse-mode-hook nil | ||
| 42 | "Hook called when entering widget-browse-mode." | ||
| 43 | :type 'hook | ||
| 44 | :group 'widget-browse) | ||
| 45 | |||
| 46 | (defun widget-browse-mode () | ||
| 47 | "Major mode for widget browser buffers. | ||
| 48 | |||
| 49 | The following commands are available: | ||
| 50 | |||
| 51 | \\[widget-forward] Move to next button or editable field. | ||
| 52 | \\[widget-backward] Move to previous button or editable field. | ||
| 53 | \\[widget-button-click] Activate button under the mouse pointer. | ||
| 54 | \\[widget-button-press] Activate button under point. | ||
| 55 | |||
| 56 | Entry to this mode calls the value of `widget-browse-mode-hook' | ||
| 57 | if that value is non-nil." | ||
| 58 | (kill-all-local-variables) | ||
| 59 | (setq major-mode 'widget-browse-mode | ||
| 60 | mode-name "Widget") | ||
| 61 | (use-local-map widget-browse-mode-map) | ||
| 62 | (easy-menu-add widget-browse-mode-menu) | ||
| 63 | (run-hooks 'widget-browse-mode-hook)) | ||
| 64 | |||
| 65 | ;;; Commands. | ||
| 66 | |||
| 67 | ;;;###autoload | ||
| 68 | (defun widget-browse-at (pos) | ||
| 69 | "Browse the widget under point." | ||
| 70 | (interactive "d") | ||
| 71 | (let* ((field (get-text-property pos 'field)) | ||
| 72 | (button (get-text-property pos 'button)) | ||
| 73 | (doc (get-text-property pos 'widget-doc)) | ||
| 74 | (text (cond (field "This is an editable text area.") | ||
| 75 | (button "This is an active area.") | ||
| 76 | (doc "This is documentation text.") | ||
| 77 | (t "This is unidentified text."))) | ||
| 78 | (widget (or field button doc))) | ||
| 79 | (when widget | ||
| 80 | (widget-browse widget)) | ||
| 81 | (message text))) | ||
| 82 | |||
| 83 | (defvar widget-browse-history nil) | ||
| 84 | |||
| 85 | (defun widget-browse (widget) | ||
| 86 | "Create a widget browser for WIDGET." | ||
| 87 | (interactive (list (completing-read "Widget: " | ||
| 88 | obarray | ||
| 89 | (lambda (symbol) | ||
| 90 | (get symbol 'widget-type)) | ||
| 91 | t nil 'widget-browse-history))) | ||
| 92 | (if (stringp widget) | ||
| 93 | (setq widget (intern widget))) | ||
| 94 | (unless (if (symbolp widget) | ||
| 95 | (get widget 'widget-type) | ||
| 96 | (and (consp widget) | ||
| 97 | (get (widget-type widget) 'widget-type))) | ||
| 98 | (error "Not a widget.")) | ||
| 99 | ;; Create the buffer. | ||
| 100 | (if (symbolp widget) | ||
| 101 | (let ((buffer (format "*Browse %s Widget*" widget))) | ||
| 102 | (kill-buffer (get-buffer-create buffer)) | ||
| 103 | (switch-to-buffer (get-buffer-create buffer))) | ||
| 104 | (kill-buffer (get-buffer-create "*Browse Widget*")) | ||
| 105 | (switch-to-buffer (get-buffer-create "*Browse Widget*"))) | ||
| 106 | (widget-browse-mode) | ||
| 107 | |||
| 108 | ;; Quick way to get out. | ||
| 109 | (widget-create 'push-button | ||
| 110 | :action (lambda (widget &optional event) | ||
| 111 | (bury-buffer)) | ||
| 112 | "Quit") | ||
| 113 | (widget-insert "\n") | ||
| 114 | |||
| 115 | ;; Top text indicating whether it is a class or object browser. | ||
| 116 | (if (listp widget) | ||
| 117 | (widget-insert "Widget object browser.\n\nClass: ") | ||
| 118 | (widget-insert "Widget class browser.\n\n") | ||
| 119 | (widget-create 'widget-browse | ||
| 120 | :format "%[%v%]\n%d" | ||
| 121 | :doc (get widget 'widget-documentation) | ||
| 122 | widget) | ||
| 123 | (unless (eq (preceding-char) ?\n) | ||
| 124 | (widget-insert "\n")) | ||
| 125 | (widget-insert "\nSuper: ") | ||
| 126 | (setq widget (get widget 'widget-type))) | ||
| 127 | |||
| 128 | ;; Now show the attributes. | ||
| 129 | (let ((name (car widget)) | ||
| 130 | (items (cdr widget)) | ||
| 131 | key value printer) | ||
| 132 | (widget-create 'widget-browse | ||
| 133 | :format "%[%v%]" | ||
| 134 | name) | ||
| 135 | (widget-insert "\n") | ||
| 136 | (while items | ||
| 137 | (setq key (nth 0 items) | ||
| 138 | value (nth 1 items) | ||
| 139 | printer (or (get key 'widget-keyword-printer) | ||
| 140 | 'widget-browse-sexp) | ||
| 141 | items (cdr (cdr items))) | ||
| 142 | (widget-insert "\n" (symbol-name key) "\n\t") | ||
| 143 | (funcall printer widget key value) | ||
| 144 | (widget-insert "\n"))) | ||
| 145 | (widget-setup) | ||
| 146 | (goto-char (point-min))) | ||
| 147 | |||
| 148 | ;;; The `widget-browse' Widget. | ||
| 149 | |||
| 150 | (define-widget 'widget-browse 'push-button | ||
| 151 | "Button for creating a widget browser. | ||
| 152 | The :value of the widget shuld be the widget to be browsed." | ||
| 153 | :format "%[[%v]%]" | ||
| 154 | :value-create 'widget-browse-value-create | ||
| 155 | :action 'widget-browse-action) | ||
| 156 | |||
| 157 | (defun widget-browse-action (widget &optional event) | ||
| 158 | ;; Create widget browser for WIDGET's :value. | ||
| 159 | (widget-browse (widget-get widget :value))) | ||
| 160 | |||
| 161 | (defun widget-browse-value-create (widget) | ||
| 162 | ;; Insert type name. | ||
| 163 | (let ((value (widget-get widget :value))) | ||
| 164 | (cond ((symbolp value) | ||
| 165 | (insert (symbol-name value))) | ||
| 166 | ((consp value) | ||
| 167 | (insert (symbol-name (widget-type value)))) | ||
| 168 | (t | ||
| 169 | (insert "strange"))))) | ||
| 170 | |||
| 171 | ;;; Keyword Printer Functions. | ||
| 172 | |||
| 173 | (defun widget-browse-widget (widget key value) | ||
| 174 | "Insert description of WIDGET's KEY VALUE. | ||
| 175 | VALUE is assumed to be a widget." | ||
| 176 | (widget-create 'widget-browse value)) | ||
| 177 | |||
| 178 | (defun widget-browse-widgets (widget key value) | ||
| 179 | "Insert description of WIDGET's KEY VALUE. | ||
| 180 | VALUE is assumed to be a list of widgets." | ||
| 181 | (while value | ||
| 182 | (widget-create 'widget-browse | ||
| 183 | (car value)) | ||
| 184 | (setq value (cdr value)) | ||
| 185 | (when value | ||
| 186 | (widget-insert " ")))) | ||
| 187 | |||
| 188 | (defun widget-browse-sexp (widget key value) | ||
| 189 | "Insert description of WIDGET's KEY VALUE. | ||
| 190 | Nothing is assumed about value." | ||
| 191 | (let ((pp (condition-case signal | ||
| 192 | (pp-to-string value) | ||
| 193 | (error (prin1-to-string signal))))) | ||
| 194 | (when (string-match "\n\\'" pp) | ||
| 195 | (setq pp (substring pp 0 (1- (length pp))))) | ||
| 196 | (if (cond ((string-match "\n" pp) | ||
| 197 | nil) | ||
| 198 | ((> (length pp) (- (window-width) (current-column))) | ||
| 199 | nil) | ||
| 200 | (t t)) | ||
| 201 | (widget-insert pp) | ||
| 202 | (widget-create 'push-button | ||
| 203 | :tag "show" | ||
| 204 | :action (lambda (widget &optional event) | ||
| 205 | (with-output-to-temp-buffer | ||
| 206 | "*Pp Eval Output*" | ||
| 207 | (princ (widget-get widget :value)))) | ||
| 208 | pp)))) | ||
| 209 | |||
| 210 | (defun widget-browse-sexps (widget key value) | ||
| 211 | "Insert description of WIDGET's KEY VALUE. | ||
| 212 | VALUE is assumed to be a list of widgets." | ||
| 213 | (let ((target (current-column))) | ||
| 214 | (while value | ||
| 215 | (widget-browse-sexp widget key (car value)) | ||
| 216 | (setq value (cdr value)) | ||
| 217 | (when value | ||
| 218 | (widget-insert "\n" (make-string target ?\ )))))) | ||
| 219 | |||
| 220 | ;;; Keyword Printers. | ||
| 221 | |||
| 222 | (put :parent 'widget-keyword-printer 'widget-browse-widget) | ||
| 223 | (put :children 'widget-keyword-printer 'widget-browse-widgets) | ||
| 224 | (put :buttons 'widget-keyword-printer 'widget-browse-widgets) | ||
| 225 | (put :button 'widget-keyword-printer 'widget-browse-widget) | ||
| 226 | (put :args 'widget-keyword-printer 'widget-browse-sexps) | ||
| 227 | |||
| 228 | ;;; The End: | ||
| 229 | |||
| 230 | (provide 'wid-browse) | ||
| 231 | |||
| 232 | ;; wid-browse.el ends here | ||
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el new file mode 100644 index 00000000000..283981d42f4 --- /dev/null +++ b/lisp/wid-edit.el | |||
| @@ -0,0 +1,2542 @@ | |||
| 1 | ;;; wid-edit.el --- Functions for creating and using widgets. | ||
| 2 | ;; | ||
| 3 | ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc. | ||
| 4 | ;; | ||
| 5 | ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> | ||
| 6 | ;; Keywords: extensions | ||
| 7 | ;; Version: 1.71 | ||
| 8 | ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ | ||
| 9 | |||
| 10 | ;;; Commentary: | ||
| 11 | ;; | ||
| 12 | ;; See `widget.el'. | ||
| 13 | |||
| 14 | ;;; Code: | ||
| 15 | |||
| 16 | (require 'widget) | ||
| 17 | |||
| 18 | (eval-and-compile | ||
| 19 | (require 'cl)) | ||
| 20 | |||
| 21 | ;;; Compatibility. | ||
| 22 | |||
| 23 | (eval-and-compile | ||
| 24 | (autoload 'pp-to-string "pp") | ||
| 25 | (autoload 'Info-goto-node "info") | ||
| 26 | |||
| 27 | (when (string-match "XEmacs" emacs-version) | ||
| 28 | (condition-case nil | ||
| 29 | (require 'overlay) | ||
| 30 | (error (load-library "x-overlay")))) | ||
| 31 | |||
| 32 | (if (string-match "XEmacs" emacs-version) | ||
| 33 | ;; XEmacs spell `intangible' as `atomic'. | ||
| 34 | (defun widget-make-intangible (from to side) | ||
| 35 | "Make text between FROM and TO atomic with regard to movement. | ||
| 36 | Third argument should be `start-open' if it should be sticky to the rear, | ||
| 37 | and `end-open' if it should sticky to the front." | ||
| 38 | (require 'atomic-extents) | ||
| 39 | (let ((ext (make-extent from to))) | ||
| 40 | ;; XEmacs doesn't understant different kinds of read-only, so | ||
| 41 | ;; we have to use extents instead. | ||
| 42 | (put-text-property from to 'read-only nil) | ||
| 43 | (set-extent-property ext 'read-only t) | ||
| 44 | (set-extent-property ext 'start-open nil) | ||
| 45 | (set-extent-property ext 'end-open nil) | ||
| 46 | (set-extent-property ext side t) | ||
| 47 | (set-extent-property ext 'atomic t))) | ||
| 48 | (defun widget-make-intangible (from to size) | ||
| 49 | "Make text between FROM and TO intangible." | ||
| 50 | (put-text-property from to 'intangible 'front))) | ||
| 51 | |||
| 52 | ;; The following should go away when bundled with Emacs. | ||
| 53 | (condition-case () | ||
| 54 | (require 'custom) | ||
| 55 | (error nil)) | ||
| 56 | |||
| 57 | (unless (and (featurep 'custom) (fboundp 'custom-declare-variable)) | ||
| 58 | ;; We have the old custom-library, hack around it! | ||
| 59 | (defmacro defgroup (&rest args) nil) | ||
| 60 | (defmacro defcustom (var value doc &rest args) | ||
| 61 | `(defvar ,var ,value ,doc)) | ||
| 62 | (defmacro defface (&rest args) nil) | ||
| 63 | (define-widget-keywords :prefix :tag :load :link :options :type :group) | ||
| 64 | (when (fboundp 'copy-face) | ||
| 65 | (copy-face 'default 'widget-documentation-face) | ||
| 66 | (copy-face 'bold 'widget-button-face) | ||
| 67 | (copy-face 'italic 'widget-field-face))) | ||
| 68 | |||
| 69 | (unless (fboundp 'event-point) | ||
| 70 | ;; XEmacs function missing in Emacs. | ||
| 71 | (defun event-point (event) | ||
| 72 | "Return the character position of the given mouse-motion, button-press, | ||
| 73 | or button-release event. If the event did not occur over a window, or did | ||
| 74 | not occur over text, then this returns nil. Otherwise, it returns an index | ||
| 75 | into the buffer visible in the event's window." | ||
| 76 | (posn-point (event-start event)))) | ||
| 77 | |||
| 78 | (unless (fboundp 'error-message-string) | ||
| 79 | ;; Emacs function missing in XEmacs. | ||
| 80 | (defun error-message-string (obj) | ||
| 81 | "Convert an error value to an error message." | ||
| 82 | (let ((buf (get-buffer-create " *error-message*"))) | ||
| 83 | (erase-buffer buf) | ||
| 84 | (display-error obj buf) | ||
| 85 | (buffer-string buf))))) | ||
| 86 | |||
| 87 | ;;; Customization. | ||
| 88 | |||
| 89 | (defgroup widgets nil | ||
| 90 | "Customization support for the Widget Library." | ||
| 91 | :link '(custom-manual "(widget)Top") | ||
| 92 | :link '(url-link :tag "Development Page" | ||
| 93 | "http://www.dina.kvl.dk/~abraham/custom/") | ||
| 94 | :prefix "widget-" | ||
| 95 | :group 'extensions | ||
| 96 | :group 'faces | ||
| 97 | :group 'hypermedia) | ||
| 98 | |||
| 99 | (defface widget-documentation-face '((((class color) | ||
| 100 | (background dark)) | ||
| 101 | (:foreground "lime green")) | ||
| 102 | (((class color) | ||
| 103 | (background light)) | ||
| 104 | (:foreground "dark green")) | ||
| 105 | (t nil)) | ||
| 106 | "Face used for documentation text." | ||
| 107 | :group 'widgets) | ||
| 108 | |||
| 109 | (defface widget-button-face '((t (:bold t))) | ||
| 110 | "Face used for widget buttons." | ||
| 111 | :group 'widgets) | ||
| 112 | |||
| 113 | (defcustom widget-mouse-face 'highlight | ||
| 114 | "Face used for widget buttons when the mouse is above them." | ||
| 115 | :type 'face | ||
| 116 | :group 'widgets) | ||
| 117 | |||
| 118 | (defface widget-field-face '((((class grayscale color) | ||
| 119 | (background light)) | ||
| 120 | (:background "light gray")) | ||
| 121 | (((class grayscale color) | ||
| 122 | (background dark)) | ||
| 123 | (:background "dark gray")) | ||
| 124 | (t | ||
| 125 | (:italic t))) | ||
| 126 | "Face used for editable fields." | ||
| 127 | :group 'widgets) | ||
| 128 | |||
| 129 | (defcustom widget-menu-max-size 40 | ||
| 130 | "Largest number of items allowed in a popup-menu. | ||
| 131 | Larger menus are read through the minibuffer." | ||
| 132 | :group 'widgets | ||
| 133 | :type 'integer) | ||
| 134 | |||
| 135 | ;;; Utility functions. | ||
| 136 | ;; | ||
| 137 | ;; These are not really widget specific. | ||
| 138 | |||
| 139 | (defsubst widget-plist-member (plist prop) | ||
| 140 | ;; Return non-nil if PLIST has the property PROP. | ||
| 141 | ;; PLIST is a property list, which is a list of the form | ||
| 142 | ;; (PROP1 VALUE1 PROP2 VALUE2 ...). PROP is a symbol. | ||
| 143 | ;; Unlike `plist-get', this allows you to distinguish between a missing | ||
| 144 | ;; property and a property with the value nil. | ||
| 145 | ;; The value is actually the tail of PLIST whose car is PROP. | ||
| 146 | (while (and plist (not (eq (car plist) prop))) | ||
| 147 | (setq plist (cdr (cdr plist)))) | ||
| 148 | plist) | ||
| 149 | |||
| 150 | (defun widget-princ-to-string (object) | ||
| 151 | ;; Return string representation of OBJECT, any Lisp object. | ||
| 152 | ;; No quoting characters are used; no delimiters are printed around | ||
| 153 | ;; the contents of strings. | ||
| 154 | (save-excursion | ||
| 155 | (set-buffer (get-buffer-create " *widget-tmp*")) | ||
| 156 | (erase-buffer) | ||
| 157 | (let ((standard-output (current-buffer))) | ||
| 158 | (princ object)) | ||
| 159 | (buffer-string))) | ||
| 160 | |||
| 161 | (defun widget-clear-undo () | ||
| 162 | "Clear all undo information." | ||
| 163 | (buffer-disable-undo (current-buffer)) | ||
| 164 | (buffer-enable-undo)) | ||
| 165 | |||
| 166 | (defun widget-choose (title items &optional event) | ||
| 167 | "Choose an item from a list. | ||
| 168 | |||
| 169 | First argument TITLE is the name of the list. | ||
| 170 | Second argument ITEMS is an alist (NAME . VALUE). | ||
| 171 | Optional third argument EVENT is an input event. | ||
| 172 | |||
| 173 | The user is asked to choose between each NAME from the items alist, | ||
| 174 | and the VALUE of the chosen element will be returned. If EVENT is a | ||
| 175 | mouse event, and the number of elements in items is less than | ||
| 176 | `widget-menu-max-size', a popup menu will be used, otherwise the | ||
| 177 | minibuffer." | ||
| 178 | (cond ((and (< (length items) widget-menu-max-size) | ||
| 179 | event (fboundp 'x-popup-menu) window-system) | ||
| 180 | ;; We are in Emacs-19, pressed by the mouse | ||
| 181 | (x-popup-menu event | ||
| 182 | (list title (cons "" items)))) | ||
| 183 | ((and (< (length items) widget-menu-max-size) | ||
| 184 | event (fboundp 'popup-menu) window-system) | ||
| 185 | ;; We are in XEmacs, pressed by the mouse | ||
| 186 | (let ((val (get-popup-menu-response | ||
| 187 | (cons title | ||
| 188 | (mapcar | ||
| 189 | (function | ||
| 190 | (lambda (x) | ||
| 191 | (vector (car x) (list (car x)) t))) | ||
| 192 | items))))) | ||
| 193 | (setq val (and val | ||
| 194 | (listp (event-object val)) | ||
| 195 | (stringp (car-safe (event-object val))) | ||
| 196 | (car (event-object val)))) | ||
| 197 | (cdr (assoc val items)))) | ||
| 198 | (t | ||
| 199 | (let ((val (completing-read (concat title ": ") items nil t))) | ||
| 200 | (if (stringp val) | ||
| 201 | (let ((try (try-completion val items))) | ||
| 202 | (when (stringp try) | ||
| 203 | (setq val try)) | ||
| 204 | (cdr (assoc val items))) | ||
| 205 | nil))))) | ||
| 206 | |||
| 207 | (defun widget-get-sibling (widget) | ||
| 208 | "Get the item WIDGET is assumed to toggle. | ||
| 209 | This is only meaningful for radio buttons or checkboxes in a list." | ||
| 210 | (let* ((parent (widget-get widget :parent)) | ||
| 211 | (children (widget-get parent :children)) | ||
| 212 | child) | ||
| 213 | (catch 'child | ||
| 214 | (while children | ||
| 215 | (setq child (car children) | ||
| 216 | children (cdr children)) | ||
| 217 | (when (eq (widget-get child :button) widget) | ||
| 218 | (throw 'child child))) | ||
| 219 | nil))) | ||
| 220 | |||
| 221 | ;;; Widget text specifications. | ||
| 222 | ;; | ||
| 223 | ;; These functions are for specifying text properties. | ||
| 224 | |||
| 225 | (defun widget-specify-none (from to) | ||
| 226 | ;; Clear all text properties between FROM and TO. | ||
| 227 | (set-text-properties from to nil)) | ||
| 228 | |||
| 229 | (defun widget-specify-text (from to) | ||
| 230 | ;; Default properties. | ||
| 231 | (add-text-properties from to (list 'read-only t | ||
| 232 | 'front-sticky t | ||
| 233 | 'start-open t | ||
| 234 | 'end-open t | ||
| 235 | 'rear-nonsticky nil))) | ||
| 236 | |||
| 237 | (defun widget-specify-field (widget from to) | ||
| 238 | ;; Specify editable button for WIDGET between FROM and TO. | ||
| 239 | (widget-specify-field-update widget from to) | ||
| 240 | |||
| 241 | ;; Make it possible to edit the front end of the field. | ||
| 242 | (add-text-properties (1- from) from (list 'rear-nonsticky t | ||
| 243 | 'end-open t | ||
| 244 | 'invisible t)) | ||
| 245 | (when (or (string-match "\\(.\\|\n\\)%v" (widget-get widget :format)) | ||
| 246 | (widget-get widget :hide-front-space)) | ||
| 247 | ;; WARNING: This is going to lose horrible if the character just | ||
| 248 | ;; before the field can be modified (e.g. if it belongs to a | ||
| 249 | ;; choice widget). We try to compensate by checking the format | ||
| 250 | ;; string, and hope the user hasn't changed the :create method. | ||
| 251 | (widget-make-intangible (- from 2) from 'end-open)) | ||
| 252 | |||
| 253 | ;; Make it possible to edit back end of the field. | ||
| 254 | (add-text-properties to (1+ to) (list 'front-sticky nil | ||
| 255 | 'read-only t | ||
| 256 | 'start-open t)) | ||
| 257 | |||
| 258 | (cond ((widget-get widget :size) | ||
| 259 | (put-text-property to (1+ to) 'invisible t) | ||
| 260 | (when (or (string-match "%v\\(.\\|\n\\)" (widget-get widget :format)) | ||
| 261 | (widget-get widget :hide-rear-space)) | ||
| 262 | ;; WARNING: This is going to lose horrible if the character just | ||
| 263 | ;; after the field can be modified (e.g. if it belongs to a | ||
| 264 | ;; choice widget). We try to compensate by checking the format | ||
| 265 | ;; string, and hope the user hasn't changed the :create method. | ||
| 266 | (widget-make-intangible to (+ to 2) 'start-open))) | ||
| 267 | ((string-match "XEmacs" emacs-version) | ||
| 268 | ;; XEmacs does not allow you to insert before a read-only | ||
| 269 | ;; character, even if it is start.open. | ||
| 270 | ;; XEmacs does allow you to delete an read-only extent, so | ||
| 271 | ;; making the terminating newline read only doesn't help. | ||
| 272 | ;; I tried putting an invisible intangible read-only space | ||
| 273 | ;; before the newline, which gave really weird effects. | ||
| 274 | ;; So for now, we just have trust the user not to delete the | ||
| 275 | ;; newline. | ||
| 276 | (put-text-property to (1+ to) 'read-only nil)))) | ||
| 277 | |||
| 278 | (defun widget-specify-field-update (widget from to) | ||
| 279 | ;; Specify editable button for WIDGET between FROM and TO. | ||
| 280 | (let ((map (widget-get widget :keymap)) | ||
| 281 | (secret (widget-get widget :secret)) | ||
| 282 | (secret-to to) | ||
| 283 | (size (widget-get widget :size)) | ||
| 284 | (face (or (widget-get widget :value-face) | ||
| 285 | 'widget-field-face)) | ||
| 286 | (help-echo (widget-get widget :help-echo)) | ||
| 287 | (help-property (if (featurep 'balloon-help) | ||
| 288 | 'balloon-help | ||
| 289 | 'help-echo))) | ||
| 290 | (unless (or (stringp help-echo) (null help-echo)) | ||
| 291 | (setq help-echo 'widget-mouse-help)) | ||
| 292 | |||
| 293 | (when secret | ||
| 294 | (while (and size | ||
| 295 | (not (zerop size)) | ||
| 296 | (> secret-to from) | ||
| 297 | (eq (char-after (1- secret-to)) ?\ )) | ||
| 298 | (setq secret-to (1- secret-to))) | ||
| 299 | |||
| 300 | (save-excursion | ||
| 301 | (goto-char from) | ||
| 302 | (while (< (point) secret-to) | ||
| 303 | (let ((old (get-text-property (point) 'secret))) | ||
| 304 | (when old | ||
| 305 | (subst-char-in-region (point) (1+ (point)) secret old))) | ||
| 306 | (forward-char)))) | ||
| 307 | |||
| 308 | (set-text-properties from to (list 'field widget | ||
| 309 | 'read-only nil | ||
| 310 | 'keymap map | ||
| 311 | 'local-map map | ||
| 312 | help-property help-echo | ||
| 313 | 'face face)) | ||
| 314 | |||
| 315 | (when secret | ||
| 316 | (save-excursion | ||
| 317 | (goto-char from) | ||
| 318 | (while (< (point) secret-to) | ||
| 319 | (let ((old (following-char))) | ||
| 320 | (subst-char-in-region (point) (1+ (point)) old secret) | ||
| 321 | (put-text-property (point) (1+ (point)) 'secret old)) | ||
| 322 | (forward-char)))) | ||
| 323 | |||
| 324 | (unless (widget-get widget :size) | ||
| 325 | (add-text-properties to (1+ to) (list 'field widget | ||
| 326 | help-property help-echo | ||
| 327 | 'face face))) | ||
| 328 | (add-text-properties to (1+ to) (list 'local-map map | ||
| 329 | 'keymap map)))) | ||
| 330 | |||
| 331 | (defun widget-specify-button (widget from to) | ||
| 332 | ;; Specify button for WIDGET between FROM and TO. | ||
| 333 | (let ((face (widget-apply widget :button-face-get)) | ||
| 334 | (help-echo (widget-get widget :help-echo)) | ||
| 335 | (help-property (if (featurep 'balloon-help) | ||
| 336 | 'balloon-help | ||
| 337 | 'help-echo))) | ||
| 338 | (unless (or (null help-echo) (stringp help-echo)) | ||
| 339 | (setq help-echo 'widget-mouse-help)) | ||
| 340 | (add-text-properties from to (list 'button widget | ||
| 341 | 'mouse-face widget-mouse-face | ||
| 342 | 'start-open t | ||
| 343 | 'end-open t | ||
| 344 | help-property help-echo | ||
| 345 | 'face face)))) | ||
| 346 | |||
| 347 | (defun widget-mouse-help (extent) | ||
| 348 | "Find mouse help string for button in extent." | ||
| 349 | (let* ((widget (widget-at (extent-start-position extent))) | ||
| 350 | (help-echo (and widget (widget-get widget :help-echo)))) | ||
| 351 | (cond ((stringp help-echo) | ||
| 352 | help-echo) | ||
| 353 | ((and (symbolp help-echo) (fboundp help-echo) | ||
| 354 | (stringp (setq help-echo (funcall help-echo widget)))) | ||
| 355 | help-echo) | ||
| 356 | (t | ||
| 357 | (format "(widget %S :help-echo %S)" widget help-echo))))) | ||
| 358 | |||
| 359 | (defun widget-specify-sample (widget from to) | ||
| 360 | ;; Specify sample for WIDGET between FROM and TO. | ||
| 361 | (let ((face (widget-apply widget :sample-face-get))) | ||
| 362 | (when face | ||
| 363 | (add-text-properties from to (list 'start-open t | ||
| 364 | 'end-open t | ||
| 365 | 'face face))))) | ||
| 366 | |||
| 367 | (defun widget-specify-doc (widget from to) | ||
| 368 | ;; Specify documentation for WIDGET between FROM and TO. | ||
| 369 | (add-text-properties from to (list 'widget-doc widget | ||
| 370 | 'face 'widget-documentation-face))) | ||
| 371 | |||
| 372 | (defmacro widget-specify-insert (&rest form) | ||
| 373 | ;; Execute FORM without inheriting any text properties. | ||
| 374 | `(save-restriction | ||
| 375 | (let ((inhibit-read-only t) | ||
| 376 | result | ||
| 377 | after-change-functions) | ||
| 378 | (insert "<>") | ||
| 379 | (narrow-to-region (- (point) 2) (point)) | ||
| 380 | (widget-specify-none (point-min) (point-max)) | ||
| 381 | (goto-char (1+ (point-min))) | ||
| 382 | (setq result (progn ,@form)) | ||
| 383 | (delete-region (point-min) (1+ (point-min))) | ||
| 384 | (delete-region (1- (point-max)) (point-max)) | ||
| 385 | (goto-char (point-max)) | ||
| 386 | result))) | ||
| 387 | |||
| 388 | (defface widget-inactive-face '((((class grayscale color) | ||
| 389 | (background dark)) | ||
| 390 | (:foreground "light gray")) | ||
| 391 | (((class grayscale color) | ||
| 392 | (background light)) | ||
| 393 | (:foreground "dark gray")) | ||
| 394 | (t | ||
| 395 | (:italic t))) | ||
| 396 | "Face used for inactive widgets." | ||
| 397 | :group 'widgets) | ||
| 398 | |||
| 399 | (defun widget-specify-inactive (widget from to) | ||
| 400 | "Make WIDGET inactive for user modifications." | ||
| 401 | (unless (widget-get widget :inactive) | ||
| 402 | (let ((overlay (make-overlay from to nil t nil))) | ||
| 403 | (overlay-put overlay 'face 'widget-inactive-face) | ||
| 404 | (overlay-put overlay 'evaporate 't) | ||
| 405 | (overlay-put overlay (if (string-match "XEmacs" emacs-version) | ||
| 406 | 'read-only | ||
| 407 | 'modification-hooks) '(widget-overlay-inactive)) | ||
| 408 | (widget-put widget :inactive overlay)))) | ||
| 409 | |||
| 410 | (defun widget-overlay-inactive (&rest junk) | ||
| 411 | "Ignoring the arguments, signal an error." | ||
| 412 | (unless inhibit-read-only | ||
| 413 | (error "Attempt to modify inactive widget"))) | ||
| 414 | |||
| 415 | |||
| 416 | (defun widget-specify-active (widget) | ||
| 417 | "Make WIDGET active for user modifications." | ||
| 418 | (let ((inactive (widget-get widget :inactive))) | ||
| 419 | (when inactive | ||
| 420 | (delete-overlay inactive) | ||
| 421 | (widget-put widget :inactive nil)))) | ||
| 422 | |||
| 423 | ;;; Widget Properties. | ||
| 424 | |||
| 425 | (defsubst widget-type (widget) | ||
| 426 | "Return the type of WIDGET, a symbol." | ||
| 427 | (car widget)) | ||
| 428 | |||
| 429 | (defun widget-put (widget property value) | ||
| 430 | "In WIDGET set PROPERTY to VALUE. | ||
| 431 | The value can later be retrived with `widget-get'." | ||
| 432 | (setcdr widget (plist-put (cdr widget) property value))) | ||
| 433 | |||
| 434 | (defun widget-get (widget property) | ||
| 435 | "In WIDGET, get the value of PROPERTY. | ||
| 436 | The value could either be specified when the widget was created, or | ||
| 437 | later with `widget-put'." | ||
| 438 | (let ((missing t) | ||
| 439 | value tmp) | ||
| 440 | (while missing | ||
| 441 | (cond ((setq tmp (widget-plist-member (cdr widget) property)) | ||
| 442 | (setq value (car (cdr tmp)) | ||
| 443 | missing nil)) | ||
| 444 | ((setq tmp (car widget)) | ||
| 445 | (setq widget (get tmp 'widget-type))) | ||
| 446 | (t | ||
| 447 | (setq missing nil)))) | ||
| 448 | value)) | ||
| 449 | |||
| 450 | (defun widget-member (widget property) | ||
| 451 | "Non-nil iff there is a definition in WIDGET for PROPERTY." | ||
| 452 | (cond ((widget-plist-member (cdr widget) property) | ||
| 453 | t) | ||
| 454 | ((car widget) | ||
| 455 | (widget-member (get (car widget) 'widget-type) property)) | ||
| 456 | (t nil))) | ||
| 457 | |||
| 458 | ;;;###autoload | ||
| 459 | (defun widget-apply (widget property &rest args) | ||
| 460 | "Apply the value of WIDGET's PROPERTY to the widget itself. | ||
| 461 | ARGS are passed as extra arguments to the function." | ||
| 462 | (apply (widget-get widget property) widget args)) | ||
| 463 | |||
| 464 | (defun widget-value (widget) | ||
| 465 | "Extract the current value of WIDGET." | ||
| 466 | (widget-apply widget | ||
| 467 | :value-to-external (widget-apply widget :value-get))) | ||
| 468 | |||
| 469 | (defun widget-value-set (widget value) | ||
| 470 | "Set the current value of WIDGET to VALUE." | ||
| 471 | (widget-apply widget | ||
| 472 | :value-set (widget-apply widget | ||
| 473 | :value-to-internal value))) | ||
| 474 | |||
| 475 | (defun widget-match-inline (widget vals) | ||
| 476 | ;; In WIDGET, match the start of VALS. | ||
| 477 | (cond ((widget-get widget :inline) | ||
| 478 | (widget-apply widget :match-inline vals)) | ||
| 479 | ((and vals | ||
| 480 | (widget-apply widget :match (car vals))) | ||
| 481 | (cons (list (car vals)) (cdr vals))) | ||
| 482 | (t nil))) | ||
| 483 | |||
| 484 | (defun widget-apply-action (widget &optional event) | ||
| 485 | "Apply :action in WIDGET in response to EVENT." | ||
| 486 | (if (widget-apply widget :active) | ||
| 487 | (widget-apply widget :action event) | ||
| 488 | (error "Attempt to perform action on inactive widget"))) | ||
| 489 | |||
| 490 | ;;; Glyphs. | ||
| 491 | |||
| 492 | (defcustom widget-glyph-directory (concat data-directory "custom/") | ||
| 493 | "Where widget glyphs are located. | ||
| 494 | If this variable is nil, widget will try to locate the directory | ||
| 495 | automatically. This does not work yet." | ||
| 496 | :group 'widgets | ||
| 497 | :type 'directory) | ||
| 498 | |||
| 499 | (defcustom widget-glyph-enable t | ||
| 500 | "If non nil, use glyphs in images when available." | ||
| 501 | :group 'widgets | ||
| 502 | :type 'boolean) | ||
| 503 | |||
| 504 | (defun widget-glyph-insert (widget tag image) | ||
| 505 | "In WIDGET, insert the text TAG or, if supported, IMAGE. | ||
| 506 | IMAGE should either be a glyph, or a name sans extension of an xpm or | ||
| 507 | xbm file located in `widget-glyph-directory'. | ||
| 508 | |||
| 509 | WARNING: If you call this with a glyph, and you want the user to be | ||
| 510 | able to activate the glyph, make sure it is unique. If you use the | ||
| 511 | same glyph for multiple widgets, activating any of the glyphs will | ||
| 512 | cause the last created widget to be activated." | ||
| 513 | (cond ((not (and (string-match "XEmacs" emacs-version) | ||
| 514 | widget-glyph-enable | ||
| 515 | (fboundp 'make-glyph) | ||
| 516 | image)) | ||
| 517 | ;; We don't want or can't use glyphs. | ||
| 518 | (insert tag)) | ||
| 519 | ((and (fboundp 'glyphp) | ||
| 520 | (glyphp image)) | ||
| 521 | ;; Already a glyph. Insert it. | ||
| 522 | (widget-glyph-insert-glyph widget tag image)) | ||
| 523 | (t | ||
| 524 | ;; A string. Look it up in. | ||
| 525 | (let ((file (concat widget-glyph-directory | ||
| 526 | (if (string-match "/\\'" widget-glyph-directory) | ||
| 527 | "" | ||
| 528 | "/") | ||
| 529 | image | ||
| 530 | (if (featurep 'xpm) ".xpm" ".xbm")))) | ||
| 531 | (if (file-readable-p file) | ||
| 532 | (widget-glyph-insert-glyph widget tag (make-glyph file)) | ||
| 533 | ;; File not readable, give up. | ||
| 534 | (insert tag)))))) | ||
| 535 | |||
| 536 | (defun widget-glyph-insert-glyph (widget tag glyph) | ||
| 537 | "In WIDGET, with alternative text TAG, insert GLYPH." | ||
| 538 | (set-glyph-image glyph (cons 'tty tag)) | ||
| 539 | (set-glyph-property glyph 'widget widget) | ||
| 540 | (insert "*") | ||
| 541 | (add-text-properties (1- (point)) (point) | ||
| 542 | (list 'invisible t | ||
| 543 | 'end-glyph glyph)) | ||
| 544 | (let ((help-echo (widget-get widget :help-echo))) | ||
| 545 | (when help-echo | ||
| 546 | (let ((extent (extent-at (1- (point)) nil 'end-glyph)) | ||
| 547 | (help-property (if (featurep 'balloon-help) | ||
| 548 | 'balloon-help | ||
| 549 | 'help-echo))) | ||
| 550 | (set-extent-property extent help-property (if (stringp help-echo) | ||
| 551 | help-echo | ||
| 552 | 'widget-mouse-help)))))) | ||
| 553 | |||
| 554 | ;;; Creating Widgets. | ||
| 555 | |||
| 556 | ;;;###autoload | ||
| 557 | (defun widget-create (type &rest args) | ||
| 558 | "Create widget of TYPE. | ||
| 559 | The optional ARGS are additional keyword arguments." | ||
| 560 | (let ((widget (apply 'widget-convert type args))) | ||
| 561 | (widget-apply widget :create) | ||
| 562 | widget)) | ||
| 563 | |||
| 564 | (defun widget-create-child-and-convert (parent type &rest args) | ||
| 565 | "As part of the widget PARENT, create a child widget TYPE. | ||
| 566 | The child is converted, using the keyword arguments ARGS." | ||
| 567 | (let ((widget (apply 'widget-convert type args))) | ||
| 568 | (widget-put widget :parent parent) | ||
| 569 | (unless (widget-get widget :indent) | ||
| 570 | (widget-put widget :indent (+ (or (widget-get parent :indent) 0) | ||
| 571 | (or (widget-get widget :extra-offset) 0) | ||
| 572 | (widget-get parent :offset)))) | ||
| 573 | (widget-apply widget :create) | ||
| 574 | widget)) | ||
| 575 | |||
| 576 | (defun widget-create-child (parent type) | ||
| 577 | "Create widget of TYPE." | ||
| 578 | (let ((widget (copy-list type))) | ||
| 579 | (widget-put widget :parent parent) | ||
| 580 | (unless (widget-get widget :indent) | ||
| 581 | (widget-put widget :indent (+ (or (widget-get parent :indent) 0) | ||
| 582 | (or (widget-get widget :extra-offset) 0) | ||
| 583 | (widget-get parent :offset)))) | ||
| 584 | (widget-apply widget :create) | ||
| 585 | widget)) | ||
| 586 | |||
| 587 | (defun widget-create-child-value (parent type value) | ||
| 588 | "Create widget of TYPE with value VALUE." | ||
| 589 | (let ((widget (copy-list type))) | ||
| 590 | (widget-put widget :value (widget-apply widget :value-to-internal value)) | ||
| 591 | (widget-put widget :parent parent) | ||
| 592 | (unless (widget-get widget :indent) | ||
| 593 | (widget-put widget :indent (+ (or (widget-get parent :indent) 0) | ||
| 594 | (or (widget-get widget :extra-offset) 0) | ||
| 595 | (widget-get parent :offset)))) | ||
| 596 | (widget-apply widget :create) | ||
| 597 | widget)) | ||
| 598 | |||
| 599 | ;;;###autoload | ||
| 600 | (defun widget-delete (widget) | ||
| 601 | "Delete WIDGET." | ||
| 602 | (widget-apply widget :delete)) | ||
| 603 | |||
| 604 | (defun widget-convert (type &rest args) | ||
| 605 | "Convert TYPE to a widget without inserting it in the buffer. | ||
| 606 | The optional ARGS are additional keyword arguments." | ||
| 607 | ;; Don't touch the type. | ||
| 608 | (let* ((widget (if (symbolp type) | ||
| 609 | (list type) | ||
| 610 | (copy-list type))) | ||
| 611 | (current widget) | ||
| 612 | (keys args)) | ||
| 613 | ;; First set the :args keyword. | ||
| 614 | (while (cdr current) ;Look in the type. | ||
| 615 | (let ((next (car (cdr current)))) | ||
| 616 | (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:)) | ||
| 617 | (setq current (cdr (cdr current))) | ||
| 618 | (setcdr current (list :args (cdr current))) | ||
| 619 | (setq current nil)))) | ||
| 620 | (while args ;Look in the args. | ||
| 621 | (let ((next (nth 0 args))) | ||
| 622 | (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:)) | ||
| 623 | (setq args (nthcdr 2 args)) | ||
| 624 | (widget-put widget :args args) | ||
| 625 | (setq args nil)))) | ||
| 626 | ;; Then Convert the widget. | ||
| 627 | (setq type widget) | ||
| 628 | (while type | ||
| 629 | (let ((convert-widget (plist-get (cdr type) :convert-widget))) | ||
| 630 | (if convert-widget | ||
| 631 | (setq widget (funcall convert-widget widget)))) | ||
| 632 | (setq type (get (car type) 'widget-type))) | ||
| 633 | ;; Finally set the keyword args. | ||
| 634 | (while keys | ||
| 635 | (let ((next (nth 0 keys))) | ||
| 636 | (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:)) | ||
| 637 | (progn | ||
| 638 | (widget-put widget next (nth 1 keys)) | ||
| 639 | (setq keys (nthcdr 2 keys))) | ||
| 640 | (setq keys nil)))) | ||
| 641 | ;; Convert the :value to internal format. | ||
| 642 | (if (widget-member widget :value) | ||
| 643 | (let ((value (widget-get widget :value))) | ||
| 644 | (widget-put widget | ||
| 645 | :value (widget-apply widget :value-to-internal value)))) | ||
| 646 | ;; Return the newly create widget. | ||
| 647 | widget)) | ||
| 648 | |||
| 649 | (defun widget-insert (&rest args) | ||
| 650 | "Call `insert' with ARGS and make the text read only." | ||
| 651 | (let ((inhibit-read-only t) | ||
| 652 | after-change-functions | ||
| 653 | (from (point))) | ||
| 654 | (apply 'insert args) | ||
| 655 | (widget-specify-text from (point)))) | ||
| 656 | |||
| 657 | ;;; Keymap and Commands. | ||
| 658 | |||
| 659 | (defvar widget-keymap nil | ||
| 660 | "Keymap containing useful binding for buffers containing widgets. | ||
| 661 | Recommended as a parent keymap for modes using widgets.") | ||
| 662 | |||
| 663 | (unless widget-keymap | ||
| 664 | (setq widget-keymap (make-sparse-keymap)) | ||
| 665 | (define-key widget-keymap "\C-k" 'widget-kill-line) | ||
| 666 | (define-key widget-keymap "\t" 'widget-forward) | ||
| 667 | (define-key widget-keymap "\M-\t" 'widget-backward) | ||
| 668 | (define-key widget-keymap [(shift tab)] 'widget-backward) | ||
| 669 | (define-key widget-keymap [backtab] 'widget-backward) | ||
| 670 | (if (string-match "XEmacs" (emacs-version)) | ||
| 671 | (progn | ||
| 672 | (define-key widget-keymap [button2] 'widget-button-click) | ||
| 673 | (define-key widget-keymap [button1] 'widget-button1-click)) | ||
| 674 | (define-key widget-keymap [mouse-2] 'ignore) | ||
| 675 | (define-key widget-keymap [down-mouse-2] 'widget-button-click)) | ||
| 676 | (define-key widget-keymap "\C-m" 'widget-button-press)) | ||
| 677 | |||
| 678 | (defvar widget-global-map global-map | ||
| 679 | "Keymap used for events the widget does not handle themselves.") | ||
| 680 | (make-variable-buffer-local 'widget-global-map) | ||
| 681 | |||
| 682 | (defvar widget-field-keymap nil | ||
| 683 | "Keymap used inside an editable field.") | ||
| 684 | |||
| 685 | (unless widget-field-keymap | ||
| 686 | (setq widget-field-keymap (copy-keymap widget-keymap)) | ||
| 687 | (unless (string-match "XEmacs" (emacs-version)) | ||
| 688 | (define-key widget-field-keymap [menu-bar] 'nil)) | ||
| 689 | (define-key widget-field-keymap "\C-m" 'widget-field-activate) | ||
| 690 | (define-key widget-field-keymap "\C-a" 'widget-beginning-of-line) | ||
| 691 | (define-key widget-field-keymap "\C-e" 'widget-end-of-line) | ||
| 692 | (set-keymap-parent widget-field-keymap global-map)) | ||
| 693 | |||
| 694 | (defvar widget-text-keymap nil | ||
| 695 | "Keymap used inside a text field.") | ||
| 696 | |||
| 697 | (unless widget-text-keymap | ||
| 698 | (setq widget-text-keymap (copy-keymap widget-keymap)) | ||
| 699 | (unless (string-match "XEmacs" (emacs-version)) | ||
| 700 | (define-key widget-text-keymap [menu-bar] 'nil)) | ||
| 701 | (define-key widget-text-keymap "\C-a" 'widget-beginning-of-line) | ||
| 702 | (define-key widget-text-keymap "\C-e" 'widget-end-of-line) | ||
| 703 | (set-keymap-parent widget-text-keymap global-map)) | ||
| 704 | |||
| 705 | (defun widget-field-activate (pos &optional event) | ||
| 706 | "Activate the ediable field at point." | ||
| 707 | (interactive "@d") | ||
| 708 | (let ((field (get-text-property pos 'field))) | ||
| 709 | (if field | ||
| 710 | (widget-apply-action field event) | ||
| 711 | (call-interactively | ||
| 712 | (lookup-key widget-global-map (this-command-keys)))))) | ||
| 713 | |||
| 714 | (defun widget-button-click (event) | ||
| 715 | "Activate button below mouse pointer." | ||
| 716 | (interactive "@e") | ||
| 717 | (cond ((and (fboundp 'event-glyph) | ||
| 718 | (event-glyph event)) | ||
| 719 | (let ((widget (glyph-property (event-glyph event) 'widget))) | ||
| 720 | (if widget | ||
| 721 | (widget-apply-action widget event) | ||
| 722 | (message "You clicked on a glyph.")))) | ||
| 723 | ((event-point event) | ||
| 724 | (let ((button (get-text-property (event-point event) 'button))) | ||
| 725 | (if button | ||
| 726 | (widget-apply-action button event) | ||
| 727 | (call-interactively | ||
| 728 | (or (lookup-key widget-global-map [ button2 ]) | ||
| 729 | (lookup-key widget-global-map [ down-mouse-2 ]) | ||
| 730 | (lookup-key widget-global-map [ mouse-2])))))) | ||
| 731 | (t | ||
| 732 | (message "You clicked somewhere weird.")))) | ||
| 733 | |||
| 734 | (defun widget-button1-click (event) | ||
| 735 | "Activate glyph below mouse pointer." | ||
| 736 | (interactive "@e") | ||
| 737 | (if (and (fboundp 'event-glyph) | ||
| 738 | (event-glyph event)) | ||
| 739 | (let ((widget (glyph-property (event-glyph event) 'widget))) | ||
| 740 | (if widget | ||
| 741 | (widget-apply-action widget event) | ||
| 742 | (message "You clicked on a glyph."))) | ||
| 743 | (call-interactively (lookup-key widget-global-map (this-command-keys))))) | ||
| 744 | |||
| 745 | (defun widget-button-press (pos &optional event) | ||
| 746 | "Activate button at POS." | ||
| 747 | (interactive "@d") | ||
| 748 | (let ((button (get-text-property pos 'button))) | ||
| 749 | (if button | ||
| 750 | (widget-apply-action button event) | ||
| 751 | (let ((command (lookup-key widget-global-map (this-command-keys)))) | ||
| 752 | (when (commandp command) | ||
| 753 | (call-interactively command)))))) | ||
| 754 | |||
| 755 | (defun widget-move (arg) | ||
| 756 | "Move point to the ARG next field or button. | ||
| 757 | ARG may be negative to move backward." | ||
| 758 | (while (> arg 0) | ||
| 759 | (setq arg (1- arg)) | ||
| 760 | (let ((next (cond ((get-text-property (point) 'button) | ||
| 761 | (next-single-property-change (point) 'button)) | ||
| 762 | ((get-text-property (point) 'field) | ||
| 763 | (next-single-property-change (point) 'field)) | ||
| 764 | (t | ||
| 765 | (point))))) | ||
| 766 | (if (null next) ; Widget extends to end. of buffer | ||
| 767 | (setq next (point-min))) | ||
| 768 | (let ((button (next-single-property-change next 'button)) | ||
| 769 | (field (next-single-property-change next 'field))) | ||
| 770 | (cond ((or (get-text-property next 'button) | ||
| 771 | (get-text-property next 'field)) | ||
| 772 | (goto-char next)) | ||
| 773 | ((and button field) | ||
| 774 | (goto-char (min button field))) | ||
| 775 | (button (goto-char button)) | ||
| 776 | (field (goto-char field)) | ||
| 777 | (t | ||
| 778 | (let ((button (next-single-property-change (point-min) 'button)) | ||
| 779 | (field (next-single-property-change (point-min) 'field))) | ||
| 780 | (cond ((and button field) (goto-char (min button field))) | ||
| 781 | (button (goto-char button)) | ||
| 782 | (field (goto-char field)) | ||
| 783 | (t | ||
| 784 | (error "No buttons or fields found")))))) | ||
| 785 | (setq button (widget-at (point))) | ||
| 786 | (if (and button (widget-get button :tab-order) | ||
| 787 | (< (widget-get button :tab-order) 0)) | ||
| 788 | (setq arg (1+ arg)))))) | ||
| 789 | (while (< arg 0) | ||
| 790 | (if (= (point-min) (point)) | ||
| 791 | (forward-char 1)) | ||
| 792 | (setq arg (1+ arg)) | ||
| 793 | (let ((previous (cond ((get-text-property (1- (point)) 'button) | ||
| 794 | (previous-single-property-change (point) 'button)) | ||
| 795 | ((get-text-property (1- (point)) 'field) | ||
| 796 | (previous-single-property-change (point) 'field)) | ||
| 797 | (t | ||
| 798 | (point))))) | ||
| 799 | (if (null previous) ; Widget extends to beg. of buffer | ||
| 800 | (setq previous (point-max))) | ||
| 801 | (let ((button (previous-single-property-change previous 'button)) | ||
| 802 | (field (previous-single-property-change previous 'field))) | ||
| 803 | (cond ((and button field) | ||
| 804 | (goto-char (max button field))) | ||
| 805 | (button (goto-char button)) | ||
| 806 | (field (goto-char field)) | ||
| 807 | (t | ||
| 808 | (let ((button (previous-single-property-change | ||
| 809 | (point-max) 'button)) | ||
| 810 | (field (previous-single-property-change | ||
| 811 | (point-max) 'field))) | ||
| 812 | (cond ((and button field) (goto-char (max button field))) | ||
| 813 | (button (goto-char button)) | ||
| 814 | (field (goto-char field)) | ||
| 815 | (t | ||
| 816 | (error "No buttons or fields found")))))))) | ||
| 817 | (let ((button (previous-single-property-change (point) 'button)) | ||
| 818 | (field (previous-single-property-change (point) 'field))) | ||
| 819 | (cond ((and button field) | ||
| 820 | (goto-char (max button field))) | ||
| 821 | (button (goto-char button)) | ||
| 822 | (field (goto-char field))) | ||
| 823 | (setq button (widget-at (point))) | ||
| 824 | (if (and button (widget-get button :tab-order) | ||
| 825 | (< (widget-get button :tab-order) 0)) | ||
| 826 | (setq arg (1- arg))))) | ||
| 827 | (widget-echo-help (point)) | ||
| 828 | (run-hooks 'widget-move-hook)) | ||
| 829 | |||
| 830 | (defun widget-forward (arg) | ||
| 831 | "Move point to the next field or button. | ||
| 832 | With optional ARG, move across that many fields." | ||
| 833 | (interactive "p") | ||
| 834 | (run-hooks 'widget-forward-hook) | ||
| 835 | (widget-move arg)) | ||
| 836 | |||
| 837 | (defun widget-backward (arg) | ||
| 838 | "Move point to the previous field or button. | ||
| 839 | With optional ARG, move across that many fields." | ||
| 840 | (interactive "p") | ||
| 841 | (run-hooks 'widget-backward-hook) | ||
| 842 | (widget-move (- arg))) | ||
| 843 | |||
| 844 | (defun widget-beginning-of-line () | ||
| 845 | "Go to beginning of field or beginning of line, whichever is first." | ||
| 846 | (interactive) | ||
| 847 | (let ((bol (save-excursion (beginning-of-line) (point))) | ||
| 848 | (prev (previous-single-property-change (point) 'field))) | ||
| 849 | (goto-char (max bol (or prev bol))))) | ||
| 850 | |||
| 851 | (defun widget-end-of-line () | ||
| 852 | "Go to end of field or end of line, whichever is first." | ||
| 853 | (interactive) | ||
| 854 | (let ((bol (save-excursion (end-of-line) (point))) | ||
| 855 | (prev (next-single-property-change (point) 'field))) | ||
| 856 | (goto-char (min bol (or prev bol))))) | ||
| 857 | |||
| 858 | (defun widget-kill-line () | ||
| 859 | "Kill to end of field or end of line, whichever is first." | ||
| 860 | (interactive) | ||
| 861 | (let ((field (get-text-property (point) 'field)) | ||
| 862 | (newline (save-excursion (search-forward "\n"))) | ||
| 863 | (next (next-single-property-change (point) 'field))) | ||
| 864 | (if (and field (> newline next)) | ||
| 865 | (kill-region (point) next) | ||
| 866 | (call-interactively 'kill-line)))) | ||
| 867 | |||
| 868 | ;;; Setting up the buffer. | ||
| 869 | |||
| 870 | (defvar widget-field-new nil) | ||
| 871 | ;; List of all newly created editable fields in the buffer. | ||
| 872 | (make-variable-buffer-local 'widget-field-new) | ||
| 873 | |||
| 874 | (defvar widget-field-list nil) | ||
| 875 | ;; List of all editable fields in the buffer. | ||
| 876 | (make-variable-buffer-local 'widget-field-list) | ||
| 877 | |||
| 878 | (defun widget-setup () | ||
| 879 | "Setup current buffer so editing string widgets works." | ||
| 880 | (let ((inhibit-read-only t) | ||
| 881 | (after-change-functions nil) | ||
| 882 | field) | ||
| 883 | (while widget-field-new | ||
| 884 | (setq field (car widget-field-new) | ||
| 885 | widget-field-new (cdr widget-field-new) | ||
| 886 | widget-field-list (cons field widget-field-list)) | ||
| 887 | (let ((from (widget-get field :value-from)) | ||
| 888 | (to (widget-get field :value-to))) | ||
| 889 | (widget-specify-field field from to) | ||
| 890 | (move-marker from (1- from)) | ||
| 891 | (move-marker to (1+ to))))) | ||
| 892 | (widget-clear-undo) | ||
| 893 | ;; We need to maintain text properties and size of the editing fields. | ||
| 894 | (make-local-variable 'after-change-functions) | ||
| 895 | (if widget-field-list | ||
| 896 | (setq after-change-functions '(widget-after-change)) | ||
| 897 | (setq after-change-functions nil))) | ||
| 898 | |||
| 899 | (defvar widget-field-last nil) | ||
| 900 | ;; Last field containing point. | ||
| 901 | (make-variable-buffer-local 'widget-field-last) | ||
| 902 | |||
| 903 | (defvar widget-field-was nil) | ||
| 904 | ;; The widget data before the change. | ||
| 905 | (make-variable-buffer-local 'widget-field-was) | ||
| 906 | |||
| 907 | (defun widget-field-find (pos) | ||
| 908 | ;; Find widget whose editing field is located at POS. | ||
| 909 | ;; Return nil if POS is not inside and editing field. | ||
| 910 | ;; | ||
| 911 | ;; This is only used in `widget-field-modified', since ordinarily | ||
| 912 | ;; you would just test the field property. | ||
| 913 | (let ((fields widget-field-list) | ||
| 914 | field found) | ||
| 915 | (while fields | ||
| 916 | (setq field (car fields) | ||
| 917 | fields (cdr fields)) | ||
| 918 | (let ((from (widget-get field :value-from)) | ||
| 919 | (to (widget-get field :value-to))) | ||
| 920 | (if (and from to (< from pos) (> to pos)) | ||
| 921 | (setq fields nil | ||
| 922 | found field)))) | ||
| 923 | found)) | ||
| 924 | |||
| 925 | (defun widget-after-change (from to old) | ||
| 926 | ;; Adjust field size and text properties. | ||
| 927 | (condition-case nil | ||
| 928 | (let ((field (widget-field-find from)) | ||
| 929 | (inhibit-read-only t)) | ||
| 930 | (cond ((null field)) | ||
| 931 | ((not (eq field (widget-field-find to))) | ||
| 932 | (debug) | ||
| 933 | (message "Error: `widget-after-change' called on two fields")) | ||
| 934 | (t | ||
| 935 | (let ((size (widget-get field :size))) | ||
| 936 | (if size | ||
| 937 | (let ((begin (1+ (widget-get field :value-from))) | ||
| 938 | (end (1- (widget-get field :value-to)))) | ||
| 939 | (widget-specify-field-update field begin end) | ||
| 940 | (cond ((< (- end begin) size) | ||
| 941 | ;; Field too small. | ||
| 942 | (save-excursion | ||
| 943 | (goto-char end) | ||
| 944 | (insert-char ?\ (- (+ begin size) end)) | ||
| 945 | (widget-specify-field-update field | ||
| 946 | begin | ||
| 947 | (+ begin size)))) | ||
| 948 | ((> (- end begin) size) | ||
| 949 | ;; Field too large and | ||
| 950 | (if (or (< (point) (+ begin size)) | ||
| 951 | (> (point) end)) | ||
| 952 | ;; Point is outside extra space. | ||
| 953 | (setq begin (+ begin size)) | ||
| 954 | ;; Point is within the extra space. | ||
| 955 | (setq begin (point))) | ||
| 956 | (save-excursion | ||
| 957 | (goto-char end) | ||
| 958 | (while (and (eq (preceding-char) ?\ ) | ||
| 959 | (> (point) begin)) | ||
| 960 | (delete-backward-char 1)))))) | ||
| 961 | (widget-specify-field-update field from to))) | ||
| 962 | (widget-apply field :notify field)))) | ||
| 963 | (error (debug)))) | ||
| 964 | |||
| 965 | ;;; Widget Functions | ||
| 966 | ;; | ||
| 967 | ;; These functions are used in the definition of multiple widgets. | ||
| 968 | |||
| 969 | (defun widget-children-value-delete (widget) | ||
| 970 | "Delete all :children and :buttons in WIDGET." | ||
| 971 | (mapcar 'widget-delete (widget-get widget :children)) | ||
| 972 | (widget-put widget :children nil) | ||
| 973 | (mapcar 'widget-delete (widget-get widget :buttons)) | ||
| 974 | (widget-put widget :buttons nil)) | ||
| 975 | |||
| 976 | (defun widget-types-convert-widget (widget) | ||
| 977 | "Convert :args as widget types in WIDGET." | ||
| 978 | (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args))) | ||
| 979 | widget) | ||
| 980 | |||
| 981 | ;;; The `default' Widget. | ||
| 982 | |||
| 983 | (define-widget 'default nil | ||
| 984 | "Basic widget other widgets are derived from." | ||
| 985 | :value-to-internal (lambda (widget value) value) | ||
| 986 | :value-to-external (lambda (widget value) value) | ||
| 987 | :create 'widget-default-create | ||
| 988 | :indent nil | ||
| 989 | :offset 0 | ||
| 990 | :format-handler 'widget-default-format-handler | ||
| 991 | :button-face-get 'widget-default-button-face-get | ||
| 992 | :sample-face-get 'widget-default-sample-face-get | ||
| 993 | :delete 'widget-default-delete | ||
| 994 | :value-set 'widget-default-value-set | ||
| 995 | :value-inline 'widget-default-value-inline | ||
| 996 | :menu-tag-get 'widget-default-menu-tag-get | ||
| 997 | :validate (lambda (widget) nil) | ||
| 998 | :active 'widget-default-active | ||
| 999 | :activate 'widget-specify-active | ||
| 1000 | :deactivate 'widget-default-deactivate | ||
| 1001 | :action 'widget-default-action | ||
| 1002 | :notify 'widget-default-notify) | ||
| 1003 | |||
| 1004 | (defun widget-default-create (widget) | ||
| 1005 | "Create WIDGET at point in the current buffer." | ||
| 1006 | (widget-specify-insert | ||
| 1007 | (let ((from (point)) | ||
| 1008 | (tag (widget-get widget :tag)) | ||
| 1009 | (glyph (widget-get widget :tag-glyph)) | ||
| 1010 | (doc (widget-get widget :doc)) | ||
| 1011 | button-begin button-end | ||
| 1012 | sample-begin sample-end | ||
| 1013 | doc-begin doc-end | ||
| 1014 | value-pos) | ||
| 1015 | (insert (widget-get widget :format)) | ||
| 1016 | (goto-char from) | ||
| 1017 | ;; Parse escapes in format. | ||
| 1018 | (while (re-search-forward "%\\(.\\)" nil t) | ||
| 1019 | (let ((escape (aref (match-string 1) 0))) | ||
| 1020 | (replace-match "" t t) | ||
| 1021 | (cond ((eq escape ?%) | ||
| 1022 | (insert "%")) | ||
| 1023 | ((eq escape ?\[) | ||
| 1024 | (setq button-begin (point))) | ||
| 1025 | ((eq escape ?\]) | ||
| 1026 | (setq button-end (point))) | ||
| 1027 | ((eq escape ?\{) | ||
| 1028 | (setq sample-begin (point))) | ||
| 1029 | ((eq escape ?\}) | ||
| 1030 | (setq sample-end (point))) | ||
| 1031 | ((eq escape ?n) | ||
| 1032 | (when (widget-get widget :indent) | ||
| 1033 | (insert "\n") | ||
| 1034 | (insert-char ? (widget-get widget :indent)))) | ||
| 1035 | ((eq escape ?t) | ||
| 1036 | (cond (glyph | ||
| 1037 | (widget-glyph-insert widget (or tag "image") glyph)) | ||
| 1038 | (tag | ||
| 1039 | (insert tag)) | ||
| 1040 | (t | ||
| 1041 | (let ((standard-output (current-buffer))) | ||
| 1042 | (princ (widget-get widget :value)))))) | ||
| 1043 | ((eq escape ?d) | ||
| 1044 | (when doc | ||
| 1045 | (setq doc-begin (point)) | ||
| 1046 | (insert doc) | ||
| 1047 | (while (eq (preceding-char) ?\n) | ||
| 1048 | (delete-backward-char 1)) | ||
| 1049 | (insert "\n") | ||
| 1050 | (setq doc-end (point)))) | ||
| 1051 | ((eq escape ?v) | ||
| 1052 | (if (and button-begin (not button-end)) | ||
| 1053 | (widget-apply widget :value-create) | ||
| 1054 | (setq value-pos (point)))) | ||
| 1055 | (t | ||
| 1056 | (widget-apply widget :format-handler escape))))) | ||
| 1057 | ;; Specify button, sample, and doc, and insert value. | ||
| 1058 | (and button-begin button-end | ||
| 1059 | (widget-specify-button widget button-begin button-end)) | ||
| 1060 | (and sample-begin sample-end | ||
| 1061 | (widget-specify-sample widget sample-begin sample-end)) | ||
| 1062 | (and doc-begin doc-end | ||
| 1063 | (widget-specify-doc widget doc-begin doc-end)) | ||
| 1064 | (when value-pos | ||
| 1065 | (goto-char value-pos) | ||
| 1066 | (widget-apply widget :value-create))) | ||
| 1067 | (let ((from (copy-marker (point-min))) | ||
| 1068 | (to (copy-marker (point-max)))) | ||
| 1069 | (widget-specify-text from to) | ||
| 1070 | (set-marker-insertion-type from t) | ||
| 1071 | (set-marker-insertion-type to nil) | ||
| 1072 | (widget-put widget :from from) | ||
| 1073 | (widget-put widget :to to)))) | ||
| 1074 | |||
| 1075 | (defun widget-default-format-handler (widget escape) | ||
| 1076 | ;; We recognize the %h escape by default. | ||
| 1077 | (let* ((buttons (widget-get widget :buttons)) | ||
| 1078 | (doc-property (widget-get widget :documentation-property)) | ||
| 1079 | (doc-try (cond ((widget-get widget :doc)) | ||
| 1080 | ((symbolp doc-property) | ||
| 1081 | (documentation-property (widget-get widget :value) | ||
| 1082 | doc-property)) | ||
| 1083 | (t | ||
| 1084 | (funcall doc-property (widget-get widget :value))))) | ||
| 1085 | (doc-text (and (stringp doc-try) | ||
| 1086 | (> (length doc-try) 1) | ||
| 1087 | doc-try))) | ||
| 1088 | (cond ((eq escape ?h) | ||
| 1089 | (when doc-text | ||
| 1090 | (and (eq (preceding-char) ?\n) | ||
| 1091 | (widget-get widget :indent) | ||
| 1092 | (insert-char ? (widget-get widget :indent))) | ||
| 1093 | ;; The `*' in the beginning is redundant. | ||
| 1094 | (when (eq (aref doc-text 0) ?*) | ||
| 1095 | (setq doc-text (substring doc-text 1))) | ||
| 1096 | ;; Get rid of trailing newlines. | ||
| 1097 | (when (string-match "\n+\\'" doc-text) | ||
| 1098 | (setq doc-text (substring doc-text 0 (match-beginning 0)))) | ||
| 1099 | (push (if (string-match "\n." doc-text) | ||
| 1100 | ;; Allow multiline doc to be hiden. | ||
| 1101 | (widget-create-child-and-convert | ||
| 1102 | widget 'widget-help | ||
| 1103 | :doc (progn | ||
| 1104 | (string-match "\\`.*" doc-text) | ||
| 1105 | (match-string 0 doc-text)) | ||
| 1106 | :widget-doc doc-text | ||
| 1107 | "?") | ||
| 1108 | ;; A single line is just inserted. | ||
| 1109 | (widget-create-child-and-convert | ||
| 1110 | widget 'item :format "%d" :doc doc-text nil)) | ||
| 1111 | buttons))) | ||
| 1112 | (t | ||
| 1113 | (error "Unknown escape `%c'" escape))) | ||
| 1114 | (widget-put widget :buttons buttons))) | ||
| 1115 | |||
| 1116 | (defun widget-default-button-face-get (widget) | ||
| 1117 | ;; Use :button-face or widget-button-face | ||
| 1118 | (or (widget-get widget :button-face) 'widget-button-face)) | ||
| 1119 | |||
| 1120 | (defun widget-default-sample-face-get (widget) | ||
| 1121 | ;; Use :sample-face. | ||
| 1122 | (widget-get widget :sample-face)) | ||
| 1123 | |||
| 1124 | (defun widget-default-delete (widget) | ||
| 1125 | ;; Remove widget from the buffer. | ||
| 1126 | (let ((from (widget-get widget :from)) | ||
| 1127 | (to (widget-get widget :to)) | ||
| 1128 | (inhibit-read-only t) | ||
| 1129 | after-change-functions) | ||
| 1130 | (widget-apply widget :value-delete) | ||
| 1131 | (when (< from to) | ||
| 1132 | ;; Kludge: this doesn't need to be true for empty formats. | ||
| 1133 | (delete-region from to)) | ||
| 1134 | (set-marker from nil) | ||
| 1135 | (set-marker to nil))) | ||
| 1136 | |||
| 1137 | (defun widget-default-value-set (widget value) | ||
| 1138 | ;; Recreate widget with new value. | ||
| 1139 | (save-excursion | ||
| 1140 | (goto-char (widget-get widget :from)) | ||
| 1141 | (widget-apply widget :delete) | ||
| 1142 | (widget-put widget :value value) | ||
| 1143 | (widget-apply widget :create))) | ||
| 1144 | |||
| 1145 | (defun widget-default-value-inline (widget) | ||
| 1146 | ;; Wrap value in a list unless it is inline. | ||
| 1147 | (if (widget-get widget :inline) | ||
| 1148 | (widget-value widget) | ||
| 1149 | (list (widget-value widget)))) | ||
| 1150 | |||
| 1151 | (defun widget-default-menu-tag-get (widget) | ||
| 1152 | ;; Use tag or value for menus. | ||
| 1153 | (or (widget-get widget :menu-tag) | ||
| 1154 | (widget-get widget :tag) | ||
| 1155 | (widget-princ-to-string (widget-get widget :value)))) | ||
| 1156 | |||
| 1157 | (defun widget-default-active (widget) | ||
| 1158 | "Return t iff this widget active (user modifiable)." | ||
| 1159 | (and (not (widget-get widget :inactive)) | ||
| 1160 | (let ((parent (widget-get widget :parent))) | ||
| 1161 | (or (null parent) | ||
| 1162 | (widget-apply parent :active))))) | ||
| 1163 | |||
| 1164 | (defun widget-default-deactivate (widget) | ||
| 1165 | "Make WIDGET inactive for user modifications." | ||
| 1166 | (widget-specify-inactive widget | ||
| 1167 | (widget-get widget :from) | ||
| 1168 | (widget-get widget :to))) | ||
| 1169 | |||
| 1170 | (defun widget-default-action (widget &optional event) | ||
| 1171 | ;; Notify the parent when a widget change | ||
| 1172 | (let ((parent (widget-get widget :parent))) | ||
| 1173 | (when parent | ||
| 1174 | (widget-apply parent :notify widget event)))) | ||
| 1175 | |||
| 1176 | (defun widget-default-notify (widget child &optional event) | ||
| 1177 | ;; Pass notification to parent. | ||
| 1178 | (widget-default-action widget event)) | ||
| 1179 | |||
| 1180 | ;;; The `item' Widget. | ||
| 1181 | |||
| 1182 | (define-widget 'item 'default | ||
| 1183 | "Constant items for inclusion in other widgets." | ||
| 1184 | :convert-widget 'widget-item-convert-widget | ||
| 1185 | :value-create 'widget-item-value-create | ||
| 1186 | :value-delete 'ignore | ||
| 1187 | :value-get 'widget-item-value-get | ||
| 1188 | :match 'widget-item-match | ||
| 1189 | :match-inline 'widget-item-match-inline | ||
| 1190 | :action 'widget-item-action | ||
| 1191 | :format "%t\n") | ||
| 1192 | |||
| 1193 | (defun widget-item-convert-widget (widget) | ||
| 1194 | ;; Initialize :value from :args in WIDGET. | ||
| 1195 | (let ((args (widget-get widget :args))) | ||
| 1196 | (when args | ||
| 1197 | (widget-put widget :value (widget-apply widget | ||
| 1198 | :value-to-internal (car args))) | ||
| 1199 | (widget-put widget :args nil))) | ||
| 1200 | widget) | ||
| 1201 | |||
| 1202 | (defun widget-item-value-create (widget) | ||
| 1203 | ;; Insert the printed representation of the value. | ||
| 1204 | (let ((standard-output (current-buffer))) | ||
| 1205 | (princ (widget-get widget :value)))) | ||
| 1206 | |||
| 1207 | (defun widget-item-match (widget value) | ||
| 1208 | ;; Match if the value is the same. | ||
| 1209 | (equal (widget-get widget :value) value)) | ||
| 1210 | |||
| 1211 | (defun widget-item-match-inline (widget values) | ||
| 1212 | ;; Match if the value is the same. | ||
| 1213 | (let ((value (widget-get widget :value))) | ||
| 1214 | (and (listp value) | ||
| 1215 | (<= (length value) (length values)) | ||
| 1216 | (let ((head (subseq values 0 (length value)))) | ||
| 1217 | (and (equal head value) | ||
| 1218 | (cons head (subseq values (length value)))))))) | ||
| 1219 | |||
| 1220 | (defun widget-item-action (widget &optional event) | ||
| 1221 | ;; Just notify itself. | ||
| 1222 | (widget-apply widget :notify widget event)) | ||
| 1223 | |||
| 1224 | (defun widget-item-value-get (widget) | ||
| 1225 | ;; Items are simple. | ||
| 1226 | (widget-get widget :value)) | ||
| 1227 | |||
| 1228 | ;;; The `push-button' Widget. | ||
| 1229 | |||
| 1230 | (defcustom widget-push-button-gui t | ||
| 1231 | "If non nil, use GUI push buttons when available." | ||
| 1232 | :group 'widgets | ||
| 1233 | :type 'boolean) | ||
| 1234 | |||
| 1235 | ;; Cache already created GUI objects. | ||
| 1236 | (defvar widget-push-button-cache nil) | ||
| 1237 | |||
| 1238 | (define-widget 'push-button 'item | ||
| 1239 | "A pushable button." | ||
| 1240 | :value-create 'widget-push-button-value-create | ||
| 1241 | :format "%[%v%]") | ||
| 1242 | |||
| 1243 | (defun widget-push-button-value-create (widget) | ||
| 1244 | ;; Insert text representing the `on' and `off' states. | ||
| 1245 | (let* ((tag (or (widget-get widget :tag) | ||
| 1246 | (widget-get widget :value))) | ||
| 1247 | (text (concat "[" tag "]")) | ||
| 1248 | (gui (cdr (assoc tag widget-push-button-cache)))) | ||
| 1249 | (if (and (fboundp 'make-gui-button) | ||
| 1250 | (fboundp 'make-glyph) | ||
| 1251 | widget-push-button-gui | ||
| 1252 | (fboundp 'device-on-window-system-p) | ||
| 1253 | (device-on-window-system-p) | ||
| 1254 | (string-match "XEmacs" emacs-version)) | ||
| 1255 | (progn | ||
| 1256 | (unless gui | ||
| 1257 | (setq gui (make-gui-button tag 'widget-gui-action widget)) | ||
| 1258 | (push (cons tag gui) widget-push-button-cache)) | ||
| 1259 | (widget-glyph-insert-glyph widget text | ||
| 1260 | (make-glyph (car (aref gui 1))))) | ||
| 1261 | (insert text)))) | ||
| 1262 | |||
| 1263 | (defun widget-gui-action (widget) | ||
| 1264 | "Apply :action for WIDGET." | ||
| 1265 | (widget-apply-action widget (this-command-keys))) | ||
| 1266 | |||
| 1267 | ;;; The `link' Widget. | ||
| 1268 | |||
| 1269 | (define-widget 'link 'item | ||
| 1270 | "An embedded link." | ||
| 1271 | :help-echo "Follow the link." | ||
| 1272 | :format "%[_%t_%]") | ||
| 1273 | |||
| 1274 | ;;; The `info-link' Widget. | ||
| 1275 | |||
| 1276 | (define-widget 'info-link 'link | ||
| 1277 | "A link to an info file." | ||
| 1278 | :action 'widget-info-link-action) | ||
| 1279 | |||
| 1280 | (defun widget-info-link-action (widget &optional event) | ||
| 1281 | "Open the info node specified by WIDGET." | ||
| 1282 | (Info-goto-node (widget-value widget))) | ||
| 1283 | |||
| 1284 | ;;; The `url-link' Widget. | ||
| 1285 | |||
| 1286 | (define-widget 'url-link 'link | ||
| 1287 | "A link to an www page." | ||
| 1288 | :action 'widget-url-link-action) | ||
| 1289 | |||
| 1290 | (defun widget-url-link-action (widget &optional event) | ||
| 1291 | "Open the url specified by WIDGET." | ||
| 1292 | (require 'browse-url) | ||
| 1293 | (funcall browse-url-browser-function (widget-value widget))) | ||
| 1294 | |||
| 1295 | ;;; The `editable-field' Widget. | ||
| 1296 | |||
| 1297 | (define-widget 'editable-field 'default | ||
| 1298 | "An editable text field." | ||
| 1299 | :convert-widget 'widget-item-convert-widget | ||
| 1300 | :keymap widget-field-keymap | ||
| 1301 | :format "%v" | ||
| 1302 | :value "" | ||
| 1303 | :action 'widget-field-action | ||
| 1304 | :validate 'widget-field-validate | ||
| 1305 | :valid-regexp "" | ||
| 1306 | :error "No match" | ||
| 1307 | :value-create 'widget-field-value-create | ||
| 1308 | :value-delete 'widget-field-value-delete | ||
| 1309 | :value-get 'widget-field-value-get | ||
| 1310 | :match 'widget-field-match) | ||
| 1311 | |||
| 1312 | ;; History of field minibuffer edits. | ||
| 1313 | (defvar widget-field-history nil) | ||
| 1314 | |||
| 1315 | (defun widget-field-action (widget &optional event) | ||
| 1316 | ;; Edit the value in the minibuffer. | ||
| 1317 | (let ((tag (widget-apply widget :menu-tag-get)) | ||
| 1318 | (invalid (widget-apply widget :validate))) | ||
| 1319 | (when invalid | ||
| 1320 | (error (widget-get invalid :error))) | ||
| 1321 | (widget-value-set widget | ||
| 1322 | (widget-apply widget | ||
| 1323 | :value-to-external | ||
| 1324 | (read-string (concat tag ": ") | ||
| 1325 | (widget-apply | ||
| 1326 | widget | ||
| 1327 | :value-to-internal | ||
| 1328 | (widget-value widget)) | ||
| 1329 | 'widget-field-history))) | ||
| 1330 | (widget-apply widget :notify widget event) | ||
| 1331 | (widget-setup))) | ||
| 1332 | |||
| 1333 | (defun widget-field-validate (widget) | ||
| 1334 | ;; Valid if the content matches `:valid-regexp'. | ||
| 1335 | (save-excursion | ||
| 1336 | (let ((value (widget-apply widget :value-get)) | ||
| 1337 | (regexp (widget-get widget :valid-regexp))) | ||
| 1338 | (if (string-match regexp value) | ||
| 1339 | nil | ||
| 1340 | widget)))) | ||
| 1341 | |||
| 1342 | (defun widget-field-value-create (widget) | ||
| 1343 | ;; Create an editable text field. | ||
| 1344 | (insert " ") | ||
| 1345 | (let ((size (widget-get widget :size)) | ||
| 1346 | (value (widget-get widget :value)) | ||
| 1347 | (from (point))) | ||
| 1348 | (insert value) | ||
| 1349 | (and size | ||
| 1350 | (< (length value) size) | ||
| 1351 | (insert-char ?\ (- size (length value)))) | ||
| 1352 | (unless (memq widget widget-field-list) | ||
| 1353 | (setq widget-field-new (cons widget widget-field-new))) | ||
| 1354 | (widget-put widget :value-to (copy-marker (point))) | ||
| 1355 | (set-marker-insertion-type (widget-get widget :value-to) nil) | ||
| 1356 | (if (null size) | ||
| 1357 | (insert ?\n) | ||
| 1358 | (insert ?\ )) | ||
| 1359 | (widget-put widget :value-from (copy-marker from)) | ||
| 1360 | (set-marker-insertion-type (widget-get widget :value-from) t))) | ||
| 1361 | |||
| 1362 | (defun widget-field-value-delete (widget) | ||
| 1363 | ;; Remove the widget from the list of active editing fields. | ||
| 1364 | (setq widget-field-list (delq widget widget-field-list)) | ||
| 1365 | ;; These are nil if the :format string doesn't contain `%v'. | ||
| 1366 | (when (widget-get widget :value-from) | ||
| 1367 | (set-marker (widget-get widget :value-from) nil)) | ||
| 1368 | (when (widget-get widget :value-from) | ||
| 1369 | (set-marker (widget-get widget :value-to) nil))) | ||
| 1370 | |||
| 1371 | (defun widget-field-value-get (widget) | ||
| 1372 | ;; Return current text in editing field. | ||
| 1373 | (let ((from (widget-get widget :value-from)) | ||
| 1374 | (to (widget-get widget :value-to)) | ||
| 1375 | (size (widget-get widget :size)) | ||
| 1376 | (secret (widget-get widget :secret)) | ||
| 1377 | (old (current-buffer))) | ||
| 1378 | (if (and from to) | ||
| 1379 | (progn | ||
| 1380 | (set-buffer (marker-buffer from)) | ||
| 1381 | (setq from (1+ from) | ||
| 1382 | to (1- to)) | ||
| 1383 | (while (and size | ||
| 1384 | (not (zerop size)) | ||
| 1385 | (> to from) | ||
| 1386 | (eq (char-after (1- to)) ?\ )) | ||
| 1387 | (setq to (1- to))) | ||
| 1388 | (let ((result (buffer-substring-no-properties from to))) | ||
| 1389 | (when secret | ||
| 1390 | (let ((index 0)) | ||
| 1391 | (while (< (+ from index) to) | ||
| 1392 | (aset result index | ||
| 1393 | (get-text-property (+ from index) 'secret)) | ||
| 1394 | (setq index (1+ index))))) | ||
| 1395 | (set-buffer old) | ||
| 1396 | result)) | ||
| 1397 | (widget-get widget :value)))) | ||
| 1398 | |||
| 1399 | (defun widget-field-match (widget value) | ||
| 1400 | ;; Match any string. | ||
| 1401 | (stringp value)) | ||
| 1402 | |||
| 1403 | ;;; The `text' Widget. | ||
| 1404 | |||
| 1405 | (define-widget 'text 'editable-field | ||
| 1406 | :keymap widget-text-keymap | ||
| 1407 | "A multiline text area.") | ||
| 1408 | |||
| 1409 | ;;; The `menu-choice' Widget. | ||
| 1410 | |||
| 1411 | (define-widget 'menu-choice 'default | ||
| 1412 | "A menu of options." | ||
| 1413 | :convert-widget 'widget-types-convert-widget | ||
| 1414 | :format "%[%t%]: %v" | ||
| 1415 | :case-fold t | ||
| 1416 | :tag "choice" | ||
| 1417 | :void '(item :format "invalid (%t)\n") | ||
| 1418 | :value-create 'widget-choice-value-create | ||
| 1419 | :value-delete 'widget-children-value-delete | ||
| 1420 | :value-get 'widget-choice-value-get | ||
| 1421 | :value-inline 'widget-choice-value-inline | ||
| 1422 | :action 'widget-choice-action | ||
| 1423 | :error "Make a choice" | ||
| 1424 | :validate 'widget-choice-validate | ||
| 1425 | :match 'widget-choice-match | ||
| 1426 | :match-inline 'widget-choice-match-inline) | ||
| 1427 | |||
| 1428 | (defun widget-choice-value-create (widget) | ||
| 1429 | ;; Insert the first choice that matches the value. | ||
| 1430 | (let ((value (widget-get widget :value)) | ||
| 1431 | (args (widget-get widget :args)) | ||
| 1432 | current) | ||
| 1433 | (while args | ||
| 1434 | (setq current (car args) | ||
| 1435 | args (cdr args)) | ||
| 1436 | (when (widget-apply current :match value) | ||
| 1437 | (widget-put widget :children (list (widget-create-child-value | ||
| 1438 | widget current value))) | ||
| 1439 | (widget-put widget :choice current) | ||
| 1440 | (setq args nil | ||
| 1441 | current nil))) | ||
| 1442 | (when current | ||
| 1443 | (let ((void (widget-get widget :void))) | ||
| 1444 | (widget-put widget :children (list (widget-create-child-and-convert | ||
| 1445 | widget void :value value))) | ||
| 1446 | (widget-put widget :choice void))))) | ||
| 1447 | |||
| 1448 | (defun widget-choice-value-get (widget) | ||
| 1449 | ;; Get value of the child widget. | ||
| 1450 | (widget-value (car (widget-get widget :children)))) | ||
| 1451 | |||
| 1452 | (defun widget-choice-value-inline (widget) | ||
| 1453 | ;; Get value of the child widget. | ||
| 1454 | (widget-apply (car (widget-get widget :children)) :value-inline)) | ||
| 1455 | |||
| 1456 | (defun widget-choice-action (widget &optional event) | ||
| 1457 | ;; Make a choice. | ||
| 1458 | (let ((args (widget-get widget :args)) | ||
| 1459 | (old (widget-get widget :choice)) | ||
| 1460 | (tag (widget-apply widget :menu-tag-get)) | ||
| 1461 | (completion-ignore-case (widget-get widget :case-fold)) | ||
| 1462 | current choices) | ||
| 1463 | ;; Remember old value. | ||
| 1464 | (if (and old (not (widget-apply widget :validate))) | ||
| 1465 | (let* ((external (widget-value widget)) | ||
| 1466 | (internal (widget-apply old :value-to-internal external))) | ||
| 1467 | (widget-put old :value internal))) | ||
| 1468 | ;; Find new choice. | ||
| 1469 | (setq current | ||
| 1470 | (cond ((= (length args) 0) | ||
| 1471 | nil) | ||
| 1472 | ((= (length args) 1) | ||
| 1473 | (nth 0 args)) | ||
| 1474 | ((and (= (length args) 2) | ||
| 1475 | (memq old args)) | ||
| 1476 | (if (eq old (nth 0 args)) | ||
| 1477 | (nth 1 args) | ||
| 1478 | (nth 0 args))) | ||
| 1479 | (t | ||
| 1480 | (while args | ||
| 1481 | (setq current (car args) | ||
| 1482 | args (cdr args)) | ||
| 1483 | (setq choices | ||
| 1484 | (cons (cons (widget-apply current :menu-tag-get) | ||
| 1485 | current) | ||
| 1486 | choices))) | ||
| 1487 | (widget-choose tag (reverse choices) event)))) | ||
| 1488 | (when current | ||
| 1489 | (widget-value-set widget | ||
| 1490 | (widget-apply current :value-to-external | ||
| 1491 | (widget-get current :value))) | ||
| 1492 | (widget-apply widget :notify widget event) | ||
| 1493 | (widget-setup))) | ||
| 1494 | ;; Notify parent. | ||
| 1495 | (widget-apply widget :notify widget event) | ||
| 1496 | (widget-clear-undo)) | ||
| 1497 | |||
| 1498 | (defun widget-choice-validate (widget) | ||
| 1499 | ;; Valid if we have made a valid choice. | ||
| 1500 | (let ((void (widget-get widget :void)) | ||
| 1501 | (choice (widget-get widget :choice)) | ||
| 1502 | (child (car (widget-get widget :children)))) | ||
| 1503 | (if (eq void choice) | ||
| 1504 | widget | ||
| 1505 | (widget-apply child :validate)))) | ||
| 1506 | |||
| 1507 | (defun widget-choice-match (widget value) | ||
| 1508 | ;; Matches if one of the choices matches. | ||
| 1509 | (let ((args (widget-get widget :args)) | ||
| 1510 | current found) | ||
| 1511 | (while (and args (not found)) | ||
| 1512 | (setq current (car args) | ||
| 1513 | args (cdr args) | ||
| 1514 | found (widget-apply current :match value))) | ||
| 1515 | found)) | ||
| 1516 | |||
| 1517 | (defun widget-choice-match-inline (widget values) | ||
| 1518 | ;; Matches if one of the choices matches. | ||
| 1519 | (let ((args (widget-get widget :args)) | ||
| 1520 | current found) | ||
| 1521 | (while (and args (null found)) | ||
| 1522 | (setq current (car args) | ||
| 1523 | args (cdr args) | ||
| 1524 | found (widget-match-inline current values))) | ||
| 1525 | found)) | ||
| 1526 | |||
| 1527 | ;;; The `toggle' Widget. | ||
| 1528 | |||
| 1529 | (define-widget 'toggle 'item | ||
| 1530 | "Toggle between two states." | ||
| 1531 | :format "%[%v%]\n" | ||
| 1532 | :value-create 'widget-toggle-value-create | ||
| 1533 | :action 'widget-toggle-action | ||
| 1534 | :match (lambda (widget value) t) | ||
| 1535 | :on "on" | ||
| 1536 | :off "off") | ||
| 1537 | |||
| 1538 | (defun widget-toggle-value-create (widget) | ||
| 1539 | ;; Insert text representing the `on' and `off' states. | ||
| 1540 | (if (widget-value widget) | ||
| 1541 | (widget-glyph-insert widget | ||
| 1542 | (widget-get widget :on) | ||
| 1543 | (widget-get widget :on-glyph)) | ||
| 1544 | (widget-glyph-insert widget | ||
| 1545 | (widget-get widget :off) | ||
| 1546 | (widget-get widget :off-glyph)))) | ||
| 1547 | |||
| 1548 | (defun widget-toggle-action (widget &optional event) | ||
| 1549 | ;; Toggle value. | ||
| 1550 | (widget-value-set widget (not (widget-value widget))) | ||
| 1551 | (widget-apply widget :notify widget event)) | ||
| 1552 | |||
| 1553 | ;;; The `checkbox' Widget. | ||
| 1554 | |||
| 1555 | (define-widget 'checkbox 'toggle | ||
| 1556 | "A checkbox toggle." | ||
| 1557 | :format "%[%v%]" | ||
| 1558 | :on "[X]" | ||
| 1559 | :on-glyph "check1" | ||
| 1560 | :off "[ ]" | ||
| 1561 | :off-glyph "check0" | ||
| 1562 | :action 'widget-checkbox-action) | ||
| 1563 | |||
| 1564 | (defun widget-checkbox-action (widget &optional event) | ||
| 1565 | "Toggle checkbox, notify parent, and set active state of sibling." | ||
| 1566 | (widget-toggle-action widget event) | ||
| 1567 | (let ((sibling (widget-get-sibling widget))) | ||
| 1568 | (when sibling | ||
| 1569 | (if (widget-value widget) | ||
| 1570 | (widget-apply sibling :activate) | ||
| 1571 | (widget-apply sibling :deactivate))))) | ||
| 1572 | |||
| 1573 | ;;; The `checklist' Widget. | ||
| 1574 | |||
| 1575 | (define-widget 'checklist 'default | ||
| 1576 | "A multiple choice widget." | ||
| 1577 | :convert-widget 'widget-types-convert-widget | ||
| 1578 | :format "%v" | ||
| 1579 | :offset 4 | ||
| 1580 | :entry-format "%b %v" | ||
| 1581 | :menu-tag "checklist" | ||
| 1582 | :greedy nil | ||
| 1583 | :value-create 'widget-checklist-value-create | ||
| 1584 | :value-delete 'widget-children-value-delete | ||
| 1585 | :value-get 'widget-checklist-value-get | ||
| 1586 | :validate 'widget-checklist-validate | ||
| 1587 | :match 'widget-checklist-match | ||
| 1588 | :match-inline 'widget-checklist-match-inline) | ||
| 1589 | |||
| 1590 | (defun widget-checklist-value-create (widget) | ||
| 1591 | ;; Insert all values | ||
| 1592 | (let ((alist (widget-checklist-match-find widget (widget-get widget :value))) | ||
| 1593 | (args (widget-get widget :args))) | ||
| 1594 | (while args | ||
| 1595 | (widget-checklist-add-item widget (car args) (assq (car args) alist)) | ||
| 1596 | (setq args (cdr args))) | ||
| 1597 | (widget-put widget :children (nreverse (widget-get widget :children))))) | ||
| 1598 | |||
| 1599 | (defun widget-checklist-add-item (widget type chosen) | ||
| 1600 | ;; Create checklist item in WIDGET of type TYPE. | ||
| 1601 | ;; If the item is checked, CHOSEN is a cons whose cdr is the value. | ||
| 1602 | (and (eq (preceding-char) ?\n) | ||
| 1603 | (widget-get widget :indent) | ||
| 1604 | (insert-char ? (widget-get widget :indent))) | ||
| 1605 | (widget-specify-insert | ||
| 1606 | (let* ((children (widget-get widget :children)) | ||
| 1607 | (buttons (widget-get widget :buttons)) | ||
| 1608 | (button-args (or (widget-get type :sibling-args) | ||
| 1609 | (widget-get widget :button-args))) | ||
| 1610 | (from (point)) | ||
| 1611 | child button) | ||
| 1612 | (insert (widget-get widget :entry-format)) | ||
| 1613 | (goto-char from) | ||
| 1614 | ;; Parse % escapes in format. | ||
| 1615 | (while (re-search-forward "%\\([bv%]\\)" nil t) | ||
| 1616 | (let ((escape (aref (match-string 1) 0))) | ||
| 1617 | (replace-match "" t t) | ||
| 1618 | (cond ((eq escape ?%) | ||
| 1619 | (insert "%")) | ||
| 1620 | ((eq escape ?b) | ||
| 1621 | (setq button (apply 'widget-create-child-and-convert | ||
| 1622 | widget 'checkbox | ||
| 1623 | :value (not (null chosen)) | ||
| 1624 | button-args))) | ||
| 1625 | ((eq escape ?v) | ||
| 1626 | (setq child | ||
| 1627 | (cond ((not chosen) | ||
| 1628 | (let ((child (widget-create-child widget type))) | ||
| 1629 | (widget-apply child :deactivate) | ||
| 1630 | child)) | ||
| 1631 | ((widget-get type :inline) | ||
| 1632 | (widget-create-child-value | ||
| 1633 | widget type (cdr chosen))) | ||
| 1634 | (t | ||
| 1635 | (widget-create-child-value | ||
| 1636 | widget type (car (cdr chosen))))))) | ||
| 1637 | (t | ||
| 1638 | (error "Unknown escape `%c'" escape))))) | ||
| 1639 | ;; Update properties. | ||
| 1640 | (and button child (widget-put child :button button)) | ||
| 1641 | (and button (widget-put widget :buttons (cons button buttons))) | ||
| 1642 | (and child (widget-put widget :children (cons child children)))))) | ||
| 1643 | |||
| 1644 | (defun widget-checklist-match (widget values) | ||
| 1645 | ;; All values must match a type in the checklist. | ||
| 1646 | (and (listp values) | ||
| 1647 | (null (cdr (widget-checklist-match-inline widget values))))) | ||
| 1648 | |||
| 1649 | (defun widget-checklist-match-inline (widget values) | ||
| 1650 | ;; Find the values which match a type in the checklist. | ||
| 1651 | (let ((greedy (widget-get widget :greedy)) | ||
| 1652 | (args (copy-list (widget-get widget :args))) | ||
| 1653 | found rest) | ||
| 1654 | (while values | ||
| 1655 | (let ((answer (widget-checklist-match-up args values))) | ||
| 1656 | (cond (answer | ||
| 1657 | (let ((vals (widget-match-inline answer values))) | ||
| 1658 | (setq found (append found (car vals)) | ||
| 1659 | values (cdr vals) | ||
| 1660 | args (delq answer args)))) | ||
| 1661 | (greedy | ||
| 1662 | (setq rest (append rest (list (car values))) | ||
| 1663 | values (cdr values))) | ||
| 1664 | (t | ||
| 1665 | (setq rest (append rest values) | ||
| 1666 | values nil))))) | ||
| 1667 | (cons found rest))) | ||
| 1668 | |||
| 1669 | (defun widget-checklist-match-find (widget vals) | ||
| 1670 | ;; Find the vals which match a type in the checklist. | ||
| 1671 | ;; Return an alist of (TYPE MATCH). | ||
| 1672 | (let ((greedy (widget-get widget :greedy)) | ||
| 1673 | (args (copy-list (widget-get widget :args))) | ||
| 1674 | found) | ||
| 1675 | (while vals | ||
| 1676 | (let ((answer (widget-checklist-match-up args vals))) | ||
| 1677 | (cond (answer | ||
| 1678 | (let ((match (widget-match-inline answer vals))) | ||
| 1679 | (setq found (cons (cons answer (car match)) found) | ||
| 1680 | vals (cdr match) | ||
| 1681 | args (delq answer args)))) | ||
| 1682 | (greedy | ||
| 1683 | (setq vals (cdr vals))) | ||
| 1684 | (t | ||
| 1685 | (setq vals nil))))) | ||
| 1686 | found)) | ||
| 1687 | |||
| 1688 | (defun widget-checklist-match-up (args vals) | ||
| 1689 | ;; Rerturn the first type from ARGS that matches VALS. | ||
| 1690 | (let (current found) | ||
| 1691 | (while (and args (null found)) | ||
| 1692 | (setq current (car args) | ||
| 1693 | args (cdr args) | ||
| 1694 | found (widget-match-inline current vals))) | ||
| 1695 | (if found | ||
| 1696 | current | ||
| 1697 | nil))) | ||
| 1698 | |||
| 1699 | (defun widget-checklist-value-get (widget) | ||
| 1700 | ;; The values of all selected items. | ||
| 1701 | (let ((children (widget-get widget :children)) | ||
| 1702 | child result) | ||
| 1703 | (while children | ||
| 1704 | (setq child (car children) | ||
| 1705 | children (cdr children)) | ||
| 1706 | (if (widget-value (widget-get child :button)) | ||
| 1707 | (setq result (append result (widget-apply child :value-inline))))) | ||
| 1708 | result)) | ||
| 1709 | |||
| 1710 | (defun widget-checklist-validate (widget) | ||
| 1711 | ;; Ticked chilren must be valid. | ||
| 1712 | (let ((children (widget-get widget :children)) | ||
| 1713 | child button found) | ||
| 1714 | (while (and children (not found)) | ||
| 1715 | (setq child (car children) | ||
| 1716 | children (cdr children) | ||
| 1717 | button (widget-get child :button) | ||
| 1718 | found (and (widget-value button) | ||
| 1719 | (widget-apply child :validate)))) | ||
| 1720 | found)) | ||
| 1721 | |||
| 1722 | ;;; The `option' Widget | ||
| 1723 | |||
| 1724 | (define-widget 'option 'checklist | ||
| 1725 | "An widget with an optional item." | ||
| 1726 | :inline t) | ||
| 1727 | |||
| 1728 | ;;; The `choice-item' Widget. | ||
| 1729 | |||
| 1730 | (define-widget 'choice-item 'item | ||
| 1731 | "Button items that delegate action events to their parents." | ||
| 1732 | :action 'widget-choice-item-action | ||
| 1733 | :format "%[%t%] \n") | ||
| 1734 | |||
| 1735 | (defun widget-choice-item-action (widget &optional event) | ||
| 1736 | ;; Tell parent what happened. | ||
| 1737 | (widget-apply (widget-get widget :parent) :action event)) | ||
| 1738 | |||
| 1739 | ;;; The `radio-button' Widget. | ||
| 1740 | |||
| 1741 | (define-widget 'radio-button 'toggle | ||
| 1742 | "A radio button for use in the `radio' widget." | ||
| 1743 | :notify 'widget-radio-button-notify | ||
| 1744 | :format "%[%v%]" | ||
| 1745 | :on "(*)" | ||
| 1746 | :on-glyph "radio1" | ||
| 1747 | :off "( )" | ||
| 1748 | :off-glyph "radio0") | ||
| 1749 | |||
| 1750 | (defun widget-radio-button-notify (widget child &optional event) | ||
| 1751 | ;; Tell daddy. | ||
| 1752 | (widget-apply (widget-get widget :parent) :action widget event)) | ||
| 1753 | |||
| 1754 | ;;; The `radio-button-choice' Widget. | ||
| 1755 | |||
| 1756 | (define-widget 'radio-button-choice 'default | ||
| 1757 | "Select one of multiple options." | ||
| 1758 | :convert-widget 'widget-types-convert-widget | ||
| 1759 | :offset 4 | ||
| 1760 | :format "%v" | ||
| 1761 | :entry-format "%b %v" | ||
| 1762 | :menu-tag "radio" | ||
| 1763 | :value-create 'widget-radio-value-create | ||
| 1764 | :value-delete 'widget-children-value-delete | ||
| 1765 | :value-get 'widget-radio-value-get | ||
| 1766 | :value-inline 'widget-radio-value-inline | ||
| 1767 | :value-set 'widget-radio-value-set | ||
| 1768 | :error "You must push one of the buttons" | ||
| 1769 | :validate 'widget-radio-validate | ||
| 1770 | :match 'widget-choice-match | ||
| 1771 | :match-inline 'widget-choice-match-inline | ||
| 1772 | :action 'widget-radio-action) | ||
| 1773 | |||
| 1774 | (defun widget-radio-value-create (widget) | ||
| 1775 | ;; Insert all values | ||
| 1776 | (let ((args (widget-get widget :args)) | ||
| 1777 | arg) | ||
| 1778 | (while args | ||
| 1779 | (setq arg (car args) | ||
| 1780 | args (cdr args)) | ||
| 1781 | (widget-radio-add-item widget arg)))) | ||
| 1782 | |||
| 1783 | (defun widget-radio-add-item (widget type) | ||
| 1784 | "Add to radio widget WIDGET a new radio button item of type TYPE." | ||
| 1785 | ;; (setq type (widget-convert type)) | ||
| 1786 | (and (eq (preceding-char) ?\n) | ||
| 1787 | (widget-get widget :indent) | ||
| 1788 | (insert-char ? (widget-get widget :indent))) | ||
| 1789 | (widget-specify-insert | ||
| 1790 | (let* ((value (widget-get widget :value)) | ||
| 1791 | (children (widget-get widget :children)) | ||
| 1792 | (buttons (widget-get widget :buttons)) | ||
| 1793 | (button-args (or (widget-get type :sibling-args) | ||
| 1794 | (widget-get widget :button-args))) | ||
| 1795 | (from (point)) | ||
| 1796 | (chosen (and (null (widget-get widget :choice)) | ||
| 1797 | (widget-apply type :match value))) | ||
| 1798 | child button) | ||
| 1799 | (insert (widget-get widget :entry-format)) | ||
| 1800 | (goto-char from) | ||
| 1801 | ;; Parse % escapes in format. | ||
| 1802 | (while (re-search-forward "%\\([bv%]\\)" nil t) | ||
| 1803 | (let ((escape (aref (match-string 1) 0))) | ||
| 1804 | (replace-match "" t t) | ||
| 1805 | (cond ((eq escape ?%) | ||
| 1806 | (insert "%")) | ||
| 1807 | ((eq escape ?b) | ||
| 1808 | (setq button (apply 'widget-create-child-and-convert | ||
| 1809 | widget 'radio-button | ||
| 1810 | :value (not (null chosen)) | ||
| 1811 | button-args))) | ||
| 1812 | ((eq escape ?v) | ||
| 1813 | (setq child (if chosen | ||
| 1814 | (widget-create-child-value | ||
| 1815 | widget type value) | ||
| 1816 | (widget-create-child widget type))) | ||
| 1817 | (unless chosen | ||
| 1818 | (widget-apply child :deactivate))) | ||
| 1819 | (t | ||
| 1820 | (error "Unknown escape `%c'" escape))))) | ||
| 1821 | ;; Update properties. | ||
| 1822 | (when chosen | ||
| 1823 | (widget-put widget :choice type)) | ||
| 1824 | (when button | ||
| 1825 | (widget-put child :button button) | ||
| 1826 | (widget-put widget :buttons (nconc buttons (list button)))) | ||
| 1827 | (when child | ||
| 1828 | (widget-put widget :children (nconc children (list child)))) | ||
| 1829 | child))) | ||
| 1830 | |||
| 1831 | (defun widget-radio-value-get (widget) | ||
| 1832 | ;; Get value of the child widget. | ||
| 1833 | (let ((chosen (widget-radio-chosen widget))) | ||
| 1834 | (and chosen (widget-value chosen)))) | ||
| 1835 | |||
| 1836 | (defun widget-radio-chosen (widget) | ||
| 1837 | "Return the widget representing the chosen radio button." | ||
| 1838 | (let ((children (widget-get widget :children)) | ||
| 1839 | current found) | ||
| 1840 | (while children | ||
| 1841 | (setq current (car children) | ||
| 1842 | children (cdr children)) | ||
| 1843 | (let* ((button (widget-get current :button)) | ||
| 1844 | (value (widget-apply button :value-get))) | ||
| 1845 | (when value | ||
| 1846 | (setq found current | ||
| 1847 | children nil)))) | ||
| 1848 | found)) | ||
| 1849 | |||
| 1850 | (defun widget-radio-value-inline (widget) | ||
| 1851 | ;; Get value of the child widget. | ||
| 1852 | (let ((children (widget-get widget :children)) | ||
| 1853 | current found) | ||
| 1854 | (while children | ||
| 1855 | (setq current (car children) | ||
| 1856 | children (cdr children)) | ||
| 1857 | (let* ((button (widget-get current :button)) | ||
| 1858 | (value (widget-apply button :value-get))) | ||
| 1859 | (when value | ||
| 1860 | (setq found (widget-apply current :value-inline) | ||
| 1861 | children nil)))) | ||
| 1862 | found)) | ||
| 1863 | |||
| 1864 | (defun widget-radio-value-set (widget value) | ||
| 1865 | ;; We can't just delete and recreate a radio widget, since children | ||
| 1866 | ;; can be added after the original creation and won't be recreated | ||
| 1867 | ;; by `:create'. | ||
| 1868 | (let ((children (widget-get widget :children)) | ||
| 1869 | current found) | ||
| 1870 | (while children | ||
| 1871 | (setq current (car children) | ||
| 1872 | children (cdr children)) | ||
| 1873 | (let* ((button (widget-get current :button)) | ||
| 1874 | (match (and (not found) | ||
| 1875 | (widget-apply current :match value)))) | ||
| 1876 | (widget-value-set button match) | ||
| 1877 | (if match | ||
| 1878 | (progn | ||
| 1879 | (widget-value-set current value) | ||
| 1880 | (widget-apply current :activate)) | ||
| 1881 | (widget-apply current :deactivate)) | ||
| 1882 | (setq found (or found match)))))) | ||
| 1883 | |||
| 1884 | (defun widget-radio-validate (widget) | ||
| 1885 | ;; Valid if we have made a valid choice. | ||
| 1886 | (let ((children (widget-get widget :children)) | ||
| 1887 | current found button) | ||
| 1888 | (while (and children (not found)) | ||
| 1889 | (setq current (car children) | ||
| 1890 | children (cdr children) | ||
| 1891 | button (widget-get current :button) | ||
| 1892 | found (widget-apply button :value-get))) | ||
| 1893 | (if found | ||
| 1894 | (widget-apply current :validate) | ||
| 1895 | widget))) | ||
| 1896 | |||
| 1897 | (defun widget-radio-action (widget child event) | ||
| 1898 | ;; Check if a radio button was pressed. | ||
| 1899 | (let ((children (widget-get widget :children)) | ||
| 1900 | (buttons (widget-get widget :buttons)) | ||
| 1901 | current) | ||
| 1902 | (when (memq child buttons) | ||
| 1903 | (while children | ||
| 1904 | (setq current (car children) | ||
| 1905 | children (cdr children)) | ||
| 1906 | (let* ((button (widget-get current :button))) | ||
| 1907 | (cond ((eq child button) | ||
| 1908 | (widget-value-set button t) | ||
| 1909 | (widget-apply current :activate)) | ||
| 1910 | ((widget-value button) | ||
| 1911 | (widget-value-set button nil) | ||
| 1912 | (widget-apply current :deactivate))))))) | ||
| 1913 | ;; Pass notification to parent. | ||
| 1914 | (widget-apply widget :notify child event)) | ||
| 1915 | |||
| 1916 | ;;; The `insert-button' Widget. | ||
| 1917 | |||
| 1918 | (define-widget 'insert-button 'push-button | ||
| 1919 | "An insert button for the `editable-list' widget." | ||
| 1920 | :tag "INS" | ||
| 1921 | :help-echo "Insert a new item into the list at this position." | ||
| 1922 | :action 'widget-insert-button-action) | ||
| 1923 | |||
| 1924 | (defun widget-insert-button-action (widget &optional event) | ||
| 1925 | ;; Ask the parent to insert a new item. | ||
| 1926 | (widget-apply (widget-get widget :parent) | ||
| 1927 | :insert-before (widget-get widget :widget))) | ||
| 1928 | |||
| 1929 | ;;; The `delete-button' Widget. | ||
| 1930 | |||
| 1931 | (define-widget 'delete-button 'push-button | ||
| 1932 | "A delete button for the `editable-list' widget." | ||
| 1933 | :tag "DEL" | ||
| 1934 | :help-echo "Delete this item from the list." | ||
| 1935 | :action 'widget-delete-button-action) | ||
| 1936 | |||
| 1937 | (defun widget-delete-button-action (widget &optional event) | ||
| 1938 | ;; Ask the parent to insert a new item. | ||
| 1939 | (widget-apply (widget-get widget :parent) | ||
| 1940 | :delete-at (widget-get widget :widget))) | ||
| 1941 | |||
| 1942 | ;;; The `editable-list' Widget. | ||
| 1943 | |||
| 1944 | (defcustom widget-editable-list-gui nil | ||
| 1945 | "If non nil, use GUI push-buttons in editable list when available." | ||
| 1946 | :type 'boolean | ||
| 1947 | :group 'widgets) | ||
| 1948 | |||
| 1949 | (define-widget 'editable-list 'default | ||
| 1950 | "A variable list of widgets of the same type." | ||
| 1951 | :convert-widget 'widget-types-convert-widget | ||
| 1952 | :offset 12 | ||
| 1953 | :format "%v%i\n" | ||
| 1954 | :format-handler 'widget-editable-list-format-handler | ||
| 1955 | :entry-format "%i %d %v" | ||
| 1956 | :menu-tag "editable-list" | ||
| 1957 | :value-create 'widget-editable-list-value-create | ||
| 1958 | :value-delete 'widget-children-value-delete | ||
| 1959 | :value-get 'widget-editable-list-value-get | ||
| 1960 | :validate 'widget-editable-list-validate | ||
| 1961 | :match 'widget-editable-list-match | ||
| 1962 | :match-inline 'widget-editable-list-match-inline | ||
| 1963 | :insert-before 'widget-editable-list-insert-before | ||
| 1964 | :delete-at 'widget-editable-list-delete-at) | ||
| 1965 | |||
| 1966 | (defun widget-editable-list-format-handler (widget escape) | ||
| 1967 | ;; We recognize the insert button. | ||
| 1968 | (let ((widget-push-button-gui widget-editable-list-gui)) | ||
| 1969 | (cond ((eq escape ?i) | ||
| 1970 | (and (widget-get widget :indent) | ||
| 1971 | (insert-char ? (widget-get widget :indent))) | ||
| 1972 | (apply 'widget-create-child-and-convert | ||
| 1973 | widget 'insert-button | ||
| 1974 | (widget-get widget :append-button-args))) | ||
| 1975 | (t | ||
| 1976 | (widget-default-format-handler widget escape))))) | ||
| 1977 | |||
| 1978 | (defun widget-editable-list-value-create (widget) | ||
| 1979 | ;; Insert all values | ||
| 1980 | (let* ((value (widget-get widget :value)) | ||
| 1981 | (type (nth 0 (widget-get widget :args))) | ||
| 1982 | (inlinep (widget-get type :inline)) | ||
| 1983 | children) | ||
| 1984 | (widget-put widget :value-pos (copy-marker (point))) | ||
| 1985 | (set-marker-insertion-type (widget-get widget :value-pos) t) | ||
| 1986 | (while value | ||
| 1987 | (let ((answer (widget-match-inline type value))) | ||
| 1988 | (if answer | ||
| 1989 | (setq children (cons (widget-editable-list-entry-create | ||
| 1990 | widget | ||
| 1991 | (if inlinep | ||
| 1992 | (car answer) | ||
| 1993 | (car (car answer))) | ||
| 1994 | t) | ||
| 1995 | children) | ||
| 1996 | value (cdr answer)) | ||
| 1997 | (setq value nil)))) | ||
| 1998 | (widget-put widget :children (nreverse children)))) | ||
| 1999 | |||
| 2000 | (defun widget-editable-list-value-get (widget) | ||
| 2001 | ;; Get value of the child widget. | ||
| 2002 | (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline)) | ||
| 2003 | (widget-get widget :children)))) | ||
| 2004 | |||
| 2005 | (defun widget-editable-list-validate (widget) | ||
| 2006 | ;; All the chilren must be valid. | ||
| 2007 | (let ((children (widget-get widget :children)) | ||
| 2008 | child found) | ||
| 2009 | (while (and children (not found)) | ||
| 2010 | (setq child (car children) | ||
| 2011 | children (cdr children) | ||
| 2012 | found (widget-apply child :validate))) | ||
| 2013 | found)) | ||
| 2014 | |||
| 2015 | (defun widget-editable-list-match (widget value) | ||
| 2016 | ;; Value must be a list and all the members must match the type. | ||
| 2017 | (and (listp value) | ||
| 2018 | (null (cdr (widget-editable-list-match-inline widget value))))) | ||
| 2019 | |||
| 2020 | (defun widget-editable-list-match-inline (widget value) | ||
| 2021 | (let ((type (nth 0 (widget-get widget :args))) | ||
| 2022 | (ok t) | ||
| 2023 | found) | ||
| 2024 | (while (and value ok) | ||
| 2025 | (let ((answer (widget-match-inline type value))) | ||
| 2026 | (if answer | ||
| 2027 | (setq found (append found (car answer)) | ||
| 2028 | value (cdr answer)) | ||
| 2029 | (setq ok nil)))) | ||
| 2030 | (cons found value))) | ||
| 2031 | |||
| 2032 | (defun widget-editable-list-insert-before (widget before) | ||
| 2033 | ;; Insert a new child in the list of children. | ||
| 2034 | (save-excursion | ||
| 2035 | (let ((children (widget-get widget :children)) | ||
| 2036 | (inhibit-read-only t) | ||
| 2037 | after-change-functions) | ||
| 2038 | (cond (before | ||
| 2039 | (goto-char (widget-get before :entry-from))) | ||
| 2040 | (t | ||
| 2041 | (goto-char (widget-get widget :value-pos)))) | ||
| 2042 | (let ((child (widget-editable-list-entry-create | ||
| 2043 | widget nil nil))) | ||
| 2044 | (when (< (widget-get child :entry-from) (widget-get widget :from)) | ||
| 2045 | (set-marker (widget-get widget :from) | ||
| 2046 | (widget-get child :entry-from))) | ||
| 2047 | (widget-specify-text (widget-get child :entry-from) | ||
| 2048 | (widget-get child :entry-to)) | ||
| 2049 | (if (eq (car children) before) | ||
| 2050 | (widget-put widget :children (cons child children)) | ||
| 2051 | (while (not (eq (car (cdr children)) before)) | ||
| 2052 | (setq children (cdr children))) | ||
| 2053 | (setcdr children (cons child (cdr children))))))) | ||
| 2054 | (widget-setup) | ||
| 2055 | widget (widget-apply widget :notify widget)) | ||
| 2056 | |||
| 2057 | (defun widget-editable-list-delete-at (widget child) | ||
| 2058 | ;; Delete child from list of children. | ||
| 2059 | (save-excursion | ||
| 2060 | (let ((buttons (copy-list (widget-get widget :buttons))) | ||
| 2061 | button | ||
| 2062 | (inhibit-read-only t) | ||
| 2063 | after-change-functions) | ||
| 2064 | (while buttons | ||
| 2065 | (setq button (car buttons) | ||
| 2066 | buttons (cdr buttons)) | ||
| 2067 | (when (eq (widget-get button :widget) child) | ||
| 2068 | (widget-put widget | ||
| 2069 | :buttons (delq button (widget-get widget :buttons))) | ||
| 2070 | (widget-delete button)))) | ||
| 2071 | (let ((entry-from (widget-get child :entry-from)) | ||
| 2072 | (entry-to (widget-get child :entry-to)) | ||
| 2073 | (inhibit-read-only t) | ||
| 2074 | after-change-functions) | ||
| 2075 | (widget-delete child) | ||
| 2076 | (delete-region entry-from entry-to) | ||
| 2077 | (set-marker entry-from nil) | ||
| 2078 | (set-marker entry-to nil)) | ||
| 2079 | (widget-put widget :children (delq child (widget-get widget :children)))) | ||
| 2080 | (widget-setup) | ||
| 2081 | (widget-apply widget :notify widget)) | ||
| 2082 | |||
| 2083 | (defun widget-editable-list-entry-create (widget value conv) | ||
| 2084 | ;; Create a new entry to the list. | ||
| 2085 | (let ((type (nth 0 (widget-get widget :args))) | ||
| 2086 | (widget-push-button-gui widget-editable-list-gui) | ||
| 2087 | child delete insert) | ||
| 2088 | (widget-specify-insert | ||
| 2089 | (save-excursion | ||
| 2090 | (and (widget-get widget :indent) | ||
| 2091 | (insert-char ? (widget-get widget :indent))) | ||
| 2092 | (insert (widget-get widget :entry-format))) | ||
| 2093 | ;; Parse % escapes in format. | ||
| 2094 | (while (re-search-forward "%\\(.\\)" nil t) | ||
| 2095 | (let ((escape (aref (match-string 1) 0))) | ||
| 2096 | (replace-match "" t t) | ||
| 2097 | (cond ((eq escape ?%) | ||
| 2098 | (insert "%")) | ||
| 2099 | ((eq escape ?i) | ||
| 2100 | (setq insert (apply 'widget-create-child-and-convert | ||
| 2101 | widget 'insert-button | ||
| 2102 | (widget-get widget :insert-button-args)))) | ||
| 2103 | ((eq escape ?d) | ||
| 2104 | (setq delete (apply 'widget-create-child-and-convert | ||
| 2105 | widget 'delete-button | ||
| 2106 | (widget-get widget :delete-button-args)))) | ||
| 2107 | ((eq escape ?v) | ||
| 2108 | (if conv | ||
| 2109 | (setq child (widget-create-child-value | ||
| 2110 | widget type value)) | ||
| 2111 | (setq child (widget-create-child widget type)))) | ||
| 2112 | (t | ||
| 2113 | (error "Unknown escape `%c'" escape))))) | ||
| 2114 | (widget-put widget | ||
| 2115 | :buttons (cons delete | ||
| 2116 | (cons insert | ||
| 2117 | (widget-get widget :buttons)))) | ||
| 2118 | (let ((entry-from (copy-marker (point-min))) | ||
| 2119 | (entry-to (copy-marker (point-max)))) | ||
| 2120 | (widget-specify-text entry-from entry-to) | ||
| 2121 | (set-marker-insertion-type entry-from t) | ||
| 2122 | (set-marker-insertion-type entry-to nil) | ||
| 2123 | (widget-put child :entry-from entry-from) | ||
| 2124 | (widget-put child :entry-to entry-to))) | ||
| 2125 | (widget-put insert :widget child) | ||
| 2126 | (widget-put delete :widget child) | ||
| 2127 | child)) | ||
| 2128 | |||
| 2129 | ;;; The `group' Widget. | ||
| 2130 | |||
| 2131 | (define-widget 'group 'default | ||
| 2132 | "A widget which group other widgets inside." | ||
| 2133 | :convert-widget 'widget-types-convert-widget | ||
| 2134 | :format "%v" | ||
| 2135 | :value-create 'widget-group-value-create | ||
| 2136 | :value-delete 'widget-children-value-delete | ||
| 2137 | :value-get 'widget-editable-list-value-get | ||
| 2138 | :validate 'widget-editable-list-validate | ||
| 2139 | :match 'widget-group-match | ||
| 2140 | :match-inline 'widget-group-match-inline) | ||
| 2141 | |||
| 2142 | (defun widget-group-value-create (widget) | ||
| 2143 | ;; Create each component. | ||
| 2144 | (let ((args (widget-get widget :args)) | ||
| 2145 | (value (widget-get widget :value)) | ||
| 2146 | arg answer children) | ||
| 2147 | (while args | ||
| 2148 | (setq arg (car args) | ||
| 2149 | args (cdr args) | ||
| 2150 | answer (widget-match-inline arg value) | ||
| 2151 | value (cdr answer)) | ||
| 2152 | (and (eq (preceding-char) ?\n) | ||
| 2153 | (widget-get widget :indent) | ||
| 2154 | (insert-char ? (widget-get widget :indent))) | ||
| 2155 | (push (cond ((null answer) | ||
| 2156 | (widget-create-child widget arg)) | ||
| 2157 | ((widget-get arg :inline) | ||
| 2158 | (widget-create-child-value widget arg (car answer))) | ||
| 2159 | (t | ||
| 2160 | (widget-create-child-value widget arg (car (car answer))))) | ||
| 2161 | children)) | ||
| 2162 | (widget-put widget :children (nreverse children)))) | ||
| 2163 | |||
| 2164 | (defun widget-group-match (widget values) | ||
| 2165 | ;; Match if the components match. | ||
| 2166 | (and (listp values) | ||
| 2167 | (let ((match (widget-group-match-inline widget values))) | ||
| 2168 | (and match (null (cdr match)))))) | ||
| 2169 | |||
| 2170 | (defun widget-group-match-inline (widget vals) | ||
| 2171 | ;; Match if the components match. | ||
| 2172 | (let ((args (widget-get widget :args)) | ||
| 2173 | argument answer found) | ||
| 2174 | (while args | ||
| 2175 | (setq argument (car args) | ||
| 2176 | args (cdr args) | ||
| 2177 | answer (widget-match-inline argument vals)) | ||
| 2178 | (if answer | ||
| 2179 | (setq vals (cdr answer) | ||
| 2180 | found (append found (car answer))) | ||
| 2181 | (setq vals nil | ||
| 2182 | args nil))) | ||
| 2183 | (if answer | ||
| 2184 | (cons found vals) | ||
| 2185 | nil))) | ||
| 2186 | |||
| 2187 | ;;; The `widget-help' Widget. | ||
| 2188 | |||
| 2189 | (define-widget 'widget-help 'push-button | ||
| 2190 | "The widget documentation button." | ||
| 2191 | :format "%[[%t]%] %d" | ||
| 2192 | :help-echo "Toggle display of documentation." | ||
| 2193 | :action 'widget-help-action) | ||
| 2194 | |||
| 2195 | (defun widget-help-action (widget &optional event) | ||
| 2196 | "Toggle documentation for WIDGET." | ||
| 2197 | (let ((old (widget-get widget :doc)) | ||
| 2198 | (new (widget-get widget :widget-doc))) | ||
| 2199 | (widget-put widget :doc new) | ||
| 2200 | (widget-put widget :widget-doc old)) | ||
| 2201 | (widget-value-set widget (widget-value widget))) | ||
| 2202 | |||
| 2203 | ;;; The Sexp Widgets. | ||
| 2204 | |||
| 2205 | (define-widget 'const 'item | ||
| 2206 | "An immutable sexp." | ||
| 2207 | :format "%t\n%d") | ||
| 2208 | |||
| 2209 | (define-widget 'function-item 'item | ||
| 2210 | "An immutable function name." | ||
| 2211 | :format "%v\n%h" | ||
| 2212 | :documentation-property (lambda (symbol) | ||
| 2213 | (condition-case nil | ||
| 2214 | (documentation symbol t) | ||
| 2215 | (error nil)))) | ||
| 2216 | |||
| 2217 | (define-widget 'variable-item 'item | ||
| 2218 | "An immutable variable name." | ||
| 2219 | :format "%v\n%h" | ||
| 2220 | :documentation-property 'variable-documentation) | ||
| 2221 | |||
| 2222 | (define-widget 'string 'editable-field | ||
| 2223 | "A string" | ||
| 2224 | :tag "String" | ||
| 2225 | :format "%[%t%]: %v") | ||
| 2226 | |||
| 2227 | (define-widget 'regexp 'string | ||
| 2228 | "A regular expression." | ||
| 2229 | ;; Should do validation. | ||
| 2230 | :tag "Regexp") | ||
| 2231 | |||
| 2232 | (define-widget 'file 'string | ||
| 2233 | "A file widget. | ||
| 2234 | It will read a file name from the minibuffer when activated." | ||
| 2235 | :format "%[%t%]: %v" | ||
| 2236 | :tag "File" | ||
| 2237 | :action 'widget-file-action) | ||
| 2238 | |||
| 2239 | (defun widget-file-action (widget &optional event) | ||
| 2240 | ;; Read a file name from the minibuffer. | ||
| 2241 | (let* ((value (widget-value widget)) | ||
| 2242 | (dir (file-name-directory value)) | ||
| 2243 | (file (file-name-nondirectory value)) | ||
| 2244 | (menu-tag (widget-apply widget :menu-tag-get)) | ||
| 2245 | (must-match (widget-get widget :must-match)) | ||
| 2246 | (answer (read-file-name (concat menu-tag ": (default `" value "') ") | ||
| 2247 | dir nil must-match file))) | ||
| 2248 | (widget-value-set widget (abbreviate-file-name answer)) | ||
| 2249 | (widget-apply widget :notify widget event) | ||
| 2250 | (widget-setup))) | ||
| 2251 | |||
| 2252 | (define-widget 'directory 'file | ||
| 2253 | "A directory widget. | ||
| 2254 | It will read a directory name from the minibuffer when activated." | ||
| 2255 | :tag "Directory") | ||
| 2256 | |||
| 2257 | (define-widget 'symbol 'string | ||
| 2258 | "A lisp symbol." | ||
| 2259 | :value nil | ||
| 2260 | :tag "Symbol" | ||
| 2261 | :match (lambda (widget value) (symbolp value)) | ||
| 2262 | :value-to-internal (lambda (widget value) | ||
| 2263 | (if (symbolp value) | ||
| 2264 | (symbol-name value) | ||
| 2265 | value)) | ||
| 2266 | :value-to-external (lambda (widget value) | ||
| 2267 | (if (stringp value) | ||
| 2268 | (intern value) | ||
| 2269 | value))) | ||
| 2270 | |||
| 2271 | (define-widget 'function 'sexp | ||
| 2272 | ;; Should complete on functions. | ||
| 2273 | "A lisp function." | ||
| 2274 | :tag "Function") | ||
| 2275 | |||
| 2276 | (define-widget 'variable 'symbol | ||
| 2277 | ;; Should complete on variables. | ||
| 2278 | "A lisp variable." | ||
| 2279 | :tag "Variable") | ||
| 2280 | |||
| 2281 | (define-widget 'sexp 'string | ||
| 2282 | "An arbitrary lisp expression." | ||
| 2283 | :tag "Lisp expression" | ||
| 2284 | :value nil | ||
| 2285 | :validate 'widget-sexp-validate | ||
| 2286 | :match (lambda (widget value) t) | ||
| 2287 | :value-to-internal 'widget-sexp-value-to-internal | ||
| 2288 | :value-to-external (lambda (widget value) (read value))) | ||
| 2289 | |||
| 2290 | (defun widget-sexp-value-to-internal (widget value) | ||
| 2291 | ;; Use pp for printer representation. | ||
| 2292 | (let ((pp (pp-to-string value))) | ||
| 2293 | (while (string-match "\n\\'" pp) | ||
| 2294 | (setq pp (substring pp 0 -1))) | ||
| 2295 | (if (or (string-match "\n\\'" pp) | ||
| 2296 | (> (length pp) 40)) | ||
| 2297 | (concat "\n" pp) | ||
| 2298 | pp))) | ||
| 2299 | |||
| 2300 | (defun widget-sexp-validate (widget) | ||
| 2301 | ;; Valid if we can read the string and there is no junk left after it. | ||
| 2302 | (save-excursion | ||
| 2303 | (let ((buffer (set-buffer (get-buffer-create " *Widget Scratch*")))) | ||
| 2304 | (erase-buffer) | ||
| 2305 | (insert (widget-apply widget :value-get)) | ||
| 2306 | (goto-char (point-min)) | ||
| 2307 | (condition-case data | ||
| 2308 | (let ((value (read buffer))) | ||
| 2309 | (if (eobp) | ||
| 2310 | (if (widget-apply widget :match value) | ||
| 2311 | nil | ||
| 2312 | (widget-put widget :error (widget-get widget :type-error)) | ||
| 2313 | widget) | ||
| 2314 | (widget-put widget | ||
| 2315 | :error (format "Junk at end of expression: %s" | ||
| 2316 | (buffer-substring (point) | ||
| 2317 | (point-max)))) | ||
| 2318 | widget)) | ||
| 2319 | (error (widget-put widget :error (error-message-string data)) | ||
| 2320 | widget))))) | ||
| 2321 | |||
| 2322 | (define-widget 'integer 'sexp | ||
| 2323 | "An integer." | ||
| 2324 | :tag "Integer" | ||
| 2325 | :value 0 | ||
| 2326 | :type-error "This field should contain an integer" | ||
| 2327 | :value-to-internal (lambda (widget value) | ||
| 2328 | (if (integerp value) | ||
| 2329 | (prin1-to-string value) | ||
| 2330 | value)) | ||
| 2331 | :match (lambda (widget value) (integerp value))) | ||
| 2332 | |||
| 2333 | (define-widget 'character 'string | ||
| 2334 | "An character." | ||
| 2335 | :tag "Character" | ||
| 2336 | :value 0 | ||
| 2337 | :size 1 | ||
| 2338 | :format "%{%t%}: %v\n" | ||
| 2339 | :type-error "This field should contain a character" | ||
| 2340 | :value-to-internal (lambda (widget value) | ||
| 2341 | (if (integerp value) | ||
| 2342 | (char-to-string value) | ||
| 2343 | value)) | ||
| 2344 | :value-to-external (lambda (widget value) | ||
| 2345 | (if (stringp value) | ||
| 2346 | (aref value 0) | ||
| 2347 | value)) | ||
| 2348 | :match (lambda (widget value) (integerp value))) | ||
| 2349 | |||
| 2350 | (define-widget 'number 'sexp | ||
| 2351 | "A floating point number." | ||
| 2352 | :tag "Number" | ||
| 2353 | :value 0.0 | ||
| 2354 | :type-error "This field should contain a number" | ||
| 2355 | :value-to-internal (lambda (widget value) | ||
| 2356 | (if (numberp value) | ||
| 2357 | (prin1-to-string value) | ||
| 2358 | value)) | ||
| 2359 | :match (lambda (widget value) (numberp value))) | ||
| 2360 | |||
| 2361 | (define-widget 'list 'group | ||
| 2362 | "A lisp list." | ||
| 2363 | :tag "List" | ||
| 2364 | :format "%{%t%}:\n%v") | ||
| 2365 | |||
| 2366 | (define-widget 'vector 'group | ||
| 2367 | "A lisp vector." | ||
| 2368 | :tag "Vector" | ||
| 2369 | :format "%{%t%}:\n%v" | ||
| 2370 | :match 'widget-vector-match | ||
| 2371 | :value-to-internal (lambda (widget value) (append value nil)) | ||
| 2372 | :value-to-external (lambda (widget value) (apply 'vector value))) | ||
| 2373 | |||
| 2374 | (defun widget-vector-match (widget value) | ||
| 2375 | (and (vectorp value) | ||
| 2376 | (widget-group-match widget | ||
| 2377 | (widget-apply :value-to-internal widget value)))) | ||
| 2378 | |||
| 2379 | (define-widget 'cons 'group | ||
| 2380 | "A cons-cell." | ||
| 2381 | :tag "Cons-cell" | ||
| 2382 | :format "%{%t%}:\n%v" | ||
| 2383 | :match 'widget-cons-match | ||
| 2384 | :value-to-internal (lambda (widget value) | ||
| 2385 | (list (car value) (cdr value))) | ||
| 2386 | :value-to-external (lambda (widget value) | ||
| 2387 | (cons (nth 0 value) (nth 1 value)))) | ||
| 2388 | |||
| 2389 | (defun widget-cons-match (widget value) | ||
| 2390 | (and (consp value) | ||
| 2391 | (widget-group-match widget | ||
| 2392 | (widget-apply widget :value-to-internal value)))) | ||
| 2393 | |||
| 2394 | (define-widget 'choice 'menu-choice | ||
| 2395 | "A union of several sexp types." | ||
| 2396 | :tag "Choice" | ||
| 2397 | :format "%[%t%]: %v") | ||
| 2398 | |||
| 2399 | (define-widget 'radio 'radio-button-choice | ||
| 2400 | "A union of several sexp types." | ||
| 2401 | :tag "Choice" | ||
| 2402 | :format "%{%t%}:\n%v") | ||
| 2403 | |||
| 2404 | (define-widget 'repeat 'editable-list | ||
| 2405 | "A variable length homogeneous list." | ||
| 2406 | :tag "Repeat" | ||
| 2407 | :format "%{%t%}:\n%v%i\n") | ||
| 2408 | |||
| 2409 | (define-widget 'set 'checklist | ||
| 2410 | "A list of members from a fixed set." | ||
| 2411 | :tag "Set" | ||
| 2412 | :format "%{%t%}:\n%v") | ||
| 2413 | |||
| 2414 | (define-widget 'boolean 'toggle | ||
| 2415 | "To be nil or non-nil, that is the question." | ||
| 2416 | :tag "Boolean" | ||
| 2417 | :format "%{%t%}: %[%v%]\n") | ||
| 2418 | |||
| 2419 | ;;; The `color' Widget. | ||
| 2420 | |||
| 2421 | (define-widget 'color-item 'choice-item | ||
| 2422 | "A color name (with sample)." | ||
| 2423 | :format "%v (%{sample%})\n" | ||
| 2424 | :sample-face-get 'widget-color-item-button-face-get) | ||
| 2425 | |||
| 2426 | (defun widget-color-item-button-face-get (widget) | ||
| 2427 | ;; We create a face from the value. | ||
| 2428 | (require 'facemenu) | ||
| 2429 | (condition-case nil | ||
| 2430 | (facemenu-get-face (intern (concat "fg:" (widget-value widget)))) | ||
| 2431 | (error 'default))) | ||
| 2432 | |||
| 2433 | (define-widget 'color 'push-button | ||
| 2434 | "Choose a color name (with sample)." | ||
| 2435 | :format "%[%t%]: %v" | ||
| 2436 | :tag "Color" | ||
| 2437 | :value "black" | ||
| 2438 | :value-create 'widget-color-value-create | ||
| 2439 | :value-delete 'widget-children-value-delete | ||
| 2440 | :value-get 'widget-color-value-get | ||
| 2441 | :value-set 'widget-color-value-set | ||
| 2442 | :action 'widget-color-action | ||
| 2443 | :match 'widget-field-match | ||
| 2444 | :tag "Color") | ||
| 2445 | |||
| 2446 | (defvar widget-color-choice-list nil) | ||
| 2447 | ;; Variable holding the possible colors. | ||
| 2448 | |||
| 2449 | (defun widget-color-choice-list () | ||
| 2450 | (unless widget-color-choice-list | ||
| 2451 | (setq widget-color-choice-list | ||
| 2452 | (mapcar '(lambda (color) (list color)) | ||
| 2453 | (x-defined-colors)))) | ||
| 2454 | widget-color-choice-list) | ||
| 2455 | |||
| 2456 | (defun widget-color-value-create (widget) | ||
| 2457 | (let ((child (widget-create-child-and-convert | ||
| 2458 | widget 'color-item (widget-get widget :value)))) | ||
| 2459 | (widget-put widget :children (list child)))) | ||
| 2460 | |||
| 2461 | (defun widget-color-value-get (widget) | ||
| 2462 | ;; Pass command to first child. | ||
| 2463 | (widget-apply (car (widget-get widget :children)) :value-get)) | ||
| 2464 | |||
| 2465 | (defun widget-color-value-set (widget value) | ||
| 2466 | ;; Pass command to first child. | ||
| 2467 | (widget-apply (car (widget-get widget :children)) :value-set value)) | ||
| 2468 | |||
| 2469 | (defvar widget-color-history nil | ||
| 2470 | "History of entered colors") | ||
| 2471 | |||
| 2472 | (defun widget-color-action (widget &optional event) | ||
| 2473 | ;; Prompt for a color. | ||
| 2474 | (let* ((tag (widget-apply widget :menu-tag-get)) | ||
| 2475 | (prompt (concat tag ": ")) | ||
| 2476 | (answer (cond ((string-match "XEmacs" emacs-version) | ||
| 2477 | (read-color prompt)) | ||
| 2478 | ((fboundp 'x-defined-colors) | ||
| 2479 | (completing-read (concat tag ": ") | ||
| 2480 | (widget-color-choice-list) | ||
| 2481 | nil nil nil 'widget-color-history)) | ||
| 2482 | (t | ||
| 2483 | (read-string prompt (widget-value widget)))))) | ||
| 2484 | (unless (zerop (length answer)) | ||
| 2485 | (widget-value-set widget answer) | ||
| 2486 | (widget-apply widget :notify widget event) | ||
| 2487 | (widget-setup)))) | ||
| 2488 | |||
| 2489 | ;;; The Help Echo | ||
| 2490 | |||
| 2491 | (defun widget-echo-help-mouse () | ||
| 2492 | "Display the help message for the widget under the mouse. | ||
| 2493 | Enable with (run-with-idle-timer 1 t 'widget-echo-help-mouse)" | ||
| 2494 | (let* ((pos (mouse-position)) | ||
| 2495 | (frame (car pos)) | ||
| 2496 | (x (car (cdr pos))) | ||
| 2497 | (y (cdr (cdr pos))) | ||
| 2498 | (win (window-at x y frame)) | ||
| 2499 | (where (coordinates-in-window-p (cons x y) win))) | ||
| 2500 | (when (consp where) | ||
| 2501 | (save-window-excursion | ||
| 2502 | (progn ; save-excursion | ||
| 2503 | (select-window win) | ||
| 2504 | (let* ((result (compute-motion (window-start win) | ||
| 2505 | '(0 . 0) | ||
| 2506 | (window-end win) | ||
| 2507 | where | ||
| 2508 | (window-width win) | ||
| 2509 | (cons (window-hscroll) 0) | ||
| 2510 | win))) | ||
| 2511 | (when (and (eq (nth 1 result) x) | ||
| 2512 | (eq (nth 2 result) y)) | ||
| 2513 | (widget-echo-help (nth 0 result)))))))) | ||
| 2514 | (unless track-mouse | ||
| 2515 | (setq track-mouse t) | ||
| 2516 | (add-hook 'post-command-hook 'widget-stop-mouse-tracking))) | ||
| 2517 | |||
| 2518 | (defun widget-stop-mouse-tracking (&rest args) | ||
| 2519 | "Stop the mouse tracking done while idle." | ||
| 2520 | (remove-hook 'post-command-hook 'widget-stop-mouse-tracking) | ||
| 2521 | (setq track-mouse nil)) | ||
| 2522 | |||
| 2523 | (defun widget-at (pos) | ||
| 2524 | "The button or field at POS." | ||
| 2525 | (or (get-text-property pos 'button) | ||
| 2526 | (get-text-property pos 'field))) | ||
| 2527 | |||
| 2528 | (defun widget-echo-help (pos) | ||
| 2529 | "Display the help echo for widget at POS." | ||
| 2530 | (let* ((widget (widget-at pos)) | ||
| 2531 | (help-echo (and widget (widget-get widget :help-echo)))) | ||
| 2532 | (cond ((stringp help-echo) | ||
| 2533 | (message "%s" help-echo)) | ||
| 2534 | ((and (symbolp help-echo) (fboundp help-echo) | ||
| 2535 | (stringp (setq help-echo (funcall help-echo widget)))) | ||
| 2536 | (message "%s" help-echo))))) | ||
| 2537 | |||
| 2538 | ;;; The End: | ||
| 2539 | |||
| 2540 | (provide 'wid-edit) | ||
| 2541 | |||
| 2542 | ;; wid-edit.el ends here | ||
diff --git a/lisp/widget.el b/lisp/widget.el new file mode 100644 index 00000000000..4e1f2ca804c --- /dev/null +++ b/lisp/widget.el | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | ;;; widget.el --- a library of user interface components. | ||
| 2 | ;; | ||
| 3 | ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc. | ||
| 4 | ;; | ||
| 5 | ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> | ||
| 6 | ;; Keywords: help, extensions, faces, hypermedia | ||
| 7 | ;; Version: 1.71 | ||
| 8 | ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ | ||
| 9 | |||
| 10 | ;;; Commentary: | ||
| 11 | ;; | ||
| 12 | ;; If you want to use this code, please visit the URL above. | ||
| 13 | ;; | ||
| 14 | ;; This file only contain the code needed to define new widget types. | ||
| 15 | ;; Everything else is autoloaded from `wid-edit.el'. | ||
| 16 | |||
| 17 | ;;; Code: | ||
| 18 | |||
| 19 | (eval-when-compile (require 'cl)) | ||
| 20 | |||
| 21 | (defmacro define-widget-keywords (&rest keys) | ||
| 22 | (` | ||
| 23 | (eval-and-compile | ||
| 24 | (let ((keywords (quote (, keys)))) | ||
| 25 | (while keywords | ||
| 26 | (or (boundp (car keywords)) | ||
| 27 | (set (car keywords) (car keywords))) | ||
| 28 | (setq keywords (cdr keywords))))))) | ||
| 29 | |||
| 30 | (define-widget-keywords :deactivate :active :inactive :activate | ||
| 31 | :sibling-args :delete-button-args | ||
| 32 | :insert-button-args :append-button-args :button-args | ||
| 33 | :tag-glyph :off-glyph :on-glyph :valid-regexp | ||
| 34 | :secret :sample-face :sample-face-get :case-fold :widget-doc | ||
| 35 | :create :convert-widget :format :value-create :offset :extra-offset | ||
| 36 | :tag :doc :from :to :args :value :value-from :value-to :action | ||
| 37 | :value-set :value-delete :match :parent :delete :menu-tag-get | ||
| 38 | :value-get :choice :void :menu-tag :on :off :on-type :off-type | ||
| 39 | :notify :entry-format :button :children :buttons :insert-before | ||
| 40 | :delete-at :format-handler :widget :value-pos :value-to-internal | ||
| 41 | :indent :size :value-to-external :validate :error :directory | ||
| 42 | :must-match :type-error :value-inline :inline :match-inline :greedy | ||
| 43 | :button-face-get :button-face :value-face :keymap :entry-from | ||
| 44 | :entry-to :help-echo :documentation-property :hide-front-space | ||
| 45 | :hide-rear-space :tab-order) | ||
| 46 | |||
| 47 | ;; These autoloads should be deleted when the file is added to Emacs. | ||
| 48 | (unless (fboundp 'load-gc) | ||
| 49 | (autoload 'widget-apply "wid-edit") | ||
| 50 | (autoload 'widget-create "wid-edit") | ||
| 51 | (autoload 'widget-insert "wid-edit") | ||
| 52 | (autoload 'widget-browse "wid-browse" nil t) | ||
| 53 | (autoload 'widget-browse-at "wid-browse" nil t)) | ||
| 54 | |||
| 55 | (defun define-widget (name class doc &rest args) | ||
| 56 | "Define a new widget type named NAME from CLASS. | ||
| 57 | |||
| 58 | NAME and CLASS should both be symbols, CLASS should be one of the | ||
| 59 | existing widget types, or nil to create the widget from scratch. | ||
| 60 | |||
| 61 | After the new widget has been defined, the following two calls will | ||
| 62 | create identical widgets: | ||
| 63 | |||
| 64 | * (widget-create NAME) | ||
| 65 | |||
| 66 | * (apply 'widget-create CLASS ARGS) | ||
| 67 | |||
| 68 | The third argument DOC is a documentation string for the widget." | ||
| 69 | (put name 'widget-type (cons class args)) | ||
| 70 | (put name 'widget-documentation doc)) | ||
| 71 | |||
| 72 | ;;; The End. | ||
| 73 | |||
| 74 | (provide 'widget) | ||
| 75 | |||
| 76 | ;; widget.el ends here | ||