diff options
Diffstat (limited to 'lisp/tool-bar.el')
-rw-r--r-- | lisp/tool-bar.el | 292 |
1 files changed, 274 insertions, 18 deletions
diff --git a/lisp/tool-bar.el b/lisp/tool-bar.el index 179b979ee2e..96b61c7b229 100644 --- a/lisp/tool-bar.el +++ b/lisp/tool-bar.el @@ -83,6 +83,14 @@ buffer-locally and add the items you want to it with `tool-bar-add-item', `tool-bar-add-item-from-menu' and related functions.") +(defvar secondary-tool-bar-map nil + "Optional secondary keymap for the tool bar. + +If non-nil, tool bar items defined within this map are displayed +in a line below the tool bar if the `tool-bar-position' frame +parameter is set to `top', and above the tool bar it is set to +`bottom'.") + (global-set-key [tool-bar] `(menu-item ,(purecopy "tool bar") ignore :filter tool-bar-make-keymap)) @@ -91,15 +99,21 @@ functions.") (defconst tool-bar-keymap-cache (make-hash-table :test #'equal)) -(defun tool-bar--cache-key () +(defsubst tool-bar--cache-key () (cons (frame-terminal) (sxhash-eq tool-bar-map))) +(defsubst tool-bar--secondary-cache-key () + (cons (frame-terminal) (sxhash-eq secondary-tool-bar-map))) + (defun tool-bar--flush-cache () "Remove all cached entries that refer to the current `tool-bar-map'." (let ((id (sxhash-eq tool-bar-map)) + (secondary-id (and secondary-tool-bar-map + (sxhash-eq secondary-tool-bar-map))) (entries nil)) (maphash (lambda (k _) - (when (equal (cdr k) id) + (when (or (equal (cdr k) id) + (equal (cdr k) secondary-id)) (push k entries))) tool-bar-keymap-cache) (dolist (k entries) @@ -107,14 +121,56 @@ functions.") (defun tool-bar-make-keymap (&optional _ignore) "Generate an actual keymap from `tool-bar-map'. +If `secondary-tool-bar-map' is non-nil, take it into account as well. Its main job is to figure out which images to use based on the display's color capability and based on the available image libraries." - (or (gethash (tool-bar--cache-key) tool-bar-keymap-cache) - (setf (gethash (tool-bar--cache-key) tool-bar-keymap-cache) - (tool-bar-make-keymap-1)))) - -(defun tool-bar-make-keymap-1 () - "Generate an actual keymap from `tool-bar-map', without caching." + (let* ((key (tool-bar--cache-key)) + (base-keymap + (or (gethash key tool-bar-keymap-cache) + (setf (gethash key tool-bar-keymap-cache) + (tool-bar-make-keymap-1)))) + (secondary-keymap + (and secondary-tool-bar-map + (or (gethash (tool-bar--secondary-cache-key) + tool-bar-keymap-cache) + (setf (gethash (tool-bar--secondary-cache-key) + tool-bar-keymap-cache) + (tool-bar-make-keymap-1 + secondary-tool-bar-map)))))) + (if secondary-keymap + (or (ignore-errors + (progn + ;; Determine the value of the `tool-bar-position' frame + ;; parameter. + (let ((position (frame-parameter nil 'tool-bar-position))) + (cond ((eq position 'top) + ;; Place `base-keymap' above `secondary-keymap'. + (append base-keymap (list (list (gensym) + 'menu-item + "" 'ignore + :wrap t)) + (cdr secondary-keymap))) + ((eq position 'bottom) + ;; Place `secondary-keymap' above `base-keymap'. + (append secondary-keymap (list (list (gensym) + 'menu-item + "" 'ignore + :wrap t)) + (cdr base-keymap))) + ;; If the tool bar position isn't known, don't + ;; display the secondary keymap at all. + (t base-keymap))))) + ;; If combining both keymaps fails, return the base + ;; keymap. + base-keymap) + base-keymap))) + +;; This function should return binds even if images can not be +;; displayed so the tool bar can still be displayed on terminals. +(defun tool-bar-make-keymap-1 (&optional map) + "Generate an actual keymap from `tool-bar-map', without caching. +MAP is either a keymap to use as a source for menu items, or nil, +in which case the value of `tool-bar-map' is used instead." (mapcar (lambda (bind) (let (image-exp plist) (when (and (eq (car-safe (cdr-safe bind)) 'menu-item) @@ -126,17 +182,16 @@ color capability and based on the available image libraries." (consp image-exp) (not (eq (car image-exp) 'image)) (fboundp (car image-exp))) - (if (not (display-images-p)) - (setq bind nil) - (let ((image (eval image-exp))) - (unless (and image (image-mask-p image)) - (setq image (append image '(:mask heuristic)))) - (setq bind (copy-sequence bind) - plist (nthcdr (if (consp (nth 4 bind)) 5 4) - bind)) - (plist-put plist :image image)))) + (let ((image (and (display-images-p) + (eval image-exp)))) + (unless (and image (image-mask-p image)) + (setq image (append image '(:mask heuristic)))) + (setq bind (copy-sequence bind) + plist (nthcdr (if (consp (nth 4 bind)) 5 4) + bind)) + (plist-put plist :image image))) bind)) - tool-bar-map)) + (or map tool-bar-map))) ;;;###autoload (defun tool-bar-add-item (icon def key &rest props) @@ -322,6 +377,207 @@ Customize `tool-bar-mode' if you want to show or hide the tool bar." (modify-all-frames-parameters (list (cons 'tool-bar-position val)))))) + + +;; Modifier bar mode. +;; This displays a small tool bar containing modifier keys +;; above or below the main tool bar itself. + +(defvar modifier-bar-modifier-list nil + "List of modifiers that are currently applied. +Each symbol in this list represents a modifier button that has +been pressed as part of decoding this key sequence.") + +(declare-function set-text-conversion-style "textconv.c") + +;; These functions are very similar to their counterparts in +;; simple.el, but allow combining multiple modifier buttons together. + +(defun tool-bar-apply-modifiers (event modifiers) + "Apply the specified list of MODIFIERS to EVENT. +MODIFIERS must be a list containing only the symbols `alt', +`super', `hyper', `shift', `control' and `meta'. +Return EVENT with the specified modifiers applied." + (dolist (modifier modifiers) + (cond + ((eq modifier 'alt) + (setq event (event-apply-modifier event 'alt 22 "A-"))) + ((eq modifier 'super) + (setq event (event-apply-modifier event 'super 23 "s-"))) + ((eq modifier 'hyper) + (setq event (event-apply-modifier event 'hyper 24 "H-"))) + ((eq modifier 'shift) + (setq event (event-apply-modifier event 'shift 25 "S-"))) + ((eq modifier 'control) + (setq event (event-apply-modifier event 'control 26 "C-"))) + ((eq modifier 'meta) + (setq event (event-apply-modifier event 'meta 27 "M-"))))) + event) + +(defvar overriding-text-conversion-style) + +(defun modifier-bar-button (init-modifier-list) + "Decode the key sequence associated with a modifier bar button. +INIT-MODIFIER-LIST is a list of one symbol describing the button +being pressed. + +Bind `modifier-bar-modifier-list' to INIT-MODIFIER-LIST. Read +events, adding each subsequent modifier bar event's associated +modifier to that list while updating the tool bar to disable +buttons that were pressed. Return any other event read with all +modifier keys read applied. + +Temporarily disable text conversion and display the on screen +keyboard while doing so." + ;; Save the previously used text conversion style. + (let ((old-text-conversion-style text-conversion-style) + ;; Clear the list of modifiers currently pressed. + (modifier-bar-modifier-list init-modifier-list)) + ;; Disable text conversion. + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style nil)) + (unwind-protect + (progn + ;; Display the on screen keyboard. + (frame-toggle-on-screen-keyboard nil nil) + ;; Update the tool bar to disable this modifier key. + (force-mode-line-update) + (let* ((modifiers init-modifier-list) event1 + (overriding-text-conversion-style nil) + (event (read-event))) + ;; Combine any more modifier key presses. + (while (eq event 'tool-bar) + (setq event1 (event-basic-type (read-event))) + ;; Reject unknown tool bar events. + (unless (memq event1 '(alt super hyper shift control meta)) + (user-error "Unknown tool-bar event %s" event1)) + ;; If `event' is the name of a modifier key, apply that + ;; modifier key as well. + (unless (memq event1 modifiers) + (push event1 modifiers) + ;; This list is used to check which tool bar buttons + ;; need to be enabled. + (push event1 modifier-bar-modifier-list)) + ;; Update the tool bar to disable the modifier button + ;; that was read. + (force-mode-line-update) + (redisplay) + ;; Read another event. + (setq event (read-event))) + ;; EVENT is a keyboard event to which the specified list of + ;; modifier keys should be applied. + (vector (tool-bar-apply-modifiers event modifiers)))) + ;; Re-enable text conversion if necessary. + (unless (or (not (fboundp 'set-text-conversion-style)) + (eq old-text-conversion-style text-conversion-style)) + (set-text-conversion-style old-text-conversion-style t)) + ;; Re-enable all modifier bar buttons which may have been + ;; disabled. + (force-mode-line-update)))) + +(defun tool-bar-event-apply-alt-modifier (_ignore-prompt) + "Like `event-apply-alt-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + (modifier-bar-button '(alt))) + +(defun tool-bar-event-apply-super-modifier (_ignore-prompt) + "Like `event-apply-super-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + (modifier-bar-button '(super))) + +(defun tool-bar-event-apply-hyper-modifier (_ignore-prompt) + "Like `event-apply-hyper-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + (modifier-bar-button '(hyper))) + +(defun tool-bar-event-apply-shift-modifier (_ignore-prompt) + "Like `event-apply-shift-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + (modifier-bar-button '(shift))) + +(defun tool-bar-event-apply-control-modifier (_ignore-prompt) + "Like `event-apply-control-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + (modifier-bar-button '(control))) + +(defun tool-bar-event-apply-meta-modifier (_ignore-prompt) + "Like `event-apply-meta-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + (modifier-bar-button '(meta))) + +(defun modifier-bar-available-p (modifier) + "Return whether the modifier button for MODIFIER should be enabled. +Return t if MODIFIER has not yet been selected as part of +decoding the current key sequence, nil otherwise." + (not (memq modifier modifier-bar-modifier-list))) + +(define-minor-mode modifier-bar-mode + "Toggle display of the modifier bar. + +When enabled, a small tool bar will be displayed next to the tool +bar containing items bound to +`tool-bar-event-apply-control-modifier' and its related commands, +which see." + :init-value nil + :global t + :group 'tool-bar + (if modifier-bar-mode + (progn + (setq secondary-tool-bar-map + ;; The commands specified in the menu items here are not + ;; used. Instead, Emacs relies on each of the tool bar + ;; events being specified in `input-decode-map'. + `(keymap (control menu-item "Control Key" + event-apply-control-modifier + :help "Add Control modifier to the following event" + :image ,(tool-bar--image-expression "ctrl") + :enable (modifier-bar-available-p 'control)) + (shift menu-item "Shift Key" + event-apply-shift-modifier + :help "Add Shift modifier to the following event" + :image ,(tool-bar--image-expression "shift") + :enable (modifier-bar-available-p 'shift)) + (meta menu-item "Meta Key" + event-apply-meta-modifier + :help "Add Meta modifier to the following event" + :image ,(tool-bar--image-expression "meta") + :enable (modifier-bar-available-p 'meta)) + (alt menu-item "Alt Key" + event-apply-alt-modifier + :help "Add Alt modifier to the following event" + :image ,(tool-bar--image-expression "alt") + :enable (modifier-bar-available-p 'alt)) + (super menu-item "Super Key" + event-apply-super-modifier + :help "Add Super modifier to the following event" + :image ,(tool-bar--image-expression "super") + :enable (modifier-bar-available-p 'super)) + (hyper menu-item "Hyper Key" + event-apply-hyper-modifier + :help "Add Hyper modifier to the following event" + :image ,(tool-bar--image-expression "hyper") + :enable (modifier-bar-available-p 'hyper)))) + (define-key input-decode-map [tool-bar control] + #'tool-bar-event-apply-control-modifier) + (define-key input-decode-map [tool-bar shift] + #'tool-bar-event-apply-shift-modifier) + (define-key input-decode-map [tool-bar meta] + #'tool-bar-event-apply-meta-modifier) + (define-key input-decode-map [tool-bar alt] + #'tool-bar-event-apply-alt-modifier) + (define-key input-decode-map [tool-bar super] + #'tool-bar-event-apply-super-modifier) + (define-key input-decode-map [tool-bar hyper] + #'tool-bar-event-apply-hyper-modifier)) + (setq secondary-tool-bar-map nil)) + ;; Update the mode line now. + (force-mode-line-update t)) (provide 'tool-bar) |