summaryrefslogtreecommitdiff
path: root/lisp/mouse.el
diff options
context:
space:
mode:
authorJuri Linkov <juri@linkov.net>2021-08-17 11:11:35 +0300
committerJuri Linkov <juri@linkov.net>2021-08-17 11:11:35 +0300
commitbf1ec4952e67b474bff813cd26e4d612a359baf1 (patch)
treeea94b1064734e54fefa909b42f27d301e119bdd0 /lisp/mouse.el
parent9e2cc406d3bc1a1f2f6008059091b9c1b8f12acf (diff)
parentd9eac0b4263c10b2ab3a428cf8faa4b5e1d99a83 (diff)
downloademacs-bf1ec4952e67b474bff813cd26e4d612a359baf1.tar.gz
emacs-bf1ec4952e67b474bff813cd26e4d612a359baf1.tar.bz2
emacs-bf1ec4952e67b474bff813cd26e4d612a359baf1.zip
Merge branch 'feature/context-menu'
Diffstat (limited to 'lisp/mouse.el')
-rw-r--r--lisp/mouse.el188
1 files changed, 188 insertions, 0 deletions
diff --git a/lisp/mouse.el b/lisp/mouse.el
index cf7c17be28f..4c4a7d35a89 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -277,6 +277,194 @@ not it is actually displayed."
minor-mode-menus)))
+;; Context menus.
+
+(defcustom context-menu-functions '(context-menu-undo
+ context-menu-region
+ context-menu-local
+ context-menu-minor)
+ "List of functions that produce the contents of the context menu.
+Each function receives the menu as its argument and should return
+the same menu with changes such as added new menu items."
+ :type '(repeat
+ (choice (function-item context-menu-undo)
+ (function-item context-menu-region)
+ (function-item context-menu-global)
+ (function-item context-menu-local)
+ (function-item context-menu-minor)
+ (function-item context-menu-vc)
+ (function-item context-menu-ffap)
+ (function :tag "Custom function")))
+ :version "28.1")
+
+(defcustom context-menu-filter-function nil
+ "Function that can filter the list produced by `context-menu-functions'."
+ :type 'function
+ :version "28.1")
+
+(defun context-menu-map ()
+ "Return composite menu map."
+ (let ((menu (make-sparse-keymap "Context Menu")))
+ (run-hook-wrapped 'context-menu-functions
+ (lambda (fun)
+ (setq menu (funcall fun menu))
+ nil))
+ (when (functionp context-menu-filter-function)
+ (setq menu (funcall context-menu-filter-function menu)))
+ menu))
+
+(defun context-menu-global (menu)
+ "Global submenus."
+ (run-hooks 'activate-menubar-hook 'menu-bar-update-hook)
+ (define-key-after menu [separator-global] menu-bar-separator)
+ (map-keymap (lambda (key binding)
+ (when (consp binding)
+ (define-key-after menu (vector key)
+ (copy-sequence binding))))
+ (lookup-key global-map [menu-bar]))
+ menu)
+
+(defun context-menu-local (menu)
+ "Major mode submenus."
+ (run-hooks 'activate-menubar-hook 'menu-bar-update-hook)
+ (define-key-after menu [separator-local] menu-bar-separator)
+ (let ((keymap (local-key-binding [menu-bar])))
+ (when keymap
+ (map-keymap (lambda (key binding)
+ (when (consp binding)
+ (define-key-after menu (vector key)
+ (copy-sequence binding))))
+ keymap)))
+ menu)
+
+(defun context-menu-minor (menu)
+ "Minor modes submenus."
+ (run-hooks 'activate-menubar-hook 'menu-bar-update-hook)
+ (define-key-after menu [separator-minor] menu-bar-separator)
+ (dolist (mode (minor-mode-key-binding [menu-bar]))
+ (when (and (consp mode) (symbol-value (car mode)))
+ (map-keymap (lambda (key binding)
+ (when (consp binding)
+ (define-key-after menu (vector key)
+ (copy-sequence binding))))
+ (cdr mode))))
+ menu)
+
+(defun context-menu-vc (menu)
+ "Version Control menu."
+ (define-key-after menu [separator-vc] menu-bar-separator)
+ (define-key-after menu [vc-menu] vc-menu-entry)
+ menu)
+
+(defun context-menu-undo (menu)
+ "Undo menu."
+ (when (cddr menu)
+ (define-key-after menu [separator-undo] menu-bar-separator))
+ (define-key-after menu [undo]
+ '(menu-item "Undo" undo
+ :visible (and (not buffer-read-only)
+ (not (eq t buffer-undo-list))
+ (if (eq last-command 'undo)
+ (listp pending-undo-list)
+ (consp buffer-undo-list)))
+ :help "Undo last edits"))
+ (define-key-after menu [undo-redo]
+ '(menu-item "Redo" undo-redo
+ :visible (and (not buffer-read-only)
+ (undo--last-change-was-undo-p buffer-undo-list))
+ :help "Redo last undone edits"))
+ menu)
+
+(defun context-menu-region (menu)
+ "Region commands menu."
+ (when (cddr menu)
+ (define-key-after menu [separator-region] menu-bar-separator))
+ (define-key-after menu [cut]
+ '(menu-item "Cut" kill-region
+ :visible (and mark-active (not buffer-read-only))
+ :help
+ "Cut (kill) text in region between mark and current position"))
+ (define-key-after menu [copy]
+ ;; ns-win.el said: Substitute a Copy function that works better
+ ;; under X (for GNUstep).
+ `(menu-item "Copy" ,(if (featurep 'ns)
+ 'ns-copy-including-secondary
+ 'kill-ring-save)
+ :visible mark-active
+ :help "Copy text in region between mark and current position"
+ :keys ,(if (featurep 'ns)
+ "\\[ns-copy-including-secondary]"
+ "\\[kill-ring-save]")))
+ (define-key-after menu [paste]
+ `(menu-item "Paste" mouse-yank-primary
+ :visible (funcall
+ ',(lambda ()
+ (and (or
+ (gui-backend-selection-exists-p 'CLIPBOARD)
+ (if (featurep 'ns) ; like paste-from-menu
+ (cdr yank-menu)
+ kill-ring))
+ (not buffer-read-only))))
+ :help "Paste (yank) text most recently cut/copied"))
+ (define-key-after menu (if (featurep 'ns) [select-paste]
+ [paste-from-menu])
+ ;; ns-win.el said: Change text to be more consistent with
+ ;; surrounding menu items `paste', etc."
+ `(menu-item ,(if (featurep 'ns) "Select and Paste" "Paste from Kill Menu")
+ yank-menu
+ :visible (and (cdr yank-menu) (not buffer-read-only))
+ :help "Choose a string from the kill ring and paste it"))
+ (define-key-after menu [clear]
+ '(menu-item "Clear" delete-active-region
+ :visible (and mark-active
+ (not buffer-read-only))
+ :help
+ "Delete the text in region between mark and current position"))
+ (define-key-after menu [mark-whole-buffer]
+ '(menu-item "Select All" mark-whole-buffer
+ :help "Mark the whole buffer for a subsequent cut/copy"))
+ menu)
+
+(defun context-menu-ffap (menu)
+ "File at point menu."
+ (save-excursion
+ (mouse-set-point last-input-event)
+ (when (ffap-guess-file-name-at-point)
+ (define-key menu [ffap-separator] menu-bar-separator)
+ (define-key menu [ffap-at-mouse]
+ '(menu-item "Find File or URL" ffap-at-mouse
+ :help "Find file or URL guessed from text around mouse click"))))
+ menu)
+
+(defvar context-menu-entry
+ `(menu-item ,(purecopy "Context Menu") ignore
+ :filter (lambda (_) (context-menu-map))))
+
+(defvar context-menu--old-down-mouse-3 nil)
+(defvar context-menu--old-mouse-3 nil)
+
+(define-minor-mode context-menu-mode
+ "Toggle Context Menu mode.
+
+When Context Menu mode is enabled, clicking the mouse button down-mouse-3
+activates the menu whose contents depends on its surrounding context."
+ :global t :group 'mouse
+ (cond
+ (context-menu-mode
+ (setq context-menu--old-mouse-3 (global-key-binding [mouse-3]))
+ (global-unset-key [mouse-3])
+ (setq context-menu--old-down-mouse-3 (global-key-binding [down-mouse-3]))
+ (global-set-key [down-mouse-3] context-menu-entry))
+ (t
+ (if (not context-menu--old-down-mouse-3)
+ (global-unset-key [down-mouse-3])
+ (global-set-key [down-mouse-3] context-menu--old-down-mouse-3)
+ (setq context-menu--old-down-mouse-3 nil))
+ (when context-menu--old-mouse-3
+ (global-set-key [mouse-3] context-menu--old-mouse-3)
+ (setq context-menu--old-mouse-3 nil)))))
+
+
;; Commands that operate on windows.
(defun mouse-minibuffer-check (event)