Mercurial > hg > xemacs-beta
comparison lisp/w3/widget-edit.el @ 80:1ce6082ce73f r20-0b90
Import from CVS: tag r20-0b90
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:06:37 +0200 |
parents | 131b0175ea99 |
children | 6a378aca36af |
comparison
equal
deleted
inserted
replaced
79:5b0a5bbffab6 | 80:1ce6082ce73f |
---|---|
1 ;;; widget-edit.el --- Functions for creating and using widgets. | 1 ;;; widget-edit.el --- Functions for creating and using widgets. |
2 ;; | 2 ;; |
3 ;; Copyright (C) 1996 Free Software Foundation, Inc. | 3 ;; Copyright (C) 1996 Free Software Foundation, Inc. |
4 ;; | 4 ;; |
5 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> | 5 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk> |
6 ;; Keywords: help, extensions, faces, hypermedia | 6 ;; Keywords: extensions |
7 ;; Version: 0.4 | 7 ;; Version: 1.13 |
8 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/ | |
8 | 9 |
9 ;;; Commentary: | 10 ;;; Commentary: |
10 ;; | 11 ;; |
11 ;; See `widget.el'. | 12 ;; See `widget.el'. |
12 | 13 |
13 ;;; Code: | 14 ;;; Code: |
14 | 15 |
15 (require 'widget) | 16 (require 'widget) |
16 (require 'cl) | 17 (require 'cl) |
18 (autoload 'pp-to-string "pp") | |
19 (autoload 'Info-goto-node "info") | |
20 | |
21 (if (string-match "XEmacs" emacs-version) | |
22 ;; XEmacs spell `intangible' as `atomic'. | |
23 (defun widget-make-intangible (from to side) | |
24 "Make text between FROM and TO atomic with regard to movement. | |
25 Third argument should be `start-open' if it should be sticky to the rear, | |
26 and `end-open' if it should sticky to the front." | |
27 (require 'atomic-extents) | |
28 (let ((ext (make-extent from to))) | |
29 ;; XEmacs doesn't understant different kinds of read-only, so | |
30 ;; we have to use extents instead. | |
31 (put-text-property from to 'read-only nil) | |
32 (set-extent-property ext 'read-only t) | |
33 (set-extent-property ext 'start-open nil) | |
34 (set-extent-property ext 'end-open nil) | |
35 (set-extent-property ext side t) | |
36 (set-extent-property ext 'atomic t))) | |
37 (defun widget-make-intangible (from to size) | |
38 "Make text between FROM and TO intangible." | |
39 (put-text-property from to 'intangible 'front))) | |
40 | |
41 ;; The following should go away when bundled with Emacs. | |
42 (eval-and-compile | |
43 (condition-case () | |
44 (require 'custom) | |
45 (error nil)) | |
46 | |
47 (unless (and (featurep 'custom) (fboundp 'custom-declare-variable)) | |
48 ;; We have the old custom-library, hack around it! | |
49 (defmacro defgroup (&rest args) nil) | |
50 (defmacro defcustom (&rest args) nil) | |
51 (defmacro defface (&rest args) nil) | |
52 (when (fboundp 'copy-face) | |
53 (copy-face 'default 'widget-documentation-face) | |
54 (copy-face 'bold 'widget-button-face) | |
55 (copy-face 'italic 'widget-field-face)) | |
56 (defvar widget-mouse-face 'highlight) | |
57 (defvar widget-menu-max-size 40))) | |
17 | 58 |
18 ;;; Compatibility. | 59 ;;; Compatibility. |
19 | 60 |
20 (or (fboundp 'event-point) | 61 (or (fboundp 'event-point) |
21 ;; XEmacs function missing in Emacs. | 62 ;; XEmacs function missing in Emacs. |
24 or button-release event. If the event did not occur over a window, or did | 65 or button-release event. If the event did not occur over a window, or did |
25 not occur over text, then this returns nil. Otherwise, it returns an index | 66 not occur over text, then this returns nil. Otherwise, it returns an index |
26 into the buffer visible in the event's window." | 67 into the buffer visible in the event's window." |
27 (posn-point (event-start event)))) | 68 (posn-point (event-start event)))) |
28 | 69 |
29 (or (fboundp 'set-keymap-parent) | |
30 ;; Xemacs function missing in Emacs. | |
31 ;; Definition stolen from `lucid.el'. | |
32 (defun set-keymap-parent (keymap new-parent) | |
33 (let ((tail keymap)) | |
34 (while (and tail (cdr tail) (not (eq (car (cdr tail)) 'keymap))) | |
35 (setq tail (cdr tail))) | |
36 (if tail | |
37 (setcdr tail new-parent))))) | |
38 | |
39 ;;; Customization. | 70 ;;; Customization. |
40 ;; | 71 |
41 ;; These should be specified with the custom package. | 72 (defgroup widgets nil |
42 | 73 "Customization support for the Widget Library." |
43 (defvar widget-button-face 'bold) | 74 :link '(custom-manual "(widget)Top") |
44 (defvar widget-mouse-face 'highlight) | 75 :link '(url-link :tag "Development Page" |
45 (defvar widget-field-face 'italic) | 76 "http://www.dina.kvl.dk/~abraham/custom/") |
46 | 77 :prefix "widget-" |
47 (defvar widget-motion-hook nil | 78 :group 'emacs) |
48 "*Hook to be run after widget traversal (via `widget-forward|backward'). | 79 |
49 The hooks will all be called with on argument - the widget that was just | 80 (defface widget-documentation-face '((t ())) |
50 selected.") | 81 "Face used for documentation text." |
82 :group 'widgets) | |
83 | |
84 (defface widget-button-face '((t (:bold t))) | |
85 "Face used for widget buttons." | |
86 :group 'widgets) | |
87 | |
88 (defcustom widget-mouse-face 'highlight | |
89 "Face used for widget buttons when the mouse is above them." | |
90 :type 'face | |
91 :group 'widgets) | |
92 | |
93 (defface widget-field-face '((((type x) | |
94 (class grayscale color) | |
95 (background light)) | |
96 (:background "light gray")) | |
97 (((type x) | |
98 (class grayscale color) | |
99 (background dark)) | |
100 (:background "dark gray")) | |
101 (t | |
102 (:italic t))) | |
103 "Face used for editable fields." | |
104 :group 'widgets) | |
105 | |
106 (defcustom widget-menu-max-size 40 | |
107 "Largest number of items allowed in a popup-menu. | |
108 Larger menus are read through the minibuffer." | |
109 :type 'integer) | |
51 | 110 |
52 ;;; Utility functions. | 111 ;;; Utility functions. |
53 ;; | 112 ;; |
54 ;; These are not really widget specific. | 113 ;; These are not really widget specific. |
55 | 114 |
78 (defun widget-clear-undo () | 137 (defun widget-clear-undo () |
79 "Clear all undo information." | 138 "Clear all undo information." |
80 (buffer-disable-undo (current-buffer)) | 139 (buffer-disable-undo (current-buffer)) |
81 (buffer-enable-undo)) | 140 (buffer-enable-undo)) |
82 | 141 |
142 (defun widget-choose (title items &optional event) | |
143 "Choose an item from a list. | |
144 | |
145 First argument TITLE is the name of the list. | |
146 Second argument ITEMS is an alist (NAME . VALUE). | |
147 Optional third argument EVENT is an input event. | |
148 | |
149 The user is asked to choose between each NAME from the items alist, | |
150 and the VALUE of the chosen element will be returned. If EVENT is a | |
151 mouse event, and the number of elements in items is less than | |
152 `widget-menu-max-size', a popup menu will be used, otherwise the | |
153 minibuffer." | |
154 (cond ((and (< (length items) widget-menu-max-size) | |
155 event (fboundp 'x-popup-menu) window-system) | |
156 ;; We are in Emacs-19, pressed by the mouse | |
157 (x-popup-menu event | |
158 (list title (cons "" items)))) | |
159 ((and (< (length items) widget-menu-max-size) | |
160 event (fboundp 'popup-menu) window-system) | |
161 ;; We are in XEmacs, pressed by the mouse | |
162 (let ((val (get-popup-menu-response | |
163 (cons "" | |
164 (mapcar | |
165 (function | |
166 (lambda (x) | |
167 (vector (car x) (list (car x)) t))) | |
168 items))))) | |
169 (setq val (and val | |
170 (listp (event-object val)) | |
171 (stringp (car-safe (event-object val))) | |
172 (car (event-object val)))) | |
173 (cdr (assoc val items)))) | |
174 (t | |
175 (cdr (assoc (completing-read (concat title ": ") | |
176 items nil t) | |
177 items))))) | |
178 | |
83 ;;; Widget text specifications. | 179 ;;; Widget text specifications. |
84 ;; | 180 ;; |
85 ;; These functions are for specifying text properties. | 181 ;; These functions are for specifying text properties. |
86 | 182 |
87 (defun widget-specify-none (from to) | 183 (defun widget-specify-none (from to) |
90 | 186 |
91 (defun widget-specify-text (from to) | 187 (defun widget-specify-text (from to) |
92 ;; Default properties. | 188 ;; Default properties. |
93 (add-text-properties from to (list 'read-only t | 189 (add-text-properties from to (list 'read-only t |
94 'front-sticky t | 190 'front-sticky t |
191 'start-open t | |
192 'end-open t | |
95 'rear-nonsticky nil))) | 193 'rear-nonsticky nil))) |
96 | 194 |
97 (defun widget-specify-field (widget from to) | 195 (defun widget-specify-field (widget from to) |
98 ;; Specify editable button for WIDGET between FROM and TO. | 196 ;; Specify editable button for WIDGET between FROM and TO. |
99 (widget-specify-field-update widget from to) | 197 (widget-specify-field-update widget from to) |
100 ;; Make it possible to edit both end of the field. | 198 |
199 ;; Make it possible to edit the front end of the field. | |
101 (add-text-properties (1- from) from (list 'rear-nonsticky t | 200 (add-text-properties (1- from) from (list 'rear-nonsticky t |
102 'end-open t | 201 'end-open t |
103 'invisible t)) | 202 'invisible t)) |
104 (add-text-properties to (1+ to) (list 'font-sticky nil | 203 (when (or (string-match "\\(.\\|\n\\)%v" (widget-get widget :format)) |
105 'start-open t))) | 204 (widget-get widget :hide-front-space)) |
205 ;; WARNING: This is going to lose horrible if the character just | |
206 ;; before the field can be modified (e.g. if it belongs to a | |
207 ;; choice widget). We try to compensate by checking the format | |
208 ;; string, and hope the user hasn't changed the :create method. | |
209 (widget-make-intangible (- from 2) from 'end-open)) | |
210 | |
211 ;; Make it possible to edit back end of the field. | |
212 (add-text-properties to (1+ to) (list 'front-sticky nil | |
213 'read-only t | |
214 'start-open t)) | |
215 | |
216 (cond ((widget-get widget :size) | |
217 (put-text-property to (1+ to) 'invisible t) | |
218 (when (or (string-match "%v\\(.\\|\n\\)" (widget-get widget :format)) | |
219 (widget-get widget :hide-rear-space)) | |
220 ;; WARNING: This is going to lose horrible if the character just | |
221 ;; after the field can be modified (e.g. if it belongs to a | |
222 ;; choice widget). We try to compensate by checking the format | |
223 ;; string, and hope the user hasn't changed the :create method. | |
224 (widget-make-intangible to (+ to 2) 'start-open))) | |
225 ((string-match "XEmacs" emacs-version) | |
226 ;; XEmacs does not allow you to insert before a read-only | |
227 ;; character, even if it is start.open. | |
228 ;; XEmacs does allow you to delete an read-only extent, so | |
229 ;; making the terminating newline read only doesn't help. | |
230 ;; I tried putting an invisible intangible read-only space | |
231 ;; before the newline, which gave really weird effects. | |
232 ;; So for now, we just have trust the user not to delete the | |
233 ;; newline. | |
234 (put-text-property to (1+ to) 'read-only nil)))) | |
106 | 235 |
107 (defun widget-specify-field-update (widget from to) | 236 (defun widget-specify-field-update (widget from to) |
108 ;; Specify editable button for WIDGET between FROM and TO. | 237 ;; Specify editable button for WIDGET between FROM and TO. |
109 (let ((map (widget-get widget :keymap)) | 238 (let ((map (widget-get widget :keymap)) |
110 (face (or (widget-get widget :value-face) | 239 (face (or (widget-get widget :value-face) |
111 widget-field-face))) | 240 'widget-field-face))) |
112 (add-text-properties from to (list 'field widget | 241 (set-text-properties from to (list 'field widget |
113 'read-only nil | 242 'read-only nil |
243 'keymap map | |
114 'local-map map | 244 'local-map map |
115 'keymap map | 245 'face face)) |
116 'face widget-field-face)))) | 246 (unless (widget-get widget :size) |
247 (put-text-property to (1+ to) 'face face)))) | |
117 | 248 |
118 (defun widget-specify-button (widget from to) | 249 (defun widget-specify-button (widget from to) |
119 ;; Specify button for WIDGET between FROM and TO. | 250 ;; Specify button for WIDGET between FROM and TO. |
120 (let ((face (or (widget-get widget :button-face) | 251 (let ((face (widget-apply widget :button-face-get))) |
121 widget-button-face))) | |
122 (add-text-properties from to (list 'button widget | 252 (add-text-properties from to (list 'button widget |
123 'mouse-face widget-mouse-face | 253 'mouse-face widget-mouse-face |
254 'start-open t | |
255 'end-open t | |
124 'face face)))) | 256 'face face)))) |
125 | 257 |
126 (defun widget-specify-doc (widget from to) | 258 (defun widget-specify-doc (widget from to) |
127 ;; Specify documentation for WIDGET between FROM and TO. | 259 ;; Specify documentation for WIDGET between FROM and TO. |
128 (put-text-property from to 'widget-doc widget)) | 260 (add-text-properties from to (list 'widget-doc widget |
129 | 261 'face 'widget-documentation-face))) |
130 | 262 |
131 (defmacro widget-specify-insert (&rest form) | 263 (defmacro widget-specify-insert (&rest form) |
132 ;; Execute FORM without inheriting any text properties. | 264 ;; Execute FORM without inheriting any text properties. |
133 (` | 265 `(save-restriction |
134 (save-restriction | |
135 (let ((inhibit-read-only t) | 266 (let ((inhibit-read-only t) |
136 result | 267 result |
137 after-change-functions) | 268 after-change-functions) |
138 (insert "<>") | 269 (insert "<>") |
139 (narrow-to-region (- (point) 2) (point)) | 270 (narrow-to-region (- (point) 2) (point)) |
140 (widget-specify-none (point-min) (point-max)) | 271 (widget-specify-none (point-min) (point-max)) |
141 (goto-char (1+ (point-min))) | 272 (goto-char (1+ (point-min))) |
142 (setq result (progn (,@ form))) | 273 (setq result (progn ,@form)) |
143 (delete-region (point-min) (1+ (point-min))) | 274 (delete-region (point-min) (1+ (point-min))) |
144 (delete-region (1- (point-max)) (point-max)) | 275 (delete-region (1- (point-max)) (point-max)) |
145 (goto-char (point-max)) | 276 (goto-char (point-max)) |
146 result)))) | 277 result))) |
147 | 278 |
148 ;;; Widget Properties. | 279 ;;; Widget Properties. |
149 | 280 |
150 (defun widget-put (widget property value) | 281 (defun widget-put (widget property value) |
151 "In WIDGET set PROPERTY to VALUE. | 282 "In WIDGET set PROPERTY to VALUE. |
184 "Set the current value of WIDGET to VALUE." | 315 "Set the current value of WIDGET to VALUE." |
185 (widget-apply widget | 316 (widget-apply widget |
186 :value-set (widget-apply widget | 317 :value-set (widget-apply widget |
187 :value-to-internal value))) | 318 :value-to-internal value))) |
188 | 319 |
189 (defun widget-match-inline (widget values) | 320 (defun widget-match-inline (widget vals) |
190 ;; Match the head of values. | 321 ;; In WIDGET, match the start of VALS. |
191 (cond ((widget-get widget :inline) | 322 (cond ((widget-get widget :inline) |
192 (widget-apply widget :match-inline values)) | 323 (widget-apply widget :match-inline vals)) |
193 ((widget-apply widget :match (car values)) | 324 ((and vals |
194 (cons (list (car values)) (cdr values))) | 325 (widget-apply widget :match (car vals))) |
326 (cons (list (car vals)) (cdr vals))) | |
195 (t nil))) | 327 (t nil))) |
196 | 328 |
197 ;;; Creating Widgets. | 329 ;;; Creating Widgets. |
198 | 330 |
331 ;;;###autoload | |
199 (defun widget-create (type &rest args) | 332 (defun widget-create (type &rest args) |
200 "Create widget of TYPE. | 333 "Create widget of TYPE. |
201 The optional ARGS are additional keyword arguments." | 334 The optional ARGS are additional keyword arguments." |
202 (let ((widget (apply 'widget-convert type args))) | 335 (let ((widget (apply 'widget-convert type args))) |
203 (widget-apply widget :create) | 336 (widget-apply widget :create) |
204 widget)) | 337 widget)) |
205 | 338 |
339 (defun widget-create-child-and-convert (parent type &rest args) | |
340 "As part of the widget PARENT, create a child widget TYPE. | |
341 The child is converted, using the keyword arguments ARGS." | |
342 (let ((widget (apply 'widget-convert type args))) | |
343 (widget-put widget :parent parent) | |
344 (unless (widget-get widget :indent) | |
345 (widget-put widget :indent (+ (or (widget-get parent :indent) 0) | |
346 (or (widget-get widget :extra-offset) 0) | |
347 (widget-get parent :offset)))) | |
348 (widget-apply widget :create) | |
349 widget)) | |
350 | |
351 (defun widget-create-child (parent type) | |
352 "Create widget of TYPE." | |
353 (let ((widget (copy-list type))) | |
354 (widget-put widget :parent parent) | |
355 (unless (widget-get widget :indent) | |
356 (widget-put widget :indent (+ (or (widget-get parent :indent) 0) | |
357 (or (widget-get widget :extra-offset) 0) | |
358 (widget-get parent :offset)))) | |
359 (widget-apply widget :create) | |
360 widget)) | |
361 | |
362 (defun widget-create-child-value (parent type value) | |
363 "Create widget of TYPE with value VALUE." | |
364 (let ((widget (copy-list type))) | |
365 (widget-put widget :value (widget-apply widget :value-to-internal value)) | |
366 (widget-put widget :parent parent) | |
367 (unless (widget-get widget :indent) | |
368 (widget-put widget :indent (+ (or (widget-get parent :indent) 0) | |
369 (or (widget-get widget :extra-offset) 0) | |
370 (widget-get parent :offset)))) | |
371 (widget-apply widget :create) | |
372 widget)) | |
373 | |
374 ;;;###autoload | |
206 (defun widget-delete (widget) | 375 (defun widget-delete (widget) |
207 "Delete WIDGET." | 376 "Delete WIDGET." |
208 (widget-apply widget :delete)) | 377 (widget-apply widget :delete)) |
209 | 378 |
210 (defun widget-convert (type &rest args) | 379 (defun widget-convert (type &rest args) |
230 (widget-put widget :args args) | 399 (widget-put widget :args args) |
231 (setq args nil)))) | 400 (setq args nil)))) |
232 ;; Then Convert the widget. | 401 ;; Then Convert the widget. |
233 (setq type widget) | 402 (setq type widget) |
234 (while type | 403 (while type |
235 (let ((convert-widget (widget-get type :convert-widget))) | 404 (let ((convert-widget (plist-get (cdr type) :convert-widget))) |
236 (if convert-widget | 405 (if convert-widget |
237 (setq widget (funcall convert-widget widget)))) | 406 (setq widget (funcall convert-widget widget)))) |
238 (setq type (get (car type) 'widget-type))) | 407 (setq type (get (car type) 'widget-type))) |
239 ;; Finally set the keyword args. | 408 ;; Finally set the keyword args. |
240 (while keys | 409 (while keys |
242 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:)) | 411 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:)) |
243 (progn | 412 (progn |
244 (widget-put widget next (nth 1 keys)) | 413 (widget-put widget next (nth 1 keys)) |
245 (setq keys (nthcdr 2 keys))) | 414 (setq keys (nthcdr 2 keys))) |
246 (setq keys nil)))) | 415 (setq keys nil)))) |
416 ;; Convert the :value to internal format. | |
417 (if (widget-member widget :value) | |
418 (let ((value (widget-get widget :value))) | |
419 (widget-put widget | |
420 :value (widget-apply widget :value-to-internal value)))) | |
247 ;; Return the newly create widget. | 421 ;; Return the newly create widget. |
248 widget)) | 422 widget)) |
249 | 423 |
250 (defun widget-insert (&rest args) | 424 (defun widget-insert (&rest args) |
251 "Call `insert' with ARGS and make the text read only." | 425 "Call `insert' with ARGS and make the text read only." |
266 (setq widget-keymap (make-sparse-keymap)) | 440 (setq widget-keymap (make-sparse-keymap)) |
267 (set-keymap-parent widget-keymap global-map) | 441 (set-keymap-parent widget-keymap global-map) |
268 (define-key widget-keymap "\t" 'widget-forward) | 442 (define-key widget-keymap "\t" 'widget-forward) |
269 (define-key widget-keymap "\M-\t" 'widget-backward) | 443 (define-key widget-keymap "\M-\t" 'widget-backward) |
270 (define-key widget-keymap [(shift tab)] 'widget-backward) | 444 (define-key widget-keymap [(shift tab)] 'widget-backward) |
445 (define-key widget-keymap [(shift tab)] 'widget-backward) | |
446 (define-key widget-keymap [backtab] 'widget-backward) | |
271 (if (string-match "XEmacs" (emacs-version)) | 447 (if (string-match "XEmacs" (emacs-version)) |
272 (define-key widget-keymap [button2] 'widget-button-click) | 448 (define-key widget-keymap [button2] 'widget-button-click) |
449 (define-key widget-keymap [menu-bar] 'nil) | |
273 (define-key widget-keymap [mouse-2] 'widget-button-click)) | 450 (define-key widget-keymap [mouse-2] 'widget-button-click)) |
274 (define-key widget-keymap "\C-m" 'widget-button-press)) | 451 (define-key widget-keymap "\C-m" 'widget-button-press)) |
275 | 452 |
276 (defvar widget-global-map global-map | 453 (defvar widget-global-map global-map |
277 "Keymap used for events the widget does not handle themselves.") | 454 "Keymap used for events the widget does not handle themselves.") |
354 (field (previous-single-property-change (point) 'field))) | 531 (field (previous-single-property-change (point) 'field))) |
355 (cond ((and button field) | 532 (cond ((and button field) |
356 (goto-char (max button field))) | 533 (goto-char (max button field))) |
357 (button (goto-char button)) | 534 (button (goto-char button)) |
358 (field (goto-char field))))) | 535 (field (goto-char field))))) |
359 (run-hook-with-args 'widget-motion-hook (or | 536 (widget-echo-help (point))) |
360 (get-text-property (point) 'button) | |
361 (get-text-property (point) 'field))) | |
362 ) | |
363 | 537 |
364 (defun widget-backward (arg) | 538 (defun widget-backward (arg) |
365 "Move point to the previous field or button. | 539 "Move point to the previous field or button. |
366 With optional ARG, move across that many fields." | 540 With optional ARG, move across that many fields." |
367 (interactive "p") | 541 (interactive "p") |
378 (make-variable-buffer-local 'widget-field-list) | 552 (make-variable-buffer-local 'widget-field-list) |
379 | 553 |
380 (defun widget-setup () | 554 (defun widget-setup () |
381 "Setup current buffer so editing string widgets works." | 555 "Setup current buffer so editing string widgets works." |
382 (let ((inhibit-read-only t) | 556 (let ((inhibit-read-only t) |
557 (after-change-functions nil) | |
383 field) | 558 field) |
384 (while widget-field-new | 559 (while widget-field-new |
385 (setq field (car widget-field-new) | 560 (setq field (car widget-field-new) |
386 widget-field-new (cdr widget-field-new) | 561 widget-field-new (cdr widget-field-new) |
387 widget-field-list (cons field widget-field-list)) | 562 widget-field-list (cons field widget-field-list)) |
428 (condition-case nil | 603 (condition-case nil |
429 (let ((field (widget-field-find from)) | 604 (let ((field (widget-field-find from)) |
430 (inhibit-read-only t)) | 605 (inhibit-read-only t)) |
431 (cond ((null field)) | 606 (cond ((null field)) |
432 ((not (eq field (widget-field-find to))) | 607 ((not (eq field (widget-field-find to))) |
608 (debug) | |
433 (message "Error: `widget-after-change' called on two fields")) | 609 (message "Error: `widget-after-change' called on two fields")) |
434 (t | 610 (t |
435 (let ((size (widget-get field :size))) | 611 (let ((size (widget-get field :size))) |
436 (if size | 612 (if size |
437 (let ((begin (1+ (widget-get field :value-from))) | 613 (let ((begin (1+ (widget-get field :value-from))) |
439 (widget-specify-field-update field begin end) | 615 (widget-specify-field-update field begin end) |
440 (cond ((< (- end begin) size) | 616 (cond ((< (- end begin) size) |
441 ;; Field too small. | 617 ;; Field too small. |
442 (save-excursion | 618 (save-excursion |
443 (goto-char end) | 619 (goto-char end) |
444 (insert-char ?\ (- (+ begin size) end)))) | 620 (insert-char ?\ (- (+ begin size) end)) |
621 (widget-specify-field-update field | |
622 begin | |
623 (+ begin size)))) | |
445 ((> (- end begin) size) | 624 ((> (- end begin) size) |
446 ;; Field too large and | 625 ;; Field too large and |
447 (if (or (< (point) (+ begin size)) | 626 (if (or (< (point) (+ begin size)) |
448 (> (point) end)) | 627 (> (point) end)) |
449 ;; Point is outside extra space. | 628 ;; Point is outside extra space. |
457 (delete-backward-char 1)))))) | 636 (delete-backward-char 1)))))) |
458 (widget-specify-field-update field from to))) | 637 (widget-specify-field-update field from to))) |
459 (widget-apply field :notify field)))) | 638 (widget-apply field :notify field)))) |
460 (error (debug)))) | 639 (error (debug)))) |
461 | 640 |
641 ;;; Widget Functions | |
642 ;; | |
643 ;; These functions are used in the definition of multiple widgets. | |
644 | |
645 (defun widget-children-value-delete (widget) | |
646 "Delete all :children and :buttons in WIDGET." | |
647 (mapcar 'widget-delete (widget-get widget :children)) | |
648 (widget-put widget :children nil) | |
649 (mapcar 'widget-delete (widget-get widget :buttons)) | |
650 (widget-put widget :buttons nil)) | |
651 | |
652 (defun widget-types-convert-widget (widget) | |
653 "Convert :args as widget types in WIDGET." | |
654 (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args))) | |
655 widget) | |
656 | |
462 ;;; The `default' Widget. | 657 ;;; The `default' Widget. |
463 | 658 |
464 (define-widget 'default nil | 659 (define-widget 'default nil |
465 "Basic widget other widgets are derived from." | 660 "Basic widget other widgets are derived from." |
466 :value-to-internal (lambda (widget value) value) | 661 :value-to-internal (lambda (widget value) value) |
467 :value-to-external (lambda (widget value) value) | 662 :value-to-external (lambda (widget value) value) |
468 :create 'widget-default-create | 663 :create 'widget-default-create |
664 :indent nil | |
665 :offset 0 | |
469 :format-handler 'widget-default-format-handler | 666 :format-handler 'widget-default-format-handler |
667 :button-face-get 'widget-default-button-face-get | |
470 :delete 'widget-default-delete | 668 :delete 'widget-default-delete |
471 :value-set 'widget-default-value-set | 669 :value-set 'widget-default-value-set |
472 :value-inline 'widget-default-value-inline | 670 :value-inline 'widget-default-value-inline |
473 :menu-tag-get 'widget-default-menu-tag-get | 671 :menu-tag-get 'widget-default-menu-tag-get |
474 :validate (lambda (widget) t) | 672 :validate (lambda (widget) nil) |
475 :action 'widget-default-action | 673 :action 'widget-default-action |
476 :notify 'widget-default-notify) | 674 :notify 'widget-default-notify) |
477 | 675 |
478 (defun widget-default-create (widget) | 676 (defun widget-default-create (widget) |
479 "Create WIDGET at point in the current buffer." | 677 "Create WIDGET at point in the current buffer." |
494 (insert "%")) | 692 (insert "%")) |
495 ((eq escape ?\[) | 693 ((eq escape ?\[) |
496 (setq button-begin (point))) | 694 (setq button-begin (point))) |
497 ((eq escape ?\]) | 695 ((eq escape ?\]) |
498 (setq button-end (point))) | 696 (setq button-end (point))) |
697 ((eq escape ?n) | |
698 (when (widget-get widget :indent) | |
699 (insert "\n") | |
700 (insert-char ? (widget-get widget :indent)))) | |
499 ((eq escape ?t) | 701 ((eq escape ?t) |
500 (if tag | 702 (if tag |
501 (insert tag) | 703 (insert tag) |
502 (let ((standard-output (current-buffer))) | 704 (let ((standard-output (current-buffer))) |
503 (princ (widget-get widget :value))))) | 705 (princ (widget-get widget :value))))) |
530 (set-marker-insertion-type to nil) | 732 (set-marker-insertion-type to nil) |
531 (widget-put widget :from from) | 733 (widget-put widget :from from) |
532 (widget-put widget :to to)))) | 734 (widget-put widget :to to)))) |
533 | 735 |
534 (defun widget-default-format-handler (widget escape) | 736 (defun widget-default-format-handler (widget escape) |
535 ;; By default unknown escapes are errors. | 737 ;; We recognize the %h escape by default. |
536 (error "Unknown escape `%c'" escape)) | 738 (let* ((buttons (widget-get widget :buttons)) |
739 (doc-property (widget-get widget :documentation-property)) | |
740 (doc-try (cond ((widget-get widget :doc)) | |
741 ((symbolp doc-property) | |
742 (documentation-property (widget-get widget :value) | |
743 doc-property)) | |
744 (t | |
745 (funcall doc-property (widget-get widget :value))))) | |
746 (doc-text (and (stringp doc-try) | |
747 (> (length doc-try) 1) | |
748 doc-try))) | |
749 (cond ((eq escape ?h) | |
750 (when doc-text | |
751 (and (eq (preceding-char) ?\n) | |
752 (widget-get widget :indent) | |
753 (insert-char ? (widget-get widget :indent))) | |
754 ;; The `*' in the beginning is redundant. | |
755 (when (eq (aref doc-text 0) ?*) | |
756 (setq doc-text (substring doc-text 1))) | |
757 ;; Get rid of trailing newlines. | |
758 (when (string-match "\n+\\'" doc-text) | |
759 (setq doc-text (substring doc-text 0 (match-beginning 0)))) | |
760 (push (if (string-match "\n." doc-text) | |
761 ;; Allow multiline doc to be hiden. | |
762 (widget-create-child-and-convert | |
763 widget 'widget-help | |
764 :doc (progn | |
765 (string-match "\\`.*" doc-text) | |
766 (match-string 0 doc-text)) | |
767 :widget-doc doc-text | |
768 "?") | |
769 ;; A single line is just inserted. | |
770 (widget-create-child-and-convert | |
771 widget 'item :format "%d" :doc doc-text nil)) | |
772 buttons))) | |
773 (t | |
774 (error "Unknown escape `%c'" escape))) | |
775 (widget-put widget :buttons buttons))) | |
776 | |
777 (defun widget-default-button-face-get (widget) | |
778 ;; Use :button-face or widget-button-face | |
779 (or (widget-get widget :button-face) 'widget-button-face)) | |
537 | 780 |
538 (defun widget-default-delete (widget) | 781 (defun widget-default-delete (widget) |
539 ;; Remove widget from the buffer. | 782 ;; Remove widget from the buffer. |
540 (let ((from (widget-get widget :from)) | 783 (let ((from (widget-get widget :from)) |
541 (to (widget-get widget :to)) | 784 (to (widget-get widget :to)) |
588 :match-inline 'widget-item-match-inline | 831 :match-inline 'widget-item-match-inline |
589 :action 'widget-item-action | 832 :action 'widget-item-action |
590 :format "%t\n") | 833 :format "%t\n") |
591 | 834 |
592 (defun widget-item-convert-widget (widget) | 835 (defun widget-item-convert-widget (widget) |
593 ;; Initialize :value and :tag from :args in WIDGET. | 836 ;; Initialize :value from :args in WIDGET. |
594 (let ((args (widget-get widget :args))) | 837 (let ((args (widget-get widget :args))) |
595 (when args | 838 (when args |
596 (widget-put widget :value (car args)) | 839 (widget-put widget :value (widget-apply widget |
840 :value-to-internal (car args))) | |
597 (widget-put widget :args nil))) | 841 (widget-put widget :args nil))) |
598 widget) | 842 widget) |
599 | 843 |
600 (defun widget-item-value-create (widget) | 844 (defun widget-item-value-create (widget) |
601 ;; Insert the printed representation of the value. | 845 ;; Insert the printed representation of the value. |
621 | 865 |
622 (defun widget-item-value-get (widget) | 866 (defun widget-item-value-get (widget) |
623 ;; Items are simple. | 867 ;; Items are simple. |
624 (widget-get widget :value)) | 868 (widget-get widget :value)) |
625 | 869 |
626 ;;; The `push' Widget. | 870 ;;; The `push-button' Widget. |
627 | 871 |
628 (define-widget 'push 'item | 872 (define-widget 'push-button 'item |
629 "A pushable button." | 873 "A pushable button." |
630 :format "%[[%t]%]") | 874 :format "%[[%t]%]") |
631 | 875 |
632 ;;; The `link' Widget. | 876 ;;; The `link' Widget. |
633 | 877 |
634 (define-widget 'link 'item | 878 (define-widget 'link 'item |
635 "An embedded link." | 879 "An embedded link." |
636 :format "%[_%t_%]") | 880 :format "%[_%t_%]") |
637 | 881 |
638 ;;; The `field' Widget. | 882 ;;; The `info-link' Widget. |
639 | 883 |
640 (define-widget 'field 'default | 884 (define-widget 'info-link 'link |
885 "A link to an info file." | |
886 :action 'widget-info-link-action) | |
887 | |
888 (defun widget-info-link-action (widget &optional event) | |
889 "Open the info node specified by WIDGET." | |
890 (Info-goto-node (widget-value widget))) | |
891 | |
892 ;;; The `url-link' Widget. | |
893 | |
894 (define-widget 'url-link 'link | |
895 "A link to an www page." | |
896 :action 'widget-url-link-action) | |
897 | |
898 (defun widget-url-link-action (widget &optional event) | |
899 "Open the url specified by WIDGET." | |
900 (require 'browse-url) | |
901 (funcall browse-url-browser-function (widget-value widget))) | |
902 | |
903 ;;; The `editable-field' Widget. | |
904 | |
905 (define-widget 'editable-field 'default | |
641 "An editable text field." | 906 "An editable text field." |
642 :convert-widget 'widget-item-convert-widget | 907 :convert-widget 'widget-item-convert-widget |
643 :format "%v" | 908 :format "%v" |
644 :value "" | 909 :value "" |
645 :tag "field" | 910 :action 'widget-field-action |
646 :value-create 'widget-field-value-create | 911 :value-create 'widget-field-value-create |
647 :value-delete 'widget-field-value-delete | 912 :value-delete 'widget-field-value-delete |
648 :value-get 'widget-field-value-get | 913 :value-get 'widget-field-value-get |
649 :match 'widget-field-match) | 914 :match 'widget-field-match) |
915 | |
916 ;; History of field minibuffer edits. | |
917 (defvar widget-field-history nil) | |
918 | |
919 (defun widget-field-action (widget &optional event) | |
920 ;; Edit the value in the minibuffer. | |
921 (let ((tag (widget-apply widget :menu-tag-get)) | |
922 (invalid (widget-apply widget :validate))) | |
923 (when invalid | |
924 (error (widget-get invalid :error))) | |
925 (widget-value-set widget | |
926 (widget-apply widget | |
927 :value-to-external | |
928 (read-string (concat tag ": ") | |
929 (widget-apply | |
930 widget | |
931 :value-to-internal | |
932 (widget-value widget)) | |
933 'widget-field-history))) | |
934 (widget-apply widget :notify widget event) | |
935 (widget-setup))) | |
650 | 936 |
651 (defun widget-field-value-create (widget) | 937 (defun widget-field-value-create (widget) |
652 ;; Create an editable text field. | 938 ;; Create an editable text field. |
653 (insert " ") | 939 (insert " ") |
654 (let ((size (widget-get widget :size)) | 940 (let ((size (widget-get widget :size)) |
655 (value (widget-get widget :value)) | 941 (value (widget-get widget :value)) |
656 (from (point))) | 942 (from (point))) |
657 (if (null size) | 943 (insert value) |
658 (insert value) | 944 (and size |
659 (insert value) | 945 (< (length value) size) |
660 (if (< (length value) size) | 946 (insert-char ?\ (- size (length value)))) |
661 (insert-char ?\ (- size (length value))))) | |
662 (unless (memq widget widget-field-list) | 947 (unless (memq widget widget-field-list) |
663 (setq widget-field-new (cons widget widget-field-new))) | 948 (setq widget-field-new (cons widget widget-field-new))) |
664 (widget-put widget :value-from (copy-marker from)) | |
665 (set-marker-insertion-type (widget-get widget :value-from) t) | |
666 (widget-put widget :value-to (copy-marker (point))) | 949 (widget-put widget :value-to (copy-marker (point))) |
667 (set-marker-insertion-type (widget-get widget :value-to) nil) | 950 (set-marker-insertion-type (widget-get widget :value-to) nil) |
668 (if (null size) | 951 (if (null size) |
669 (insert ?\n) | 952 (insert ?\n) |
670 (insert ?\ )))) | 953 (insert ?\ )) |
954 (widget-put widget :value-from (copy-marker from)) | |
955 (set-marker-insertion-type (widget-get widget :value-from) t))) | |
671 | 956 |
672 (defun widget-field-value-delete (widget) | 957 (defun widget-field-value-delete (widget) |
673 ;; Remove the widget from the list of active editing fields. | 958 ;; Remove the widget from the list of active editing fields. |
674 (setq widget-field-list (delq widget widget-field-list)) | 959 (setq widget-field-list (delq widget widget-field-list)) |
675 (set-marker (widget-get widget :value-from) nil) | 960 (set-marker (widget-get widget :value-from) nil) |
676 (set-marker (widget-get widget :value-to) nil)) | 961 (set-marker (widget-get widget :value-to) nil)) |
677 | 962 |
678 (defun widget-field-value-get (widget) | 963 (defun widget-field-value-get (widget) |
679 ;; Return current text in editing field. | 964 ;; Return current text in editing field. |
680 (let ((from (widget-get widget :value-from)) | 965 (let ((from (widget-get widget :value-from)) |
681 (to (widget-get widget :value-to))) | 966 (to (widget-get widget :value-to)) |
967 (size (widget-get widget :size)) | |
968 (old (current-buffer))) | |
682 (if (and from to) | 969 (if (and from to) |
683 (progn | 970 (progn |
971 (set-buffer (marker-buffer from)) | |
684 (setq from (1+ from) | 972 (setq from (1+ from) |
685 to (1- to)) | 973 to (1- to)) |
686 (while (and (> to from) | 974 (while (and size |
975 (not (zerop size)) | |
976 (> to from) | |
687 (eq (char-after (1- to)) ?\ )) | 977 (eq (char-after (1- to)) ?\ )) |
688 (setq to (1- to))) | 978 (setq to (1- to))) |
689 (buffer-substring-no-properties from to)) | 979 (prog1 (buffer-substring-no-properties from to) |
980 (set-buffer old))) | |
690 (widget-get widget :value)))) | 981 (widget-get widget :value)))) |
691 | 982 |
692 (defun widget-field-match (widget value) | 983 (defun widget-field-match (widget value) |
693 ;; Match any string. | 984 ;; Match any string. |
694 (stringp value)) | 985 (stringp value)) |
695 | 986 |
696 ;;; The `choice' Widget. | 987 ;;; The `text' Widget. |
697 | 988 |
698 (define-widget 'choice 'default | 989 (define-widget 'text 'editable-field |
990 "A multiline text area.") | |
991 | |
992 ;;; The `menu-choice' Widget. | |
993 | |
994 (define-widget 'menu-choice 'default | |
699 "A menu of options." | 995 "A menu of options." |
700 :convert-widget 'widget-choice-convert-widget | 996 :convert-widget 'widget-types-convert-widget |
701 :format "%[%t%]: %v" | 997 :format "%[%t%]: %v" |
998 :case-fold t | |
702 :tag "choice" | 999 :tag "choice" |
703 :inline t | 1000 :void '(item :format "invalid (%t)\n") |
704 :void '(item "void") | |
705 :value-create 'widget-choice-value-create | 1001 :value-create 'widget-choice-value-create |
706 :value-delete 'widget-radio-value-delete | 1002 :value-delete 'widget-children-value-delete |
707 :value-get 'widget-choice-value-get | 1003 :value-get 'widget-choice-value-get |
708 :value-inline 'widget-choice-value-inline | 1004 :value-inline 'widget-choice-value-inline |
709 :action 'widget-choice-action | 1005 :action 'widget-choice-action |
710 :error "Make a choice" | 1006 :error "Make a choice" |
711 :validate 'widget-choice-validate | 1007 :validate 'widget-choice-validate |
712 :match 'widget-choice-match | 1008 :match 'widget-choice-match |
713 :match-inline 'widget-choice-match-inline) | 1009 :match-inline 'widget-choice-match-inline) |
714 | |
715 (defun widget-choice-convert-widget (widget) | |
716 ;; Expand type args into widget objects. | |
717 (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args))) | |
718 widget) | |
719 | 1010 |
720 (defun widget-choice-value-create (widget) | 1011 (defun widget-choice-value-create (widget) |
721 ;; Insert the first choice that matches the value. | 1012 ;; Insert the first choice that matches the value. |
722 (let ((value (widget-get widget :value)) | 1013 (let ((value (widget-get widget :value)) |
723 (args (widget-get widget :args)) | 1014 (args (widget-get widget :args)) |
724 current) | 1015 current) |
725 (while args | 1016 (while args |
726 (setq current (car args) | 1017 (setq current (car args) |
727 args (cdr args)) | 1018 args (cdr args)) |
728 (when (widget-apply current :match value) | 1019 (when (widget-apply current :match value) |
729 (widget-put widget :children (list (widget-create current | 1020 (widget-put widget :children (list (widget-create-child-value |
730 :parent widget | 1021 widget current value))) |
731 :value value))) | |
732 (widget-put widget :choice current) | 1022 (widget-put widget :choice current) |
733 (setq args nil | 1023 (setq args nil |
734 current nil))) | 1024 current nil))) |
735 (when current | 1025 (when current |
736 (let ((void (widget-get widget :void))) | 1026 (let ((void (widget-get widget :void))) |
737 (widget-put widget :children (list (widget-create void | 1027 (widget-put widget :children (list (widget-create-child-and-convert |
738 :parent widget | 1028 widget void :value value))) |
739 :value value))) | |
740 (widget-put widget :choice void))))) | 1029 (widget-put widget :choice void))))) |
741 | 1030 |
742 (defun widget-choice-value-get (widget) | 1031 (defun widget-choice-value-get (widget) |
743 ;; Get value of the child widget. | 1032 ;; Get value of the child widget. |
744 (widget-value (car (widget-get widget :children)))) | 1033 (widget-value (car (widget-get widget :children)))) |
750 (defun widget-choice-action (widget &optional event) | 1039 (defun widget-choice-action (widget &optional event) |
751 ;; Make a choice. | 1040 ;; Make a choice. |
752 (let ((args (widget-get widget :args)) | 1041 (let ((args (widget-get widget :args)) |
753 (old (widget-get widget :choice)) | 1042 (old (widget-get widget :choice)) |
754 (tag (widget-apply widget :menu-tag-get)) | 1043 (tag (widget-apply widget :menu-tag-get)) |
1044 (completion-ignore-case (widget-get widget :case-fold)) | |
755 current choices) | 1045 current choices) |
1046 ;; Remember old value. | |
1047 (if (and old (not (widget-apply widget :validate))) | |
1048 (let* ((external (widget-value widget)) | |
1049 (internal (widget-apply old :value-to-internal external))) | |
1050 (widget-put old :value internal))) | |
1051 ;; Find new choice. | |
756 (setq current | 1052 (setq current |
757 (cond ((= (length args) 0) | 1053 (cond ((= (length args) 0) |
758 nil) | 1054 nil) |
759 ((= (length args) 1) | 1055 ((= (length args) 1) |
760 (nth 0 args)) | 1056 (nth 0 args)) |
769 args (cdr args)) | 1065 args (cdr args)) |
770 (setq choices | 1066 (setq choices |
771 (cons (cons (widget-apply current :menu-tag-get) | 1067 (cons (cons (widget-apply current :menu-tag-get) |
772 current) | 1068 current) |
773 choices))) | 1069 choices))) |
774 (cond | 1070 (widget-choose tag (reverse choices) event)))) |
775 ((and event (fboundp 'x-popup-menu) window-system) | |
776 ;; We are in Emacs-19, pressed by the mouse | |
777 (x-popup-menu event | |
778 (list tag (cons "" (reverse choices))))) | |
779 ((and event (fboundp 'popup-menu) window-system) | |
780 ;; We are in XEmacs, pressed by the mouse | |
781 (let ((val (get-popup-menu-response | |
782 (cons "" | |
783 (mapcar | |
784 (function | |
785 (lambda (x) | |
786 (vector (car x) (list (car x)) t))) | |
787 (reverse choices)))))) | |
788 (setq val (and val | |
789 (listp (event-object val)) | |
790 (stringp (car-safe (event-object val))) | |
791 (car (event-object val)))) | |
792 (cdr (assoc val choices)))) | |
793 (t | |
794 (cdr (assoc (completing-read (concat tag ": ") | |
795 choices nil t) | |
796 choices))))))) | |
797 (when current | 1071 (when current |
798 (widget-value-set widget (widget-value current)) | 1072 (widget-value-set widget |
799 (widget-setup))) | 1073 (widget-apply current :value-to-external |
1074 (widget-get current :value))) | |
1075 (widget-apply widget :notify widget event) | |
1076 (widget-setup))) | |
800 ;; Notify parent. | 1077 ;; Notify parent. |
801 (widget-apply widget :notify widget event) | 1078 (widget-apply widget :notify widget event) |
802 (widget-clear-undo)) | 1079 (widget-clear-undo)) |
803 | 1080 |
804 (defun widget-choice-validate (widget) | 1081 (defun widget-choice-validate (widget) |
830 found (widget-match-inline current values))) | 1107 found (widget-match-inline current values))) |
831 found)) | 1108 found)) |
832 | 1109 |
833 ;;; The `toggle' Widget. | 1110 ;;; The `toggle' Widget. |
834 | 1111 |
835 (define-widget 'toggle 'choice | 1112 (define-widget 'toggle 'menu-choice |
836 "Toggle between two states." | 1113 "Toggle between two states." |
837 :convert-widget 'widget-toggle-convert-widget | 1114 :convert-widget 'widget-toggle-convert-widget |
838 :format "%[%v%]" | 1115 :format "%v" |
839 :on "on" | 1116 :on "on" |
840 :off "off") | 1117 :off "off") |
841 | 1118 |
842 (defun widget-toggle-convert-widget (widget) | 1119 (defun widget-toggle-convert-widget (widget) |
843 ;; Create the types representing the `on' and `off' states. | 1120 ;; Create the types representing the `on' and `off' states. |
844 (let ((args (widget-get widget :args)) | 1121 (let ((on-type (widget-get widget :on-type)) |
845 (on-type (widget-get widget :on-type)) | |
846 (off-type (widget-get widget :off-type))) | 1122 (off-type (widget-get widget :off-type))) |
847 (unless on-type | 1123 (unless on-type |
848 (setq on-type (list 'item :value t :tag (widget-get widget :on)))) | 1124 (setq on-type |
1125 (list 'choice-item | |
1126 :value t | |
1127 :match (lambda (widget value) value) | |
1128 :tag (widget-get widget :on)))) | |
849 (unless off-type | 1129 (unless off-type |
850 (setq off-type (list 'item :value nil :tag (widget-get widget :off)))) | 1130 (setq off-type |
1131 (list 'choice-item :value nil :tag (widget-get widget :off)))) | |
851 (widget-put widget :args (list on-type off-type))) | 1132 (widget-put widget :args (list on-type off-type))) |
852 widget) | 1133 widget) |
853 | 1134 |
854 ;;; The `checkbox' Widget. | 1135 ;;; The `checkbox' Widget. |
855 | 1136 |
856 (define-widget 'checkbox 'toggle | 1137 (define-widget 'checkbox 'toggle |
857 "A checkbox toggle." | 1138 "A checkbox toggle." |
858 :convert-widget 'widget-item-convert-widget | 1139 :convert-widget 'widget-item-convert-widget |
859 :on-type '(item :format "[X]" t) | 1140 :on-type '(choice-item :format "%[[X]%]" t) |
860 :off-type '(item :format "[ ]" nil)) | 1141 :off-type '(choice-item :format "%[[ ]%]" nil)) |
861 | 1142 |
862 ;;; The `checklist' Widget. | 1143 ;;; The `checklist' Widget. |
863 | 1144 |
864 (define-widget 'checklist 'default | 1145 (define-widget 'checklist 'default |
865 "A multiple choice widget." | 1146 "A multiple choice widget." |
866 :convert-widget 'widget-choice-convert-widget | 1147 :convert-widget 'widget-types-convert-widget |
867 :format "%v" | 1148 :format "%v" |
1149 :offset 4 | |
868 :entry-format "%b %v" | 1150 :entry-format "%b %v" |
869 :menu-tag "checklist" | 1151 :menu-tag "checklist" |
1152 :greedy nil | |
870 :value-create 'widget-checklist-value-create | 1153 :value-create 'widget-checklist-value-create |
871 :value-delete 'widget-radio-value-delete | 1154 :value-delete 'widget-children-value-delete |
872 :value-get 'widget-checklist-value-get | 1155 :value-get 'widget-checklist-value-get |
873 :validate 'widget-checklist-validate | 1156 :validate 'widget-checklist-validate |
874 :match 'widget-checklist-match | 1157 :match 'widget-checklist-match |
875 :match-inline 'widget-checklist-match-inline) | 1158 :match-inline 'widget-checklist-match-inline) |
876 | 1159 |
884 (widget-put widget :children (nreverse (widget-get widget :children))))) | 1167 (widget-put widget :children (nreverse (widget-get widget :children))))) |
885 | 1168 |
886 (defun widget-checklist-add-item (widget type chosen) | 1169 (defun widget-checklist-add-item (widget type chosen) |
887 ;; Create checklist item in WIDGET of type TYPE. | 1170 ;; Create checklist item in WIDGET of type TYPE. |
888 ;; If the item is checked, CHOSEN is a cons whose cdr is the value. | 1171 ;; If the item is checked, CHOSEN is a cons whose cdr is the value. |
1172 (and (eq (preceding-char) ?\n) | |
1173 (widget-get widget :indent) | |
1174 (insert-char ? (widget-get widget :indent))) | |
889 (widget-specify-insert | 1175 (widget-specify-insert |
890 (let* ((children (widget-get widget :children)) | 1176 (let* ((children (widget-get widget :children)) |
891 (buttons (widget-get widget :buttons)) | 1177 (buttons (widget-get widget :buttons)) |
892 (from (point)) | 1178 (from (point)) |
893 child button) | 1179 child button) |
898 (let ((escape (aref (match-string 1) 0))) | 1184 (let ((escape (aref (match-string 1) 0))) |
899 (replace-match "" t t) | 1185 (replace-match "" t t) |
900 (cond ((eq escape ?%) | 1186 (cond ((eq escape ?%) |
901 (insert "%")) | 1187 (insert "%")) |
902 ((eq escape ?b) | 1188 ((eq escape ?b) |
903 (setq button (widget-create 'checkbox | 1189 (setq button (widget-create-child-and-convert |
904 :parent widget | 1190 widget 'checkbox :value (not (null chosen))))) |
905 :value (not (null chosen))))) | |
906 ((eq escape ?v) | 1191 ((eq escape ?v) |
907 (setq child | 1192 (setq child |
908 (cond ((not chosen) | 1193 (cond ((not chosen) |
909 (widget-create type :parent widget)) | 1194 (widget-create-child widget type)) |
910 ((widget-get type :inline) | 1195 ((widget-get type :inline) |
911 (widget-create type | 1196 (widget-create-child-value |
912 :parent widget | 1197 widget type (cdr chosen))) |
913 :value (cdr chosen))) | |
914 (t | 1198 (t |
915 (widget-create type | 1199 (widget-create-child-value |
916 :parent widget | 1200 widget type (car (cdr chosen))))))) |
917 :value (car (cdr chosen))))))) | |
918 (t | 1201 (t |
919 (error "Unknown escape `%c'" escape))))) | 1202 (error "Unknown escape `%c'" escape))))) |
920 ;; Update properties. | 1203 ;; Update properties. |
921 (and button child (widget-put child :button button)) | 1204 (and button child (widget-put child :button button)) |
922 (and button (widget-put widget :buttons (cons button buttons))) | 1205 (and button (widget-put widget :buttons (cons button buttons))) |
945 (t | 1228 (t |
946 (setq rest (append rest values) | 1229 (setq rest (append rest values) |
947 values nil))))) | 1230 values nil))))) |
948 (cons found rest))) | 1231 (cons found rest))) |
949 | 1232 |
950 (defun widget-checklist-match-find (widget values) | 1233 (defun widget-checklist-match-find (widget vals) |
951 ;; Find the values which match a type in the checklist. | 1234 ;; Find the vals which match a type in the checklist. |
952 ;; Return an alist of (TYPE MATCH). | 1235 ;; Return an alist of (TYPE MATCH). |
953 (let ((greedy (widget-get widget :greedy)) | 1236 (let ((greedy (widget-get widget :greedy)) |
954 (args (copy-list (widget-get widget :args))) | 1237 (args (copy-list (widget-get widget :args))) |
955 found) | 1238 found) |
956 (while values | 1239 (while vals |
957 (let ((answer (widget-checklist-match-up args values))) | 1240 (let ((answer (widget-checklist-match-up args vals))) |
958 (cond (answer | 1241 (cond (answer |
959 (let ((vals (widget-match-inline answer values))) | 1242 (let ((match (widget-match-inline answer vals))) |
960 (setq found (cons (cons answer (car vals)) found) | 1243 (setq found (cons (cons answer (car match)) found) |
961 values (cdr vals) | 1244 vals (cdr match) |
962 args (delq answer args)))) | 1245 args (delq answer args)))) |
963 (greedy | 1246 (greedy |
964 (setq values (cdr values))) | 1247 (setq vals (cdr vals))) |
965 (t | 1248 (t |
966 (setq values nil))))) | 1249 (setq vals nil))))) |
967 found)) | 1250 found)) |
968 | 1251 |
969 (defun widget-checklist-match-up (args values) | 1252 (defun widget-checklist-match-up (args vals) |
970 ;; Rerturn the first type from ARGS that matches VALUES. | 1253 ;; Rerturn the first type from ARGS that matches VALS. |
971 (let (current found) | 1254 (let (current found) |
972 (while (and args (null found)) | 1255 (while (and args (null found)) |
973 (setq current (car args) | 1256 (setq current (car args) |
974 args (cdr args) | 1257 args (cdr args) |
975 found (widget-match-inline current values))) | 1258 found (widget-match-inline current vals))) |
976 (and found current))) | 1259 (if found |
1260 current | |
1261 nil))) | |
977 | 1262 |
978 (defun widget-checklist-value-get (widget) | 1263 (defun widget-checklist-value-get (widget) |
979 ;; The values of all selected items. | 1264 ;; The values of all selected items. |
980 (let ((children (widget-get widget :children)) | 1265 (let ((children (widget-get widget :children)) |
981 child result) | 1266 child result) |
1007 ;;; The `choice-item' Widget. | 1292 ;;; The `choice-item' Widget. |
1008 | 1293 |
1009 (define-widget 'choice-item 'item | 1294 (define-widget 'choice-item 'item |
1010 "Button items that delegate action events to their parents." | 1295 "Button items that delegate action events to their parents." |
1011 :action 'widget-choice-item-action | 1296 :action 'widget-choice-item-action |
1012 :format "%[%t%]\n") | 1297 :format "%[%t%] \n") |
1013 | 1298 |
1014 (defun widget-choice-item-action (widget &optional event) | 1299 (defun widget-choice-item-action (widget &optional event) |
1015 ;; Tell parent what happened. | 1300 ;; Tell parent what happened. |
1016 (widget-apply (widget-get widget :parent) :action event)) | 1301 (widget-apply (widget-get widget :parent) :action event)) |
1017 | 1302 |
1018 ;;; The `radio-button' Widget. | 1303 ;;; The `radio-button' Widget. |
1019 | 1304 |
1020 (define-widget 'radio-button 'toggle | 1305 (define-widget 'radio-button 'toggle |
1021 "A radio button for use in the `radio' widget." | 1306 "A radio button for use in the `radio' widget." |
1022 :format "%v" | |
1023 :notify 'widget-radio-button-notify | 1307 :notify 'widget-radio-button-notify |
1024 :on-type '(choice-item :format "%[(*)%]" t) | 1308 :on-type '(choice-item :format "%[(*)%]" t) |
1025 :off-type '(choice-item :format "%[( )%]" nil)) | 1309 :off-type '(choice-item :format "%[( )%]" nil)) |
1026 | 1310 |
1027 (defun widget-radio-button-notify (widget child &optional event) | 1311 (defun widget-radio-button-notify (widget child &optional event) |
1028 ;; Notify the parent. | 1312 ;; Notify the parent. |
1029 (widget-apply (widget-get widget :parent) :action widget event)) | 1313 (widget-apply (widget-get widget :parent) :action widget event)) |
1030 | 1314 |
1031 ;;; The `radio' Widget. | 1315 ;;; The `radio-button-choice' Widget. |
1032 | 1316 |
1033 (define-widget 'radio 'default | 1317 (define-widget 'radio-button-choice 'default |
1034 "Select one of multiple options." | 1318 "Select one of multiple options." |
1035 :convert-widget 'widget-choice-convert-widget | 1319 :convert-widget 'widget-types-convert-widget |
1320 :offset 4 | |
1036 :format "%v" | 1321 :format "%v" |
1037 :entry-format "%b %v" | 1322 :entry-format "%b %v" |
1038 :menu-tag "radio" | 1323 :menu-tag "radio" |
1039 :value-create 'widget-radio-value-create | 1324 :value-create 'widget-radio-value-create |
1040 :value-delete 'widget-radio-value-delete | 1325 :value-delete 'widget-children-value-delete |
1041 :value-get 'widget-radio-value-get | 1326 :value-get 'widget-radio-value-get |
1042 :value-inline 'widget-radio-value-inline | 1327 :value-inline 'widget-radio-value-inline |
1043 :value-set 'widget-radio-value-set | 1328 :value-set 'widget-radio-value-set |
1044 :error "You must push one of the buttons" | 1329 :error "You must push one of the buttons" |
1045 :validate 'widget-radio-validate | 1330 :validate 'widget-radio-validate |
1048 :action 'widget-radio-action) | 1333 :action 'widget-radio-action) |
1049 | 1334 |
1050 (defun widget-radio-value-create (widget) | 1335 (defun widget-radio-value-create (widget) |
1051 ;; Insert all values | 1336 ;; Insert all values |
1052 (let ((args (widget-get widget :args)) | 1337 (let ((args (widget-get widget :args)) |
1053 (indent (widget-get widget :indent)) | |
1054 arg) | 1338 arg) |
1055 (while args | 1339 (while args |
1056 (setq arg (car args) | 1340 (setq arg (car args) |
1057 args (cdr args)) | 1341 args (cdr args)) |
1058 (widget-radio-add-item widget arg) | 1342 (widget-radio-add-item widget arg)))) |
1059 (and indent args (insert-char ?\ indent))))) | |
1060 | 1343 |
1061 (defun widget-radio-add-item (widget type) | 1344 (defun widget-radio-add-item (widget type) |
1062 "Add to radio widget WIDGET a new radio button item of type TYPE." | 1345 "Add to radio widget WIDGET a new radio button item of type TYPE." |
1063 (setq type (widget-convert type)) | 1346 ;; (setq type (widget-convert type)) |
1347 (and (eq (preceding-char) ?\n) | |
1348 (widget-get widget :indent) | |
1349 (insert-char ? (widget-get widget :indent))) | |
1064 (widget-specify-insert | 1350 (widget-specify-insert |
1065 (let* ((value (widget-get widget :value)) | 1351 (let* ((value (widget-get widget :value)) |
1066 (children (widget-get widget :children)) | 1352 (children (widget-get widget :children)) |
1067 (buttons (widget-get widget :buttons)) | 1353 (buttons (widget-get widget :buttons)) |
1068 (from (point)) | 1354 (from (point)) |
1076 (let ((escape (aref (match-string 1) 0))) | 1362 (let ((escape (aref (match-string 1) 0))) |
1077 (replace-match "" t t) | 1363 (replace-match "" t t) |
1078 (cond ((eq escape ?%) | 1364 (cond ((eq escape ?%) |
1079 (insert "%")) | 1365 (insert "%")) |
1080 ((eq escape ?b) | 1366 ((eq escape ?b) |
1081 (setq button (widget-create 'radio-button | 1367 (setq button (widget-create-child-and-convert |
1082 :parent widget | 1368 widget 'radio-button |
1083 :value (not (null chosen))))) | 1369 :value (not (null chosen))))) |
1084 ((eq escape ?v) | 1370 ((eq escape ?v) |
1085 (setq child (if chosen | 1371 (setq child (if chosen |
1086 (widget-create type | 1372 (widget-create-child-value |
1087 :parent widget | 1373 widget type value) |
1088 :value value) | 1374 (widget-create-child widget type)))) |
1089 (widget-create type :parent widget)))) | |
1090 (t | 1375 (t |
1091 (error "Unknown escape `%c'" escape))))) | 1376 (error "Unknown escape `%c'" escape))))) |
1092 ;; Update properties. | 1377 ;; Update properties. |
1093 (when chosen | 1378 (when chosen |
1094 (widget-put widget :choice type)) | 1379 (widget-put widget :choice type)) |
1096 (widget-put child :button button) | 1381 (widget-put child :button button) |
1097 (widget-put widget :buttons (nconc buttons (list button)))) | 1382 (widget-put widget :buttons (nconc buttons (list button)))) |
1098 (when child | 1383 (when child |
1099 (widget-put widget :children (nconc children (list child)))) | 1384 (widget-put widget :children (nconc children (list child)))) |
1100 child))) | 1385 child))) |
1101 | |
1102 (defun widget-radio-value-delete (widget) | |
1103 ;; Delete the child widgets. | |
1104 (mapcar 'widget-delete (widget-get widget :children)) | |
1105 (widget-put widget :children nil) | |
1106 (mapcar 'widget-delete (widget-get widget :buttons)) | |
1107 (widget-put widget :buttons nil)) | |
1108 | 1386 |
1109 (defun widget-radio-value-get (widget) | 1387 (defun widget-radio-value-get (widget) |
1110 ;; Get value of the child widget. | 1388 ;; Get value of the child widget. |
1111 (let ((chosen (widget-radio-chosen widget))) | 1389 (let ((chosen (widget-radio-chosen widget))) |
1112 (and chosen (widget-value chosen)))) | 1390 (and chosen (widget-value chosen)))) |
1186 ;; Pass notification to parent. | 1464 ;; Pass notification to parent. |
1187 (widget-apply widget :notify child event)) | 1465 (widget-apply widget :notify child event)) |
1188 | 1466 |
1189 ;;; The `insert-button' Widget. | 1467 ;;; The `insert-button' Widget. |
1190 | 1468 |
1191 (define-widget 'insert-button 'push | 1469 (define-widget 'insert-button 'push-button |
1192 "An insert button for the `repeat' widget." | 1470 "An insert button for the `editable-list' widget." |
1193 :tag "INS" | 1471 :tag "INS" |
1194 :action 'widget-insert-button-action) | 1472 :action 'widget-insert-button-action) |
1195 | 1473 |
1196 (defun widget-insert-button-action (widget &optional event) | 1474 (defun widget-insert-button-action (widget &optional event) |
1197 ;; Ask the parent to insert a new item. | 1475 ;; Ask the parent to insert a new item. |
1198 (widget-apply (widget-get widget :parent) | 1476 (widget-apply (widget-get widget :parent) |
1199 :insert-before (widget-get widget :widget))) | 1477 :insert-before (widget-get widget :widget))) |
1200 | 1478 |
1201 ;;; The `delete-button' Widget. | 1479 ;;; The `delete-button' Widget. |
1202 | 1480 |
1203 (define-widget 'delete-button 'push | 1481 (define-widget 'delete-button 'push-button |
1204 "A delete button for the `repeat' widget." | 1482 "A delete button for the `editable-list' widget." |
1205 :tag "DEL" | 1483 :tag "DEL" |
1206 :action 'widget-delete-button-action) | 1484 :action 'widget-delete-button-action) |
1207 | 1485 |
1208 (defun widget-delete-button-action (widget &optional event) | 1486 (defun widget-delete-button-action (widget &optional event) |
1209 ;; Ask the parent to insert a new item. | 1487 ;; Ask the parent to insert a new item. |
1210 (widget-apply (widget-get widget :parent) | 1488 (widget-apply (widget-get widget :parent) |
1211 :delete-at (widget-get widget :widget))) | 1489 :delete-at (widget-get widget :widget))) |
1212 | 1490 |
1213 ;;; The `repeat' Widget. | 1491 ;;; The `editable-list' Widget. |
1214 | 1492 |
1215 (define-widget 'repeat 'default | 1493 (define-widget 'editable-list 'default |
1216 "A variable list of widgets of the same type." | 1494 "A variable list of widgets of the same type." |
1217 :convert-widget 'widget-choice-convert-widget | 1495 :convert-widget 'widget-types-convert-widget |
1496 :offset 12 | |
1218 :format "%v%i\n" | 1497 :format "%v%i\n" |
1219 :format-handler 'widget-repeat-format-handler | 1498 :format-handler 'widget-editable-list-format-handler |
1220 :entry-format "%i %d %v" | 1499 :entry-format "%i %d %v" |
1221 :menu-tag "repeat" | 1500 :menu-tag "editable-list" |
1222 :value-create 'widget-repeat-value-create | 1501 :value-create 'widget-editable-list-value-create |
1223 :value-delete 'widget-radio-value-delete | 1502 :value-delete 'widget-children-value-delete |
1224 :value-get 'widget-repeat-value-get | 1503 :value-get 'widget-editable-list-value-get |
1225 :validate 'widget-repeat-validate | 1504 :validate 'widget-editable-list-validate |
1226 :match 'widget-repeat-match | 1505 :match 'widget-editable-list-match |
1227 :match-inline 'widget-repeat-match-inline | 1506 :match-inline 'widget-editable-list-match-inline |
1228 :insert-before 'widget-repeat-insert-before | 1507 :insert-before 'widget-editable-list-insert-before |
1229 :delete-at 'widget-repeat-delete-at) | 1508 :delete-at 'widget-editable-list-delete-at) |
1230 | 1509 |
1231 (defun widget-repeat-format-handler (widget escape) | 1510 (defun widget-editable-list-format-handler (widget escape) |
1232 ;; We recognize the insert button. | 1511 ;; We recognize the insert button. |
1233 (cond ((eq escape ?i) | 1512 (cond ((eq escape ?i) |
1234 (insert " ") | 1513 (and (widget-get widget :indent) |
1235 (backward-char 1) | 1514 (insert-char ? (widget-get widget :indent))) |
1236 (let* ((from (point)) | 1515 (widget-create-child-and-convert widget 'insert-button)) |
1237 (button (widget-create (list 'insert-button | |
1238 :parent widget)))) | |
1239 (widget-specify-button button from (point))) | |
1240 (forward-char 1)) | |
1241 (t | 1516 (t |
1242 (widget-default-format-handler widget escape)))) | 1517 (widget-default-format-handler widget escape)))) |
1243 | 1518 |
1244 (defun widget-repeat-value-create (widget) | 1519 (defun widget-editable-list-value-create (widget) |
1245 ;; Insert all values | 1520 ;; Insert all values |
1246 (let* ((value (widget-get widget :value)) | 1521 (let* ((value (widget-get widget :value)) |
1247 (type (nth 0 (widget-get widget :args))) | 1522 (type (nth 0 (widget-get widget :args))) |
1248 (inlinep (widget-get type :inline)) | 1523 (inlinep (widget-get type :inline)) |
1249 children) | 1524 children) |
1250 (widget-put widget :value-pos (copy-marker (point))) | 1525 (widget-put widget :value-pos (copy-marker (point))) |
1251 (set-marker-insertion-type (widget-get widget :value-pos) t) | 1526 (set-marker-insertion-type (widget-get widget :value-pos) t) |
1252 (while value | 1527 (while value |
1253 (let ((answer (widget-match-inline type value))) | 1528 (let ((answer (widget-match-inline type value))) |
1254 (if answer | 1529 (if answer |
1255 (setq children (cons (widget-repeat-entry-create | 1530 (setq children (cons (widget-editable-list-entry-create |
1256 widget (if inlinep | 1531 widget |
1257 (car answer) | 1532 (if inlinep |
1258 (car (car answer)))) | 1533 (car answer) |
1534 (car (car answer))) | |
1535 t) | |
1259 children) | 1536 children) |
1260 value (cdr answer)) | 1537 value (cdr answer)) |
1261 (setq value nil)))) | 1538 (setq value nil)))) |
1262 (widget-put widget :children (nreverse children)))) | 1539 (widget-put widget :children (nreverse children)))) |
1263 | 1540 |
1264 (defun widget-repeat-value-get (widget) | 1541 (defun widget-editable-list-value-get (widget) |
1265 ;; Get value of the child widget. | 1542 ;; Get value of the child widget. |
1266 (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline)) | 1543 (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline)) |
1267 (widget-get widget :children)))) | 1544 (widget-get widget :children)))) |
1268 | 1545 |
1269 (defun widget-repeat-validate (widget) | 1546 (defun widget-editable-list-validate (widget) |
1270 ;; All the chilren must be valid. | 1547 ;; All the chilren must be valid. |
1271 (let ((children (widget-get widget :children)) | 1548 (let ((children (widget-get widget :children)) |
1272 child found) | 1549 child found) |
1273 (while (and children (not found)) | 1550 (while (and children (not found)) |
1274 (setq child (car children) | 1551 (setq child (car children) |
1275 children (cdr children) | 1552 children (cdr children) |
1276 found (widget-apply child :validate))) | 1553 found (widget-apply child :validate))) |
1277 found)) | 1554 found)) |
1278 | 1555 |
1279 (defun widget-repeat-match (widget value) | 1556 (defun widget-editable-list-match (widget value) |
1280 ;; Value must be a list and all the members must match the repeat type. | 1557 ;; Value must be a list and all the members must match the type. |
1281 (and (listp value) | 1558 (and (listp value) |
1282 (null (cdr (widget-repeat-match-inline widget value))))) | 1559 (null (cdr (widget-editable-list-match-inline widget value))))) |
1283 | 1560 |
1284 (defun widget-repeat-match-inline (widget value) | 1561 (defun widget-editable-list-match-inline (widget value) |
1285 (let ((type (nth 0 (widget-get widget :args))) | 1562 (let ((type (nth 0 (widget-get widget :args))) |
1286 (ok t) | 1563 (ok t) |
1287 found) | 1564 found) |
1288 (while (and value ok) | 1565 (while (and value ok) |
1289 (let ((answer (widget-match-inline type value))) | 1566 (let ((answer (widget-match-inline type value))) |
1291 (setq found (append found (car answer)) | 1568 (setq found (append found (car answer)) |
1292 value (cdr answer)) | 1569 value (cdr answer)) |
1293 (setq ok nil)))) | 1570 (setq ok nil)))) |
1294 (cons found value))) | 1571 (cons found value))) |
1295 | 1572 |
1296 (defun widget-repeat-insert-before (widget before) | 1573 (defun widget-editable-list-insert-before (widget before) |
1297 ;; Insert a new child in the list of children. | 1574 ;; Insert a new child in the list of children. |
1298 (save-excursion | 1575 (save-excursion |
1299 (let ((children (widget-get widget :children)) | 1576 (let ((children (widget-get widget :children)) |
1300 (inhibit-read-only t) | 1577 (inhibit-read-only t) |
1301 after-change-functions) | 1578 after-change-functions) |
1302 (cond (before | 1579 (cond (before |
1303 (goto-char (widget-get before :from))) | 1580 (goto-char (widget-get before :entry-from))) |
1304 (t | 1581 (t |
1305 (goto-char (widget-get widget :value-pos)))) | 1582 (goto-char (widget-get widget :value-pos)))) |
1306 (let ((child (widget-repeat-entry-create | 1583 (let ((child (widget-editable-list-entry-create |
1307 widget (widget-get (nth 0 (widget-get widget :args)) | 1584 widget nil nil))) |
1308 :value)))) | 1585 (when (< (widget-get child :entry-from) (widget-get widget :from)) |
1309 (widget-specify-text (widget-get child :from) | 1586 (set-marker (widget-get widget :from) |
1310 (widget-get child :to)) | 1587 (widget-get child :entry-from))) |
1588 (widget-specify-text (widget-get child :entry-from) | |
1589 (widget-get child :entry-to)) | |
1311 (if (eq (car children) before) | 1590 (if (eq (car children) before) |
1312 (widget-put widget :children (cons child children)) | 1591 (widget-put widget :children (cons child children)) |
1313 (while (not (eq (car (cdr children)) before)) | 1592 (while (not (eq (car (cdr children)) before)) |
1314 (setq children (cdr children))) | 1593 (setq children (cdr children))) |
1315 (setcdr children (cons child (cdr children))))))) | 1594 (setcdr children (cons child (cdr children))))))) |
1316 (widget-setup) | 1595 (widget-setup) |
1317 (widget-apply widget :notify widget)) | 1596 (widget-apply widget :notify widget)) |
1318 | 1597 |
1319 (defun widget-repeat-delete-at (widget child) | 1598 (defun widget-editable-list-delete-at (widget child) |
1320 ;; Delete child from list of children. | 1599 ;; Delete child from list of children. |
1321 (save-excursion | 1600 (save-excursion |
1322 (let ((buttons (copy-list (widget-get widget :buttons))) | 1601 (let ((buttons (copy-list (widget-get widget :buttons))) |
1323 button | 1602 button |
1324 (inhibit-read-only t) | 1603 (inhibit-read-only t) |
1328 buttons (cdr buttons)) | 1607 buttons (cdr buttons)) |
1329 (when (eq (widget-get button :widget) child) | 1608 (when (eq (widget-get button :widget) child) |
1330 (widget-put widget | 1609 (widget-put widget |
1331 :buttons (delq button (widget-get widget :buttons))) | 1610 :buttons (delq button (widget-get widget :buttons))) |
1332 (widget-delete button)))) | 1611 (widget-delete button)))) |
1333 (widget-delete child) | 1612 (let ((entry-from (widget-get child :entry-from)) |
1613 (entry-to (widget-get child :entry-to)) | |
1614 (inhibit-read-only t) | |
1615 after-change-functions) | |
1616 (widget-delete child) | |
1617 (delete-region entry-from entry-to) | |
1618 (set-marker entry-from nil) | |
1619 (set-marker entry-to nil)) | |
1334 (widget-put widget :children (delq child (widget-get widget :children)))) | 1620 (widget-put widget :children (delq child (widget-get widget :children)))) |
1335 (widget-setup) | 1621 (widget-setup) |
1336 (widget-apply widget :notify widget)) | 1622 (widget-apply widget :notify widget)) |
1337 | 1623 |
1338 (defun widget-repeat-entry-create (widget value) | 1624 (defun widget-editable-list-entry-create (widget value conv) |
1339 ;; Create a new entry to the list. | 1625 ;; Create a new entry to the list. |
1340 (let ((type (nth 0 (widget-get widget :args))) | 1626 (let ((type (nth 0 (widget-get widget :args))) |
1341 (indent (widget-get widget :indent)) | |
1342 child delete insert) | 1627 child delete insert) |
1343 (widget-specify-insert | 1628 (widget-specify-insert |
1344 (save-excursion | 1629 (save-excursion |
1345 (insert (widget-get widget :entry-format)) | 1630 (and (widget-get widget :indent) |
1346 (if indent | 1631 (insert-char ? (widget-get widget :indent))) |
1347 (insert-char ?\ indent))) | 1632 (insert (widget-get widget :entry-format))) |
1348 ;; Parse % escapes in format. | 1633 ;; Parse % escapes in format. |
1349 (while (re-search-forward "%\\(.\\)" nil t) | 1634 (while (re-search-forward "%\\(.\\)" nil t) |
1350 (let ((escape (aref (match-string 1) 0))) | 1635 (let ((escape (aref (match-string 1) 0))) |
1351 (replace-match "" t t) | 1636 (replace-match "" t t) |
1352 (cond ((eq escape ?%) | 1637 (cond ((eq escape ?%) |
1353 (insert "%")) | 1638 (insert "%")) |
1354 ((eq escape ?i) | 1639 ((eq escape ?i) |
1355 (setq insert (widget-create 'insert-button | 1640 (setq insert (widget-create-child-and-convert |
1356 :parent widget))) | 1641 widget 'insert-button))) |
1357 ((eq escape ?d) | 1642 ((eq escape ?d) |
1358 (setq delete (widget-create 'delete-button | 1643 (setq delete (widget-create-child-and-convert |
1359 :parent widget))) | 1644 widget 'delete-button))) |
1360 ((eq escape ?v) | 1645 ((eq escape ?v) |
1361 (setq child (widget-create type | 1646 (if conv |
1362 :parent widget | 1647 (setq child (widget-create-child-value |
1363 :value value))) | 1648 widget type value)) |
1649 (setq child (widget-create-child widget type)))) | |
1364 (t | 1650 (t |
1365 (error "Unknown escape `%c'" escape))))) | 1651 (error "Unknown escape `%c'" escape))))) |
1366 (widget-put widget | 1652 (widget-put widget |
1367 :buttons (cons delete | 1653 :buttons (cons delete |
1368 (cons insert | 1654 (cons insert |
1369 (widget-get widget :buttons)))) | 1655 (widget-get widget :buttons)))) |
1370 (move-marker (widget-get child :from) (point-min)) | 1656 (let ((entry-from (copy-marker (point-min))) |
1371 (move-marker (widget-get child :to) (point-max))) | 1657 (entry-to (copy-marker (point-max)))) |
1658 (widget-specify-text entry-from entry-to) | |
1659 (set-marker-insertion-type entry-from t) | |
1660 (set-marker-insertion-type entry-to nil) | |
1661 (widget-put child :entry-from entry-from) | |
1662 (widget-put child :entry-to entry-to))) | |
1372 (widget-put insert :widget child) | 1663 (widget-put insert :widget child) |
1373 (widget-put delete :widget child) | 1664 (widget-put delete :widget child) |
1374 child)) | 1665 child)) |
1375 | 1666 |
1376 ;;; The `group' Widget. | 1667 ;;; The `group' Widget. |
1377 | 1668 |
1378 (define-widget 'group 'default | 1669 (define-widget 'group 'default |
1379 "A widget which group other widgets inside." | 1670 "A widget which group other widgets inside." |
1380 :convert-widget 'widget-choice-convert-widget | 1671 :convert-widget 'widget-types-convert-widget |
1381 :format "%v" | 1672 :format "%v" |
1382 :value-create 'widget-group-value-create | 1673 :value-create 'widget-group-value-create |
1383 :value-delete 'widget-radio-value-delete | 1674 :value-delete 'widget-children-value-delete |
1384 :value-get 'widget-repeat-value-get | 1675 :value-get 'widget-editable-list-value-get |
1385 :validate 'widget-repeat-validate | 1676 :validate 'widget-editable-list-validate |
1386 :match 'widget-group-match | 1677 :match 'widget-group-match |
1387 :match-inline 'widget-group-match-inline) | 1678 :match-inline 'widget-group-match-inline) |
1388 | 1679 |
1389 (defun widget-group-value-create (widget) | 1680 (defun widget-group-value-create (widget) |
1390 ;; Create each component. | 1681 ;; Create each component. |
1391 (let ((args (widget-get widget :args)) | 1682 (let ((args (widget-get widget :args)) |
1392 (value (widget-get widget :value)) | 1683 (value (widget-get widget :value)) |
1393 (indent (widget-get widget :indent)) | |
1394 arg answer children) | 1684 arg answer children) |
1395 (while args | 1685 (while args |
1396 (setq arg (car args) | 1686 (setq arg (car args) |
1397 args (cdr args) | 1687 args (cdr args) |
1398 answer (widget-match-inline arg value) | 1688 answer (widget-match-inline arg value) |
1399 value (cdr answer) | 1689 value (cdr answer)) |
1400 children (cons (cond ((null answer) | 1690 (and (eq (preceding-char) ?\n) |
1401 (widget-create arg :parent widget)) | 1691 (widget-get widget :indent) |
1402 ((widget-get arg :inline) | 1692 (insert-char ? (widget-get widget :indent))) |
1403 (widget-create arg | 1693 (push (cond ((null answer) |
1404 :parent widget | 1694 (widget-create-child widget arg)) |
1405 :value (car answer))) | 1695 ((widget-get arg :inline) |
1406 (t | 1696 (widget-create-child-value widget arg (car answer))) |
1407 (widget-create arg | 1697 (t |
1408 :parent widget | 1698 (widget-create-child-value widget arg (car (car answer))))) |
1409 :value (car (car answer))))) | 1699 children)) |
1410 children)) | |
1411 (and args indent (insert-char ?\ indent))) | |
1412 (widget-put widget :children (nreverse children)))) | 1700 (widget-put widget :children (nreverse children)))) |
1413 | 1701 |
1414 (defun widget-group-match (widget values) | 1702 (defun widget-group-match (widget values) |
1415 ;; Match if the components match. | 1703 ;; Match if the components match. |
1416 (and (listp values) | 1704 (and (listp values) |
1417 (null (cdr (widget-group-match-inline widget values))))) | 1705 (let ((match (widget-group-match-inline widget values))) |
1418 | 1706 (and match (null (cdr match)))))) |
1419 (defun widget-group-match-inline (widget values) | 1707 |
1708 (defun widget-group-match-inline (widget vals) | |
1420 ;; Match if the components match. | 1709 ;; Match if the components match. |
1421 (let ((args (widget-get widget :args)) | 1710 (let ((args (widget-get widget :args)) |
1422 (match t) | 1711 argument answer found) |
1423 arg answer found) | |
1424 (while args | 1712 (while args |
1425 (setq arg (car args) | 1713 (setq argument (car args) |
1426 args (cdr args) | 1714 args (cdr args) |
1427 answer (widget-match-inline arg values)) | 1715 answer (widget-match-inline argument vals)) |
1428 (if answer | 1716 (if answer |
1429 (setq values (cdr answer) | 1717 (setq vals (cdr answer) |
1430 found (append found (car answer))) | 1718 found (append found (car answer))) |
1431 (setq values nil))) | 1719 (setq vals nil |
1720 args nil))) | |
1432 (if answer | 1721 (if answer |
1433 (cons found values) | 1722 (cons found vals) |
1434 nil))) | 1723 nil))) |
1435 | 1724 |
1725 ;;; The `widget-help' Widget. | |
1726 | |
1727 (define-widget 'widget-help 'push-button | |
1728 "The widget documentation button." | |
1729 :format "%[[%t]%] %d" | |
1730 :help-echo "Push me to toggle the documentation." | |
1731 :action 'widget-help-action) | |
1732 | |
1733 (defun widget-help-action (widget &optional event) | |
1734 "Toggle documentation for WIDGET." | |
1735 (let ((old (widget-get widget :doc)) | |
1736 (new (widget-get widget :widget-doc))) | |
1737 (widget-put widget :doc new) | |
1738 (widget-put widget :widget-doc old)) | |
1739 (widget-value-set widget (widget-value widget))) | |
1740 | |
1436 ;;; The Sexp Widgets. | 1741 ;;; The Sexp Widgets. |
1437 | 1742 |
1438 (define-widget 'const 'item | 1743 (define-widget 'const 'item |
1439 nil | 1744 "An immutable sexp." |
1440 :format "%t\n") | 1745 :format "%t\n%d") |
1441 | 1746 |
1442 (define-widget 'string 'field | 1747 (define-widget 'function-item 'item |
1443 nil) | 1748 "An immutable function name." |
1749 :format "%v\n%h" | |
1750 :documentation-property (lambda (symbol) | |
1751 (condition-case nil | |
1752 (documentation symbol t) | |
1753 (error nil)))) | |
1754 | |
1755 (define-widget 'variable-item 'item | |
1756 "An immutable variable name." | |
1757 :format "%v\n%h" | |
1758 :documentation-property 'variable-documentation) | |
1759 | |
1760 (define-widget 'string 'editable-field | |
1761 "A string" | |
1762 :tag "String" | |
1763 :format "%[%t%]: %v") | |
1764 | |
1765 (define-widget 'regexp 'string | |
1766 "A regular expression." | |
1767 ;; Should do validation. | |
1768 :tag "Regexp") | |
1444 | 1769 |
1445 (define-widget 'file 'string | 1770 (define-widget 'file 'string |
1446 nil | 1771 "A file widget. |
1447 :format "%[%t%]:%v" | 1772 It will read a file name from the minibuffer when activated." |
1773 :format "%[%t%]: %v" | |
1448 :tag "File" | 1774 :tag "File" |
1449 :action 'widget-file-action) | 1775 :action 'widget-file-action) |
1450 | 1776 |
1451 (defun widget-file-action (widget &optional event) | 1777 (defun widget-file-action (widget &optional event) |
1452 nil | |
1453 ;; Read a file name from the minibuffer. | 1778 ;; Read a file name from the minibuffer. |
1454 (widget-value-set widget | 1779 (let* ((value (widget-value widget)) |
1455 (read-file-name (widget-apply widget :menu-tag-get) | 1780 (dir (file-name-directory value)) |
1456 (widget-get widget :directory) | 1781 (file (file-name-nondirectory value)) |
1457 (widget-value widget) | 1782 (menu-tag (widget-apply widget :menu-tag-get)) |
1458 (widget-get widget :must-match) | 1783 (must-match (widget-get widget :must-match)) |
1459 (widget-get widget :initial)))) | 1784 (answer (read-file-name (concat menu-tag ": (defalt `" value "') ") |
1785 dir nil must-match file))) | |
1786 (widget-value-set widget (abbreviate-file-name answer)) | |
1787 (widget-apply widget :notify widget event) | |
1788 (widget-setup))) | |
1460 | 1789 |
1461 (define-widget 'directory 'file | 1790 (define-widget 'directory 'file |
1462 nil | 1791 "A directory widget. |
1792 It will read a directory name from the minibuffer when activated." | |
1463 :tag "Directory") | 1793 :tag "Directory") |
1464 | 1794 |
1465 (define-widget 'symbol 'string | 1795 (define-widget 'symbol 'string |
1466 nil | 1796 "A lisp symbol." |
1797 :value nil | |
1798 :tag "Symbol" | |
1467 :match (lambda (widget value) (symbolp value)) | 1799 :match (lambda (widget value) (symbolp value)) |
1468 :value-to-internal (lambda (widget value) (symbol-name value)) | 1800 :value-to-internal (lambda (widget value) |
1469 :value-to-external (lambda (widget value) (intern value))) | 1801 (if (symbolp value) |
1802 (symbol-name value) | |
1803 value)) | |
1804 :value-to-external (lambda (widget value) | |
1805 (if (stringp value) | |
1806 (intern value) | |
1807 value))) | |
1808 | |
1809 (define-widget 'function 'sexp | |
1810 ;; Should complete on functions. | |
1811 "A lisp function." | |
1812 :tag "Function") | |
1813 | |
1814 (define-widget 'variable 'symbol | |
1815 ;; Should complete on variables. | |
1816 "A lisp variable." | |
1817 :tag "Variable") | |
1470 | 1818 |
1471 (define-widget 'sexp 'string | 1819 (define-widget 'sexp 'string |
1472 nil | 1820 "An arbitrary lisp expression." |
1821 :tag "Lisp expression" | |
1822 :value nil | |
1473 :validate 'widget-sexp-validate | 1823 :validate 'widget-sexp-validate |
1474 :match (lambda (widget value) t) | 1824 :match (lambda (widget value) t) |
1475 :value-to-internal (lambda (widget value) (pp-to-string value)) | 1825 :value-to-internal 'widget-sexp-value-to-internal |
1476 :value-to-external (lambda (widget value) (read value))) | 1826 :value-to-external (lambda (widget value) (read value))) |
1827 | |
1828 (defun widget-sexp-value-to-internal (widget value) | |
1829 ;; Use pp for printer representation. | |
1830 (let ((pp (pp-to-string value))) | |
1831 (while (string-match "\n\\'" pp) | |
1832 (setq pp (substring pp 0 -1))) | |
1833 (if (or (string-match "\n\\'" pp) | |
1834 (> (length pp) 40)) | |
1835 (concat "\n" pp) | |
1836 pp))) | |
1477 | 1837 |
1478 (defun widget-sexp-validate (widget) | 1838 (defun widget-sexp-validate (widget) |
1479 ;; Valid if we can read the string and there is no junk left after it. | 1839 ;; Valid if we can read the string and there is no junk left after it. |
1480 (save-excursion | 1840 (save-excursion |
1481 (set-buffer (get-buffer-create " *Widget Scratch*")) | 1841 (let ((buffer (set-buffer (get-buffer-create " *Widget Scratch*")))) |
1482 (erase-buffer) | 1842 (erase-buffer) |
1483 (insert (widget-apply :value-get widget)) | 1843 (insert (widget-apply widget :value-get)) |
1484 (goto-char (point-min)) | 1844 (goto-char (point-min)) |
1485 (condition-case data | 1845 (condition-case data |
1486 (let ((value (read (current-buffer)))) | 1846 (let ((value (read buffer))) |
1487 (if (eobp) | 1847 (if (eobp) |
1488 (if (widget-apply widget :match value) | 1848 (if (widget-apply widget :match value) |
1489 t | 1849 nil |
1490 (widget-put widget :error (widget-get widget :type-error)) | 1850 (widget-put widget :error (widget-get widget :type-error)) |
1491 nil) | 1851 widget) |
1492 (widget-put widget | 1852 (widget-put widget |
1493 :error (format "Junk at end of expression: %s" | 1853 :error (format "Junk at end of expression: %s" |
1494 (buffer-substring (point) (point-max)))) | 1854 (buffer-substring (point) |
1495 nil)) | 1855 (point-max)))) |
1496 (error (widget-put widget :error (error-message-string data)) | 1856 widget)) |
1497 nil)))) | 1857 (error (widget-put widget :error (error-message-string data)) |
1858 widget))))) | |
1498 | 1859 |
1499 (define-widget 'integer 'sexp | 1860 (define-widget 'integer 'sexp |
1500 nil | 1861 "An integer." |
1862 :tag "Integer" | |
1863 :value 0 | |
1501 :type-error "This field should contain an integer" | 1864 :type-error "This field should contain an integer" |
1865 :value-to-internal (lambda (widget value) | |
1866 (if (integerp value) | |
1867 (prin1-to-string value) | |
1868 value)) | |
1502 :match (lambda (widget value) (integerp value))) | 1869 :match (lambda (widget value) (integerp value))) |
1503 | 1870 |
1871 (define-widget 'character 'string | |
1872 "An character." | |
1873 :tag "Character" | |
1874 :value 0 | |
1875 :size 1 | |
1876 :format "%t: %v\n" | |
1877 :type-error "This field should contain a character" | |
1878 :value-to-internal (lambda (widget value) | |
1879 (if (integerp value) | |
1880 (char-to-string value) | |
1881 value)) | |
1882 :value-to-external (lambda (widget value) | |
1883 (if (stringp value) | |
1884 (aref value 0) | |
1885 value)) | |
1886 :match (lambda (widget value) (integerp value))) | |
1887 | |
1504 (define-widget 'number 'sexp | 1888 (define-widget 'number 'sexp |
1505 nil | 1889 "A floating point number." |
1890 :tag "Number" | |
1891 :value 0.0 | |
1506 :type-error "This field should contain a number" | 1892 :type-error "This field should contain a number" |
1893 :value-to-internal (lambda (widget value) | |
1894 (if (numberp value) | |
1895 (prin1-to-string value) | |
1896 value)) | |
1507 :match (lambda (widget value) (numberp value))) | 1897 :match (lambda (widget value) (numberp value))) |
1508 | 1898 |
1509 (define-widget 'list 'group | 1899 (define-widget 'list 'group |
1510 nil) | 1900 "A lisp list." |
1901 :tag "List" | |
1902 :format "%t:\n%v") | |
1511 | 1903 |
1512 (define-widget 'vector 'group | 1904 (define-widget 'vector 'group |
1513 nil | 1905 "A lisp vector." |
1906 :tag "Vector" | |
1907 :format "%t:\n%v" | |
1514 :match 'widget-vector-match | 1908 :match 'widget-vector-match |
1515 :value-to-internal (lambda (widget value) (append value nil)) | 1909 :value-to-internal (lambda (widget value) (append value nil)) |
1516 :value-to-external (lambda (widget value) (apply 'vector value))) | 1910 :value-to-external (lambda (widget value) (apply 'vector value))) |
1517 | 1911 |
1518 (defun widget-vector-match (widget value) | 1912 (defun widget-vector-match (widget value) |
1519 (and (vectorp value) | 1913 (and (vectorp value) |
1520 (widget-group-match widget | 1914 (widget-group-match widget |
1521 (widget-apply :value-to-internal widget value)))) | 1915 (widget-apply :value-to-internal widget value)))) |
1522 | 1916 |
1523 (define-widget 'cons 'group | 1917 (define-widget 'cons 'group |
1524 nil | 1918 "A cons-cell." |
1919 :tag "Cons-cell" | |
1920 :format "%t:\n%v" | |
1525 :match 'widget-cons-match | 1921 :match 'widget-cons-match |
1526 :value-to-internal (lambda (widget value) | 1922 :value-to-internal (lambda (widget value) |
1527 (list (car value) (cdr value))) | 1923 (list (car value) (cdr value))) |
1528 :value-to-external (lambda (widget value) | 1924 :value-to-external (lambda (widget value) |
1529 (cons (nth 0 value) (nth 1 value)))) | 1925 (cons (nth 0 value) (nth 1 value)))) |
1530 | 1926 |
1531 (defun widget-cons-match (widget value) | 1927 (defun widget-cons-match (widget value) |
1532 (and (consp value) | 1928 (and (consp value) |
1533 (widget-group-match widget | 1929 (widget-group-match widget |
1534 (widget-apply :value-to-internal widget value)))) | 1930 (widget-apply widget :value-to-internal value)))) |
1931 | |
1932 (define-widget 'choice 'menu-choice | |
1933 "A union of several sexp types." | |
1934 :tag "Choice" | |
1935 :format "%[%t%]: %v") | |
1936 | |
1937 (define-widget 'radio 'radio-button-choice | |
1938 "A union of several sexp types." | |
1939 :tag "Choice" | |
1940 :format "%t:\n%v") | |
1941 | |
1942 (define-widget 'repeat 'editable-list | |
1943 "A variable length homogeneous list." | |
1944 :tag "Repeat" | |
1945 :format "%t:\n%v%i\n") | |
1946 | |
1947 (define-widget 'set 'checklist | |
1948 "A list of members from a fixed set." | |
1949 :tag "Set" | |
1950 :format "%t:\n%v") | |
1951 | |
1952 (define-widget 'boolean 'toggle | |
1953 "To be nil or non-nil, that is the question." | |
1954 :tag "Boolean" | |
1955 :format "%t: %v") | |
1956 | |
1957 ;;; The `color' Widget. | |
1958 | |
1959 (define-widget 'color-item 'choice-item | |
1960 "A color name (with sample)." | |
1961 :format "%v (%[sample%])\n" | |
1962 :button-face-get 'widget-color-item-button-face-get) | |
1963 | |
1964 (defun widget-color-item-button-face-get (widget) | |
1965 ;; We create a face from the value. | |
1966 (require 'facemenu) | |
1967 (condition-case nil | |
1968 (facemenu-get-face (intern (concat "fg:" (widget-value widget)))) | |
1969 (error 'default))) | |
1970 | |
1971 (define-widget 'color 'push-button | |
1972 "Choose a color name (with sample)." | |
1973 :format "%[%t%]: %v" | |
1974 :tag "Color" | |
1975 :value "default" | |
1976 :value-create 'widget-color-value-create | |
1977 :value-delete 'widget-children-value-delete | |
1978 :value-get 'widget-color-value-get | |
1979 :value-set 'widget-color-value-set | |
1980 :action 'widget-color-action | |
1981 :match 'widget-field-match | |
1982 :tag "Color") | |
1983 | |
1984 (defvar widget-color-choice-list nil) | |
1985 ;; Variable holding the possible colors. | |
1986 | |
1987 (defun widget-color-choice-list () | |
1988 (unless widget-color-choice-list | |
1989 (setq widget-color-choice-list | |
1990 (mapcar '(lambda (color) (list color)) | |
1991 (x-defined-colors)))) | |
1992 widget-color-choice-list) | |
1993 | |
1994 (defun widget-color-value-create (widget) | |
1995 (let ((child (widget-create-child-and-convert | |
1996 widget 'color-item (widget-get widget :value)))) | |
1997 (widget-put widget :children (list child)))) | |
1998 | |
1999 (defun widget-color-value-get (widget) | |
2000 ;; Pass command to first child. | |
2001 (widget-apply (car (widget-get widget :children)) :value-get)) | |
2002 | |
2003 (defun widget-color-value-set (widget value) | |
2004 ;; Pass command to first child. | |
2005 (widget-apply (car (widget-get widget :children)) :value-set value)) | |
2006 | |
2007 (defvar widget-color-history nil | |
2008 "History of entered colors") | |
2009 | |
2010 (defun widget-color-action (widget &optional event) | |
2011 ;; Prompt for a color. | |
2012 (let* ((tag (widget-apply widget :menu-tag-get)) | |
2013 (prompt (concat tag ": ")) | |
2014 (answer (cond ((string-match "XEmacs" emacs-version) | |
2015 (read-color prompt)) | |
2016 ((fboundp 'x-defined-colors) | |
2017 (completing-read (concat tag ": ") | |
2018 (widget-color-choice-list) | |
2019 nil nil nil 'widget-color-history)) | |
2020 (t | |
2021 (read-string prompt (widget-value widget)))))) | |
2022 (unless (zerop (length answer)) | |
2023 (widget-value-set widget answer) | |
2024 (widget-apply widget :notify widget event) | |
2025 (widget-setup)))) | |
2026 | |
2027 ;;; The Help Echo | |
2028 | |
2029 (defun widget-echo-help-mouse () | |
2030 "Display the help message for the widget under the mouse. | |
2031 Enable with (run-with-idle-timer 1 t 'widget-echo-help-mouse)" | |
2032 (let* ((pos (mouse-position)) | |
2033 (frame (car pos)) | |
2034 (x (car (cdr pos))) | |
2035 (y (cdr (cdr pos))) | |
2036 (win (window-at x y frame)) | |
2037 (where (coordinates-in-window-p (cons x y) win))) | |
2038 (when (consp where) | |
2039 (save-window-excursion | |
2040 (progn ; save-excursion | |
2041 (select-window win) | |
2042 (let* ((result (compute-motion (window-start win) | |
2043 '(0 . 0) | |
2044 (window-end win) | |
2045 where | |
2046 (window-width win) | |
2047 (cons (window-hscroll) 0) | |
2048 win))) | |
2049 (when (and (eq (nth 1 result) x) | |
2050 (eq (nth 2 result) y)) | |
2051 (widget-echo-help (nth 0 result)))))))) | |
2052 (unless track-mouse | |
2053 (setq track-mouse t) | |
2054 (add-hook 'post-command-hook 'widget-stop-mouse-tracking))) | |
2055 | |
2056 (defun widget-stop-mouse-tracking (&rest args) | |
2057 "Stop the mouse tracking done while idle." | |
2058 (remove-hook 'post-command-hook 'widget-stop-mouse-tracking) | |
2059 (setq track-mouse nil)) | |
2060 | |
2061 (defun widget-at (pos) | |
2062 "The button or field at POS." | |
2063 (or (get-text-property pos 'button) | |
2064 (get-text-property pos 'field))) | |
2065 | |
2066 (defun widget-echo-help (pos) | |
2067 "Display the help echo for widget at POS." | |
2068 (let* ((widget (widget-at pos)) | |
2069 (help-echo (and widget (widget-get widget :help-echo)))) | |
2070 (cond ((stringp help-echo) | |
2071 (message "%s" help-echo)) | |
2072 ((and (symbolp help-echo) (fboundp help-echo) | |
2073 (stringp (setq help-echo (funcall help-echo widget)))) | |
2074 (message "%s" help-echo))))) | |
1535 | 2075 |
1536 ;;; The End: | 2076 ;;; The End: |
1537 | 2077 |
1538 (provide 'widget-edit) | 2078 (provide 'widget-edit) |
1539 | 2079 |