1 ;;; gutter-items.el --- Gutter content for XEmacs.
3 ;; Copyright (C) 1999 Free Software Foundation, Inc.
4 ;; Copyright (C) 1999, 2000 Andy Piper.
6 ;; Maintainer: XEmacs Development Team
7 ;; Keywords: frames, extensions, internal, dumped
9 ;; This file is part of XEmacs.
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ;; General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with Xmacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
26 ;; Some of this is taken from the buffer-menu stuff in menubar-items.el
27 ;; and the custom specs in toolbar.el.
30 "Input from the gutters."
33 ;; Although these customizations appear bogus, they are neccessary in
34 ;; order to be able to save options through the options menu.
35 (defcustom default-gutter-position
36 (default-gutter-position)
37 "The location of the default gutter. It can be 'top, 'bottom, 'left or
38 'right. This option should be customized through the options menu.
39 To set the gutter position explicitly use `set-default-gutter-position'"
41 :type '(choice (const :tag "top" top)
42 (const :tag "bottom" bottom)
43 (const :tag "left" left)
44 (const :tag "right" right))
45 :set #'(lambda (var val)
46 (set-default-gutter-position val)
47 (setq default-gutter-position val)))
49 ;;; Gutter helper functions
51 ;; called by Fset_default_gutter_position()
52 (defvar default-gutter-position-changed-hook nil
53 "Function or functions to be called when the gutter position is changed.
54 The value of this variable may be buffer-local.")
56 ;; called by set-gutter-element-visible-p
57 (defvar gutter-element-visibility-changed-hook nil
58 "Function or functions to be called when the visibility of an
59 element in the gutter changes. The value of this variable may be
60 buffer-local. The gutter element symbol is passed as an argument to
61 the hook, as is the visibility flag.")
63 (defun set-gutter-element (gutter-specifier prop val &optional locale tag-set)
64 "Set GUTTER-SPECIFIER gutter element PROP to VAL in optional LOCALE.
65 This is a convenience function for setting gutter elements."
66 (map-extents #'(lambda (extent arg)
67 (set-extent-property extent 'duplicable t)) val)
68 (modify-specifier-instances gutter-specifier #'plist-put (list prop val)
69 'force nil locale tag-set))
71 (defun remove-gutter-element (gutter-specifier prop &optional locale tag-set)
72 "Remove gutter element PROP from GUTTER-SPECIFIER in optional LOCALE.
73 This is a convenience function for removing gutter elements."
74 (modify-specifier-instances gutter-specifier #'plist-remprop (list prop)
75 'force nil locale tag-set))
77 (defun set-gutter-element-visible-p (gutter-visible-specifier-p
78 prop &optional visible-p
80 "Change the visibility of gutter elements.
81 Set the visibility of element PROP to VISIBLE-P for
82 GUTTER-SPECIFIER-VISIBLE-P in optional LOCALE.
83 This is a convenience function for hiding and showing gutter elements."
84 (modify-specifier-instances
85 gutter-visible-specifier-p #'(lambda (spec prop visible-p)
88 (if (memq prop spec) spec
91 (if visible-p (list prop))))
93 'force nil locale tag-set)
94 (run-hook-with-args 'gutter-element-visibility-changed-hook prop visible-p))
96 (defun gutter-element-visible-p (gutter-visible-specifier-p
97 prop &optional domain)
98 "Determine whether a gutter element is visible.
99 Given GUTTER-VISIBLE-SPECIFIER-P and gutter element PROP, return
100 non-nil if it is visible in optional DOMAIN."
101 (let ((spec (specifier-instance gutter-visible-specifier-p domain)))
102 (or (and (listp spec) (memq 'buffers-tab spec))
105 (defun init-gutter ()
106 "Initialize the gutter."
107 ;; do nothing as yet.
112 (defgroup buffers-tab nil
113 "Customization of `Buffers' tab."
116 (defvar gutter-buffers-tab nil
117 "A tab widget in the gutter for displaying buffers.
118 Do not set this. Use `glyph-image-instance' and
119 `set-image-instance-property' to change the properties of the tab.")
121 (defcustom gutter-buffers-tab-visible-p
122 (gutter-element-visible-p default-gutter-visible-p 'buffers-tab)
123 "Whether the buffers tab is globally visible.
124 This option should be set through the options menu."
127 :set #'(lambda (var val)
128 (set-gutter-element-visible-p default-gutter-visible-p 'buffers-tab val)
129 (setq gutter-buffers-tab-visible-p val)))
131 (defvar gutter-buffers-tab-orientation 'top
132 "Where the buffers tab currently is. Do not set this.")
134 (defvar gutter-buffers-tab-extent nil)
136 (defcustom buffers-tab-max-size 6
137 "*Maximum number of entries which may appear on the \"Buffers\" tab.
138 If this is 10, then only the ten most-recently-selected buffers will be
139 shown. If this is nil, then all buffers will be shown. Setting this to
140 a large number or nil will slow down tab responsiveness."
141 :type '(choice (const :tag "Show all" nil)
145 (defcustom buffers-tab-switch-to-buffer-function 'buffers-tab-switch-to-buffer
146 "*The function to call to select a buffer from the buffers tab.
147 `switch-to-buffer' is a good choice, as is `pop-to-buffer'."
148 :type '(radio (function-item switch-to-buffer)
149 (function-item pop-to-buffer)
150 (function :tag "Other"))
153 (defcustom buffers-tab-omit-function 'buffers-menu-omit-invisible-buffers
154 "*If non-nil, a function specifying the buffers to omit from the buffers tab.
155 This is passed a buffer and should return non-nil if the buffer should be
156 omitted. The default value `buffers-tab-omit-invisible-buffers' omits
157 buffers that are normally considered \"invisible\" (those whose name
158 begins with a space)."
159 :type '(choice (const :tag "None" nil)
163 (defcustom buffers-tab-selection-function 'select-buffers-tab-buffers-by-mode
164 "*If non-nil, a function specifying the buffers to select from the
165 buffers tab. This is passed two buffers and should return non-nil if
166 the second buffer should be selected. The default value
167 `select-buffers-tab-buffers-by-mode' groups buffers by major mode and
168 by `buffers-tab-grouping-regexp'."
170 :type '(choice (const :tag "None" nil)
174 (defcustom buffers-tab-sort-function nil
175 "*If non-nil, a function specifying the buffers to select from the
176 buffers tab. This is passed the buffer list and returns the list in the
177 order desired for the tab widget. The default value `nil' leaves the
178 list in `buffer-list' order (usual most-recently-selected-first)."
180 :type '(choice (const :tag "None" nil)
184 (make-face 'buffers-tab "Face for displaying the buffers tab.")
185 (set-face-parent 'buffers-tab 'default)
187 (defcustom buffers-tab-face 'buffers-tab
188 "*Face to use for displaying the buffers tab."
192 (defcustom buffers-tab-grouping-regexp
193 '("^\\(gnus-\\|message-mode\\|mime/viewer-mode\\)"
194 "^\\(emacs-lisp-\\|lisp-\\)")
195 "*If non-nil, a list of regular expressions for buffer grouping.
196 Each regular expression is applied to the current major-mode symbol
197 name and mode-name, if it matches then any other buffers that match
198 the same regular expression be added to the current group."
199 :type '(choice (const :tag "None" nil)
203 (defcustom buffers-tab-format-buffer-line-function 'format-buffers-tab-line
204 "*The function to call to return a string to represent a buffer in the
205 buffers tab. The function is passed a buffer and should return a
206 string. The default value `format-buffers-tab-line' just returns the
207 name of the buffer, optionally truncated to
208 `buffers-tab-max-buffer-line-length'. Also check out
209 `slow-format-buffers-menu-line' which returns a whole bunch of info
214 (defvar buffers-tab-default-buffer-line-length
215 (make-specifier-and-init 'generic '((global ((default) . 25))) t)
216 "*Maximum length of text which may appear in a \"Buffers\" tab.
217 This is a specifier, use set-specifier to modify it.")
219 (defcustom buffers-tab-max-buffer-line-length
220 (specifier-instance buffers-tab-default-buffer-line-length)
221 "*Maximum length of text which may appear in a \"Buffers\" tab.
222 Buffer names over this length will be truncated with elipses.
223 If this is 0, then the full buffer name will be shown."
224 :type '(choice (const :tag "Show all" 0)
227 :set #'(lambda (var val)
228 (set-specifier buffers-tab-default-buffer-line-length val)
229 (setq buffers-tab-max-buffer-line-length val)))
231 (defun buffers-tab-switch-to-buffer (buffer)
232 "For use as a value for `buffers-tab-switch-to-buffer-function'."
233 (unless (eq (window-buffer) buffer)
234 (if (> (length (windows-of-buffer buffer)) 0)
235 (select-window (car (windows-of-buffer buffer)) t)
236 (switch-to-buffer buffer t))))
238 (defun select-buffers-tab-buffers-by-mode (buf1 buf2)
239 "For use as a value of `buffers-tab-selection-function'.
240 This selects buffers by major mode `buffers-tab-grouping-regexp'."
241 (let ((mode1 (symbol-name (symbol-value-in-buffer 'major-mode buf1)))
242 (mode2 (symbol-name (symbol-value-in-buffer 'major-mode buf2)))
243 (modenm1 (symbol-value-in-buffer 'mode-name buf1))
244 (modenm2 (symbol-value-in-buffer 'mode-name buf2)))
245 (cond ((or (eq mode1 mode2)
247 (and (string-match "^[^-]+-" mode1)
249 (concat "^" (regexp-quote
250 (substring mode1 0 (match-end 0))))
252 (and buffers-tab-grouping-regexp
253 (find-if #'(lambda (x)
255 (and (string-match x mode1)
256 (string-match x mode2))
257 (and (string-match x modenm1)
258 (string-match x modenm2))))
259 buffers-tab-grouping-regexp)))
263 (defun format-buffers-tab-line (buffer)
264 "For use as a value of `buffers-tab-format-buffer-line-function'.
265 This just returns the buffer's name, optionally truncated."
266 (let ((len (specifier-instance buffers-tab-default-buffer-line-length)))
268 (> (length (buffer-name buffer)) len))
269 (if (string-match ".*<.>$" (buffer-name buffer))
270 (concat (substring (buffer-name buffer)
272 (substring (buffer-name buffer) -3))
273 (concat (substring (buffer-name buffer)
275 (buffer-name buffer))))
277 (defsubst build-buffers-tab-internal (buffers)
283 (funcall buffers-tab-format-buffer-line-function
285 (list buffers-tab-switch-to-buffer-function
286 (buffer-name buffer))
288 (when selected (setq selected nil))))
291 ;;; #### SJT I'd really like this function to have just two hooks: (1) the
292 ;;; buffer filter list and (2) a sort function list. Both should be lists
293 ;;; of functions. Each filter takes two arguments: a buffer and a model
294 ;;; buffer. (The model buffer argument allows selecting according to the
295 ;;; mode or directory of that buffer.) The filter returns t if the buffer
296 ;;; should be listed and nil otherwise. Effectively the filter amounts to
297 ;;; the conjuction of the filter list. (Optionally the filter could take a
298 ;;; frame instead of a buffer or generalize to a locale as in a specifier?)
299 ;;; The filtering is done this way to preserve the ordering imposed by
300 ;;; `buffer-list'. In addition, the in-deletion argument will be used the
301 ;;; same way as in the current design.
302 ;;; The list is checked for length and pruned according to least-recently-
303 ;;; selected. (Optionally there could be some kind of sort function here,
305 ;;; Finally the list is sorted to gutter display order, and the tab data
306 ;;; structure is created and returned.
307 ;;; #### Docstring isn't very well expressed.
308 (defun buffers-tab-items (&optional in-deletion frame force-selection)
309 "This is the tab filter for the top-level buffers \"Buffers\" tab.
310 It dynamically creates a list of buffers to use as the contents of the tab.
311 Only the most-recently-used few buffers will be listed on the tab, for
312 efficiency reasons. You can control how many buffers will be shown by
313 setting `buffers-tab-max-size'. You can control the text of the tab
314 items by redefining the function `format-buffers-menu-line'."
316 (let* ((buffers (delete-if buffers-tab-omit-function (buffer-list frame)))
317 (first-buf (car buffers)))
318 ;; maybe force the selected window
319 (when (and force-selection
321 (not (eq first-buf (window-buffer (selected-window frame)))))
322 (setq buffers (cons (window-buffer (selected-window frame))
323 (delq first-buf buffers))))
324 ;; if we're in deletion ignore the current buffer
326 (setq buffers (delq (current-buffer) buffers))
327 (setq first-buf (car buffers)))
328 ;; select buffers in group (default is by mode)
329 (when buffers-tab-selection-function
330 (delete-if-not #'(lambda (buf)
331 (funcall buffers-tab-selection-function
332 first-buf buf)) buffers))
333 ;; maybe shorten list of buffers
334 (and (integerp buffers-tab-max-size)
335 (> buffers-tab-max-size 1)
336 (> (length buffers) buffers-tab-max-size)
337 (setcdr (nthcdr buffers-tab-max-size buffers) nil))
338 ;; sort buffers in group (default is most-recently-selected)
339 (when buffers-tab-sort-function
340 (setq buffers (funcall buffers-tab-sort-function buffers)))
341 ;; convert list of buffers to list of structures used by tab widget
342 (setq buffers (build-buffers-tab-internal buffers))
345 (defun add-tab-to-gutter ()
346 "Put a tab control in the gutter area to hold the most recent buffers."
347 (setq gutter-buffers-tab-orientation (default-gutter-position))
348 (let ((gutter-string "\n"))
349 (unless gutter-buffers-tab-extent
350 (setq gutter-buffers-tab-extent (make-extent 0 1 gutter-string)))
351 (set-extent-begin-glyph
352 gutter-buffers-tab-extent
353 (setq gutter-buffers-tab
355 (vector 'tab-control :descriptor "Buffers" :face buffers-tab-face
356 :orientation gutter-buffers-tab-orientation
357 (if (or (eq gutter-buffers-tab-orientation 'top)
358 (eq gutter-buffers-tab-orientation 'bottom))
359 :pixel-width :pixel-height)
360 (if (or (eq gutter-buffers-tab-orientation 'top)
361 (eq gutter-buffers-tab-orientation 'bottom))
362 '(gutter-pixel-width) '(gutter-pixel-height))
363 :properties (list :items (buffers-tab-items nil nil t))))))
365 ;; Nuke all existing tabs
366 (remove-gutter-element top-gutter 'buffers-tab)
367 (remove-gutter-element bottom-gutter 'buffers-tab)
368 (remove-gutter-element left-gutter 'buffers-tab)
369 (remove-gutter-element right-gutter 'buffers-tab)
370 ;; Put tabs into all devices that will be able to display them
373 (when (valid-image-instantiator-format-p 'tab-control x)
374 (cond ((eq gutter-buffers-tab-orientation 'top)
375 ;; This looks better than a 3d border
376 (set-specifier top-gutter-border-width 0 'global x)
377 (set-gutter-element top-gutter 'buffers-tab
378 gutter-string 'global x))
379 ((eq gutter-buffers-tab-orientation 'bottom)
380 (set-specifier bottom-gutter-border-width 0 'global x)
381 (set-gutter-element bottom-gutter 'buffers-tab
382 gutter-string 'global x))
383 ((eq gutter-buffers-tab-orientation 'left)
384 (set-specifier left-gutter-border-width 0 'global x)
385 (set-gutter-element left-gutter 'buffers-tab
386 gutter-string 'global x)
387 (set-specifier left-gutter-width
388 (glyph-width gutter-buffers-tab)
390 ((eq gutter-buffers-tab-orientation 'right)
391 (set-specifier right-gutter-border-width 0 'global x)
392 (set-gutter-element right-gutter 'buffers-tab
393 gutter-string 'global x)
394 (set-specifier right-gutter-width
395 (glyph-width gutter-buffers-tab)
398 (console-type-list))))
400 (defun update-tab-in-gutter (&optional frame-or-buffer force-selection)
401 "Update the tab control in the gutter area."
402 (let ((locale (if (framep frame-or-buffer) frame-or-buffer)))
403 ;; dedicated frames don't get tabs
404 (unless (and (framep locale)
405 (window-dedicated-p (frame-selected-window locale)))
406 (when (specifier-instance default-gutter-visible-p locale)
407 (unless (and gutter-buffers-tab
408 (eq (default-gutter-position)
409 gutter-buffers-tab-orientation))
411 (when (valid-image-instantiator-format-p 'tab-control locale)
412 (let ((inst (glyph-image-instance
414 (when (framep frame-or-buffer)
415 (last-nonminibuf-window frame-or-buffer)))))
416 (set-image-instance-property inst :items
418 nil locale force-selection))))))))
420 (defun remove-buffer-from-gutter-tab ()
421 "Remove the current buffer from the tab control in the gutter area."
422 (when (and (valid-image-instantiator-format-p 'tab-control)
423 (specifier-instance default-gutter-visible-p))
424 (let ((inst (glyph-image-instance gutter-buffers-tab))
425 (buffers (buffers-tab-items t)))
427 (setq buffers (build-buffers-tab-internal
429 (get-buffer-create "*scratch*")))))
430 (set-image-instance-property inst :items buffers))))
432 ;; A myriad of different update hooks all doing slightly different things
433 (add-hook 'kill-buffer-hook 'remove-buffer-from-gutter-tab)
434 (add-hook 'create-frame-hook
436 (when gutter-buffers-tab (update-tab-in-gutter frame t))))
437 (add-hook 'record-buffer-hook 'update-tab-in-gutter)
438 (add-hook 'default-gutter-position-changed
440 (when gutter-buffers-tab (update-tab-in-gutter arg))))
441 (add-hook 'gutter-element-visibility-changed-hook
442 #'(lambda (prop visible-p)
443 (when (and (eq prop 'buffers-tab) visible-p)
444 (update-tab-in-gutter))))
448 ;; ripped off from message display
450 (defcustom progress-display-use-echo-area nil
451 "*Whether progress gauge display should display in the echo area.
452 If NIL then progress gauges will be displayed with whatever native widgets
453 are available on the current console. If non-NIL then progress display will be
454 textual and displayed in the echo area."
458 (defvar progress-stack nil
459 "An alist of label/string pairs representing active progress gauges.
460 The first element in the list is currently displayed in the gutter area.
461 Do not modify this directly--use the `progress-display' or
462 `display-progress-display'/`clear-progress-display' functions.")
464 (defvar progress-glyph-height 32
465 "Height of the gutter area for progress messages.")
467 (defvar progress-display-stop-callback 'progress-display-quit-function
468 "Function to call to stop the progress operation.")
470 (defvar progress-display-popup-period 0.5
471 "The time that the progress gauge should remain up after completion")
473 (defun progress-display-quit-function ()
474 "Default function to call for the stop button in a progress gauge.
475 This just removes the progress gauge and calls quit."
477 (clear-progress-display)
481 (defvar progress-gauge-glyph
483 (vector 'progress-gauge
484 :pixel-height (- progress-glyph-height 8)
486 :descriptor "Progress")))
488 (defvar progress-text-glyph
489 (make-glyph [string :data ""]))
491 (defvar progress-layout-glyph
494 'layout :orientation 'vertical :justify 'left
499 'layout :pixel-height progress-glyph-height
500 :orientation 'horizontal
504 'button :pixel-height (- progress-glyph-height 8)
506 :callback '(funcall progress-display-stop-callback)))))))))
508 (defvar progress-abort-glyph
510 (vector 'layout :orientation 'vertical :justify 'left
511 :items (list progress-text-glyph
514 :pixel-height progress-glyph-height
515 :orientation 'horizontal))))))
517 (defvar progress-extent-text "\n")
518 (defvar progress-extent nil)
520 (defun progress-displayed-p (&optional return-string frame)
521 "Return a non-nil value if a progress gauge is presently displayed in the
522 gutter area. If optional argument RETURN-STRING is non-nil,
523 return a string containing the message, otherwise just return t."
524 (let ((buffer (get-buffer-create " *Gutter Area*")))
525 (and (< (point-min buffer) (point-max buffer))
527 (buffer-substring nil nil buffer)
530 ;;; Returns the string which remains in the echo area, or nil if none.
531 ;;; If label is nil, the whole message stack is cleared.
532 (defun clear-progress-display (&optional label frame no-restore)
533 "Remove any progress gauge with LABEL from the progress gauge-stack,
534 erasing it from the gutter area if it's currently displayed there.
535 If a message remains at the head of the progress-stack and NO-RESTORE
536 is nil, it will be displayed. The string which remains in the gutter
537 area will be returned, or nil if the progress-stack is now empty.
538 If LABEL is nil, the entire progress-stack is cleared.
540 Unless you need the return value or you need to specify a label,
541 you should just use (progress nil)."
542 (if (or (not (valid-image-instantiator-format-p 'progress-gauge frame))
543 progress-display-use-echo-area)
544 (clear-message label frame nil no-restore)
545 (or frame (setq frame (selected-frame)))
546 (remove-progress-display label frame)
547 (let ((inhibit-read-only t)
548 (zmacs-region-stays zmacs-region-stays)) ; preserve from change
549 (erase-buffer (get-buffer-create " *Gutter Area*")))
551 nil ; just preparing to put another msg up
553 (let ((oldmsg (cdr (car progress-stack))))
554 (raw-append-progress-display oldmsg frame)
556 ;; nothing to display so get rid of the gauge
557 (set-specifier bottom-gutter-border-width 0 frame)
558 (set-gutter-element-visible-p bottom-gutter-visible-p
559 'progress nil frame)))))
561 (defun progress-display-clear-when-idle (&optional label)
562 (add-hook 'pre-idle-hook
563 (defun progress-display-clear-pre-idle-hook ()
564 (clear-progress-display label)
565 (remove-hook 'pre-idle-hook
566 'progress-display-clear-pre-idle-hook))))
568 (defun remove-progress-display (&optional label frame)
569 ;; If label is nil, we want to remove all matching progress gauges.
570 (while (and progress-stack
571 (or (null label) ; null label means clear whole stack
572 (eq label (car (car progress-stack)))))
573 (setq progress-stack (cdr progress-stack)))
574 (let ((s progress-stack))
576 (let ((msg (car (cdr s))))
577 (if (eq label (car msg))
579 (setcdr s (cdr (cdr s))))
580 (setq s (cdr s)))))))
582 (defun append-progress-display (label message &optional value frame)
583 (or frame (setq frame (selected-frame)))
584 ;; Add a new entry to the message-stack, or modify an existing one
585 (let* ((top (car progress-stack))
587 (if (eq label (car top))
590 (if (equal tmsg message)
591 (set-image-instance-property
592 (glyph-image-instance progress-gauge-glyph)
594 (raw-append-progress-display message value frame))
595 (redisplay-gutter-area))
596 (push (cons label message) progress-stack)
597 (raw-append-progress-display message value frame))
598 (dispatch-non-command-events)
599 ;; either get command events or sit waiting for them
600 (if (not (eq value 100))
601 (when (input-pending-p)
602 (dispatch-event (next-command-event)))
603 (sit-for progress-display-popup-period nil)
604 (clear-progress-display label))))
606 (defun abort-progress-display (label message &optional frame)
607 (if (or (not (valid-image-instantiator-format-p 'progress-gauge frame))
608 progress-display-use-echo-area)
609 (display-message label (concat message "aborted.") frame)
610 (or frame (setq frame (selected-frame)))
611 ;; Add a new entry to the message-stack, or modify an existing one
612 (let* ((top (car progress-stack))
613 (inhibit-read-only t)
614 (zmacs-region-stays zmacs-region-stays))
615 (if (eq label (car top))
617 (push (cons label message) progress-stack))
618 (unless (equal message "")
619 (insert-string message (get-buffer-create " *Gutter Area*"))
620 ;; Do what the device is able to cope with.
621 ;; do some funky display here.
622 (unless progress-extent
623 (setq progress-extent (make-extent 0 1 progress-extent-text)))
624 (let ((bglyph (extent-begin-glyph progress-extent)))
625 (set-extent-begin-glyph progress-extent progress-abort-glyph)
626 ;; fixup the gutter specifiers
627 (set-gutter-element bottom-gutter
628 'progress progress-extent-text frame)
629 (set-specifier bottom-gutter-border-width 2 frame)
630 (set-image-instance-property
631 (glyph-image-instance progress-text-glyph) :data message)
632 (set-specifier bottom-gutter-height 'autodetect frame)
633 (set-gutter-element-visible-p bottom-gutter-visible-p
635 ;; we have to do this so redisplay is up-to-date and so
636 ;; redisplay-gutter-area performs optimally.
637 (redisplay-gutter-area)
638 (sit-for progress-display-popup-period nil)
639 (clear-progress-display label)
640 (set-extent-begin-glyph progress-extent bglyph)
643 (defun raw-append-progress-display (message &optional value frame)
644 (unless (equal message "")
645 (let ((inhibit-read-only t)
646 (zmacs-region-stays zmacs-region-stays)
648 (insert-string message (get-buffer-create " *Gutter Area*"))
649 ;; do some funky display here.
650 (unless progress-extent
651 (setq progress-extent (make-extent 0 1 progress-extent-text))
652 (set-extent-begin-glyph progress-extent progress-layout-glyph))
653 ;; fixup the gutter specifiers
654 (set-gutter-element bottom-gutter 'progress progress-extent-text frame)
655 (set-specifier bottom-gutter-border-width 2 frame)
656 (set-image-instance-property
657 (glyph-image-instance progress-gauge-glyph) :percent val)
658 (set-image-instance-property
659 (glyph-image-instance progress-text-glyph) :data message)
660 (if (and (eq (specifier-instance bottom-gutter-height frame)
662 (gutter-element-visible-p bottom-gutter-visible-p
665 ;; if the gauge is already visible then just draw the gutter
666 ;; checking for user events
667 (redisplay-gutter-area)
668 (dispatch-non-command-events)
669 (when (input-pending-p)
670 (dispatch-event (next-command-event))))
671 ;; otherwise make the gutter visible and redraw the frame
672 (set-specifier bottom-gutter-height 'autodetect frame)
673 (set-gutter-element-visible-p bottom-gutter-visible-p
675 ;; we have to do this so redisplay is up-to-date and so
676 ;; redisplay-gutter-area performs optimally. This may also
677 ;; make sure the frame geometry looks ok.
678 (dispatch-non-command-events)
682 (defun display-progress-display (label message &optional value frame)
683 "Display a progress gauge and message in the bottom gutter area.
684 First argument LABEL is an identifier for this message. MESSAGE is
685 the string to display. Use `clear-progress-display' to remove a labelled
687 (cond ((eq value 'abort)
688 (abort-progress-display label message frame))
689 ((or (not (valid-image-instantiator-format-p 'progress-gauge frame))
690 progress-display-use-echo-area)
691 (display-message label
692 (concat message (if (eq value 100) "done."
693 (make-string (/ value 5) ?.)))
696 (append-progress-display label message value frame))))
698 (defun current-progress-display (&optional frame)
699 "Return the current progress gauge in the gutter area, or nil.
700 The FRAME argument is currently unused."
701 (cdr (car progress-stack)))
703 ;;; may eventually be frame-dependent
704 (defun current-progress-display-label (&optional frame)
705 (car (car progress-stack)))
707 (defun progress-display (fmt &optional value &rest args)
708 "Print a progress gauge and message in the bottom gutter area of the frame.
709 The arguments are the same as to `format'.
711 If the only argument is nil, clear any existing progress gauge."
713 (if (and (null fmt) (null args))
715 (clear-progress-display nil))
716 (let ((str (apply 'format fmt args)))
717 (display-progress-display 'progress str value)
720 (defun lprogress-display (label fmt &optional value &rest args)
721 "Print a progress gauge and message in the bottom gutter area of the frame.
722 First argument LABEL is an identifier for this progress gauge. The rest of the
723 arguments are the same as to `format'."
724 ;; #### sometimes the buffer gets changed temporarily. I don't know
725 ;; why this is, so protect against it.
727 (if (and (null fmt) (null args))
729 (clear-progress-display label nil))
730 (let ((str (apply 'format fmt args)))
731 (display-progress-display label str value)
734 (provide 'gutter-items)
735 ;;; gutter-items.el ends here.