summaryrefslogtreecommitdiff
path: root/lisp/menu-bar.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/menu-bar.el')
-rw-r--r--lisp/menu-bar.el292
1 files changed, 223 insertions, 69 deletions
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index ef64c74acda..c6ced689a67 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -540,6 +540,12 @@
(if (featurep 'ns)
(bindings--define-key menu [separator-undo] menu-bar-separator))
+ (bindings--define-key menu [undo-redo]
+ '(menu-item "Redo" undo-redo
+ :enable (and (not buffer-read-only)
+ (undo--last-change-was-undo-p buffer-undo-list))
+ :help "Redo last undone edits"))
+
(bindings--define-key menu [undo]
'(menu-item "Undo" undo
:enable (and (not buffer-read-only)
@@ -547,7 +553,7 @@
(if (eq last-command 'undo)
(listp pending-undo-list)
(consp buffer-undo-list)))
- :help "Undo last operation"))
+ :help "Undo last edits"))
menu))
@@ -661,31 +667,63 @@ PROPS are additional properties."
:button (:toggle . (and (default-boundp ',fname)
(default-value ',fname)))))
-(defmacro menu-bar-make-toggle (name variable doc message help &rest body)
+(defmacro menu-bar-make-toggle (command variable item-name message help
+ &rest body)
+ "Define a menu-bar toggle command.
+See `menu-bar-make-toggle-command', for which this is a
+compatibility wrapper. BODY is passed in as SETTING-SEXP in that macro."
+ (declare (obsolete menu-bar-make-toggle-command "28.1"))
+ `(menu-bar-make-toggle-command ,command ,variable ,item-name ,message ,help
+ ,(and body
+ `(progn
+ ,@body))))
+
+(defmacro menu-bar-make-toggle-command (command variable item-name message
+ help
+ &optional setting-sexp
+ &rest keywords)
+ "Define a menu-bar toggle command.
+COMMAND (a symbol) is the toggle command to define.
+
+VARIABLE (a symbol) is the variable to set.
+
+ITEM-NAME (a string) is the menu-item name.
+
+MESSAGE is a format string for the toggle message, with %s for the new
+status.
+
+HELP (a string) is the `:help' tooltip text and the doc string first
+line (minus final period) for the command.
+
+SETTING-SEXP is a Lisp sexp that sets VARIABLE, or it is nil meaning
+set it according to its `defcustom' or using `set-default'.
+
+KEYWORDS is a plist for `menu-item' for keywords other than `:help'."
`(progn
- (defun ,name (&optional interactively)
+ (defun ,command (&optional interactively)
,(concat "Toggle whether to " (downcase (substring help 0 1))
- (substring help 1) ".
+ (substring help 1) ".
In an interactive call, record this option as a candidate for saving
by \"Save Options\" in Custom buffers.")
(interactive "p")
- (if ,(if body `(progn . ,body)
- `(progn
+ (if ,(if setting-sexp
+ `,setting-sexp
+ `(progn
(custom-load-symbol ',variable)
(let ((set (or (get ',variable 'custom-set) 'set-default))
(get (or (get ',variable 'custom-get) 'default-value)))
(funcall set ',variable (not (funcall get ',variable))))))
- (message ,message "enabled globally")
- (message ,message "disabled globally"))
- ;; The function `customize-mark-as-set' must only be called when
- ;; a variable is set interactively, as the purpose is to mark it as
- ;; a candidate for "Save Options", and we do not want to save options
- ;; the user have already set explicitly in his init file.
- (if interactively (customize-mark-as-set ',variable)))
- '(menu-item ,doc ,name
- :help ,help
- :button (:toggle . (and (default-boundp ',variable)
- (default-value ',variable))))))
+ (message ,message "enabled globally")
+ (message ,message "disabled globally"))
+ ;; `customize-mark-as-set' must only be called when a variable is set
+ ;; interactively, because the purpose is to mark the variable as a
+ ;; candidate for `Save Options', and we do not want to save options that
+ ;; the user has already set explicitly in the init file.
+ (when interactively (customize-mark-as-set ',variable)))
+ '(menu-item ,item-name ,command :help ,help
+ :button (:toggle . (and (default-boundp ',variable)
+ (default-value ',variable)))
+ ,@keywords)))
;; Function for setting/saving default font.
@@ -957,10 +995,11 @@ The selected font will be the default on both the existing and future frames."
:help "Indicate buffer boundaries in fringe"))
(bindings--define-key menu [indicate-empty-lines]
- (menu-bar-make-toggle toggle-indicate-empty-lines indicate-empty-lines
- "Empty Line Indicators"
- "Indicating of empty lines %s"
- "Indicate trailing empty lines in fringe, globally"))
+ (menu-bar-make-toggle-command
+ toggle-indicate-empty-lines indicate-empty-lines
+ "Empty Line Indicators"
+ "Indicating of empty lines %s"
+ "Indicate trailing empty lines in fringe, globally"))
(bindings--define-key menu [customize]
'(menu-item "Customize Fringe" menu-bar-showhide-fringe-menu-customize
@@ -1405,7 +1444,7 @@ mail status in mode line"))
(bindings--define-key menu [custom-separator]
menu-bar-separator)
(bindings--define-key menu [case-fold-search]
- (menu-bar-make-toggle
+ (menu-bar-make-toggle-command
toggle-case-fold-search case-fold-search
"Ignore Case"
"Case-Insensitive Search %s"
@@ -1436,7 +1475,7 @@ mail status in mode line"))
(if (featurep 'system-font-setting)
(bindings--define-key menu [menu-system-font]
- (menu-bar-make-toggle
+ (menu-bar-make-toggle-command
toggle-use-system-font font-use-system-font
"Use System Font"
"Use system font: %s"
@@ -1462,13 +1501,15 @@ mail status in mode line"))
menu-bar-separator)
(bindings--define-key menu [debug-on-quit]
- (menu-bar-make-toggle toggle-debug-on-quit debug-on-quit
- "Enter Debugger on Quit/C-g" "Debug on Quit %s"
- "Enter Lisp debugger when C-g is pressed"))
+ (menu-bar-make-toggle-command
+ toggle-debug-on-quit debug-on-quit
+ "Enter Debugger on Quit/C-g" "Debug on Quit %s"
+ "Enter Lisp debugger when C-g is pressed"))
(bindings--define-key menu [debug-on-error]
- (menu-bar-make-toggle toggle-debug-on-error debug-on-error
- "Enter Debugger on Error" "Debug on Error %s"
- "Enter Lisp debugger when an error is signaled"))
+ (menu-bar-make-toggle-command
+ toggle-debug-on-error debug-on-error
+ "Enter Debugger on Error" "Debug on Error %s"
+ "Enter Lisp debugger when an error is signaled"))
(bindings--define-key menu [debugger-separator]
menu-bar-separator)
@@ -1480,20 +1521,34 @@ mail status in mode line"))
(bindings--define-key menu [cursor-separator]
menu-bar-separator)
+ (bindings--define-key menu [save-desktop]
+ (menu-bar-make-toggle-command
+ toggle-save-desktop-globally desktop-save-mode
+ "Save State between Sessions"
+ "Saving desktop state %s"
+ "Visit desktop of previous session when restarting Emacs"
+ (progn
+ (require 'desktop)
+ ;; Do it by name, to avoid a free-variable
+ ;; warning during byte compilation.
+ (set-default
+ 'desktop-save-mode (not (symbol-value 'desktop-save-mode))))))
+
(bindings--define-key menu [save-place]
- (menu-bar-make-toggle
+ (menu-bar-make-toggle-command
toggle-save-place-globally save-place-mode
"Save Place in Files between Sessions"
"Saving place in files %s"
"Visit files of previous session when restarting Emacs"
- (require 'saveplace)
- ;; Do it by name, to avoid a free-variable
- ;; warning during byte compilation.
- (set-default
- 'save-place-mode (not (symbol-value 'save-place-mode)))))
+ (progn
+ (require 'saveplace)
+ ;; Do it by name, to avoid a free-variable
+ ;; warning during byte compilation.
+ (set-default
+ 'save-place-mode (not (symbol-value 'save-place-mode))))))
(bindings--define-key menu [uniquify]
- (menu-bar-make-toggle
+ (menu-bar-make-toggle-command
toggle-uniquify-buffer-names uniquify-buffer-name-style
"Use Directory Names in Buffer Names"
"Directory name in buffer names (uniquify) %s"
@@ -1507,7 +1562,7 @@ mail status in mode line"))
(bindings--define-key menu [cua-mode]
(menu-bar-make-mm-toggle
cua-mode
- "Use CUA Keys (Cut/Paste with C-x/C-c/C-v)"
+ "Cut/Paste with C-x/C-c/C-v (CUA Mode)"
"Use C-z/C-x/C-c/C-v keys for undo/cut/copy/paste"
(:visible (or (not (boundp 'cua-enable-cua-keys))
cua-enable-cua-keys))))
@@ -1515,8 +1570,8 @@ mail status in mode line"))
(bindings--define-key menu [cua-emulation-mode]
(menu-bar-make-mm-toggle
cua-mode
- "Shift movement mark region (CUA)"
- "Use shifted movement keys to set and extend the region"
+ "CUA Mode (without C-x/C-c/C-v)"
+ "Enable CUA Mode without rebinding C-x/C-c/C-v keys"
(:visible (and (boundp 'cua-enable-cua-keys)
(not cua-enable-cua-keys)))))
@@ -1807,6 +1862,10 @@ mail status in mode line"))
(bindings--define-key menu [list-keybindings]
'(menu-item "List Key Bindings" describe-bindings
:help "Display all current key bindings (keyboard shortcuts)"))
+ (bindings--define-key menu [list-recent-keystrokes]
+ '(menu-item "Show Recent Inputs" view-lossage
+ :help "Display last few input events and the commands \
+they ran"))
(bindings--define-key menu [describe-current-display-table]
'(menu-item "Describe Display Table" describe-current-display-table
:help "Describe the current display table"))
@@ -1822,6 +1881,9 @@ mail status in mode line"))
(bindings--define-key menu [describe-function]
'(menu-item "Describe Function..." describe-function
:help "Display documentation of function/command"))
+ (bindings--define-key menu [shortdoc-display-group]
+ '(menu-item "Function Group Overview..." shortdoc-display-group
+ :help "Display a function overview for a specific topic"))
(bindings--define-key menu [describe-key-1]
'(menu-item "Describe Key or Mouse Operation..." describe-key
;; Users typically don't identify keys and menu items...
@@ -2026,6 +2088,8 @@ key, a click, or a menu-item"))
(bindings--define-key global-map [menu-bar help-menu]
(cons (purecopy "Help") menu-bar-help-menu))
+(define-key global-map [menu-bar mouse-1] 'menu-bar-open-mouse)
+
(defun menu-bar-menu-frame-live-and-visible-p ()
"Return non-nil if the menu frame is alive and visible.
The menu frame is the frame for which we are updating the menu."
@@ -2601,6 +2665,92 @@ If FRAME is nil or not given, use the selected frame."
(global-set-key [f10] 'menu-bar-open)
+(defun menu-bar-open-mouse (event)
+ "Open the menu bar for the menu item clicked on by the mouse.
+EVENT should be a mouse down or click event.
+
+Also see `menu-bar-open', which this calls.
+This command is to be used when you click the mouse in the menubar."
+ (interactive "e")
+ ;; This only should be bound to clicks on the menu-bar, outside of
+ ;; any window.
+ (let ((window (posn-window (event-start event))))
+ (when window
+ (error "Event is inside window %s" window)))
+
+ (let* ((x-position (car (posn-x-y (event-start event))))
+ (menu-bar-item-cons (menu-bar-item-at-x x-position)))
+ (menu-bar-open nil
+ (if menu-bar-item-cons
+ (cdr menu-bar-item-cons)
+ 0))))
+
+(defun menu-bar-keymap ()
+ "Return the current menu-bar keymap.
+
+The ordering of the return value respects `menu-bar-final-items'."
+ (let ((menu-bar '())
+ (menu-end '()))
+ (map-keymap
+ (lambda (key binding)
+ (let ((pos (seq-position menu-bar-final-items key))
+ (menu-item (cons key binding)))
+ (if pos
+ ;; If KEY is the name of an item that we want to put
+ ;; last, store it separately with explicit ordering for
+ ;; sorting.
+ (push (cons pos menu-item) menu-end)
+ (push menu-item menu-bar))))
+ (lookup-key (menu-bar-current-active-maps) [menu-bar]))
+ `(keymap ,@(nreverse menu-bar)
+ ,@(mapcar #'cdr (sort menu-end
+ (lambda (a b)
+ (< (car a) (car b))))))))
+
+(defun menu-bar-current-active-maps ()
+ "Return the current active maps in the order the menu bar displays them.
+This value does not take into account `menu-bar-final-items' as that applies
+per-item."
+ ;; current-active-maps returns maps in the order local then
+ ;; global. The menu bar displays items in the opposite order.
+ (cons 'keymap (nreverse (current-active-maps))))
+
+(defun menu-bar-item-at-x (x-position)
+ "Return a cons of the form (KEY . X) for a menu item.
+The returned X is the left X coordinate for that menu item.
+
+X-POSITION is the X coordinate being queried. If nothing is clicked on,
+returns nil."
+ (let ((column 0)
+ (menu-bar (menu-bar-keymap))
+ prev-key
+ prev-column
+ found)
+ (catch 'done
+ (map-keymap
+ (lambda (key binding)
+ (when (> column x-position)
+ (setq found t)
+ (throw 'done nil))
+ (setq prev-key key)
+ (pcase binding
+ ((or `(,(and (pred stringp) name) . ,_) ;Simple menu item.
+ `(menu-item ,name ,_cmd ;Extended menu item.
+ . ,(and props
+ (guard (let ((visible
+ (plist-get props :visible)))
+ (or (null visible)
+ (eval visible)))))))
+ (setq prev-column column
+ column (+ column (length name) 1)))))
+ menu-bar)
+ ;; Check the last menu item.
+ (when (> column x-position)
+ (setq found t)))
+ (if found
+ (cons prev-key prev-column)
+ nil)))
+
(defun buffer-menu-open ()
"Start key navigation of the buffer menu.
This is the keyboard interface to \\[mouse-buffer-menu]."
@@ -2620,6 +2770,16 @@ This is the keyboard interface to \\[mouse-buffer-menu]."
(menu-bar-buffer-vector item)))))
km))
+(defun menu-bar-define-mouse-key (map key def)
+ "Like `define-key', but adds all possible prefixes for the mouse."
+ (define-key map (vector key) def)
+ (mapc (lambda (prefix) (define-key map (vector prefix key) def))
+ ;; This list only needs to contain special window areas that
+ ;; are rendered in TTYs. No need for *-scroll-bar, *-fringe,
+ ;; or *-divider.
+ '(tab-line header-line menu-bar tab-bar mode-line vertical-line
+ left-margin right-margin)))
+
(defvar tty-menu-navigation-map
(let ((map (make-sparse-keymap)))
;; The next line is disabled because it breaks interpretation of
@@ -2654,39 +2814,33 @@ This is the keyboard interface to \\[mouse-buffer-menu]."
(define-key map [?\C-j] 'tty-menu-select)
(define-key map [return] 'tty-menu-select)
(define-key map [linefeed] 'tty-menu-select)
- (define-key map [mouse-1] 'tty-menu-select)
- (define-key map [drag-mouse-1] 'tty-menu-select)
- (define-key map [mouse-2] 'tty-menu-select)
- (define-key map [drag-mouse-2] 'tty-menu-select)
- (define-key map [mouse-3] 'tty-menu-select)
- (define-key map [drag-mouse-3] 'tty-menu-select)
- (define-key map [wheel-down] 'tty-menu-next-item)
- (define-key map [wheel-up] 'tty-menu-prev-item)
- (define-key map [wheel-left] 'tty-menu-prev-menu)
- (define-key map [wheel-right] 'tty-menu-next-menu)
- ;; The following 4 bindings are for those whose text-mode mouse
+ (menu-bar-define-mouse-key map 'mouse-1 'tty-menu-select)
+ (menu-bar-define-mouse-key map 'drag-mouse-1 'tty-menu-select)
+ (menu-bar-define-mouse-key map 'mouse-2 'tty-menu-select)
+ (menu-bar-define-mouse-key map 'drag-mouse-2 'tty-menu-select)
+ (menu-bar-define-mouse-key map 'mouse-3 'tty-menu-select)
+ (menu-bar-define-mouse-key map 'drag-mouse-3 'tty-menu-select)
+ (menu-bar-define-mouse-key map 'wheel-down 'tty-menu-next-item)
+ (menu-bar-define-mouse-key map 'wheel-up 'tty-menu-prev-item)
+ (menu-bar-define-mouse-key map 'wheel-left 'tty-menu-prev-menu)
+ (menu-bar-define-mouse-key map 'wheel-right 'tty-menu-next-menu)
+ ;; The following 6 bindings are for those whose text-mode mouse
;; lack the wheel.
- (define-key map [S-mouse-1] 'tty-menu-next-item)
- (define-key map [S-drag-mouse-1] 'tty-menu-next-item)
- (define-key map [S-mouse-2] 'tty-menu-prev-item)
- (define-key map [S-drag-mouse-2] 'tty-menu-prev-item)
- (define-key map [S-mouse-3] 'tty-menu-prev-item)
- (define-key map [S-drag-mouse-3] 'tty-menu-prev-item)
- (define-key map [header-line mouse-1] 'tty-menu-select)
- (define-key map [header-line drag-mouse-1] 'tty-menu-select)
+ (menu-bar-define-mouse-key map 'S-mouse-1 'tty-menu-next-item)
+ (menu-bar-define-mouse-key map 'S-drag-mouse-1 'tty-menu-next-item)
+ (menu-bar-define-mouse-key map 'S-mouse-2 'tty-menu-prev-item)
+ (menu-bar-define-mouse-key map 'S-drag-mouse-2 'tty-menu-prev-item)
+ (menu-bar-define-mouse-key map 'S-mouse-3 'tty-menu-prev-item)
+ (menu-bar-define-mouse-key map 'S-drag-mouse-3 'tty-menu-prev-item)
;; The down-mouse events must be bound to tty-menu-ignore, so that
;; only releasing the mouse button pops up the menu.
- (define-key map [mode-line down-mouse-1] 'tty-menu-ignore)
- (define-key map [mode-line down-mouse-2] 'tty-menu-ignore)
- (define-key map [mode-line down-mouse-3] 'tty-menu-ignore)
- (define-key map [mode-line C-down-mouse-1] 'tty-menu-ignore)
- (define-key map [mode-line C-down-mouse-2] 'tty-menu-ignore)
- (define-key map [mode-line C-down-mouse-3] 'tty-menu-ignore)
- (define-key map [down-mouse-1] 'tty-menu-ignore)
- (define-key map [C-down-mouse-1] 'tty-menu-ignore)
- (define-key map [C-down-mouse-2] 'tty-menu-ignore)
- (define-key map [C-down-mouse-3] 'tty-menu-ignore)
- (define-key map [mouse-movement] 'tty-menu-mouse-movement)
+ (menu-bar-define-mouse-key map 'down-mouse-1 'tty-menu-ignore)
+ (menu-bar-define-mouse-key map 'down-mouse-2 'tty-menu-ignore)
+ (menu-bar-define-mouse-key map 'down-mouse-3 'tty-menu-ignore)
+ (menu-bar-define-mouse-key map 'C-down-mouse-1 'tty-menu-ignore)
+ (menu-bar-define-mouse-key map 'C-down-mouse-2 'tty-menu-ignore)
+ (menu-bar-define-mouse-key map 'C-down-mouse-3 'tty-menu-ignore)
+ (menu-bar-define-mouse-key map 'mouse-movement 'tty-menu-mouse-movement)
map)
"Keymap used while processing TTY menus.")