comparison lisp/gutter-items.el @ 442:abe6d1db359e r21-2-36

Import from CVS: tag r21-2-36
author cvs
date Mon, 13 Aug 2007 11:35:02 +0200
parents 8de8e3f6228a
children 576fb035e263
comparison
equal deleted inserted replaced
441:72a7cfa4a488 442:abe6d1db359e
1 ;;; gutter-items.el --- Gutter content for XEmacs. 1 ;;; gutter-items.el --- Gutter content for XEmacs.
2 2
3 ;; Copyright (C) 1999 Free Software Foundation, Inc. 3 ;; Copyright (C) 1999 Free Software Foundation, Inc.
4 ;; Copyright (C) 1999 Andy Piper. 4 ;; Copyright (C) 1999, 2000 Andy Piper.
5 ;; Copyright (C) 2000 Ben Wing.
5 6
6 ;; Maintainer: XEmacs Development Team 7 ;; Maintainer: XEmacs Development Team
7 ;; Keywords: frames, extensions, internal, dumped 8 ;; Keywords: frames, extensions, internal, dumped
8 9
9 ;; This file is part of XEmacs. 10 ;; This file is part of XEmacs.
22 ;; along with Xmacs; see the file COPYING. If not, write to the 23 ;; along with Xmacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, 59 Temple Place - Suite 330, 24 ;; Free Software Foundation, 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA. 25 ;; Boston, MA 02111-1307, USA.
25 26
26 ;; Some of this is taken from the buffer-menu stuff in menubar-items.el 27 ;; Some of this is taken from the buffer-menu stuff in menubar-items.el
27 ;; and the custom specs in toolbar.el.
28
29 (defgroup gutter nil
30 "Input from the gutters."
31 :group 'environment)
32
33 (defvar gutter-buffers-tab nil
34 "A tab widget in the gutter for displaying buffers.
35 Do not set this. Use `glyph-image-instance' and
36 `set-image-instance-property' to change the properties of the tab.")
37
38 (defcustom gutter-visible-p
39 (specifier-instance default-gutter-visible-p)
40 "Whether the default gutter is globally visible. This option can be
41 customized through the options menu."
42 :group 'display
43 :type 'boolean
44 :set #'(lambda (var val)
45 (set-specifier default-gutter-visible-p val)
46 (setq gutter-visible-p val)
47 (when gutter-buffers-tab (update-tab-in-gutter))))
48
49 (defcustom default-gutter-position
50 (default-gutter-position)
51 "The location of the default gutter. It can be 'top, 'bottom, 'left or
52 'right. This option can be customized through the options menu."
53 :group 'display
54 :type '(choice (const :tag "top" 'top)
55 (const :tag "bottom" 'bottom)
56 (const :tag "left" 'left)
57 (const :tag "right" 'right))
58 :set #'(lambda (var val)
59 (set-default-gutter-position val)
60 (setq default-gutter-position val)
61 (when gutter-buffers-tab (update-tab-in-gutter))))
62 28
63 ;;; The Buffers tab 29 ;;; The Buffers tab
64 30
65 (defgroup buffers-tab nil 31 (defgroup buffers-tab nil
66 "Customization of `Buffers' tab." 32 "Customization of `Buffers' tab."
67 :group 'gutter) 33 :group 'gutter)
34
35 (defvar gutter-buffers-tab nil
36 "A tab widget in the gutter for displaying buffers.
37 Do not set this. Use `set-glyph-image' to change the properties of the tab.")
38
39 (defcustom gutter-buffers-tab-visible-p
40 (gutter-element-visible-p default-gutter-visible-p 'buffers-tab)
41 "Whether the buffers tab is globally visible.
42 This option should be set through the options menu."
43 :group 'buffers-tab
44 :type 'boolean
45 :set #'(lambda (var val)
46 (set-gutter-element-visible-p default-gutter-visible-p
47 'buffers-tab val)
48 (setq gutter-buffers-tab-visible-p val)))
68 49
69 (defvar gutter-buffers-tab-orientation 'top 50 (defvar gutter-buffers-tab-orientation 'top
70 "Where the buffers tab currently is. Do not set this.") 51 "Where the buffers tab currently is. Do not set this.")
71 52
72 (defvar gutter-buffers-tab-extent nil) 53 (defvar gutter-buffers-tab-extent nil)
102 "*If non-nil, a function specifying the buffers to select from the 83 "*If non-nil, a function specifying the buffers to select from the
103 buffers tab. This is passed two buffers and should return non-nil if 84 buffers tab. This is passed two buffers and should return non-nil if
104 the second buffer should be selected. The default value 85 the second buffer should be selected. The default value
105 `select-buffers-tab-buffers-by-mode' groups buffers by major mode and 86 `select-buffers-tab-buffers-by-mode' groups buffers by major mode and
106 by `buffers-tab-grouping-regexp'." 87 by `buffers-tab-grouping-regexp'."
88
89 :type '(choice (const :tag "None" nil)
90 function)
91 :group 'buffers-tab)
92
93 (defcustom buffers-tab-sort-function nil
94 "*If non-nil, a function specifying the buffers to select from the
95 buffers tab. This is passed the buffer list and returns the list in the
96 order desired for the tab widget. The default value `nil' leaves the
97 list in `buffer-list' order (usual most-recently-selected-first)."
107 98
108 :type '(choice (const :tag "None" nil) 99 :type '(choice (const :tag "None" nil)
109 function) 100 function)
110 :group 'buffers-tab) 101 :group 'buffers-tab)
111 102
157 (setq buffers-tab-max-buffer-line-length val))) 148 (setq buffers-tab-max-buffer-line-length val)))
158 149
159 (defun buffers-tab-switch-to-buffer (buffer) 150 (defun buffers-tab-switch-to-buffer (buffer)
160 "For use as a value for `buffers-tab-switch-to-buffer-function'." 151 "For use as a value for `buffers-tab-switch-to-buffer-function'."
161 (unless (eq (window-buffer) buffer) 152 (unless (eq (window-buffer) buffer)
153 ;; this used to add the norecord flag to both calls below.
154 ;; this is bogus because it is a pervasive assumption in XEmacs
155 ;; that the current buffer is at the front of the buffers list.
156 ;; for example, select an item and then do M-C-l
157 ;; (switch-to-other-buffer). Things get way confused.
162 (if (> (length (windows-of-buffer buffer)) 0) 158 (if (> (length (windows-of-buffer buffer)) 0)
163 (select-window (car (windows-of-buffer buffer))) 159 (select-window (car (windows-of-buffer buffer)))
164 (switch-to-buffer buffer t)))) 160 (switch-to-buffer buffer))))
165 161
166 (defun select-buffers-tab-buffers-by-mode (buf1 buf2) 162 (defun select-buffers-tab-buffers-by-mode (buf1 buf2)
167 "For use as a value of `buffers-tab-selection-function'. 163 "For use as a value of `buffers-tab-selection-function'.
168 This selects buffers by major mode `buffers-tab-grouping-regexp'." 164 This selects buffers by major mode `buffers-tab-grouping-regexp'."
169 (let ((mode1 (symbol-name (symbol-value-in-buffer 'major-mode buf1))) 165 (let ((mode1 (symbol-name (symbol-value-in-buffer 'major-mode buf1)))
201 (concat (substring (buffer-name buffer) 197 (concat (substring (buffer-name buffer)
202 0 (- len 3)) "...")) 198 0 (- len 3)) "..."))
203 (buffer-name buffer)))) 199 (buffer-name buffer))))
204 200
205 (defsubst build-buffers-tab-internal (buffers) 201 (defsubst build-buffers-tab-internal (buffers)
206 (let (line) 202 (let ((selected t))
207 (mapcar 203 (mapcar
208 #'(lambda (buffer) 204 #'(lambda (buffer)
209 (setq line (funcall buffers-tab-format-buffer-line-function 205 (prog1
210 buffer)) 206 (vector
211 (vector line (list buffers-tab-switch-to-buffer-function 207 (funcall buffers-tab-format-buffer-line-function
212 (buffer-name buffer)))) 208 buffer)
209 (list buffers-tab-switch-to-buffer-function
210 (buffer-name buffer))
211 :selected selected)
212 (when selected (setq selected nil))))
213 buffers))) 213 buffers)))
214 214
215 (defun buffers-tab-items (&optional in-deletion frame) 215 ;;; #### SJT I'd really like this function to have just two hooks: (1) the
216 ;;; buffer filter list and (2) a sort function list. Both should be lists
217 ;;; of functions. Each filter takes two arguments: a buffer and a model
218 ;;; buffer. (The model buffer argument allows selecting according to the
219 ;;; mode or directory of that buffer.) The filter returns t if the buffer
220 ;;; should be listed and nil otherwise. Effectively the filter amounts to
221 ;;; the conjuction of the filter list. (Optionally the filter could take a
222 ;;; frame instead of a buffer or generalize to a locale as in a specifier?)
223 ;;; The filtering is done this way to preserve the ordering imposed by
224 ;;; `buffer-list'. In addition, the in-deletion argument will be used the
225 ;;; same way as in the current design.
226 ;;; The list is checked for length and pruned according to least-recently-
227 ;;; selected. (Optionally there could be some kind of sort function here,
228 ;;; too.)
229 ;;; Finally the list is sorted to gutter display order, and the tab data
230 ;;; structure is created and returned.
231 ;;; #### Docstring isn't very well expressed.
232 (defun buffers-tab-items (&optional in-deletion frame force-selection)
216 "This is the tab filter for the top-level buffers \"Buffers\" tab. 233 "This is the tab filter for the top-level buffers \"Buffers\" tab.
217 It dynamically creates a list of buffers to use as the contents of the tab. 234 It dynamically creates a list of buffers to use as the contents of the tab.
218 Only the most-recently-used few buffers will be listed on the tab, for 235 Only the most-recently-used few buffers will be listed on the tab, for
219 efficiency reasons. You can control how many buffers will be shown by 236 efficiency reasons. You can control how many buffers will be shown by
220 setting `buffers-tab-max-size'. You can control the text of the tab 237 setting `buffers-tab-max-size'. You can control the text of the tab
221 items by redefining the function `format-buffers-menu-line'." 238 items by redefining the function `format-buffers-menu-line'."
222 (save-match-data 239 (save-match-data
223 (let* ((buffers (delete-if buffers-tab-omit-function (buffer-list frame))) 240 (let* ((buffers (delete-if buffers-tab-omit-function (buffer-list frame)))
224 (first-buf (car buffers))) 241 (first-buf (car buffers)))
242 ;; maybe force the selected window
243 (when (and force-selection
244 (not in-deletion)
245 (not (eq first-buf (window-buffer (selected-window frame)))))
246 (setq buffers (cons (window-buffer (selected-window frame))
247 (delq first-buf buffers))))
225 ;; if we're in deletion ignore the current buffer 248 ;; if we're in deletion ignore the current buffer
226 (when in-deletion 249 (when in-deletion
227 (setq buffers (delq (current-buffer) buffers)) 250 (setq buffers (delq (current-buffer) buffers))
228 (setq first-buf (car buffers))) 251 (setq first-buf (car buffers)))
229 ;; group buffers by mode 252 ;; select buffers in group (default is by mode)
230 (when buffers-tab-selection-function 253 (when buffers-tab-selection-function
231 (delete-if-not #'(lambda (buf) 254 (delete-if-not #'(lambda (buf)
232 (funcall buffers-tab-selection-function 255 (funcall buffers-tab-selection-function
233 first-buf buf)) buffers)) 256 first-buf buf)) buffers))
257 ;; maybe shorten list of buffers
234 (and (integerp buffers-tab-max-size) 258 (and (integerp buffers-tab-max-size)
235 (> buffers-tab-max-size 1) 259 (> buffers-tab-max-size 1)
236 (> (length buffers) buffers-tab-max-size) 260 (> (length buffers) buffers-tab-max-size)
237 ;; shorten list of buffers
238 (setcdr (nthcdr buffers-tab-max-size buffers) nil)) 261 (setcdr (nthcdr buffers-tab-max-size buffers) nil))
262 ;; sort buffers in group (default is most-recently-selected)
263 (when buffers-tab-sort-function
264 (setq buffers (funcall buffers-tab-sort-function buffers)))
265 ;; convert list of buffers to list of structures used by tab widget
239 (setq buffers (build-buffers-tab-internal buffers)) 266 (setq buffers (build-buffers-tab-internal buffers))
240 buffers))) 267 buffers)))
241 268
242 (defun add-tab-to-gutter () 269 (defun add-tab-to-gutter ()
243 "Put a tab control in the gutter area to hold the most recent buffers." 270 "Put a tab control in the gutter area to hold the most recent buffers."
244 (setq gutter-buffers-tab-orientation (default-gutter-position)) 271 (setq gutter-buffers-tab-orientation (default-gutter-position))
245 (let ((gutter-string "")) 272 (let ((gutter-string (copy-sequence "\n")))
246 (unless gutter-buffers-tab-extent 273 (unless gutter-buffers-tab-extent
247 (setq gutter-buffers-tab-extent (make-extent 0 0 gutter-string))) 274 (setq gutter-buffers-tab-extent (make-extent 0 1 gutter-string)))
248 (set-extent-begin-glyph 275 (set-extent-begin-glyph
249 gutter-buffers-tab-extent 276 gutter-buffers-tab-extent
250 (setq gutter-buffers-tab 277 (setq gutter-buffers-tab
251 (make-glyph 278 (make-glyph)))
252 (vector 'tab-control :descriptor "Buffers" :face buffers-tab-face 279
253 :orientation gutter-buffers-tab-orientation 280 ;; Nuke all existing tabs
254 :properties (list :items (buffers-tab-items)))))) 281 (remove-gutter-element top-gutter 'buffers-tab)
255 ;; This looks better than a 3d border 282 (remove-gutter-element bottom-gutter 'buffers-tab)
256 (mapcar '(lambda (x) 283 (remove-gutter-element left-gutter 'buffers-tab)
257 (when (valid-image-instantiator-format-p 'tab-control x) 284 (remove-gutter-element right-gutter 'buffers-tab)
258 (set-specifier default-gutter-border-width 0 'global x) 285 ;; Put tabs into all devices that will be able to display them
259 (set-specifier top-gutter nil 'global x) 286 (mapcar
260 (set-specifier bottom-gutter nil 'global x) 287 #'(lambda (x)
261 (set-specifier left-gutter nil 'global x) 288 (when (valid-image-instantiator-format-p 'tab-control x)
262 (set-specifier right-gutter nil 'global x) 289 (cond ((eq gutter-buffers-tab-orientation 'top)
263 (set-specifier left-gutter-width 0 'global x) 290 ;; This looks better than a 3d border
264 (set-specifier right-gutter-width 0 'global x) 291 (set-specifier top-gutter-border-width 0 'global x)
265 (cond ((eq gutter-buffers-tab-orientation 'top) 292 (set-gutter-element top-gutter 'buffers-tab
266 (set-specifier top-gutter gutter-string 'global x)) 293 gutter-string 'global x))
267 ((eq gutter-buffers-tab-orientation 'bottom) 294 ((eq gutter-buffers-tab-orientation 'bottom)
268 (set-specifier bottom-gutter gutter-string 'global x)) 295 (set-specifier bottom-gutter-border-width 0 'global x)
269 ((eq gutter-buffers-tab-orientation 'left) 296 (set-gutter-element bottom-gutter 'buffers-tab
270 (set-specifier left-gutter gutter-string 'global x) 297 gutter-string 'global x))
271 (set-specifier left-gutter-width 298 ((eq gutter-buffers-tab-orientation 'left)
272 (glyph-width gutter-buffers-tab) 299 (set-specifier left-gutter-border-width 0 'global x)
273 'global x)) 300 (set-gutter-element left-gutter 'buffers-tab
274 ((eq gutter-buffers-tab-orientation 'right) 301 gutter-string 'global x)
275 (set-specifier right-gutter gutter-string 'global x) 302 (set-specifier left-gutter-width
276 (set-specifier right-gutter-width 303 (glyph-width gutter-buffers-tab)
277 (glyph-width gutter-buffers-tab) 304 'global x))
278 'global x)) 305 ((eq gutter-buffers-tab-orientation 'right)
279 ))) 306 (set-specifier right-gutter-border-width 0 'global x)
280 (console-type-list)))) 307 (set-gutter-element right-gutter 'buffers-tab
281 308 gutter-string 'global x)
282 (defun update-tab-in-gutter (&optional frame-or-buffer) 309 (set-specifier right-gutter-width
310 (glyph-width gutter-buffers-tab)
311 'global x))
312 )))
313 (console-type-list))))
314
315 (defun update-tab-in-gutter (frame &optional force-selection)
283 "Update the tab control in the gutter area." 316 "Update the tab control in the gutter area."
284 (let ((locale (if (framep frame-or-buffer) frame-or-buffer))) 317 ;; dedicated frames don't get tabs
285 (when (specifier-instance default-gutter-visible-p locale) 318 (unless (window-dedicated-p (frame-selected-window frame))
286 (unless (and gutter-buffers-tab 319 (when (specifier-instance default-gutter-visible-p frame)
320 (unless (and gutter-buffers-tab
287 (eq (default-gutter-position) 321 (eq (default-gutter-position)
288 gutter-buffers-tab-orientation)) 322 gutter-buffers-tab-orientation))
289 (add-tab-to-gutter)) 323 (add-tab-to-gutter))
290 (when (valid-image-instantiator-format-p 'tab-control locale) 324 (when (valid-image-instantiator-format-p 'tab-control frame)
291 (let ((inst (glyph-image-instance 325 (set-glyph-image
292 gutter-buffers-tab 326 gutter-buffers-tab
293 (when (framep frame-or-buffer) 327 (vector 'tab-control :descriptor "Buffers" :face buffers-tab-face
294 (last-nonminibuf-window frame-or-buffer))))) 328 :orientation gutter-buffers-tab-orientation
295 (set-image-instance-property inst :items 329 (if (or (eq gutter-buffers-tab-orientation 'top)
296 (buffers-tab-items 330 (eq gutter-buffers-tab-orientation 'bottom))
297 nil locale))))))) 331 :pixel-width :pixel-height)
298 332 (if (or (eq gutter-buffers-tab-orientation 'top)
299 (defun remove-buffer-from-gutter-tab () 333 (eq gutter-buffers-tab-orientation 'bottom))
300 "Remove the current buffer from the tab control in the gutter area." 334 '(gutter-pixel-width) '(gutter-pixel-height))
301 (when (and (valid-image-instantiator-format-p 'tab-control) 335 :items (buffers-tab-items nil frame force-selection))
302 (specifier-instance default-gutter-visible-p)) 336 frame)))))
303 (let ((inst (glyph-image-instance gutter-buffers-tab)) 337
304 (buffers (buffers-tab-items t))) 338 ;; A myriad of different update hooks all doing slightly different things
305 (unless buffers 339 (add-hook 'create-frame-hook
306 (setq buffers (build-buffers-tab-internal 340 #'(lambda (frame)
307 (list 341 (when gutter-buffers-tab (update-tab-in-gutter frame t))))
308 (get-buffer-create "*scratch*"))))) 342 (add-hook 'buffer-list-changed-hook 'update-tab-in-gutter)
309 (set-image-instance-property inst :items buffers)))) 343 (add-hook 'default-gutter-position-changed-hook
310 344 #'(lambda ()
311 (add-hook 'kill-buffer-hook 'remove-buffer-from-gutter-tab) 345 (when gutter-buffers-tab
312 (add-hook 'create-frame-hook 'update-tab-in-gutter) 346 (mapc #'update-tab-in-gutter (frame-list)))))
313 (add-hook 'record-buffer-hook 'update-tab-in-gutter) 347 (add-hook 'gutter-element-visibility-changed-hook
314 348 #'(lambda (prop visible-p)
349 (when (and (eq prop 'buffers-tab) visible-p)
350 (mapc #'update-tab-in-gutter (frame-list)))))
315 ;; 351 ;;
316 ;; progress display 352 ;; progress display
317 ;; ripped off from message display 353 ;; ripped off from message display
318 ;; 354 ;;
355 (defcustom progress-feedback-use-echo-area nil
356 "*Whether progress gauge display should display in the echo area.
357 If NIL then progress gauges will be displayed with whatever native widgets
358 are available on the current console. If non-NIL then progress display will be
359 textual and displayed in the echo area."
360 :type 'boolean
361 :group 'gutter)
362
363 (defvar progress-glyph-height 24
364 "Height of the progress gauge glyph.")
365
366 (defvar progress-feedback-popup-period 0.5
367 "The time that the progress gauge should remain up after completion")
368
369 (defcustom progress-feedback-style 'large
370 "*Control the appearance of the progress gauge.
371 If 'large, the default, then the progress-feedback text is displayed
372 above the gauge itself. If 'small then the gauge and text are arranged
373 side-by-side."
374 :group 'gutter
375 :type '(choice (const :tag "large" large)
376 (const :tag "small" small)))
377
378 ;; private variables
379 (defvar progress-text-instantiator [string :data ""])
380 (defvar progress-layout-glyph (make-glyph))
381 (defvar progress-layout-instantiator nil)
382
383 (defvar progress-gauge-instantiator
384 [progress-gauge
385 :value 0
386 :pixel-height (eval progress-glyph-height)
387 :pixel-width 250
388 :descriptor "Progress"])
389
390 (defun set-progress-feedback-instantiator (&optional locale)
391 (cond
392 ((eq progress-feedback-style 'small)
393 (setq progress-glyph-height 16)
394 (setq progress-layout-instantiator
395 `[layout
396 :orientation horizontal
397 :margin-width 4
398 :items (,progress-gauge-instantiator
399 [button
400 :pixel-height (eval progress-glyph-height)
401 ;; 'quit is special and acts "asynchronously".
402 :descriptor "Stop" :callback 'quit]
403 ,progress-text-instantiator)])
404 (set-glyph-image progress-layout-glyph progress-layout-instantiator locale))
405 (t
406 (setq progress-glyph-height 24)
407 (setq progress-layout-instantiator
408 `[layout
409 :orientation vertical :justify left
410 :margin-width 4
411 :items (,progress-text-instantiator
412 [layout
413 :orientation horizontal
414 :items (,progress-gauge-instantiator
415 [button
416 :pixel-height (eval progress-glyph-height)
417 :descriptor " Stop "
418 ;; 'quit is special and acts "asynchronously".
419 :callback 'quit])])])
420 (set-glyph-image progress-layout-glyph progress-layout-instantiator locale))))
421
319 (defvar progress-stack nil 422 (defvar progress-stack nil
320 "An alist of label/string pairs representing active progress gauges. 423 "An alist of label/string pairs representing active progress gauges.
321 The first element in the list is currently displayed in the gutter area. 424 The first element in the list is currently displayed in the gutter area.
322 Do not modify this directly--use the `progress' or 425 Do not modify this directly--use the `progress-feedback' or
323 `display-progress'/`clear-progress' functions.") 426 `display-progress-feedback'/`clear-progress-feedback' functions.")
324
325 (defvar progress-glyph-height 32
326 "Height of the gutter area for progress messages.")
327
328 (defvar progress-stop-callback 'progress-quit-function
329 "Function to call to stop the progress operation.")
330
331 (defun progress-quit-function ()
332 "Default function to call for the stop button in a progress gauge.
333 This just removes the progress gauge and calls quit."
334 (interactive)
335 (clear-progress)
336 (keyboard-quit))
337
338 ;; private variables
339 (defvar progress-gauge-glyph
340 (make-glyph
341 (vector 'progress-gauge
342 :pixel-height (- progress-glyph-height 8)
343 :pixel-width 50
344 :descriptor "Progress")))
345
346 (defvar progress-text-glyph
347 (make-glyph [string :data ""]))
348
349 (defvar progress-layout-glyph
350 (make-glyph
351 (vector
352 'layout :orientation 'vertical :justify 'left
353 :items (list
354 progress-text-glyph
355 (make-glyph
356 (vector
357 'layout :pixel-height progress-glyph-height
358 :orientation 'horizontal
359 :items (list
360 progress-gauge-glyph
361 (vector
362 'button :pixel-height (- progress-glyph-height 8)
363 :descriptor " Stop "
364 :callback '(funcall progress-stop-callback)))))))))
365 427
366 (defvar progress-abort-glyph 428 (defvar progress-abort-glyph
367 (make-glyph 429 (make-glyph
368 (vector 'layout :orientation 'vertical :justify 'left 430 `[layout :orientation vertical :justify left
369 :items (list progress-text-glyph 431 :items (,progress-text-instantiator
370 (make-glyph 432 [layout
371 (vector 'layout 433 :margin-width 4
372 :pixel-height progress-glyph-height 434 :pixel-height progress-glyph-height
373 :orientation 'horizontal)))))) 435 :orientation horizontal])]))
374 436
375 (defvar progress-extent-text "") 437 (defun progress-feedback-displayed-p (&optional return-string frame)
376 (defvar progress-extent nil)
377
378 (defun progress-displayed-p (&optional return-string frame)
379 "Return a non-nil value if a progress gauge is presently displayed in the 438 "Return a non-nil value if a progress gauge is presently displayed in the
380 gutter area. If optional argument RETURN-STRING is non-nil, 439 gutter area. If optional argument RETURN-STRING is non-nil,
381 return a string containing the message, otherwise just return t." 440 return a string containing the message, otherwise just return t."
382 (let ((buffer (get-buffer-create " *Gutter Area*"))) 441 (let ((buffer (get-buffer-create " *Gutter Area*")))
383 (and (< (point-min buffer) (point-max buffer)) 442 (and (< (point-min buffer) (point-max buffer))
385 (buffer-substring nil nil buffer) 444 (buffer-substring nil nil buffer)
386 t)))) 445 t))))
387 446
388 ;;; Returns the string which remains in the echo area, or nil if none. 447 ;;; Returns the string which remains in the echo area, or nil if none.
389 ;;; If label is nil, the whole message stack is cleared. 448 ;;; If label is nil, the whole message stack is cleared.
390 (defun clear-progress (&optional label frame no-restore) 449 (defun clear-progress-feedback (&optional label frame no-restore)
391 "Remove any progress gauge with the given LABEL from the progress gauge-stack, 450 "Remove any progress gauge with LABEL from the progress gauge-stack,
392 erasing it from the gutter area if it's currently displayed there. 451 erasing it from the gutter area if it's currently displayed there.
393 If a message remains at the head of the progress-stack and NO-RESTORE 452 If a message remains at the head of the progress-stack and NO-RESTORE
394 is nil, it will be displayed. The string which remains in the gutter 453 is nil, it will be displayed. The string which remains in the gutter
395 area will be returned, or nil if the progress-stack is now empty. 454 area will be returned, or nil if the progress-stack is now empty.
396 If LABEL is nil, the entire progress-stack is cleared. 455 If LABEL is nil, the entire progress-stack is cleared.
397 456
398 Unless you need the return value or you need to specify a label, 457 Unless you need the return value or you need to specify a label,
399 you should just use (progress nil)." 458 you should just use (progress nil)."
400 (or frame (setq frame (selected-frame))) 459 (if (or (not (valid-image-instantiator-format-p 'progress-gauge frame))
401 (remove-progress label frame) 460 progress-feedback-use-echo-area)
402 (let ((inhibit-read-only t) 461 (clear-message label frame nil no-restore)
403 (zmacs-region-stays zmacs-region-stays)) ; preserve from change 462 (or frame (setq frame (selected-frame)))
404 (erase-buffer " *Echo Area*") 463 (remove-progress-feedback label frame)
405 (erase-buffer (get-buffer-create " *Gutter Area*"))) 464 (let ((inhibit-read-only t)
406 (if no-restore 465 (zmacs-region-stays zmacs-region-stays)) ; preserve from change
407 nil ; just preparing to put another msg up 466 (erase-buffer (get-buffer-create " *Gutter Area*")))
408 (if progress-stack 467 (if no-restore
409 (let ((oldmsg (cdr (car progress-stack)))) 468 nil ; just preparing to put another msg up
410 (raw-append-progress oldmsg frame) 469 (if progress-stack
411 oldmsg) 470 (let ((oldmsg (cdr (car progress-stack))))
412 ;; nothing to display so get rid of the gauge 471 (raw-append-progress-feedback oldmsg nil frame)
413 (set-specifier bottom-gutter-border-width 0 frame) 472 oldmsg)
414 (set-specifier bottom-gutter-visible-p nil frame)))) 473 ;; nothing to display so get rid of the gauge
415 474 (set-specifier bottom-gutter-border-width 0 frame)
416 (defun remove-progress (&optional label frame) 475 (set-gutter-element-visible-p bottom-gutter-visible-p
476 'progress nil frame)))))
477
478 (defun progress-feedback-clear-when-idle (&optional label)
479 (add-one-shot-hook 'pre-idle-hook
480 `(lambda ()
481 (clear-progress-feedback ',label))))
482
483 (defun remove-progress-feedback (&optional label frame)
417 ;; If label is nil, we want to remove all matching progress gauges. 484 ;; If label is nil, we want to remove all matching progress gauges.
418 (while (and progress-stack 485 (while (and progress-stack
419 (or (null label) ; null label means clear whole stack 486 (or (null label) ; null label means clear whole stack
420 (eq label (car (car progress-stack))))) 487 (eq label (car (car progress-stack)))))
421 (setq progress-stack (cdr progress-stack))) 488 (setq progress-stack (cdr progress-stack)))
425 (if (eq label (car msg)) 492 (if (eq label (car msg))
426 (progn 493 (progn
427 (setcdr s (cdr (cdr s)))) 494 (setcdr s (cdr (cdr s))))
428 (setq s (cdr s))))))) 495 (setq s (cdr s)))))))
429 496
430 (defun append-progress (label message &optional value frame) 497 (defun progress-feedback-dispatch-non-command-events ()
498 ;; don't allow errors to hose things
499 (condition-case t
500 ;; (sit-for 0) is too agressive and cause more display than we
501 ;; want.
502 (dispatch-non-command-events)
503 nil))
504
505 (defun append-progress-feedback (label message &optional value frame)
431 (or frame (setq frame (selected-frame))) 506 (or frame (setq frame (selected-frame)))
432 ;; Add a new entry to the message-stack, or modify an existing one 507 ;; Add a new entry to the message-stack, or modify an existing one
433 (let* ((top (car progress-stack)) 508 (let* ((top (car progress-stack))
434 (tmsg (cdr top))) 509 (tmsg (cdr top)))
435 (if (eq label (car top)) 510 (if (eq label (car top))
436 (progn 511 (progn
437 (setcdr top message) 512 (setcdr top message)
438 (if (eq tmsg message) 513 (if (equal tmsg message)
439 (set-image-instance-property 514 (progn
440 (glyph-image-instance progress-gauge-glyph) 515 (set-instantiator-property progress-gauge-instantiator :value value)
441 :percent value) 516 (set-progress-feedback-instantiator (frame-selected-window frame)))
442 (raw-append-progress message value frame)) 517 (raw-append-progress-feedback message value frame))
443 (redisplay-gutter-area) 518 (redisplay-gutter-area))
444 (when (input-pending-p)
445 (dispatch-event (next-command-event))))
446 (push (cons label message) progress-stack) 519 (push (cons label message) progress-stack)
447 (raw-append-progress message value frame)) 520 (raw-append-progress-feedback message value frame))
448 (when (eq value 100) 521 (progress-feedback-dispatch-non-command-events)
449 (sit-for 0.5 nil) 522 ;; either get command events or sit waiting for them
450 (clear-progress label)))) 523 (when (eq value 100)
451 524 ; (sit-for progress-feedback-popup-period nil)
452 (defun abort-progress (label message &optional frame) 525 (clear-progress-feedback label))))
453 (or frame (setq frame (selected-frame))) 526
454 ;; Add a new entry to the message-stack, or modify an existing one 527 (defun abort-progress-feedback (label message &optional frame)
455 (let* ((top (car progress-stack)) 528 (if (or (not (valid-image-instantiator-format-p 'progress-gauge frame))
456 (inhibit-read-only t) 529 progress-feedback-use-echo-area)
457 (zmacs-region-stays zmacs-region-stays)) 530 (display-message label (concat message "aborted.") frame)
458 (if (eq label (car top)) 531 (or frame (setq frame (selected-frame)))
459 (setcdr top message) 532 ;; Add a new entry to the message-stack, or modify an existing one
460 (push (cons label message) progress-stack)) 533 (let* ((top (car progress-stack))
461 (unless (equal message "") 534 (inhibit-read-only t)
462 (insert-string message (get-buffer-create " *Gutter Area*")) 535 (zmacs-region-stays zmacs-region-stays))
463 ;; Do what the device is able to cope with. 536 (if (eq label (car top))
464 (if (not (valid-image-instantiator-format-p 'progress-gauge frame)) 537 (setcdr top message)
465 (progn 538 (push (cons label message) progress-stack))
466 (insert-string message " *Echo Area*") 539 (unless (equal message "")
467 (if (not executing-kbd-macro) 540 (insert-string message (get-buffer-create " *Gutter Area*"))
468 (redisplay-echo-area))) 541 (let* ((gutter-string (copy-sequence "\n"))
469 ;; do some funky display here. 542 (ext (make-extent 0 1 gutter-string)))
470 (unless progress-extent 543 ;; do some funky display here.
471 (setq progress-extent (make-extent 0 0 progress-extent-text))) 544 (set-extent-begin-glyph ext progress-abort-glyph)
472 (let ((bglyph (extent-begin-glyph progress-extent)))
473 (set-extent-begin-glyph progress-extent progress-abort-glyph)
474 ;; fixup the gutter specifiers 545 ;; fixup the gutter specifiers
475 (set-specifier bottom-gutter progress-extent-text frame) 546 (set-gutter-element bottom-gutter 'progress gutter-string frame)
476 (set-specifier bottom-gutter-border-width 2 frame) 547 (set-specifier bottom-gutter-border-width 2 frame)
477 (set-image-instance-property 548 (set-instantiator-property progress-text-instantiator :datat message)
478 (glyph-image-instance progress-text-glyph) :data message) 549 (set-progress-feedback-instantiator (frame-selected-window frame))
479 (set-specifier bottom-gutter-height 'autodetect frame) 550 (set-specifier bottom-gutter-height 'autodetect frame)
480 (set-specifier bottom-gutter-visible-p t frame) 551 (set-gutter-element-visible-p bottom-gutter-visible-p
552 'progress t frame)
481 ;; we have to do this so redisplay is up-to-date and so 553 ;; we have to do this so redisplay is up-to-date and so
482 ;; redisplay-gutter-area performs optimally. 554 ;; redisplay-gutter-area performs optimally.
483 (redisplay-gutter-area) 555 (redisplay-gutter-area)
484 (sit-for 0.5 nil) 556 (sit-for progress-feedback-popup-period nil)
485 (clear-progress label) 557 (clear-progress-feedback label frame)
486 (set-extent-begin-glyph progress-extent bglyph) 558 (set-extent-begin-glyph ext progress-layout-glyph)
559 (set-gutter-element bottom-gutter 'progress gutter-string frame)
487 ))))) 560 )))))
488 561
489 (defun raw-append-progress (message &optional value frame) 562 (defun raw-append-progress-feedback (message &optional value frame)
490 (unless (equal message "") 563 (unless (equal message "")
491 (let ((inhibit-read-only t) 564 (let* ((inhibit-read-only t)
492 (zmacs-region-stays zmacs-region-stays) 565 (zmacs-region-stays zmacs-region-stays)
493 (val (or value 0))) ; preserve from change 566 (val (or value 0))
567 (gutter-string (copy-sequence "\n"))
568 (ext (make-extent 0 1 gutter-string)))
494 (insert-string message (get-buffer-create " *Gutter Area*")) 569 (insert-string message (get-buffer-create " *Gutter Area*"))
495 ;; Do what the device is able to cope with. 570 ;; do some funky display here.
496 (if (not (valid-image-instantiator-format-p 'progress-gauge frame)) 571 (set-extent-begin-glyph ext progress-layout-glyph)
572 ;; fixup the gutter specifiers
573 (set-gutter-element bottom-gutter 'progress gutter-string frame)
574 (set-specifier bottom-gutter-border-width 2 frame)
575 (set-instantiator-property progress-gauge-instantiator :value val)
576 (set-progress-feedback-instantiator (frame-selected-window frame))
577
578 (set-instantiator-property progress-text-instantiator :data message)
579 (set-progress-feedback-instantiator (frame-selected-window frame))
580 (if (and (eq (specifier-instance bottom-gutter-height frame)
581 'autodetect)
582 (gutter-element-visible-p bottom-gutter-visible-p
583 'progress frame))
584 ;; if the gauge is already visible then just draw the gutter
585 ;; checking for user events
497 (progn 586 (progn
498 (insert-string 587 (redisplay-gutter-area)
499 (concat message (if (eq val 100) "done.") 588 (progress-feedback-dispatch-non-command-events))
500 (make-string (/ val 5) ?.)) 589 ;; otherwise make the gutter visible and redraw the frame
501 " *Echo Area*") 590 (set-specifier bottom-gutter-height 'autodetect frame)
502 (if (not executing-kbd-macro) 591 (set-gutter-element-visible-p bottom-gutter-visible-p
503 (redisplay-echo-area))) 592 'progress t frame)
504 ;; do some funky display here. 593 ;; we have to do this so redisplay is up-to-date and so
505 (unless progress-extent 594 ;; redisplay-gutter-area performs optimally. This may also
506 (setq progress-extent (make-extent 0 0 progress-extent-text)) 595 ;; make sure the frame geometry looks ok.
507 (set-extent-begin-glyph progress-extent progress-layout-glyph)) 596 (progress-feedback-dispatch-non-command-events)
508 ;; fixup the gutter specifiers 597 (redisplay-frame frame)
509 (set-specifier bottom-gutter progress-extent-text frame) 598 ))))
510 (set-specifier bottom-gutter-border-width 2 frame) 599
511 (set-image-instance-property 600 (defun display-progress-feedback (label message &optional value frame)
512 (glyph-image-instance progress-gauge-glyph) :percent val)
513 (set-image-instance-property
514 (glyph-image-instance progress-text-glyph) :data message)
515 (if (and (eq (specifier-instance bottom-gutter-height frame)
516 'autodetect)
517 (specifier-instance bottom-gutter-visible-p frame))
518 (progn
519 ;; if the gauge is already visible then just draw the gutter
520 ;; checking for user events
521 (redisplay-gutter-area)
522 (when (input-pending-p)
523 (dispatch-event (next-command-event))))
524 ;; otherwise make the gutter visible and redraw the frame
525 (set-specifier bottom-gutter-height 'autodetect frame)
526 (set-specifier bottom-gutter-visible-p t frame)
527 ;; we have to do this so redisplay is up-to-date and so
528 ;; redisplay-gutter-area performs optimally.
529 (redisplay-frame)
530 )))))
531
532 (defun display-progress (label message &optional value frame)
533 "Display a progress gauge and message in the bottom gutter area. 601 "Display a progress gauge and message in the bottom gutter area.
534 First argument LABEL is an identifier for this message. MESSAGE is 602 First argument LABEL is an identifier for this message. MESSAGE is
535 the string to display. Use `clear-progress' to remove a labelled 603 the string to display. Use `clear-progress-feedback' to remove a labelled
536 message." 604 message."
537 (clear-progress label frame t) 605 (cond ((eq value 'abort)
538 (if (eq value 'abort) 606 (abort-progress-feedback label message frame))
539 (abort-progress label message frame) 607 ((or (not (valid-image-instantiator-format-p 'progress-gauge frame))
540 (append-progress label message value frame))) 608 progress-feedback-use-echo-area)
541 609 (display-message label
542 (defun current-progress (&optional frame) 610 (concat message (if (eq value 100) "done."
611 (make-string (/ value 5) ?.)))
612 frame))
613 (t
614 (append-progress-feedback label message value frame))))
615
616 (defun current-progress-feedback (&optional frame)
543 "Return the current progress gauge in the gutter area, or nil. 617 "Return the current progress gauge in the gutter area, or nil.
544 The FRAME argument is currently unused." 618 The FRAME argument is currently unused."
545 (cdr (car progress-stack))) 619 (cdr (car progress-stack)))
546 620
547 ;;; may eventually be frame-dependent 621 ;;; may eventually be frame-dependent
548 (defun current-progress-label (&optional frame) 622 (defun current-progress-feedback-label (&optional frame)
549 (car (car progress-stack))) 623 (car (car progress-stack)))
550 624
551 (defun progress (fmt &optional value &rest args) 625 (defun progress-feedback (fmt &optional value &rest args)
552 "Print a progress gauge and message in the bottom gutter area of the frame. 626 "Print a progress gauge and message in the bottom gutter area of the frame.
553 The arguments are the same as to `format'. 627 The arguments are the same as to `format'.
554 628
555 If the only argument is nil, clear any existing progress gauge." 629 If the only argument is nil, clear any existing progress gauge."
556 (if (and (null fmt) (null args)) 630 (save-excursion
557 (prog1 nil 631 (if (and (null fmt) (null args))
558 (clear-progress nil)) 632 (prog1 nil
559 (let ((str (apply 'format fmt args))) 633 (clear-progress-feedback nil))
560 (display-progress 'progress str value) 634 (let ((str (apply 'format fmt args)))
561 str))) 635 (display-progress-feedback 'progress str value)
562 636 str))))
563 (defun lprogress (label fmt &optional value &rest args) 637
638 (defun progress-feedback-with-label (label fmt &optional value &rest args)
564 "Print a progress gauge and message in the bottom gutter area of the frame. 639 "Print a progress gauge and message in the bottom gutter area of the frame.
565 First argument LABEL is an identifier for this progress gauge. The rest of the 640 First argument LABEL is an identifier for this progress gauge. The rest of the
566 arguments are the same as to `format'." 641 arguments are the same as to `format'."
567 (if (and (null fmt) (null args)) 642 ;; #### sometimes the buffer gets changed temporarily. I don't know
568 (prog1 nil 643 ;; why this is, so protect against it.
569 (clear-progress label nil)) 644 (save-excursion
570 (let ((str (apply 'format fmt args))) 645 (if (and (null fmt) (null args))
571 (display-progress label str value) 646 (prog1 nil
572 str))) 647 (clear-progress-feedback label nil))
648 (let ((str (apply 'format fmt args)))
649 (display-progress-feedback label str value)
650 str))))
573 651
574 (provide 'gutter-items) 652 (provide 'gutter-items)
575 ;;; gutter-items.el ends here. 653 ;;; gutter-items.el ends here.