* Re: Context menus and mouse-3
@ 2020-09-16 6:28 Tak Kunihiro
2020-09-16 14:18 ` Eli Zaretskii
2020-09-16 19:45 ` Juri Linkov
0 siblings, 2 replies; 59+ messages in thread
From: Tak Kunihiro @ 2020-09-16 6:28 UTC (permalink / raw)
To: emacs-devel; +Cc: Juri Linkov
[-- Attachment #1: Type: text/plain, Size: 686 bytes --]
> Mouse support is poor in Emacs, this is the reason
> why I don't use the mouse in Emacs.
I have an impression that relative to Emacs's capability, mouse support
is not as good as expected.
- Horizontal scroll by wheel is supported.
- Moving text using mouse is supported.
* Contextual menu is not supported yet.
I think that depending on a thing at mouse event (file, dir, or URL),
choice of operation should be popped up. When there is no suggestion,
`Edit' menu should be popped up. Also, by click on mode-line, buffer
list should be popped up.
I am using a global minor mode `poplife-mode' that puts commands on
mouse-3. I attach a file with poplife-mode to show the idea.
[-- Attachment #2: poplife.el --]
[-- Type: application/octet-stream, Size: 62640 bytes --]
;;; poplife.el --- Pop choices up on mouse click
;; Copyright (C) 2017-2020 Tak Kunihiro
;; Author: Tak Kunihiro <tkk@misasa.okayama-u.ac.jp>
;; Package-Requires: ((emacs "24.4"))
;; Keywords: mouse
;; Version: 1.0
;; Package-Version: 20200916.1451
;; This file is NOT part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; Usage:
;;
;; To interactively toggle the mode:
;;
;; M-x poplife-mode RET
;;
;; To make the mode permanent, put these in your init file:
;;
;; (require 'poplife)
;; (poplife-mode 1)
;;; Commentary:
;;
;; This package pops context menu triggered by right click. On a
;; click, depending on a thing under the mouse event, (1) FILE menu
;; (2) DIR menu, (3) WORD menu, (4) URL menu, (5) INFO menu, (6) HELP
;; menu, or (7) EDIT menu will be popped. The EDIT menu lets you copy
;; and paste only using mouse. As an option, the EDIT menu lets you
;; visit buffers, frames, bookmarks, and files. The seven menus
;; are detailed as below.
;;
;; (1) FILE menu -- Pop how-to-open-a-file menu.
;; (2) DIR menu -- Pop files in default-directory.
;; (3) WORD menu -- Pop word candidates when word under a mouse event is not correct.
;; (4) URL menu -- Pop how-to-open-an-url menu.
;; (5) INFO menu -- Pop how-to-open-Info menu.
;; (6) HELP menu -- Pop how-to-open-Help menu.
;; (7) EDIT menu -- Pop basic edition-commands, optional edition-commands,
;; and visiting menus. Details are shown below.
;;
;; Basic edition-commands are defined by `poplife-mouse-edit-cmd-0'.
;; Optional edition-commands are defined by
;; `poplife-mouse-edit-cmd-1'. with format similar to
;; `recentf-menu-items-for-commands'.
;;
;; DIR menu to visit files in default-directory is included by
;; default. To include a series of visiting menus, set each item of
;; `poplife-mouse-edit-cottager' to non-nil, as listed below.
;;
;; :buffer List buffers by `global-buffers-menu-map'.
;; :imenu List table of contents of current buffer by iMenu.
;; :frame List frames by `global-buffers-menu-map'.
;; :bookmark List bookmarks by `bookmark-all-names'.
;; :recentf List recent files by `recentf-menu-elements'.
;;
;; To reduce overhead, FILE menu is not shown when file-remote-p is
;; non-nil. To reduce overhead by a remote file that was once opened
;; by Tramp and stored in the list for recentf, configure a variable
;; `recentf-exclude'.
;;; References:
;; * Paste text with erasing active region.
;;
;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2017-07/msg00086.html
;; http://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00796.html
;;
;; * Pop menu up by long-click
;;
;; https://lists.gnu.org/archive/html/emacs-devel/2017-11/msg00267.html
;;
;; * Following codes from Emacs core are useful for development.
;;
;; (define-key global-map [mouse-3] menu-bar-edit-menu)
;; (popup-menu menu-bar-edit-menu)
;; (popup-menu (mouse-menu-bar-map))
;; (popup-menu (mouse-menu-major-mode-map)) ; C-mouse-3
;; (popup-menu menu-bar-bookmark-map)
;; (popup-menu global-buffers-menu-map)
;; (define-key global-map [mouse-3] 'mouse-buffer-menu)
;; (buffer-menu-open)
;;; Templates of keymap:
;;
;; This package integrates keymaps from imenu, menu-bar, bookmark, and
;; recentf. They are converted to easymenu from standard keymap.
;; Typical easymenu and standard keymap are shown below for
;; convenience.
;;
;; (label
;; [label callback]
;; (label
;; [label callback]
;; ["--" ignore]
;; [label callback])
;; [label callback])
;;
;; (keymap
;; label
;; (symbol menu-item label callback)
;; [(label lambda nil (interactive) commands)
;; (label keymap
;; (symbol menu-item label callback)
;; (symbol menu-item label callback))
;; (label lambda nil (interactive) commands)]
;; (symbol "--")
;; (symbol menu-item label
;; (keymap label
;; (symbol menu-item label callback)
;; (symbol menu-item label callback)))
;; (symbol menu-item label callback))
;;; Code:
(defvar poplife-context-candidates
'(poplife-mouse-help-menu ; HELP menu
poplife-mouse-info-menu ; INFO menu
poplife-mouse-file-menu ; FILE menu
poplife-mouse-dir-menu ; DIR menu
poplife-mouse-word-menu ; WORD menu
poplife-mouse-url-menu ; URL menu
;; menu-bar-edit-menu ; EDIT menu (default)
poplife-mouse-edit-menu) ; EDIT menu
"List of candidates for context menu.
Candidates are function or keymap. They will be evaluated in the
order of the list. A function should accept mouse EVENT, and
return keymap or nil. The last candidate should return valid
keymap.")
(defvar poplife-mouse-edit-cmd-0 '(cut copy paste select-paste paste-from-menu clear mark-whole-buffer)
"Basic edition-commands in edit menu.
Items must be one listed in `menu-bar-edit-menu'.")
(defvar poplife-mouse-edit-cmd-1
(list
["Close"
(lambda () (interactive)
(if (one-window-p)
(if (> (length (visible-frame-list)) 1)
(call-interactively 'delete-frame)
(kill-buffer (window-buffer))) ; (quit-window)
(delete-window)))
:help "Remove this window or this frame, or kill this buffer"
:visible (not (region-active-p))
:active t]
;; ["Spell-Check"
;; flyspell-correct-word-before-point
;; :help "Spell check word at point"
;; :visible (and (fboundp 'flyspell-correct-word-before-point)
;; (not (region-active-p)))
;; :active t]
["Spell-Check"
ispell-region
:help "Spell check selected text"
:visible (region-active-p)
:active t]
;; ["Search Web"
;; (lambda () (interactive)
;; (let ((keyword (buffer-substring-no-properties (region-beginning) (region-end))))
;; (switch-to-buffer-other-window (generate-new-buffer "*eww*"))
;; (eww-mode)
;; (eww keyword)))
;; :help "Search selected text by online service"
;; :visible (region-active-p)
;; :active t]
["--"
ignore
:visible (region-active-p)
:active t])
"List of optional commands in edit map.") ; recentf-menu-items-for-commands
(defvar poplife-mouse-edit-cottager
'(:imenu t :buffer t :frame t :bookmark t :recentf t)
"Extra menus to be included in edit menu besides file.")
\f
(defvar poplife-file-max-menu-items 20
"Maximum number of items in DIR menu.
See also `recentf-max-menu-items', `buffers-menu-max-size', and
`imenu-max-items'.")
(defvar poplife-file-recursive 1
"Depth of directory scan on DIR menu.")
(defvar poplife-dir-do-not-scan-regexp "inbox"
"Contents of directory matching this regexp will not be shown in DIR menu.
Instead contents of home directory are displayed.")
(defvar poplife-file-do-not-show-regexp
"\\`\\.\\.?$\\|\\`#\\|\\.elc\\'\\|\\.exe\\'\\|\\`\\.[^e]\\|\\.lnk\\'\\|\\~\\'\\|\\`desktop\\.ini\\'\\|\\`\\.DS_store\\'\\|\\`\\.dropbox\\'\\|\\`auto\\'\\|\\`ntuser\\|\\`_master_\\|\\`_region_\\|\\.aux\\'\\|\\.bbl\\'\\|\\.blg\\'\\|\\.fdb_latexmk\\'\\|\\.fls\\'\\|\\.lof\\'\\|\\.lot\\'\\|\\.out\\'\\|\\.toc\\'\\|\\.synctex\\'\\|\\.ico\\'\\|\\`Thumbs\\.db\\'\\|\\`Icon"
"Filenames matching this regexp will not be displayed in DIR menu.") ; dired-trivial-filenames, dired-omit-files
(defvar poplife-file-do-not-open-regexp
"\\.pdf\\'\\|\\.doc\\'\\|\\.docx\\'\\|\\.xls\\'\\|\\.xlsx\\'\\|\\.ppt\\'\\|\\.pptx\\'\\|\\.jpg\\'\\|\\.png\\'\\|\\.tif\\'\\|\\.tiff\\'\\|\\.bmp\\'\\|\\.aif\\'\\|\\.wav\\'\\|\\.7z\\'\\|\\.tar\\'\\|\\.dll\\'\\|\\.zip\\'\\|\\.info\\'\\|\\.igpi\\'\\|\\.ttf\\'\\|\\.otf\\'\\|\\.pkg\\'\\|\\.exe\\'"
"Filenames matching this regexp will be displayed in DIR menu and open by `poplife-func-find-file-by-default-app'.")
(defvar poplife-func-find-file 'find-file
"Function to visit a file, and a Recentf element.
Depending on context, this is internally overwritten by
`find-file', `find-file-other-window', and
`find-file-other-frame'. This is referred to visit a buffer, an
imenu element, and a bookmark element via
`poplife-func-switch-to-buffer'. A buffer is visited by
`menu-bar-select-buffer-function' on `global-buffers-menu-map'.
An imenu element is always visited on current buffer. A bookmark
element is visited by `display-func' on `bookmark-jump'.")
(defvar poplife-func-find-file-by-default-app 'poplife-find-file-by-default-app
"Function to visit file by default application.")
\f
(require 'ffap)
(require 'easymenu)
;;;###autoload
(define-minor-mode poplife-mode
"A global minor-mode to show context menu by right click."
:init-value nil
:group 'mouse
:global t
:keymap (let ((map (make-sparse-keymap))
(context-menu
`(menu-item "Context menu" poplife-context-menu
:filter ,(lambda (_) (poplife-context-menu (aref (this-command-keys) 0))))))
;; bug#27569 (gnus-read-ephemeral-emacs-bug-group 27569)
;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-07/msg00086.html
;; https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00757.html
;; https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00796.html
;; https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00840.html
(define-key map [mouse-3] context-menu)
(define-key map [drag-mouse-3] context-menu)
(define-key map [C-down-mouse-1] #'ignore)
(define-key map [C-mouse-1] context-menu)
(define-key map [C-double-mouse-1] context-menu)
(define-key map [C-triple-mouse-1] context-menu)
(define-key map [C-drag-mouse-1] context-menu)
(define-key map [remap buffer-menu-open] #'poplife-menu-open)
;; (define-key map [C-S-down-mouse-1] 'mouse-buffer-menu)
;; (define-key map [C-M-mouse-1] 'poplife-what-mouse-position)
;; (define-key mode-line-buffer-identification-keymap [mode-line mouse-3] #'poplife-pwd-menu-open)
;; (define-key mode-line-buffer-identification-keymap [mode-line mouse-1] #'poplife-buffer-menu-open)
(define-key map [remap mode-line-previous-buffer] #'poplife-buffer-menu-open) ; mouse-1
;; (define-key map [remap mode-line-previous-buffer] #'poplife-global-mark-ring-menu-open) ; mouse-1
(define-key map [remap mode-line-next-buffer] #'poplife-pwd-menu-open) ; mouse-3
(define-key map [mode-line C-mouse-1] #'poplife-pwd-menu-open)
(define-key map [mode-line M-mouse-1] #'poplife-pwd-menu-open) ; as if Mac
map))
;; * How to implement into core
;;
;; https://lists.gnu.org/archive/html/emacs-devel/2017-11/msg00416.html
;; (defvar mouse-context-menu-function #'mouse-default-context-menu
;; "Function that builds the context-menu.
;; Takes one argument (the EVENT that requests the menu) and should return
;; a list of menu items.")
;; (defun mouse-default-context-menu (event)
;; "Return default context menu."
;; (interactive "e")
;; menu-bar-edit-menu)
;; (defun mouse-context-menu (event)
;; "Open up the context menu."
;; (interactive "@e")
;; (let* ((menu-items (funcall mouse-context-menu-function event))
;; (keymap `(keymap ,(apply #'vector menu-items))))
;; (popup-menu keymap event)))
;; (define-key global-map [mouse-3] 'mouse-context-menu)
(defun poplife-menu-open ()
"Start key navigation of the poplife menu.
This is the keyboard interface to \\[poplife-context-menu]. This is
fork of `buffer-menu-open'."
(interactive)
(popup-menu (poplife-context-menu last-nonmenu-event)
(posn-at-x-y 0 0 nil t)))
(defun poplife-context-menu (event)
"Return key's definition depending on thing at mouse click EVENT.
Items in `poplife-context-candidates' are examined sequentially.
See `define-key' for the key's definition"
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
(when (fboundp 'secondary-selection-to-region) ; 26.1
(secondary-selection-to-region)) ; When there is only secondary, turn it to region.
(let ((candidates poplife-context-candidates)
context-menu)
(while (not context-menu)
(let ((item (car candidates)))
(setq candidates (cdr candidates))
;; See how dired-guess-shell-alist-user is used in dired-guess-default.
(setq context-menu (cond ((fboundp item)
(funcall item event))
((and (symbolp item)
(keymapp (symbol-value item)))
(symbol-value item))
(t ; else
nil)))))
context-menu))
;; (let ((foo (poplife-context-menu last-nonmenu-event))) (describe-variable 'foo))
(defun poplife-what-mouse-position (event)
"Evaluate text properties under mouse click."
(interactive "e")
(with-output-to-temp-buffer "*Result*"
(princ (format "Event was %S\n" event))
(princ (format "Click was on face <%S>.\n"
(mouse-posn-property (event-start event) 'face)))
(princ (format "Click was on dired-filename <%S>.\n"
(mouse-posn-property (event-start event) 'dired-filename)))))
(with-eval-after-load "help-mode"
(button-type-put 'help-function-def 'help-function 'poplife-help-find-function))
(defvar poplife-help-switch-buffer-function 'pop-to-buffer
"Function to display buffer in help-mode.
This can be `switch-to-buffer', `switch-to-buffer-other-window',
or `switch-to-buffer-other-frame'.")
(defun poplife-help-find-function (fun &optional file type)
"Find object shown in help-mode."
;; This is fork of lambda function of 'help-function, that is
;; defined for a button type 'help-function-def in `help-mode.el'.
(or file
(setq file (find-lisp-object-file-name fun type)))
(if (not file)
(message "Unable to find defining file")
(require 'find-func)
(when (eq file 'C-source)
(setq file
(help-C-file-name (indirect-function fun) 'fun)))
;; Don't use find-function-noselect because it follows
;; aliases (which fails for built-in functions).
(let ((location
(find-function-search-for-symbol fun type file)))
;; (pop-to-buffer (car location))
(funcall poplife-help-switch-buffer-function (car location)) ; Revised for poplife
(run-hooks 'find-function-after-hook)
(if (cdr location)
(goto-char (cdr location))
(message "Unable to find location in file")))))
(defun poplife-mouse-help-menu (event)
"Return help-menu when thing at mouse click EVENT is button
with button type of 'help-function-def."
(and
(not (region-active-p))
(let ((help-easymap
(save-excursion
(mouse-set-point event)
;; (text-properties-at (point))
;; '(push-button "/Applications/MacPorts/Emacs.app/Contents/Resources/lisp/button.el")
;; (push-button (point))
;; (help-button-action (button-at (point)))
;; (help-do-xref nil
;; (button-get (button-at (point)) 'help-function)
;; (button-get (button-at (point)) 'help-args))
;; (button-type (button-at (point)))
(let ((button (button-at (point))))
(when (and button
(eq (button-type button) 'help-function-def))
(let* ((button-func (button-get button 'help-function))
(button-arg (button-get button 'help-args))
(message (replace-regexp-in-string "[()\"]" "" (format "%S" button-arg) t t)))
(list
message
(vector "Open Function"
`(let* ((poplife-func-find-file 'find-file) ; switch-to-buffer
(poplife-help-switch-buffer-function (poplife-func-switch-to-buffer)))
(help-button-action ,button))
:visible t :active t :help message)
["--" ignore]
(vector "Open Function in Other Window"
`(let* ((poplife-func-find-file 'find-file-other-window) ; switch-to-buffer-other-window
(poplife-help-switch-buffer-function (poplife-func-switch-to-buffer)))
(help-button-action ,button))
:visible t :active t :help message)
(vector "Open Function in Frame"
`(let* ((poplife-func-find-file 'find-file-other-frame) ; switch-to-buffer-other-frame
(poplife-help-switch-buffer-function (poplife-func-switch-to-buffer)))
(help-button-action ,button))
:visible t :active t :help message))))))))
(when help-easymap
(easy-menu-create-menu (car help-easymap) (cdr help-easymap))))))
;; (poplife-mouse-help-menu last-nonmenu-event)
(defun poplife-info-node-at-point ()
"Return a node reference at point.
Return non-nil if successful. This is fork of
`Info-try-follow-nearest-node'."
(let (file-or-node)
(cond
((setq file-or-node (Info-get-token (point) "[hf]t?tps?://"
"\\([hf]t?tps?://[^ \t\n\"`‘({<>})’']+\\)"))
;; (browse-url file-or-node)
(setq file-or-node nil))
((setq file-or-node (Info-get-token (point) "\\*note[ \n\t]+"
"\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))) ; (system)Data format IONML
;; footnote
((setq file-or-node (Info-get-token (point) "(" "\\(([0-9]+)\\)"))
(setq file-or-node nil))
;; menu item: node name
((setq file-or-node (Info-get-token (point) "\\* +" "\\* +\\([^:]*\\)::"))) ; What is DREAM?
;; menu item: node name or index entry
((Info-get-token (point) "\\* +" "\\* +\\(.*\\): ") ; FAQ
(save-excursion
(beginning-of-line)
(forward-char 2)
(setq file-or-node (Info-extract-menu-node-name nil (Info-index-node))))) ; (pmlfaq)
((setq file-or-node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)")) ; pmlfaq.info
(when (string-match "\\.info\\'" file-or-node)
(string-match "\\`\\(.+\\)\\.info\\'" file-or-node) ; pmlfaq
(setq file-or-node (format "(%s) Top" (match-string 1 file-or-node)))))) ; (pmlfaq) Top
(when (and file-or-node
(stringp Info-current-file)
(not (string-match "\\`(.*)" file-or-node)))
(setq file-or-node
(format "(%s) %s"
(file-name-sans-extension
(file-name-nondirectory Info-current-file))
file-or-node))) ; see Info-copy-current-node-name
file-or-node))
(defun poplife-mouse-info-menu (event)
"Return info-menu when thing at mouse click EVENT is link."
;; see Info-try-follow-nearest-node
(and
(not (region-active-p))
(let ((info-easymap
(save-excursion
(mouse-set-point event)
(let ((file-or-node (poplife-info-node-at-point)))
(when file-or-node
(list
file-or-node
(vector "Open Info"
;; `(info ,file-or-node)
`(info-setup
,file-or-node
(switch-to-buffer (format "*info-%s*" ,file-or-node)))
:visible t :active t :help file-or-node)
["--" ignore]
(vector "Copy Info"
`(progn (kill-new ,file-or-node) (message ,file-or-node))
:visible t :active t :help file-or-node)
(vector "Open Info in Other Window"
`(info-setup
,file-or-node
(switch-to-buffer-other-window (format "*info-%s*" ,file-or-node)))
:visible t :active t :help file-or-node)
(vector "Open Info in Frame"
`(info-setup
,file-or-node
(switch-to-buffer-other-frame (format "*info-%s*" ,file-or-node)))
:visible t :active t :help file-or-node)))))))
(when info-easymap
(easy-menu-create-menu (car info-easymap) (cdr info-easymap))))))
;; (let ((foo (poplife-mouse-info-menu last-nonmenu-event))) (describe-variable 'foo))
;; (popup-menu (poplife-mouse-info-menu last-nonmenu-event))
(defun poplife-mouse-file-menu (event)
"Return file-menu when thing at mouse click EVENT is file.
The file is identified by `ffap-guesser'."
(and
(not (region-active-p))
(let ((file-easymap
(save-excursion
(mouse-set-point event)
(if (equal major-mode 'dired-mode)
(and
(mouse-posn-property (event-start event) 'dired-filename)
(poplife-file-easymap (dired-get-file-for-visit) t))
(let ((file (ffap-guesser))) ; ffap-at-mouse
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
;; ffap-guesser cannot guess file with asterisk such as "bookmark.html*"
(when (and file
(not (ffap-file-remote-p file)))
(poplife-file-easymap file t)))))))
(when file-easymap
(easy-menu-create-menu (car file-easymap) (cdr file-easymap))))))
(defun poplife-mouse-dir-menu (event)
"Return dir-menu when thing under mouse cursor on EVENT is directory.
The directory is identified by `ffap-guesser'."
(and
(not (region-active-p))
(if (equal major-mode 'dired-mode)
(mouse-posn-property (event-start event) 'dired-filename)
t)
(let ((dir-easymap (save-excursion
(mouse-set-point event)
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
(let ((dir (ffap-guesser)))
(when (and dir
(not (ffap-file-remote-p dir)))
(poplife-dir-easymap (file-name-as-directory dir) t))))))
(when dir-easymap
(easy-menu-create-menu (car dir-easymap) (cdr dir-easymap))))))
(defun poplife-mouse-word-menu (event)
"Return 'flyspell-correct-word when word under mouse cursor on EVENT is incorrect."
(and
(not (region-active-p))
;; Check face by (what-cursor-position t) or C-u C-x =.
(let ((faces-at-point (mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at (posn-point (event-start event))))))
(when (or (member 'flyspell-incorrect faces-at-point)
(member 'flyspell-duplicate faces-at-point))
#'flyspell-correct-word)))) ; flyspell-correct-word-before-point
(defun poplife-mouse-url-menu (event)
"Return url-menu when thing under mouse cursor on EVENT is url.
The url is identified by `thing-at-point-url-at-point'."
(and
(not (region-active-p))
(let ((url-easymap
(save-excursion
(mouse-set-point event)
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
(let ((url (or (thing-at-point-url-at-point t) ; browse-url-at-mouse
(get-text-property (point) 'shr-url)
(get-text-property (point) 'image-url))))
(when url
(list (let ((url (replace-regexp-in-string "https?://" "" url))
(len 40)) ; Make URL short
(if (> (length url) len)
(concat (substring url 0 (1- len)) "...")
url))
(vector "Open Link"
`(eww ,url)
:visible t :active t :help url)
["--" ignore]
(vector "Copy Link"
`(progn (kill-new ,url) (message "Copied %s" ,url))
:visible t :active t :help url)
(vector "Open Link in Other Window"
;; eww-browse-url, eww-open-in-new-buffer
`(progn
(switch-to-buffer-other-window (generate-new-buffer "*eww*"))
(eww-mode)
(eww ,url))
:visible t :active t :help url)
(vector "Open Link in Frame" ; "Open in Frame"
`(progn
(switch-to-buffer-other-frame (generate-new-buffer "*eww*"))
(eww-mode)
(eww ,url))
:visible t :active t :help url)
(vector "Open Link by Default App" ; "Open Link using browse-url"
`(let ((browse-url-browser-function 'browse-url-default-browser))
(browse-url ,url))
:visible t
:active t
:help url)))))))
(when url-easymap
(easy-menu-create-menu (car url-easymap) (cdr url-easymap))))))
(defun poplife-mouse-edit-menu (event)
"Define edit menu on mouse click EVENT."
;; initialize
(when (plist-get poplife-mouse-edit-cottager :bookmark)
(require 'bookmark))
(when (plist-get poplife-mouse-edit-cottager :recentf)
(require 'recentf)
(recentf-mode 1))
(when (plist-get poplife-mouse-edit-cottager :imenu)
(require 'imenu))
(save-excursion
(mouse-set-point event)
(let ((map (make-sparse-keymap "Edit")))
(unless (region-active-p)
;; Visit buffers with iMenu
(when (plist-get poplife-mouse-edit-cottager :buffer)
(easy-menu-add-item map nil (poplife-buffer-easymap)))
;; Visit frames
(when (plist-get poplife-mouse-edit-cottager :frame)
(easy-menu-add-item map nil (poplife-frame-easymap)))
;; Visit bookmarks
(when (plist-get poplife-mouse-edit-cottager :bookmark)
(easy-menu-add-item map nil (poplife-bookmark-easymap)))
;; Visit recent files
(when (plist-get poplife-mouse-edit-cottager :recentf)
(easy-menu-add-item map nil (poplife-recentf-easymap)))
;; Visit directory
(unless (file-remote-p default-directory)
(let ((dir-map (poplife-dir-easymap default-directory)))
(setcar dir-map "File") ; instead of ".emacs.d/"
(easy-menu-add-item map nil dir-map)))
;; Separator
(define-key map [separator-edit] menu-bar-separator))
;; Option -- TODO: Fix location of item with recursive structure.
(when poplife-mouse-edit-cmd-1
(dolist (item poplife-mouse-edit-cmd-1)
(if (vectorp item)
(let* ((item (append item nil)) ; Convert vector to list.
(nickname (car item)))
(bindings--define-key map (vector (easy-menu-make-symbol nickname))
(append (list 'menu-item nickname) (cdr item))))
(easy-menu-add-item map nil item)))) ; with recursive structure
;; Main
(dolist (item (reverse (cdr menu-bar-edit-menu)))
(when (and (listp item)
(member (car item) poplife-mouse-edit-cmd-0)) ; pick some
(bindings--define-key map (vector (car item)) (cdr item))))
map)))
;; (let ((foo (poplife-mouse-edit-menu last-nonmenu-event))) (describe-variable 'foo))
\f
;;; iMenu
(defun poplife-imenu-easymap (&optional submenu)
"Define easymenu to list index by iMenu.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
;; TODO: Selection of menu does not move point when called from
;; click on mode-line.
(let* ((imenu-max-items poplife-file-max-menu-items) ; 25
(map-0 (ignore-errors
(imenu--split-menu
(delq nil (cdr (imenu--make-index-alist t))) ; remove "*Rescan*"
(buffer-name))))
(map (poplife-imenu-alist-to-easymap (car map-0) (cdr map-0)
'imenu--menubar-select)))
(when (>= (length map) 2) ; Return map only when map is with useful items.
(if submenu
(poplife-imenu-submenufy-easymap map)
(let ((map-rev (reverse map)))
(push (vector "More..."
'(popup-menu (poplife-imenu-easymap t)
(popup-menu-normalize-position last-nonmenu-event)))
map-rev)
(reverse map-rev))))))
;; (let ((foo (poplife-imenu-easymap))) (describe-variable 'foo))
;; (let* ((poplife-func-find-file 'find-file-other-frame) (foo (poplife-imenu-easymap))) (describe-variable 'foo))
;; (let ((foo (poplife-imenu-easymap t))) (describe-variable 'foo))
;; (popup-menu (poplife-imenu-easymap))
(defun poplife-imenu-alist-to-easymap (title alist &optional cmd)
"Create easymenu from alist by iMenu to display index by CMD.
This is fork of `imenu--create-keymap'."
(let (map)
(dolist (item alist)
(push (cond
((imenu--subalist-p item)
(poplife-imenu-alist-to-easymap (car item) (cdr item) cmd))
(t
(if cmd
(vector (car item)
(list 'let
'((display-buffer--other-frame-action ; hack for switch-to-buffer-other-frame
'((display-buffer-pop-up-frame)
(inhibit-same-window . t))))
`(funcall (quote ,(poplife-func-switch-to-buffer)) ,(current-buffer))
`(,cmd (quote ,item))))
(list 'quote item))))
map))
(setq map (reverse map))
(push title map)
map))
(defun poplife-imenu-submenufy-easymap (map)
"Return easymenu of iMenu MAP with recursive structure."
(let (map-1)
(dolist (item map)
(push (cond
((listp item) ; when an item is a list
(poplife-imenu-submenufy-easymap item))
((vectorp item) ; when an item is vector
(poplife-imenu-elt-easymap item))
(t item)) ; else such for "poplife.el"
map-1))
(reverse map-1)))
(defun poplife-imenu-elt-easymap (elt)
"Return easymenu of iMenu ELT with submenu added."
(let ((label (aref elt 0)) ; "poplife-mouse-edit-cottager"
(cmd (nth 3 (aref elt 1))) ; (imenu--menubar-select '("poplife-mouse-edit-cottager" . #<marker at 3207 in poplife.el>))
(buf (current-buffer))) ; #<buffer poplife.el>
(list label
(vector "Open" `(progn (switch-to-buffer ,buf) ,cmd))
["--" ignore]
(vector "Open in Other Window" `(progn (switch-to-buffer-other-window ,buf) ,cmd))
(vector "Open in Frame" `(progn (pop-to-buffer ,buf '((display-buffer-pop-up-frame) (inhibit-same-window . t))) ,cmd)))))
\f
;;; Frame
(defun poplife-frame-easymap ()
"Define easymenu to list frames."
(let ((frame-vec (nth 2 (cadddr (assoc 'frames global-buffers-menu-map))))
(poplife-func-find-file 'find-file-other-frame)
map)
(dolist (elt (append frame-vec nil)) ; Convert vector to list.
(let* ((nickname (car elt))
(cmd (nth 4 elt))
(frame (cadr cmd)))
(push (vector nickname cmd :active (not (equal frame (selected-frame)))) map)))
(push ["New" (progn (make-frame-command) (menu-find-file-existing))] map)
(push ["--" ignore] map)
(when (plist-get poplife-mouse-edit-cottager :buffer)
(push (poplife-buffer-easymap) map))
(when (plist-get poplife-mouse-edit-cottager :bookmark)
(push (poplife-bookmark-easymap) map))
(when (plist-get poplife-mouse-edit-cottager :recentf)
(push (poplife-recentf-easymap) map))
(unless (file-remote-p default-directory)
(let ((dir-map (poplife-dir-easymap default-directory)))
(setcar dir-map "File") ; "Directory"
(push dir-map map)))
(setq map (reverse map))
(push "Frames" map)
map))
;; (let ((foo (poplife-frame-easymap))) (describe-variable 'foo))
\f
;;; Buffer
(defun poplife-buffer-easymap (&optional submenu)
"Define easymenu to list buffers.
This extracts list of buffers from `global-buffers-menu-map'.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
(let ((buffer-list (poplife-buffer-list))
;; (poplife-func-find-file 'find-file) ; 20190128.1647
(menu-bar-select-buffer-function (poplife-func-switch-to-buffer))
map)
;; Add submenu on request.
(dolist (elt buffer-list)
(push (poplife-buffer-elt-easymap elt submenu) map))
;; Add option.
(push (vector "More..."
'(let (buffer-full-map)
(let (buffers-menu-max-size)
(menu-bar-update-buffers t)
(setq buffer-full-map
(poplife-buffer-easymap t)))
(menu-bar-update-buffers t)
(popup-menu buffer-full-map
(popup-menu-normalize-position last-nonmenu-event)))
:visible t
:active (not submenu))
map)
;; Reverse map and add a key.
(setq map (reverse map))
(push "Buffers" map)
map))
;; (let ((foo (poplife-buffer-easymap))) (describe-variable 'foo))
;; (let ((foo (poplife-buffer-easymap t))) (describe-variable 'foo))
(defun poplife-buffer-list-on-menu ()
"Return a list of buffers on `global-buffers-menu-map'."
;; on 25.2, pick 4th out of 5 items
;; ("menu-bar.el.gz " lambda nil (interactive) (funcall menu-bar-select-buffer-function #<buffer menu-bar.el.gz>))
;; on 24.5, pick 5th out of 6 items
;; ("menu-bar.el.gz " (nil) lambda nil (interactive) (funcall menu-bar-select-buffer-function #<buffer menu-bar.el.gz>))
(let ((len245 6)
(nth252 4)
(nth245 5)
(buffers-menu (nth 2 global-buffers-menu-map))
;; nickname-list
buffer-list)
(dolist (elt (append buffers-menu nil)) ; Convert vector to list.
;; (push (car elt) nickname-list)
(push (nth 2 (nth (if (equal (length elt) len245)
nth245
nth252)
elt)) buffer-list))
(reverse buffer-list)))
;; (let ((foo (poplife-buffer-list-on-menu))) (describe-variable 'foo))
;; (let ((foo (buffer-list))) (describe-variable 'foo))
(defun poplife-buffer-list ()
"Return a list of buffers."
(delete-dups (append (list (current-buffer))
(poplife-buffer-list-with-marks)
(poplife-buffer-list-on-menu))))
;; (let ((foo (poplife-buffer-list))) (describe-variable 'foo))
(defun poplife-buffer-list-with-marks ()
"Return a list of buffers on `global-mark-ring'."
(let (buffer-list buf)
(dolist (marker (reverse global-mark-ring))
(when (setq buf (marker-buffer marker)) ; See `pop-global-mark'
(push buf buffer-list)))
buffer-list))
;; (let ((foo (poplife-buffer-list-with-marks))) (describe-variable 'foo))
(defun poplife-buffer-elt-easymap (buffer &optional submenu)
"Define easymenu for a BUFFER.
When SUBMENU is non-nil, this returns an easymenu with multiple
actions."
(let ((nickname (buffer-name buffer)))
(if (not submenu)
(let (imenu-map)
(if (and (plist-get poplife-mouse-edit-cottager :imenu)
(equal (current-buffer) buffer)
(setq imenu-map (poplife-imenu-easymap)))
imenu-map
(vector nickname `(funcall (quote ,menu-bar-select-buffer-function) ,buffer)
:active `(not (equal ,(current-buffer) ,buffer)))))
(list nickname
(vector "Open" `(switch-to-buffer ,buffer) :active `(not (equal ,(current-buffer) ,buffer)))
["--" ignore]
(vector "Open in Other Window" `(switch-to-buffer-other-window ,buffer))
(vector "Open in Frame" `(switch-to-buffer-other-frame ,buffer))))))
\f
;;; Recentf
(defun poplife-recentf-easymap (&optional submenu)
"Define easymenu to list recentf.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
(let (map
(recentf-menu-shortcuts 0)
(elements (recentf-menu-elements recentf-max-menu-items)))
;; See `recentf-make-menu-items'.
(setq map (mapcar (lambda (elt)
(funcall 'poplife-recentf-elt-easymap elt submenu))
(recentf-apply-menu-filter
recentf-menu-filter
elements)))
(let ((map-rev (reverse map)))
(push (vector "More..."
'(let ((recentf-max-menu-items recentf-max-saved-items))
(popup-menu (poplife-recentf-easymap t)
(popup-menu-normalize-position last-nonmenu-event)))
:help "Show more Recentf"
:visible t
:active (not submenu))
map-rev)
(push ["--" ignore] map-rev)
(push ["Edit..."
recentf-edit-list
:help "Manually remove files from the recent list"
:active t]
map-rev)
;; (push ["Save List Now"
;; recentf-save-list
;; :help "Save the list of recently opened files now"
;; :active t]
;; map-rev)
(setq map (reverse map-rev)))
(cons "Recent" map)))
;; (let ((foo (poplife-recentf-easymap))) (describe-variable 'foo))
(defun poplife-recentf-elt-easymap (elt &optional submenu)
"Define easymenu to popup recentf item ELT.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
;; This is fork of `recentf-make-menu-item'.
(let ((name (recentf-menu-element-item elt))
(file (recentf-menu-element-value elt)))
(if (recentf-sub-menu-element-p elt)
(cons name (mapcar (lambda (elt) (funcall 'poplife-recentf-elt-easymap elt)) file)) ; for `recentf-arrange-by-dir'
;; (vector name
;; `(,recentf-menu-action ,file) ; poplife-func-find-file
;; :help (concat "Open " file)
;; :active t)
(let ((map
(if (file-directory-p file)
(poplife-dir-easymap file submenu) ; directory
(poplife-file-easymap file submenu)))) ; file
(when (listp map)
(setcar map name))
map))))
\f
;;; Bookmark
(defun poplife-bookmark-easymap (&optional submenu)
"Define easymenu to list bookmarks.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
;; TODO: On emacs-27.1 with smb, I see "tramp-error: Method ‘smb’ is
;; not known".
(let ((map
(list
(vector "More..." '(popup-menu (poplife-bookmark-easymap t)
(popup-menu-normalize-position last-nonmenu-event))
:visible t
:active (not submenu)
:help "Set a bookmark named inside a file.")
["--" ignore]
["Add..." bookmark-set :visible t :active (or (buffer-file-name) (eq major-mode 'dired-mode))
:help "Set a bookmark named inside a file."]
["Edit..." bookmark-bmenu-list :visible t :active t
:help "Display a list of existing bookmarks"]
;; ["Save List Now" bookmark-save :visible t :active t
;; :help "Save currently defined bookmarks"]
)))
(dolist (bookmark (bookmark-all-names))
(push (poplife-bookmark-elt-easymap bookmark submenu) map))
(cons "Bookmark" map)))
;; (let ((foo (poplife-bookmark-easymap))) (describe-variable 'foo))
;; (let ((foo (poplife-bookmark-easymap t))) (describe-variable 'foo))
(defun poplife-bookmark-elt-easymap (bookmark &optional submenu)
"Define easymenu to list a BOOKMARK.
When SUBMENU it non-nil, this returns an easymenu with multiple actions."
(let (map)
(if (not submenu)
(let ((file (bookmark-get-filename bookmark)))
(if (and (not (file-remote-p file)) ; when bookmark is directory
(file-exists-p file)
(file-directory-p file))
(poplife-dir-easymap file submenu) ; offer DIR menu
(vector bookmark
;; `(bookmark-jump ,bookmark DISPLAY-FUNC)
`(bookmark-jump ,bookmark (quote ,(poplife-func-switch-to-buffer))) ; switch-to-buffer
:visible t
:active (not (string= ; gray the vising file out
(and (buffer-file-name) (expand-file-name (buffer-file-name)))
(expand-file-name (bookmark-get-filename bookmark))))
:help (format "Jump to %s" bookmark))))
;; (push (vector "Show Annotation..." `(bookmark-show-annotation ,bookmark) :visible t :active `(bookmark-get-annotation ,bookmark) :help bookmark) map)
;; (push (vector "Edit Annotation..." `(bookmark-edit-annotation ,bookmark) :visible t :help bookmark) map)
(let ((annot-map (poplife-bookmark-annotation-easymap bookmark)))
(if (vectorp annot-map) ; with no annotation and with "Add Annotation..."
(push annot-map map)
(dolist (annot-item (reverse (cdr annot-map)))
(push annot-item map))
(push ["--" ignore] map)
(push (vector "Edit Annotation..." `(bookmark-edit-annotation ,bookmark) :visible t :help bookmark) map)))
(push (vector "Delete..." `(and (y-or-n-p (format "Are you sure you want to delete a bookmark %s? " ,bookmark)) (bookmark-delete ,bookmark)) :visible t :help bookmark) map)
(push (vector "Edit Location..." `(bookmark-relocate ,bookmark) :visible t :help bookmark) map)
(push (vector "Rename..." `(bookmark-rename ,bookmark) :visible t :help bookmark) map)
(push (vector "Insert Location" `(bookmark-locate ,bookmark) :visible t :help bookmark) map)
(push (vector "Insert Contents" `(bookmark-insert ,bookmark) :visible t :help bookmark) map)
(push (vector "Open by File Browser" `(poplife-find-location (expand-file-name (bookmark-get-filename ,bookmark))) :visible t :active t :help bookmark) map)
(push (vector "Open in Frame" `(bookmark-jump ,bookmark 'switch-to-buffer-other-frame) :visible t :help bookmark) map)
(push (vector "Open in Other Window" `(bookmark-jump-other-window ,bookmark) :visible t :help bookmark) map)
(push ["--" ignore] map)
(push (vector "Open" `(bookmark-jump ,bookmark) :visible t :help bookmark) map) ; switch-to-buffer
(push (vector ".." `(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir (expand-file-name "../" (bookmark-get-filename ,bookmark)) ,submenu))
:visible t :active t) map)
(let ((annotation (bookmark-get-annotation bookmark)))
(cons (format "%s%s" bookmark (if (and annotation (not (string-equal annotation ""))) "*" "")) map)))))
;; (let ((foo (poplife-bookmark-elt-easymap "poplife.el\\site-lisp"))) (describe-variable 'foo))
;; (let ((foo (poplife-bookmark-elt-easymap "poplife.el\\site-lisp" t))) (describe-variable 'foo))
(defvar poplife-bookmark-annotation-detail-flag t
"Show full contents of annotation in popup-menu.")
(defun poplife-bookmark-annotation-easymap (bookmark)
"Define easymenu to list annotation."
(let ((annot (bookmark-get-annotation bookmark))
(annot-column 36)) ; 36 is arbitrary number or (length "Open in Other Window")
(if (and annot (not (string-equal annot "")))
(if poplife-bookmark-annotation-detail-flag
(let ((lines (poplife-split-string annot annot-column))
map)
(dolist (line (reverse lines))
(push (vector line `(bookmark-edit-annotation ,bookmark) :visible t :active t :help bookmark) map))
(cons "Edit Annotation..." map))
(let (annot-name)
(setq annot-name (format "Edit Annotation `%s'..." (if (> (length annot) annot-column) (substring annot 0 annot-column) annot)))
(vector annot-name `(bookmark-edit-annotation ,bookmark) :visible t :active t :help bookmark)))
(vector "Add Annotation..." `(bookmark-edit-annotation ,bookmark) :visible t :active t :help bookmark))))
(defun poplife-split-string (string fill-length)
"Split STRING into list of string.
Argument FILL-LENGTH determines length of each line."
(setq string (replace-regexp-in-string
(rx (* (any " \t\n")) eos) "" string)) ; Chomp text.
(with-temp-buffer
(insert string)
(let ((fill-column fill-length) ; Replace text.
(find-repl-list '(("\\`\\'" . " ") (" +" . " ")))) ; Avoid having "--" on menu.
(fill-region (point-min) (point-max))
(dolist (find-repl find-repl-list)
(goto-char (point-min))
(while (re-search-forward (car find-repl) nil t)
(replace-match (cdr find-repl)))))
(split-string (buffer-string) "\n"))) ; List of text lines.
\f
;;; File
(defun poplife-file-easymap (file &optional submenu)
"Define easymenu to list a FILE.
When SUBMENU it non-nil, this returns an easymenu with multiple actions."
(setq file (expand-file-name file))
(and (not (file-directory-p file))
(let* (map
(file-nickname (file-name-nondirectory file))
(file-readable-flag (and (file-regular-p file)
(file-readable-p file)))
(open-file-flag (and (not (string-match-p poplife-file-do-not-open-regexp file))
file-readable-flag))
(dir (file-name-directory file))
(open-dir-flag (and (file-directory-p dir)
(file-accessible-directory-p dir))))
(if (not submenu)
(vector file-nickname
`(funcall (if ,open-file-flag
(quote ,poplife-func-find-file)
(quote ,poplife-func-find-file-by-default-app)) ,file)
:active (and file-readable-flag ; gray the vising file out
(not (string=
(and (buffer-file-name) (expand-file-name (buffer-file-name)))
file)))
:help file)
(push (vector "Open with File Browser" `(poplife-find-location ,file)
:visible t
:active open-dir-flag
:help dir) map)
(push (vector "Open with Default App" `(,poplife-func-find-file-by-default-app ,file)
:visible t
:active t
:help file) map)
(push (vector "Open and Bookmark..." `(progn (find-file ,file) (bookmark-set))
:visible (fboundp 'bookmark-set)
:active open-file-flag
:help "Open and Bookmark this file") map)
(push (vector "Open in Frame" `(find-file-other-frame ,file)
:visible t
:active open-file-flag
:help file) map)
(push (vector "Open in Other Window" `(find-file-other-window ,file)
:visible t
:active open-file-flag
:help file) map)
(push ["--" ignore] map)
(push (vector "Open" `(find-file ,file)
:visible t
:active (and open-file-flag ; gray the vising file out
(not (string=
(and (buffer-file-name) (expand-file-name (buffer-file-name)))
file)))
:help file) map)
(push (vector ".." `(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,dir, submenu))
:visible t :active open-dir-flag :help dir) map)
(cons file-nickname map)))))
;; (let ((foo (poplife-file-easymap "~/.emacs.d/init.el"))) (describe-variable 'foo))
;; (let ((foo (poplife-file-easymap "~/.emacs.d/init.el" t))) (describe-variable 'foo))
(defun poplife-dir-easymap (dir &optional submenu depth)
"Define easymenu to list files and directories in DIR.
When SUBMENU is non-nil, this returns an easymenu with multiple actions.
When DEPTH is more than 1, DIR is recursively scanned."
(when (string-match-p poplife-dir-do-not-scan-regexp dir)
(setq dir "~"))
(setq dir (directory-file-name (expand-file-name dir))) ; Remove slash at the end.
(or depth (setq depth 1))
(let (map base-dir parent-dir rawfiles menufiles)
(setq base-dir (file-name-as-directory (if (string= (file-name-nondirectory dir) "")
dir ; In a case for "c:/"
(file-name-nondirectory dir)))) ; Add slash at the end.
(setq parent-dir (directory-file-name (file-name-directory dir)))
;; Obtain a file list.
(setq rawfiles (ignore-errors (directory-files dir t)))
;; Filter out trivial files.
(dolist (fullfile rawfiles)
(let ((file-nickname (file-name-nondirectory fullfile)))
(unless (string-match-p poplife-file-do-not-show-regexp file-nickname)
(push fullfile menufiles))))
;; Limit number of menufiles
(when (and poplife-file-max-menu-items
(not submenu))
(let ((nfile (length menufiles)))
(setq menufiles (nthcdr (- nfile poplife-file-max-menu-items) menufiles))))
;; Add more menu.
(push (vector "More..." ; Item to open a current directory.
`(let (poplife-file-max-menu-items
;; (poplife-file-recursive ,(1+ poplife-file-recursive))
(poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,dir t))
:active `(not ,submenu)
:visible t
:help dir)
map)
;; Create map with files and directories.
(dolist (fullfile menufiles)
(let ((file-nickname (file-name-nondirectory fullfile)))
(if (file-directory-p fullfile) ; when item is directory
(push (if (or (>= depth poplife-file-recursive)
(not (file-accessible-directory-p fullfile)))
(vector (file-name-as-directory file-nickname)
`(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,fullfile ,submenu))
:active (file-accessible-directory-p fullfile) :help fullfile)
(poplife-dir-easymap fullfile submenu (1+ depth))) ; recursive
map)
(push ; when item is file (that is defined as `not a directory')
(poplife-file-easymap fullfile submenu)
map))))
(push ["--" ignore] map)
(push (if (not (or submenu poplife-dir-.-submenu)) ; (not submenu)
;; Single item
(vector "." ; item to open current directory
`(,poplife-func-find-file ,dir)
;; `(,poplife-func-find-file-by-default-app ,dir)
:visible t :active t :help dir)
;; Multiple items in submenu.
(delq nil
(list "."
(vector "Open" ; item to open current directory
`(find-file ,dir) ; ,poplife-func-find-file
:visible t :active t :help dir)
["--" ignore]
(vector "Open in Other Window"
`(find-file-other-window ,dir)
:visible t :active t :help dir)
(vector "Open in Frame"
`(find-file-other-frame ,dir)
:visible t :active t :help dir)
(vector "Open with Bookmarked..."
`(progn (find-file ,dir) (bookmark-set))
:visible (fboundp 'bookmark-set) :active t :help "Open and Add to Bookmarks")
(vector "Open by File Browser"
`(,poplife-func-find-file-by-default-app ,dir)
:visible t
:active t
:help dir))))
map)
(push (vector ".." ; Item to open parent directory.
`(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,parent-dir ,submenu))
:active `(not (equal ,parent-dir ,dir)) :help parent-dir)
map)
(setq base-dir (replace-regexp-in-string "^@" "at" base-dir t t))
(cons base-dir map)))
;; (let ((foo (poplife-dir-easymap "~/.emacs.d"))) (describe-variable 'foo))
;; (let ((foo (poplife-dir-easymap "~/.emacs.d" t))) (describe-variable 'foo))
;; (popup-menu (poplife-dir-easymap "~/.emacs.d"))
;; (popup-menu (poplife-dir-easymap "~/"))
;; (let ((foo (poplife-dir-easymap "c:/"))) (describe-variable 'foo))
;; (popup-menu (poplife-dir-easymap "c:/"))
(defvar poplife-dir-.-submenu t
"Show always submenu for the current directory.")
(defun poplife-pwd-easymap (path &optional submenu)
"Define easymenu to list directories that are above PATH."
(setq path (directory-file-name (expand-file-name path))) ; remove slash
(let* ((title (format (if (file-directory-p path)
"%s/"
"%s")
(file-name-nondirectory path)))
dirpath
map)
(while (not (string= (file-name-nondirectory path) "" ))
(setq dirpath (directory-file-name (file-name-directory path))) ; remove slash
(push (vector (format "%s/" (file-name-nondirectory dirpath)) ; dirname
`(poplife-find-location ,path)) map)
(setq path dirpath))
(cons title (reverse map))))
;; (let ((foo (poplife-pwd-easymap "~/.emacs.d/site-lisp/poplife.el"))) (describe-variable 'foo))
;; (popup-menu (poplife-pwd-easymap default-directory))
;; (popup-menu (poplife-pwd-easymap "~/.emacs.d/site-lisp/"))
(defun poplife-pwd-menu-open (event)
"Open popup-menu that opens a folder by File Browser."
(interactive "e")
(mouse-set-point event)
(popup-menu
(poplife-pwd-easymap (or buffer-file-name default-directory))))
;; (poplife-pwd-menu-open last-nonmenu-event)
(defun poplife-buffer-menu-open (event)
"Open popup-menu that switches current buffer."
(interactive "e")
(mouse-set-point event)
(popup-menu (poplife-buffer-easymap)))
;; (poplife-buffer-menu-open last-nonmenu-event)
\f
;;; Util
(defun poplife-func-switch-to-buffer ()
"Return switch-to-buffer function that corresponds to `poplife-func-find-file'."
(cdr (assoc poplife-func-find-file
'((find-file . switch-to-buffer)
(find-file-other-window . switch-to-buffer-other-window)
(find-file-other-frame . switch-to-buffer-other-frame)))))
;; (let ((foo (poplife-func-switch-to-buffer))) (describe-variable 'foo))
(defun poplife-find-dir (dir &optional submenu)
"Visit directory DIR using `popup-menu'.
When SUBMENU is non-nil, this offers multiple actions."
;; (interactive (list (read-directory-name "Find directory: ")))
(popup-menu (poplife-dir-easymap dir submenu)
(popup-menu-normalize-position last-nonmenu-event)))
(defun poplife-finder-directory ()
"Return directory where Finder is visiting."
(when (eq system-type 'darwin)
(ns-do-applescript
"tell application \"Finder\"
if exists Finder window 1 then
set currentDir to target of Finder window 1 as alias
else
set currentDir to desktop as alias
end if
set thePath to POSIX path of currentDir
end tell")))
;; (let ((foo (poplife-finder-selection))) (describe-variable 'foo))
(defun poplife-find-location (file)
"Visit directory that contains FILE."
;; org-open-file
(cond
((eq system-type 'darwin)
;; Select file in Finder.
(ns-do-applescript ; do-applescript, osascript
(format "tell application \"Finder\"
set thePath to POSIX file \"%s\" as string
activate
reveal thePath
end tell" file)))
((eq system-type 'windows-nt)
;; Select file in File Explorer.
(w32-shell-execute "open" "explorer"
(concat "/e,/select,"
(poplife-convert-w32-filename file))))
((eq system-type 'gnu/linux)
;; Visit dir that contains file by default-app.
;; (start-process "select-file-by-nautilus" nil "nautilus" file)
(start-process "open-dir-by-xdg-open" nil "xdg-open" (file-name-directory file)))
(t
;; Select file in Dired
(dired-other-frame (file-name-directory file)) ; Visit dir that contains file
(dired-goto-file file)))) ; Move point to file
(defun poplife-convert-w32-filename (filename)
"Mirror slash characters in FILENAME into backslashes."
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=24387 (bug#24387)
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=28883 (bug#28883)
;; (setq filename (convert-standard-filename filename))
(let ((start 0))
(while (string-match "/" filename start)
(aset filename (match-beginning 0) ?\\)
(setq start (match-end 0)))
filename))
;;; (poplife-convert-w32-filename "c:/Users/dream/.emacs.d")
(defun poplife-find-file-by-default-app (file)
"Visit FILE by default application or default file browser."
(when (plist-get poplife-mouse-edit-cottager :recentf)
(recentf-push file))
(cond
((eq system-type 'gnu/linux)
(start-process "find-file-by-default-app" nil "xdg-open" file)) ; Visit file by default-app.
((eq system-type 'darwin)
(start-process "find-file-by-default-app" nil "open" file)) ; Visit file by default-app.
((eq system-type 'cygwin)
(start-process "find-file-by-default-app" nil "cygstart" file)) ; Visit file by default-app.
((eq system-type 'windows-nt)
(w32-shell-execute "open" "explorer" (poplife-convert-w32-filename file))) ; Visit file by default-app.
(t
(find-file-other-frame file))))
(provide 'poplife)
;;; poplife.el ends here
[-- Attachment #3: Type: text/plain, Size: 2 bytes --]
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 6:28 Context menus and mouse-3 Tak Kunihiro
@ 2020-09-16 14:18 ` Eli Zaretskii
2020-09-16 14:37 ` Thibaut Verron
2020-09-16 19:45 ` Juri Linkov
1 sibling, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2020-09-16 14:18 UTC (permalink / raw)
To: Tak Kunihiro; +Cc: juri, emacs-devel
> From: Tak Kunihiro <tkk@misasa.okayama-u.ac.jp>
> Date: Wed, 16 Sep 2020 15:28:56 +0900
> Cc: Juri Linkov <juri@linkov.net>
>
> - Horizontal scroll by wheel is supported.
> - Moving text using mouse is supported.
> * Contextual menu is not supported yet.
Yes, we do support contextual menus, just press C-mouse-3.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 14:18 ` Eli Zaretskii
@ 2020-09-16 14:37 ` Thibaut Verron
2020-09-16 15:06 ` Eli Zaretskii
2020-09-17 3:57 ` Richard Stallman
0 siblings, 2 replies; 59+ messages in thread
From: Thibaut Verron @ 2020-09-16 14:37 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Tak Kunihiro, emacs-devel, juri
Le mer. 16 sept. 2020 à 16:19, Eli Zaretskii <eliz@gnu.org> a écrit :
>
> > From: Tak Kunihiro <tkk@misasa.okayama-u.ac.jp>
> > Date: Wed, 16 Sep 2020 15:28:56 +0900
> > Cc: Juri Linkov <juri@linkov.net>
> >
> > - Horizontal scroll by wheel is supported.
> > - Moving text using mouse is supported.
> > * Contextual menu is not supported yet.
>
> Yes, we do support contextual menus, just press C-mouse-3.
It is only contextual if "context" just means "major mode".
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 14:37 ` Thibaut Verron
@ 2020-09-16 15:06 ` Eli Zaretskii
2020-09-16 15:39 ` Thibaut Verron
2020-09-17 3:57 ` Richard Stallman
1 sibling, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2020-09-16 15:06 UTC (permalink / raw)
To: thibaut.verron; +Cc: tkk, emacs-devel, juri
> From: Thibaut Verron <thibaut.verron@gmail.com>
> Date: Wed, 16 Sep 2020 16:37:16 +0200
> Cc: Tak Kunihiro <tkk@misasa.okayama-u.ac.jp>, juri@linkov.net,
> emacs-devel <emacs-devel@gnu.org>
>
> > > - Horizontal scroll by wheel is supported.
> > > - Moving text using mouse is supported.
> > > * Contextual menu is not supported yet.
> >
> > Yes, we do support contextual menus, just press C-mouse-3.
>
> It is only contextual if "context" just means "major mode".
That's the "context" that we decided was relevant. We can decide to
give it a different interpretation, but that doesn't mean we don't
support contextual menus.
Also, please don't forget that we have the most popular
context-dependent action for mouse-sensitive text on mouse-2.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 15:06 ` Eli Zaretskii
@ 2020-09-16 15:39 ` Thibaut Verron
0 siblings, 0 replies; 59+ messages in thread
From: Thibaut Verron @ 2020-09-16 15:39 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Tak Kunihiro, emacs-devel, juri
Le mer. 16 sept. 2020 à 17:06, Eli Zaretskii <eliz@gnu.org> a écrit :
>
> > From: Thibaut Verron <thibaut.verron@gmail.com>
> > Date: Wed, 16 Sep 2020 16:37:16 +0200
> > Cc: Tak Kunihiro <tkk@misasa.okayama-u.ac.jp>, juri@linkov.net,
> > emacs-devel <emacs-devel@gnu.org>
> >
> > > > - Horizontal scroll by wheel is supported.
> > > > - Moving text using mouse is supported.
> > > > * Contextual menu is not supported yet.
> > >
> > > Yes, we do support contextual menus, just press C-mouse-3.
> >
> > It is only contextual if "context" just means "major mode".
>
> That's the "context" that we decided was relevant. We can decide to
> give it a different interpretation, but that doesn't mean we don't
> support contextual menus.
We can go with that definition, but then we will have to accept that
it will be yet another point where the Emacs language differs from the
usual understanding. I don't suggest reforming the language to fix the
existing such points, but I believe that we should do our best to
avoid creating new ones.
It seems easy enough here, since the C-mouse-3 menu already has a
name: it is the major-mode menu.
By contrast, the suggested library mouse3.el does offer a contextual menu.
> Also, please don't forget that we have the most popular
> context-dependent action for mouse-sensitive text on mouse-2.
[And in the absence of a context, the same button does something
useful too, but with a confusing twist (the primary selection which is
neither the kill ring nor the system clipboard).]
I don't find mouse-2 easy to enter reliably, and I'd much rather see
those actions easy to reach in a mouse-3 menu.
Also, as pointed earlier in the thread, there are still mice with 2
buttons, for example laptop touchpads, and the buttons are typically
mouse-1 and mouse-3, making those actions inaccessible.
[It might be related to the fact that most applications and most users
call those buttons mouse-1 and mouse-2 respectively, mouse-3 being the
middle-click. Cf the aforementioned language divergence. :)]
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 6:28 Context menus and mouse-3 Tak Kunihiro
2020-09-16 14:18 ` Eli Zaretskii
@ 2020-09-16 19:45 ` Juri Linkov
2020-09-16 23:49 ` Tak Kunihiro
1 sibling, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2020-09-16 19:45 UTC (permalink / raw)
To: Tak Kunihiro; +Cc: emacs-devel
> I have an impression that relative to Emacs's capability, mouse support
> is not as good as expected.
>
> - Horizontal scroll by wheel is supported.
Please explain how horizontal scroll by wheel is supported.
I tried to use Shift-wheel like in other apps,
but it doesn't scroll horizontally.
> - Moving text using mouse is supported.
Also please explain how moving text using mouse is supported.
I tried to drag the selection with mouse-1,
but it doesn't move text.
> * Contextual menu is not supported yet.
>
> I think that depending on a thing at mouse event (file, dir, or URL),
> choice of operation should be popped up. When there is no suggestion,
> `Edit' menu should be popped up. Also, by click on mode-line, buffer
> list should be popped up.
>
> I am using a global minor mode `poplife-mode' that puts commands on
> mouse-3. I attach a file with poplife-mode to show the idea.
Thanks, this is a good starting point for adding contextual menu.
I tried poplife-mode, and it pops up the menu on mouse-3,
but it seems only when there is the selection already,
i.e. it doesn't pop up the menu when nothing is selected.
Is this intended to work this way?
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 19:45 ` Juri Linkov
@ 2020-09-16 23:49 ` Tak Kunihiro
2020-09-17 2:33 ` Tak Kunihiro
2020-09-17 7:43 ` Juri Linkov
0 siblings, 2 replies; 59+ messages in thread
From: Tak Kunihiro @ 2020-09-16 23:49 UTC (permalink / raw)
To: Juri Linkov; +Cc: 国広卓也, emacs-devel
>> - Horizontal scroll by wheel is supported.
>> - Moving text using mouse is supported.
> Please explain how horizontal scroll by wheel is supported.
> Also please explain how moving text using mouse is supported.
Try something like this.
(setq mouse-wheel-tilt-scroll t)
(setq mouse-drag-and-drop-region 'meta)
>> * Contextual menu is not supported yet.
>>
>> I think that depending on a thing at mouse event (file, dir, or URL),
>> choice of operation should be popped up. When there is no suggestion,
>> `Edit' menu should be popped up. Also, by click on mode-line, buffer
>> list should be popped up.
>>
>> I am using a global minor mode `poplife-mode' that puts commands on
>> mouse-3. I attach a file with poplife-mode to show the idea.
>
> Thanks, this is a good starting point for adding contextual menu.
> I tried poplife-mode, and it pops up the menu on mouse-3,
> but it seems only when there is the selection already,
> i.e. it doesn't pop up the menu when nothing is selected.
> Is this intended to work this way?
That’s not intended behavior!
I found that for unknown reason, poplife-mode does not overwrite
[mouse-3] started with emacs -Q. I’ll fix it and come back
soon while the topic is hot.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 23:49 ` Tak Kunihiro
@ 2020-09-17 2:33 ` Tak Kunihiro
2020-09-17 7:43 ` Juri Linkov
1 sibling, 0 replies; 59+ messages in thread
From: Tak Kunihiro @ 2020-09-17 2:33 UTC (permalink / raw)
To: Juri Linkov; +Cc: 国広卓也, emacs-devel
[-- Attachment #1: Type: text/plain, Size: 830 bytes --]
>>> * Contextual menu is not supported yet.
>>>
>>> I think that depending on a thing at mouse event (file, dir, or URL),
>>> choice of operation should be popped up. When there is no suggestion,
>>> `Edit' menu should be popped up. Also, by click on mode-line, buffer
>>> list should be popped up.
>>>
>>> I am using a global minor mode `poplife-mode' that puts commands on
>>> mouse-3. I attach a file with poplife-mode to show the idea.
>>
>> Thanks, this is a good starting point for adding contextual menu.
>> I tried poplife-mode, and it pops up the menu on mouse-3,
>> but it seems only when there is the selection already,
>> i.e. it doesn't pop up the menu when nothing is selected.
>> Is this intended to work this way?
I fixed it and confirm with Emacs -Q. Can you try again to see
the approach to start with?
[-- Attachment #2: poplife.el --]
[-- Type: application/octet-stream, Size: 62823 bytes --]
;;; poplife.el --- Pop choices up on mouse click
;; Copyright (C) 2017-2020 Tak Kunihiro
;; Author: Tak Kunihiro <tkk@misasa.okayama-u.ac.jp>
;; Package-Requires: ((emacs "24.4"))
;; Keywords: mouse
;; Version: 1.0
;; Package-Version: 20200917.1125
;; This file is NOT part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; Usage:
;;
;; To interactively toggle the mode:
;;
;; M-x poplife-mode RET
;;
;; To make the mode permanent, put these in your init file:
;;
;; (require 'poplife)
;; (poplife-mode 1)
;;; Commentary:
;;
;; This package pops context menu triggered by right click. On a
;; click, depending on a thing under the mouse event, (1) FILE menu
;; (2) DIR menu, (3) WORD menu, (4) URL menu, (5) INFO menu, (6) HELP
;; menu, or (7) EDIT menu will be popped. The EDIT menu lets you copy
;; and paste only using mouse. As an option, the EDIT menu lets you
;; visit buffers, frames, bookmarks, and files. The seven menus
;; are detailed as below.
;;
;; (1) FILE menu -- Pop how-to-open-a-file menu.
;; (2) DIR menu -- Pop files in default-directory.
;; (3) WORD menu -- Pop word candidates when word under a mouse event is not correct.
;; (4) URL menu -- Pop how-to-open-an-url menu.
;; (5) INFO menu -- Pop how-to-open-Info menu.
;; (6) HELP menu -- Pop how-to-open-Help menu.
;; (7) EDIT menu -- Pop basic edition-commands, optional edition-commands,
;; and visiting menus. Details are shown below.
;;
;; Basic edition-commands are defined by `poplife-mouse-edit-cmd-0'.
;; Optional edition-commands are defined by
;; `poplife-mouse-edit-cmd-1' with format similar to
;; `recentf-menu-items-for-commands'.
;;
;; DIR menu to visit files in default-directory is included by
;; default. To include a series of visiting menus in EDIT menu, set
;; each item of `poplife-mouse-edit-cottager' to non-nil, as listed
;; below.
;;
;; :buffer List buffers by `global-buffers-menu-map'.
;; :imenu List table of contents of current buffer by iMenu.
;; :frame List frames by `global-buffers-menu-map'.
;; :bookmark List bookmarks by `bookmark-all-names'.
;; :recentf List recent files by `recentf-menu-elements'.
;;
;; To reduce overhead, FILE menu is not shown when file-remote-p is
;; non-nil. To reduce overhead by a remote file that was once opened
;; by Tramp and stored in the list for recentf, configure a variable
;; `recentf-exclude'.
;;; References:
;; * Contextual menu
;; https://lists.gnu.org/archive/html/emacs-devel/2020-09/msg01277.html
;;
;; * Paste text with erasing active region.
;;
;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2017-07/msg00086.html
;; http://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00796.html
;;
;; * Pop menu up by long-click
;;
;; https://lists.gnu.org/archive/html/emacs-devel/2017-11/msg00267.html
;;
;; * Following codes from Emacs core are useful for development.
;;
;; (define-key global-map [mouse-3] menu-bar-edit-menu)
;; (popup-menu menu-bar-edit-menu)
;; (popup-menu (mouse-menu-bar-map))
;; (popup-menu (mouse-menu-major-mode-map)) ; C-mouse-3
;; (popup-menu menu-bar-bookmark-map)
;; (popup-menu global-buffers-menu-map)
;; (define-key global-map [mouse-3] 'mouse-buffer-menu)
;; (buffer-menu-open)
;;; Templates of keymap:
;;
;; This package integrates keymaps from imenu, menu-bar, bookmark, and
;; recentf. They are converted to easymenu from standard keymap.
;; Typical easymenu and standard keymap are shown below for
;; convenience.
;;
;; (label
;; [label callback]
;; (label
;; [label callback]
;; ["--" ignore]
;; [label callback])
;; [label callback])
;;
;; (keymap
;; label
;; (symbol menu-item label callback)
;; [(label lambda nil (interactive) commands)
;; (label keymap
;; (symbol menu-item label callback)
;; (symbol menu-item label callback))
;; (label lambda nil (interactive) commands)]
;; (symbol "--")
;; (symbol menu-item label
;; (keymap label
;; (symbol menu-item label callback)
;; (symbol menu-item label callback)))
;; (symbol menu-item label callback))
;;; Code:
(defvar poplife-context-candidates
'(poplife-mouse-help-menu ; HELP menu
poplife-mouse-info-menu ; INFO menu
poplife-mouse-file-menu ; FILE menu
poplife-mouse-dir-menu ; DIR menu
poplife-mouse-word-menu ; WORD menu
poplife-mouse-url-menu ; URL menu
;; menu-bar-edit-menu ; EDIT menu (default)
poplife-mouse-edit-menu) ; EDIT menu
"List of candidates for context menu.
Candidates are function or keymap. They will be evaluated in the
order of the list. A function should accept mouse EVENT, and
return keymap or nil. The last candidate should return valid
keymap.")
(defvar poplife-mouse-edit-cmd-0 '(cut copy paste select-paste paste-from-menu clear mark-whole-buffer)
"Basic edition-commands in edit menu.
Items must be one listed in `menu-bar-edit-menu'.")
(defvar poplife-mouse-edit-cmd-1
(list
["Close"
(lambda () (interactive)
(if (one-window-p)
(if (> (length (visible-frame-list)) 1)
(call-interactively 'delete-frame)
(kill-buffer (window-buffer))) ; (quit-window)
(delete-window)))
:help "Remove this window or this frame, or kill this buffer"
:visible (not (region-active-p))
:active t]
;; ["Spell-Check"
;; flyspell-correct-word-before-point
;; :help "Spell check word at point"
;; :visible (and (fboundp 'flyspell-correct-word-before-point)
;; (not (region-active-p)))
;; :active t]
["Spell-Check"
ispell-region
:help "Spell check selected text"
:visible (region-active-p)
:active t]
;; ["Search Web"
;; (lambda () (interactive)
;; (let ((keyword (buffer-substring-no-properties (region-beginning) (region-end))))
;; (switch-to-buffer-other-window (generate-new-buffer "*eww*"))
;; (eww-mode)
;; (eww keyword)))
;; :help "Search selected text by online service"
;; :visible (region-active-p)
;; :active t]
["--"
ignore
:visible (region-active-p)
:active t])
"List of optional commands in edit map.") ; recentf-menu-items-for-commands
(defvar poplife-mouse-edit-cottager
'(:imenu t :buffer t :frame t :bookmark t :recentf t)
"Extra menus to be included in edit menu besides file.")
\f
(defvar poplife-file-max-menu-items 20
"Maximum number of items in DIR menu.
See also `recentf-max-menu-items', `buffers-menu-max-size', and
`imenu-max-items'.")
(defvar poplife-file-recursive 1
"Depth of directory scan on DIR menu.")
(defvar poplife-dir-do-not-scan-regexp "inbox"
"Contents of directory matching this regexp will not be shown in DIR menu.
Instead contents of home directory are displayed.")
(defvar poplife-file-do-not-show-regexp
"\\`\\.\\.?$\\|\\`#\\|\\.elc\\'\\|\\.exe\\'\\|\\`\\.[^e]\\|\\.lnk\\'\\|\\~\\'\\|\\`desktop\\.ini\\'\\|\\`\\.DS_store\\'\\|\\`\\.dropbox\\'\\|\\`auto\\'\\|\\`ntuser\\|\\`_master_\\|\\`_region_\\|\\.aux\\'\\|\\.bbl\\'\\|\\.blg\\'\\|\\.fdb_latexmk\\'\\|\\.fls\\'\\|\\.lof\\'\\|\\.lot\\'\\|\\.out\\'\\|\\.toc\\'\\|\\.synctex\\'\\|\\.ico\\'\\|\\`Thumbs\\.db\\'\\|\\`Icon"
"Filenames matching this regexp will not be displayed in DIR menu.") ; dired-trivial-filenames, dired-omit-files
(defvar poplife-file-do-not-open-regexp
"\\.pdf\\'\\|\\.doc\\'\\|\\.docx\\'\\|\\.xls\\'\\|\\.xlsx\\'\\|\\.ppt\\'\\|\\.pptx\\'\\|\\.jpg\\'\\|\\.png\\'\\|\\.tif\\'\\|\\.tiff\\'\\|\\.bmp\\'\\|\\.aif\\'\\|\\.wav\\'\\|\\.7z\\'\\|\\.tar\\'\\|\\.dll\\'\\|\\.zip\\'\\|\\.info\\'\\|\\.igpi\\'\\|\\.ttf\\'\\|\\.otf\\'\\|\\.pkg\\'\\|\\.exe\\'"
"Filenames matching this regexp will be displayed in DIR menu and open by `poplife-func-find-file-by-default-app'.")
(defvar poplife-func-find-file 'find-file
"Function to visit a file, and a Recentf element.
Depending on context, this is internally overwritten by
`find-file', `find-file-other-window', and
`find-file-other-frame'. This is referred to visit a buffer, an
imenu element, and a bookmark element via
`poplife-func-switch-to-buffer'. A buffer is visited by
`menu-bar-select-buffer-function' on `global-buffers-menu-map'.
An imenu element is always visited on current buffer. A bookmark
element is visited by `display-func' on `bookmark-jump'.")
(defvar poplife-func-find-file-by-default-app 'poplife-find-file-by-default-app
"Function to visit file by default application.")
\f
(require 'ffap)
(require 'easymenu)
(require 'info)
;;;###autoload
(define-minor-mode poplife-mode
"A global minor-mode to show context menu by right click."
:init-value nil
:group 'mouse
:global t
:keymap (let ((map (make-sparse-keymap))
(context-menu
`(menu-item "Context menu" poplife-context-menu
:filter ,(lambda (_) (poplife-context-menu (aref (this-command-keys) 0))))))
;; bug#27569 (gnus-read-ephemeral-emacs-bug-group 27569)
;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2017-07/msg00086.html
;; https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00757.html
;; https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00796.html
;; https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00840.html
(define-key map [mouse-3] context-menu)
(define-key map [drag-mouse-3] context-menu)
(define-key map [C-down-mouse-1] #'ignore)
(define-key map [C-mouse-1] context-menu)
(define-key map [C-double-mouse-1] context-menu)
(define-key map [C-triple-mouse-1] context-menu)
(define-key map [C-drag-mouse-1] context-menu)
(define-key map [remap buffer-menu-open] #'poplife-menu-open)
;; (define-key map [C-S-down-mouse-1] 'mouse-buffer-menu)
;; (define-key map [C-M-mouse-1] 'poplife-what-mouse-position)
;; (define-key mode-line-buffer-identification-keymap [mode-line mouse-3] #'poplife-pwd-menu-open)
;; (define-key mode-line-buffer-identification-keymap [mode-line mouse-1] #'poplife-buffer-menu-open)
(define-key map [remap mode-line-previous-buffer] #'poplife-buffer-menu-open) ; mouse-1
;; (define-key map [remap mode-line-previous-buffer] #'poplife-global-mark-ring-menu-open) ; mouse-1
(define-key map [remap mode-line-next-buffer] #'poplife-pwd-menu-open) ; mouse-3
(define-key map [mode-line C-mouse-1] #'poplife-pwd-menu-open)
(define-key map [mode-line M-mouse-1] #'poplife-pwd-menu-open) ; as if Mac
map))
;; * How to implement into core
;;
;; https://lists.gnu.org/archive/html/emacs-devel/2017-11/msg00416.html
;; (defvar mouse-context-menu-function #'mouse-default-context-menu
;; "Function that builds the context-menu.
;; Takes one argument (the EVENT that requests the menu) and should return
;; a list of menu items.")
;; (defun mouse-default-context-menu (event)
;; "Return default context menu."
;; (interactive "e")
;; menu-bar-edit-menu)
;; (defun mouse-context-menu (event)
;; "Open up the context menu."
;; (interactive "@e")
;; (let* ((menu-items (funcall mouse-context-menu-function event))
;; (keymap `(keymap ,(apply #'vector menu-items))))
;; (popup-menu keymap event)))
;; (define-key global-map [mouse-3] 'mouse-context-menu)
(defun poplife-menu-open ()
"Start key navigation of the poplife menu.
This is the keyboard interface to \\[poplife-context-menu]. This is
fork of `buffer-menu-open'."
(interactive)
(popup-menu (poplife-context-menu last-nonmenu-event)
(posn-at-x-y 0 0 nil t)))
(defun poplife-context-menu (event)
"Return key's definition depending on thing at mouse click EVENT.
Items in `poplife-context-candidates' are examined sequentially.
See `define-key' for the key's definition"
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
(when (fboundp 'secondary-selection-to-region) ; 26.1
(secondary-selection-to-region)) ; When there is only secondary, turn it to region.
(let ((candidates poplife-context-candidates)
context-menu)
(while (not context-menu)
(let ((item (car candidates)))
(setq candidates (cdr candidates))
;; See how dired-guess-shell-alist-user is used in dired-guess-default.
(setq context-menu (cond ((fboundp item)
(funcall item event))
((and (symbolp item)
(keymapp (symbol-value item)))
(symbol-value item))
(t ; else
nil)))))
context-menu))
;; (let ((foo (poplife-context-menu last-nonmenu-event))) (describe-variable 'foo))
(defun poplife-what-mouse-position (event)
"Evaluate text properties under mouse click."
(interactive "e")
(with-output-to-temp-buffer "*Result*"
(princ (format "Event was %S\n" event))
(princ (format "Click was on face <%S>.\n"
(mouse-posn-property (event-start event) 'face)))
(princ (format "Click was on dired-filename <%S>.\n"
(mouse-posn-property (event-start event) 'dired-filename)))))
(with-eval-after-load "help-mode"
(button-type-put 'help-function-def 'help-function 'poplife-help-find-function))
(defvar poplife-help-switch-buffer-function 'pop-to-buffer
"Function to display buffer in help-mode.
This can be `switch-to-buffer', `switch-to-buffer-other-window',
or `switch-to-buffer-other-frame'.")
(defun poplife-help-find-function (fun &optional file type)
"Find object shown in help-mode."
;; This is fork of lambda function of 'help-function, that is
;; defined for a button type 'help-function-def in `help-mode.el'.
(or file
(setq file (find-lisp-object-file-name fun type)))
(if (not file)
(message "Unable to find defining file")
(require 'find-func)
(when (eq file 'C-source)
(setq file
(help-C-file-name (indirect-function fun) 'fun)))
;; Don't use find-function-noselect because it follows
;; aliases (which fails for built-in functions).
(let ((location
(find-function-search-for-symbol fun type file)))
;; (pop-to-buffer (car location))
(funcall poplife-help-switch-buffer-function (car location)) ; Revised for poplife
(run-hooks 'find-function-after-hook)
(if (cdr location)
(goto-char (cdr location))
(message "Unable to find location in file")))))
(defun poplife-mouse-help-menu (event)
"Return help-menu when thing at mouse click EVENT is button
with button type of 'help-function-def."
(and
(not (region-active-p))
(let ((help-easymap
(save-excursion
(mouse-set-point event)
;; (text-properties-at (point))
;; '(push-button "/Applications/MacPorts/Emacs.app/Contents/Resources/lisp/button.el")
;; (push-button (point))
;; (help-button-action (button-at (point)))
;; (help-do-xref nil
;; (button-get (button-at (point)) 'help-function)
;; (button-get (button-at (point)) 'help-args))
;; (button-type (button-at (point)))
(let ((button (button-at (point))))
(when (and button
(eq (button-type button) 'help-function-def))
(let* ((button-func (button-get button 'help-function))
(button-arg (button-get button 'help-args))
(message (replace-regexp-in-string "[()\"]" "" (format "%S" button-arg) t t)))
(list
message
(vector "Open Function"
`(let* ((poplife-func-find-file 'find-file) ; switch-to-buffer
(poplife-help-switch-buffer-function (poplife-func-switch-to-buffer)))
(help-button-action ,button))
:visible t :active t :help message)
["--" ignore]
(vector "Open Function in Other Window"
`(let* ((poplife-func-find-file 'find-file-other-window) ; switch-to-buffer-other-window
(poplife-help-switch-buffer-function (poplife-func-switch-to-buffer)))
(help-button-action ,button))
:visible t :active t :help message)
(vector "Open Function in Frame"
`(let* ((poplife-func-find-file 'find-file-other-frame) ; switch-to-buffer-other-frame
(poplife-help-switch-buffer-function (poplife-func-switch-to-buffer)))
(help-button-action ,button))
:visible t :active t :help message))))))))
(when help-easymap
(easy-menu-create-menu (car help-easymap) (cdr help-easymap))))))
;; (poplife-mouse-help-menu last-nonmenu-event)
(defun poplife-info-node-at-point ()
"Return a node reference at point.
Return non-nil if successful. This is fork of
`Info-try-follow-nearest-node'."
(let (file-or-node)
(cond
((setq file-or-node (Info-get-token (point) "[hf]t?tps?://"
"\\([hf]t?tps?://[^ \t\n\"`‘({<>})’']+\\)"))
;; (browse-url file-or-node)
(setq file-or-node nil))
((setq file-or-node (Info-get-token (point) "\\*note[ \n\t]+"
"\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))) ; (system)Data format IONML
;; footnote
((setq file-or-node (Info-get-token (point) "(" "\\(([0-9]+)\\)"))
(setq file-or-node nil))
;; menu item: node name
((setq file-or-node (Info-get-token (point) "\\* +" "\\* +\\([^:]*\\)::"))) ; What is DREAM?
;; menu item: node name or index entry
((Info-get-token (point) "\\* +" "\\* +\\(.*\\): ") ; FAQ
(save-excursion
(beginning-of-line)
(forward-char 2)
(setq file-or-node (Info-extract-menu-node-name nil (Info-index-node))))) ; (pmlfaq)
((setq file-or-node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)")) ; pmlfaq.info
(when (string-match "\\.info\\'" file-or-node)
(string-match "\\`\\(.+\\)\\.info\\'" file-or-node) ; pmlfaq
(setq file-or-node (format "(%s) Top" (match-string 1 file-or-node)))))) ; (pmlfaq) Top
(when (and file-or-node
(stringp Info-current-file)
(not (string-match "\\`(.*)" file-or-node)))
(setq file-or-node
(format "(%s) %s"
(file-name-sans-extension
(file-name-nondirectory Info-current-file))
file-or-node))) ; see Info-copy-current-node-name
file-or-node))
(defun poplife-mouse-info-menu (event)
"Return info-menu when thing at mouse click EVENT is link."
;; see Info-try-follow-nearest-node
(and
(not (region-active-p))
(let ((info-easymap
(save-excursion
(mouse-set-point event)
(let ((file-or-node (poplife-info-node-at-point)))
(when file-or-node
(list
file-or-node
(vector "Open Info"
;; `(info ,file-or-node)
`(info-setup
,file-or-node
(switch-to-buffer (format "*info-%s*" ,file-or-node)))
:visible t :active t :help file-or-node)
["--" ignore]
(vector "Copy Info"
`(progn (kill-new ,file-or-node) (message ,file-or-node))
:visible t :active t :help file-or-node)
(vector "Open Info in Other Window"
`(info-setup
,file-or-node
(switch-to-buffer-other-window (format "*info-%s*" ,file-or-node)))
:visible t :active t :help file-or-node)
(vector "Open Info in Frame"
`(info-setup
,file-or-node
(switch-to-buffer-other-frame (format "*info-%s*" ,file-or-node)))
:visible t :active t :help file-or-node)))))))
(when info-easymap
(easy-menu-create-menu (car info-easymap) (cdr info-easymap))))))
;; (let ((foo (poplife-mouse-info-menu last-nonmenu-event))) (describe-variable 'foo))
;; (popup-menu (poplife-mouse-info-menu last-nonmenu-event))
(defun poplife-mouse-file-menu (event)
"Return file-menu when thing at mouse click EVENT is file.
The file is identified by `ffap-guesser'."
(and
(not (region-active-p))
(let ((file-easymap
(save-excursion
(mouse-set-point event)
(if (equal major-mode 'dired-mode)
(and
(mouse-posn-property (event-start event) 'dired-filename)
(poplife-file-easymap (dired-get-file-for-visit) t))
(let* ((ffap-url-regexp nil) (file (ffap-guesser))) ; ffap-at-mouse
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
;; ffap-guesser cannot guess file with asterisk such as "bookmark.html*"
(when (and file
(not (ffap-file-remote-p file)))
(poplife-file-easymap file t)))))))
(when file-easymap
(easy-menu-create-menu (car file-easymap) (cdr file-easymap))))))
(defun poplife-mouse-dir-menu (event)
"Return dir-menu when thing under mouse cursor on EVENT is directory.
The directory is identified by `ffap-guesser'."
(and
(not (region-active-p))
(if (equal major-mode 'dired-mode)
(mouse-posn-property (event-start event) 'dired-filename)
t)
(let ((dir-easymap (save-excursion
(mouse-set-point event)
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
(let* ((ffap-url-regexp nil) (dir (ffap-guesser)))
(when (and dir
(not (ffap-file-remote-p dir)))
(poplife-dir-easymap (file-name-as-directory dir) t))))))
(when dir-easymap
(easy-menu-create-menu (car dir-easymap) (cdr dir-easymap))))))
(defun poplife-mouse-word-menu (event)
"Return 'flyspell-correct-word when word under mouse cursor on EVENT is incorrect."
(and
(not (region-active-p))
;; Check face by (what-cursor-position t) or C-u C-x =.
(let ((faces-at-point (mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at (posn-point (event-start event))))))
(when (or (member 'flyspell-incorrect faces-at-point)
(member 'flyspell-duplicate faces-at-point))
#'flyspell-correct-word)))) ; flyspell-correct-word-before-point
(defun poplife-mouse-url-menu (event)
"Return url-menu when thing under mouse cursor on EVENT is url.
The url is identified by `thing-at-point-url-at-point'."
(and
(not (region-active-p))
(let ((url-easymap
(save-excursion
(mouse-set-point event)
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
(let ((url (or (thing-at-point-url-at-point t) ; browse-url-at-mouse
(get-text-property (point) 'shr-url)
(get-text-property (point) 'image-url))))
(when url
(list (let ((url (replace-regexp-in-string "https?://" "" url))
(len 40)) ; Make URL short
(if (> (length url) len)
(concat (substring url 0 (1- len)) "...")
url))
(vector "Open Link"
`(eww ,url)
:visible t :active t :help url)
["--" ignore]
(vector "Copy Link"
`(progn (kill-new ,url) (message "Copied %s" ,url))
:visible t :active t :help url)
(vector "Open Link in Other Window"
;; eww-browse-url, eww-open-in-new-buffer
`(progn
(switch-to-buffer-other-window (generate-new-buffer "*eww*"))
(eww-mode)
(eww ,url))
:visible t :active t :help url)
(vector "Open Link in Frame" ; "Open in Frame"
`(progn
(switch-to-buffer-other-frame (generate-new-buffer "*eww*"))
(eww-mode)
(eww ,url))
:visible t :active t :help url)
(vector "Open Link by Default App" ; "Open Link using browse-url"
`(let ((browse-url-browser-function 'browse-url-default-browser))
(browse-url ,url))
:visible t
:active t
:help url)))))))
(when url-easymap
(easy-menu-create-menu (car url-easymap) (cdr url-easymap))))))
(defun poplife-mouse-edit-menu (event)
"Define edit menu on mouse click EVENT."
;; initialize
(when (plist-get poplife-mouse-edit-cottager :bookmark)
(require 'bookmark))
(when (plist-get poplife-mouse-edit-cottager :recentf)
(require 'recentf)
(recentf-mode 1))
(when (plist-get poplife-mouse-edit-cottager :imenu)
(require 'imenu))
(save-excursion
(mouse-set-point event)
(let ((map (make-sparse-keymap "Edit")))
(unless (region-active-p)
;; Visit buffers with iMenu
(when (plist-get poplife-mouse-edit-cottager :buffer)
(easy-menu-add-item map nil (poplife-buffer-easymap)))
;; Visit frames
(when (plist-get poplife-mouse-edit-cottager :frame)
(easy-menu-add-item map nil (poplife-frame-easymap)))
;; Visit bookmarks
(when (plist-get poplife-mouse-edit-cottager :bookmark)
(easy-menu-add-item map nil (poplife-bookmark-easymap)))
;; Visit recent files
(when (plist-get poplife-mouse-edit-cottager :recentf)
(easy-menu-add-item map nil (poplife-recentf-easymap)))
;; Visit directory
(unless (file-remote-p default-directory)
(let ((dir-map (poplife-dir-easymap default-directory)))
(setcar dir-map "File") ; instead of ".emacs.d/"
(easy-menu-add-item map nil dir-map)))
;; Separator
(define-key map [separator-edit] menu-bar-separator))
;; Option -- TODO: Fix location of item with recursive structure.
(when poplife-mouse-edit-cmd-1
(dolist (item poplife-mouse-edit-cmd-1)
(if (vectorp item)
(let* ((item (append item nil)) ; Convert vector to list.
(nickname (car item)))
(bindings--define-key map (vector (easy-menu-make-symbol nickname))
(append (list 'menu-item nickname) (cdr item))))
(easy-menu-add-item map nil item)))) ; with recursive structure
;; Main
(dolist (item (reverse (cdr menu-bar-edit-menu)))
(when (and (listp item)
(member (car item) poplife-mouse-edit-cmd-0)) ; pick some
(bindings--define-key map (vector (car item)) (cdr item))))
map)))
;; (let ((foo (poplife-mouse-edit-menu last-nonmenu-event))) (describe-variable 'foo))
\f
;;; iMenu
(defun poplife-imenu-easymap (&optional submenu)
"Define easymenu to list index by iMenu.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
;; TODO: Selection of menu does not move point when called from
;; click on mode-line.
(let* ((imenu-max-items poplife-file-max-menu-items) ; 25
(map-0 (ignore-errors
(imenu--split-menu
(delq nil (cdr (imenu--make-index-alist t))) ; remove "*Rescan*"
(buffer-name))))
(map (poplife-imenu-alist-to-easymap (car map-0) (cdr map-0)
'imenu--menubar-select)))
(when (>= (length map) 2) ; Return map only when map is with useful items.
(if submenu
(poplife-imenu-submenufy-easymap map)
(let ((map-rev (reverse map)))
(push (vector "More..."
'(popup-menu (poplife-imenu-easymap t)
(popup-menu-normalize-position last-nonmenu-event)))
map-rev)
(reverse map-rev))))))
;; (let ((foo (poplife-imenu-easymap))) (describe-variable 'foo))
;; (let* ((poplife-func-find-file 'find-file-other-frame) (foo (poplife-imenu-easymap))) (describe-variable 'foo))
;; (let ((foo (poplife-imenu-easymap t))) (describe-variable 'foo))
;; (popup-menu (poplife-imenu-easymap))
(defun poplife-imenu-alist-to-easymap (title alist &optional cmd)
"Create easymenu from alist by iMenu to display index by CMD.
This is fork of `imenu--create-keymap'."
(let (map)
(dolist (item alist)
(push (cond
((imenu--subalist-p item)
(poplife-imenu-alist-to-easymap (car item) (cdr item) cmd))
(t
(if cmd
(vector (car item)
(list 'let
'((display-buffer--other-frame-action ; hack for switch-to-buffer-other-frame
'((display-buffer-pop-up-frame)
(inhibit-same-window . t))))
`(funcall (quote ,(poplife-func-switch-to-buffer)) ,(current-buffer))
`(,cmd (quote ,item))))
(list 'quote item))))
map))
(setq map (reverse map))
(push title map)
map))
(defun poplife-imenu-submenufy-easymap (map)
"Return easymenu of iMenu MAP with recursive structure."
(let (map-1)
(dolist (item map)
(push (cond
((listp item) ; when an item is a list
(poplife-imenu-submenufy-easymap item))
((vectorp item) ; when an item is vector
(poplife-imenu-elt-easymap item))
(t item)) ; else such for "poplife.el"
map-1))
(reverse map-1)))
(defun poplife-imenu-elt-easymap (elt)
"Return easymenu of iMenu ELT with submenu added."
(let ((label (aref elt 0)) ; "poplife-mouse-edit-cottager"
(cmd (nth 3 (aref elt 1))) ; (imenu--menubar-select '("poplife-mouse-edit-cottager" . #<marker at 3207 in poplife.el>))
(buf (current-buffer))) ; #<buffer poplife.el>
(list label
(vector "Open" `(progn (switch-to-buffer ,buf) ,cmd))
["--" ignore]
(vector "Open in Other Window" `(progn (switch-to-buffer-other-window ,buf) ,cmd))
(vector "Open in Frame" `(progn (pop-to-buffer ,buf '((display-buffer-pop-up-frame) (inhibit-same-window . t))) ,cmd)))))
\f
;;; Frame
(defun poplife-frame-easymap ()
"Define easymenu to list frames."
(let ((frame-vec (nth 2 (cadddr (assoc 'frames global-buffers-menu-map))))
(poplife-func-find-file 'find-file-other-frame)
map)
(dolist (elt (append frame-vec nil)) ; Convert vector to list.
(let* ((nickname (car elt))
(cmd (nth 4 elt))
(frame (cadr cmd)))
(push (vector nickname cmd :active (not (equal frame (selected-frame)))) map)))
(push ["New" (progn (make-frame-command) (menu-find-file-existing))] map)
(push ["--" ignore] map)
(when (plist-get poplife-mouse-edit-cottager :buffer)
(push (poplife-buffer-easymap) map))
(when (plist-get poplife-mouse-edit-cottager :bookmark)
(push (poplife-bookmark-easymap) map))
(when (plist-get poplife-mouse-edit-cottager :recentf)
(push (poplife-recentf-easymap) map))
(unless (file-remote-p default-directory)
(let ((dir-map (poplife-dir-easymap default-directory)))
(setcar dir-map "File") ; "Directory"
(push dir-map map)))
(setq map (reverse map))
(push "Frames" map)
map))
;; (let ((foo (poplife-frame-easymap))) (describe-variable 'foo))
\f
;;; Buffer
(defun poplife-buffer-easymap (&optional submenu)
"Define easymenu to list buffers.
This extracts list of buffers from `global-buffers-menu-map'.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
(let ((buffer-list (poplife-buffer-list))
;; (poplife-func-find-file 'find-file) ; 20190128.1647
(menu-bar-select-buffer-function (poplife-func-switch-to-buffer))
map)
;; Add submenu on request.
(dolist (elt buffer-list)
(push (poplife-buffer-elt-easymap elt submenu) map))
;; Add option.
(push (vector "More..."
'(let (buffer-full-map)
(let (buffers-menu-max-size)
(menu-bar-update-buffers t)
(setq buffer-full-map
(poplife-buffer-easymap t)))
(menu-bar-update-buffers t)
(popup-menu buffer-full-map
(popup-menu-normalize-position last-nonmenu-event)))
:visible t
:active (not submenu))
map)
;; Reverse map and add a key.
(setq map (reverse map))
(push "Buffers" map)
map))
;; (let ((foo (poplife-buffer-easymap))) (describe-variable 'foo))
;; (let ((foo (poplife-buffer-easymap t))) (describe-variable 'foo))
(defun poplife-buffer-list-on-menu ()
"Return a list of buffers on `global-buffers-menu-map'."
;; on 25.2, pick 4th out of 5 items
;; ("menu-bar.el.gz " lambda nil (interactive) (funcall menu-bar-select-buffer-function #<buffer menu-bar.el.gz>))
;; on 24.5, pick 5th out of 6 items
;; ("menu-bar.el.gz " (nil) lambda nil (interactive) (funcall menu-bar-select-buffer-function #<buffer menu-bar.el.gz>))
(let ((len245 6)
(nth252 4)
(nth245 5)
(buffers-menu (nth 2 global-buffers-menu-map))
;; nickname-list
buffer-list)
(dolist (elt (append buffers-menu nil)) ; Convert vector to list.
;; (push (car elt) nickname-list)
(push (nth 2 (nth (if (equal (length elt) len245)
nth245
nth252)
elt)) buffer-list))
(reverse buffer-list)))
;; (let ((foo (poplife-buffer-list-on-menu))) (describe-variable 'foo))
;; (let ((foo (buffer-list))) (describe-variable 'foo))
(defun poplife-buffer-list ()
"Return a list of buffers."
(delete-dups (append (list (current-buffer))
(poplife-buffer-list-with-marks)
(poplife-buffer-list-on-menu))))
;; (let ((foo (poplife-buffer-list))) (describe-variable 'foo))
(defun poplife-buffer-list-with-marks ()
"Return a list of buffers on `global-mark-ring'."
(let (buffer-list buf)
(dolist (marker (reverse global-mark-ring))
(when (setq buf (marker-buffer marker)) ; See `pop-global-mark'
(push buf buffer-list)))
buffer-list))
;; (let ((foo (poplife-buffer-list-with-marks))) (describe-variable 'foo))
(defun poplife-buffer-elt-easymap (buffer &optional submenu)
"Define easymenu for a BUFFER.
When SUBMENU is non-nil, this returns an easymenu with multiple
actions."
(let ((nickname (buffer-name buffer)))
(if (not submenu)
(let (imenu-map)
(if (and (plist-get poplife-mouse-edit-cottager :imenu)
(equal (current-buffer) buffer)
(setq imenu-map (poplife-imenu-easymap)))
imenu-map
(vector nickname `(funcall (quote ,menu-bar-select-buffer-function) ,buffer)
:active `(not (equal ,(current-buffer) ,buffer)))))
(list nickname
(vector "Open" `(switch-to-buffer ,buffer) :active `(not (equal ,(current-buffer) ,buffer)))
["--" ignore]
(vector "Open in Other Window" `(switch-to-buffer-other-window ,buffer))
(vector "Open in Frame" `(switch-to-buffer-other-frame ,buffer))))))
\f
;;; Recentf
(defun poplife-recentf-easymap (&optional submenu)
"Define easymenu to list recentf.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
(let (map
(recentf-menu-shortcuts 0)
(elements (recentf-menu-elements recentf-max-menu-items)))
;; See `recentf-make-menu-items'.
(setq map (mapcar (lambda (elt)
(funcall 'poplife-recentf-elt-easymap elt submenu))
(recentf-apply-menu-filter
recentf-menu-filter
elements)))
(let ((map-rev (reverse map)))
(push (vector "More..."
'(let ((recentf-max-menu-items recentf-max-saved-items))
(popup-menu (poplife-recentf-easymap t)
(popup-menu-normalize-position last-nonmenu-event)))
:help "Show more Recentf"
:visible t
:active (not submenu))
map-rev)
(push ["--" ignore] map-rev)
(push ["Edit..."
recentf-edit-list
:help "Manually remove files from the recent list"
:active t]
map-rev)
;; (push ["Save List Now"
;; recentf-save-list
;; :help "Save the list of recently opened files now"
;; :active t]
;; map-rev)
(setq map (reverse map-rev)))
(cons "Recent" map)))
;; (let ((foo (poplife-recentf-easymap))) (describe-variable 'foo))
(defun poplife-recentf-elt-easymap (elt &optional submenu)
"Define easymenu to popup recentf item ELT.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
;; This is fork of `recentf-make-menu-item'.
(let ((name (recentf-menu-element-item elt))
(file (recentf-menu-element-value elt)))
(if (recentf-sub-menu-element-p elt)
(cons name (mapcar (lambda (elt) (funcall 'poplife-recentf-elt-easymap elt)) file)) ; for `recentf-arrange-by-dir'
;; (vector name
;; `(,recentf-menu-action ,file) ; poplife-func-find-file
;; :help (concat "Open " file)
;; :active t)
(let ((map
(if (file-directory-p file)
(poplife-dir-easymap file submenu) ; directory
(poplife-file-easymap file submenu)))) ; file
(when (listp map)
(setcar map name))
map))))
\f
;;; Bookmark
(defun poplife-bookmark-easymap (&optional submenu)
"Define easymenu to list bookmarks.
When SUBMENU is non-nil, this returns an easymenu with multiple actions."
;; TODO: On emacs-27.1 with smb, I see "tramp-error: Method ‘smb’ is
;; not known".
(let ((map
(list
(vector "More..." '(popup-menu (poplife-bookmark-easymap t)
(popup-menu-normalize-position last-nonmenu-event))
:visible t
:active (not submenu)
:help "Set a bookmark named inside a file.")
["--" ignore]
["Add..." bookmark-set :visible t :active (or (buffer-file-name) (eq major-mode 'dired-mode))
:help "Set a bookmark named inside a file."]
["Edit..." bookmark-bmenu-list :visible t :active t
:help "Display a list of existing bookmarks"]
;; ["Save List Now" bookmark-save :visible t :active t
;; :help "Save currently defined bookmarks"]
)))
(dolist (bookmark (bookmark-all-names))
(push (poplife-bookmark-elt-easymap bookmark submenu) map))
(cons "Bookmark" map)))
;; (let ((foo (poplife-bookmark-easymap))) (describe-variable 'foo))
;; (let ((foo (poplife-bookmark-easymap t))) (describe-variable 'foo))
(defun poplife-bookmark-elt-easymap (bookmark &optional submenu)
"Define easymenu to list a BOOKMARK.
When SUBMENU it non-nil, this returns an easymenu with multiple actions."
(let (map)
(if (not submenu)
(let ((file (bookmark-get-filename bookmark)))
(if (and (not (file-remote-p file)) ; when bookmark is directory
(file-exists-p file)
(file-directory-p file))
(poplife-dir-easymap file submenu) ; offer DIR menu
(vector bookmark
;; `(bookmark-jump ,bookmark DISPLAY-FUNC)
`(bookmark-jump ,bookmark (quote ,(poplife-func-switch-to-buffer))) ; switch-to-buffer
:visible t
:active (not (string= ; gray the vising file out
(and (buffer-file-name) (expand-file-name (buffer-file-name)))
(expand-file-name (bookmark-get-filename bookmark))))
:help (format "Jump to %s" bookmark))))
;; (push (vector "Show Annotation..." `(bookmark-show-annotation ,bookmark) :visible t :active `(bookmark-get-annotation ,bookmark) :help bookmark) map)
;; (push (vector "Edit Annotation..." `(bookmark-edit-annotation ,bookmark) :visible t :help bookmark) map)
(let ((annot-map (poplife-bookmark-annotation-easymap bookmark)))
(if (vectorp annot-map) ; with no annotation and with "Add Annotation..."
(push annot-map map)
(dolist (annot-item (reverse (cdr annot-map)))
(push annot-item map))
(push ["--" ignore] map)
(push (vector "Edit Annotation..." `(bookmark-edit-annotation ,bookmark) :visible t :help bookmark) map)))
(push (vector "Delete..." `(and (y-or-n-p (format "Are you sure you want to delete a bookmark %s? " ,bookmark)) (bookmark-delete ,bookmark)) :visible t :help bookmark) map)
(push (vector "Edit Location..." `(bookmark-relocate ,bookmark) :visible t :help bookmark) map)
(push (vector "Rename..." `(bookmark-rename ,bookmark) :visible t :help bookmark) map)
(push (vector "Insert Location" `(bookmark-locate ,bookmark) :visible t :help bookmark) map)
(push (vector "Insert Contents" `(bookmark-insert ,bookmark) :visible t :help bookmark) map)
(push (vector "Open by File Browser" `(poplife-find-location (expand-file-name (bookmark-get-filename ,bookmark))) :visible t :active t :help bookmark) map)
(push (vector "Open in Frame" `(bookmark-jump ,bookmark 'switch-to-buffer-other-frame) :visible t :help bookmark) map)
(push (vector "Open in Other Window" `(bookmark-jump-other-window ,bookmark) :visible t :help bookmark) map)
(push ["--" ignore] map)
(push (vector "Open" `(bookmark-jump ,bookmark) :visible t :help bookmark) map) ; switch-to-buffer
(push (vector ".." `(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir (expand-file-name "../" (bookmark-get-filename ,bookmark)) ,submenu))
:visible t :active t) map)
(let ((annotation (bookmark-get-annotation bookmark)))
(cons (format "%s%s" bookmark (if (and annotation (not (string-equal annotation ""))) "*" "")) map)))))
;; (let ((foo (poplife-bookmark-elt-easymap "poplife.el\\site-lisp"))) (describe-variable 'foo))
;; (let ((foo (poplife-bookmark-elt-easymap "poplife.el\\site-lisp" t))) (describe-variable 'foo))
(defvar poplife-bookmark-annotation-detail-flag t
"Show full contents of annotation in popup-menu.")
(defun poplife-bookmark-annotation-easymap (bookmark)
"Define easymenu to list annotation."
(let ((annot (bookmark-get-annotation bookmark))
(annot-column 36)) ; 36 is arbitrary number or (length "Open in Other Window")
(if (and annot (not (string-equal annot "")))
(if poplife-bookmark-annotation-detail-flag
(let ((lines (poplife-split-string annot annot-column))
map)
(dolist (line (reverse lines))
(push (vector line `(bookmark-edit-annotation ,bookmark) :visible t :active t :help bookmark) map))
(cons "Edit Annotation..." map))
(let (annot-name)
(setq annot-name (format "Edit Annotation `%s'..." (if (> (length annot) annot-column) (substring annot 0 annot-column) annot)))
(vector annot-name `(bookmark-edit-annotation ,bookmark) :visible t :active t :help bookmark)))
(vector "Add Annotation..." `(bookmark-edit-annotation ,bookmark) :visible t :active t :help bookmark))))
(defun poplife-split-string (string fill-length)
"Split STRING into list of string.
Argument FILL-LENGTH determines length of each line."
(setq string (replace-regexp-in-string
(rx (* (any " \t\n")) eos) "" string)) ; Chomp text.
(with-temp-buffer
(insert string)
(let ((fill-column fill-length) ; Replace text.
(find-repl-list '(("\\`\\'" . " ") (" +" . " ")))) ; Avoid having "--" on menu.
(fill-region (point-min) (point-max))
(dolist (find-repl find-repl-list)
(goto-char (point-min))
(while (re-search-forward (car find-repl) nil t)
(replace-match (cdr find-repl)))))
(split-string (buffer-string) "\n"))) ; List of text lines.
\f
;;; File
(defun poplife-file-easymap (file &optional submenu)
"Define easymenu to list a FILE.
When SUBMENU it non-nil, this returns an easymenu with multiple actions."
(setq file (expand-file-name file))
(and (not (file-directory-p file))
(let* (map
(file-nickname (file-name-nondirectory file))
(file-readable-flag (and (file-regular-p file)
(file-readable-p file)))
(open-file-flag (and (not (string-match-p poplife-file-do-not-open-regexp file))
file-readable-flag))
(dir (file-name-directory file))
(open-dir-flag (and (file-directory-p dir)
(file-accessible-directory-p dir))))
(if (not submenu)
(vector file-nickname
`(funcall (if ,open-file-flag
(quote ,poplife-func-find-file)
(quote ,poplife-func-find-file-by-default-app)) ,file)
:active (and file-readable-flag ; gray the vising file out
(not (string=
(and (buffer-file-name) (expand-file-name (buffer-file-name)))
file)))
:help file)
(push (vector "Open with File Browser" `(poplife-find-location ,file)
:visible t
:active open-dir-flag
:help dir) map)
(push (vector "Open with Default App" `(,poplife-func-find-file-by-default-app ,file)
:visible t
:active t
:help file) map)
(push (vector "Open and Bookmark..." `(progn (find-file ,file) (bookmark-set))
:visible (fboundp 'bookmark-set)
:active open-file-flag
:help "Open and Bookmark this file") map)
(push (vector "Open in Frame" `(find-file-other-frame ,file)
:visible t
:active open-file-flag
:help file) map)
(push (vector "Open in Other Window" `(find-file-other-window ,file)
:visible t
:active open-file-flag
:help file) map)
(push ["--" ignore] map)
(push (vector "Open" `(find-file ,file)
:visible t
:active (and open-file-flag ; gray the vising file out
(not (string=
(and (buffer-file-name) (expand-file-name (buffer-file-name)))
file)))
:help file) map)
(push (vector ".." `(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,dir, submenu))
:visible t :active open-dir-flag :help dir) map)
(cons file-nickname map)))))
;; (let ((foo (poplife-file-easymap "~/.emacs.d/init.el"))) (describe-variable 'foo))
;; (let ((foo (poplife-file-easymap "~/.emacs.d/init.el" t))) (describe-variable 'foo))
(defun poplife-dir-easymap (dir &optional submenu depth)
"Define easymenu to list files and directories in DIR.
When SUBMENU is non-nil, this returns an easymenu with multiple actions.
When DEPTH is more than 1, DIR is recursively scanned."
(when (string-match-p poplife-dir-do-not-scan-regexp dir)
(setq dir "~"))
(setq dir (directory-file-name (expand-file-name dir))) ; Remove slash at the end.
(or depth (setq depth 1))
(let (map base-dir parent-dir rawfiles menufiles)
(setq base-dir (file-name-as-directory (if (string= (file-name-nondirectory dir) "")
dir ; In a case for "c:/"
(file-name-nondirectory dir)))) ; Add slash at the end.
(setq parent-dir (directory-file-name (file-name-directory dir)))
;; Obtain a file list.
(setq rawfiles (ignore-errors (directory-files dir t)))
;; Filter out trivial files.
(dolist (fullfile rawfiles)
(let ((file-nickname (file-name-nondirectory fullfile)))
(unless (string-match-p poplife-file-do-not-show-regexp file-nickname)
(push fullfile menufiles))))
;; Limit number of menufiles
(when (and poplife-file-max-menu-items
(not submenu))
(let ((nfile (length menufiles)))
(setq menufiles (nthcdr (- nfile poplife-file-max-menu-items) menufiles))))
;; Add more menu.
(push (vector "More..." ; Item to open a current directory.
`(let (poplife-file-max-menu-items
;; (poplife-file-recursive ,(1+ poplife-file-recursive))
(poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,dir t))
:active `(not ,submenu)
:visible t
:help dir)
map)
;; Create map with files and directories.
(dolist (fullfile menufiles)
(let ((file-nickname (file-name-nondirectory fullfile)))
(if (file-directory-p fullfile) ; when item is directory
(push (if (or (>= depth poplife-file-recursive)
(not (file-accessible-directory-p fullfile)))
(vector (file-name-as-directory file-nickname)
`(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,fullfile ,submenu))
:active (file-accessible-directory-p fullfile) :help fullfile)
(poplife-dir-easymap fullfile submenu (1+ depth))) ; recursive
map)
(push ; when item is file (that is defined as `not a directory')
(poplife-file-easymap fullfile submenu)
map))))
(push ["--" ignore] map)
(push (if (not (or submenu poplife-dir-.-submenu)) ; (not submenu)
;; Single item
(vector "." ; item to open current directory
`(,poplife-func-find-file ,dir)
;; `(,poplife-func-find-file-by-default-app ,dir)
:visible t :active t :help dir)
;; Multiple items in submenu.
(delq nil
(list "."
(vector "Open" ; item to open current directory
`(find-file ,dir) ; ,poplife-func-find-file
:visible t :active t :help dir)
["--" ignore]
(vector "Open in Other Window"
`(find-file-other-window ,dir)
:visible t :active t :help dir)
(vector "Open in Frame"
`(find-file-other-frame ,dir)
:visible t :active t :help dir)
(vector "Open with Bookmarked..."
`(progn (find-file ,dir) (bookmark-set))
:visible (fboundp 'bookmark-set) :active t :help "Open and Add to Bookmarks")
(vector "Open by File Browser"
`(,poplife-func-find-file-by-default-app ,dir)
:visible t
:active t
:help dir))))
map)
(push (vector ".." ; Item to open parent directory.
`(let ((poplife-file-recursive ,poplife-file-recursive)
(poplife-func-find-file (quote ,poplife-func-find-file)))
(poplife-find-dir ,parent-dir ,submenu))
:active `(not (equal ,parent-dir ,dir)) :help parent-dir)
map)
(setq base-dir (replace-regexp-in-string "^@" "at" base-dir t t))
(cons base-dir map)))
;; (let ((foo (poplife-dir-easymap "~/.emacs.d"))) (describe-variable 'foo))
;; (let ((foo (poplife-dir-easymap "~/.emacs.d" t))) (describe-variable 'foo))
;; (popup-menu (poplife-dir-easymap "~/.emacs.d"))
;; (popup-menu (poplife-dir-easymap "~/"))
;; (let ((foo (poplife-dir-easymap "c:/"))) (describe-variable 'foo))
;; (popup-menu (poplife-dir-easymap "c:/"))
(defvar poplife-dir-.-submenu t
"Show always submenu for the current directory.")
(defun poplife-pwd-easymap (path &optional submenu)
"Define easymenu to list directories that are above PATH."
(setq path (directory-file-name (expand-file-name path))) ; remove slash
(let* ((title (format (if (file-directory-p path)
"%s/"
"%s")
(file-name-nondirectory path)))
dirpath
map)
(while (not (string= (file-name-nondirectory path) "" ))
(setq dirpath (directory-file-name (file-name-directory path))) ; remove slash
(push (vector (format "%s/" (file-name-nondirectory dirpath)) ; dirname
`(poplife-find-location ,path)) map)
(setq path dirpath))
(cons title (reverse map))))
;; (let ((foo (poplife-pwd-easymap "~/.emacs.d/site-lisp/poplife.el"))) (describe-variable 'foo))
;; (popup-menu (poplife-pwd-easymap default-directory))
;; (popup-menu (poplife-pwd-easymap "~/.emacs.d/site-lisp/"))
(defun poplife-pwd-menu-open (event)
"Open popup-menu that opens a folder by File Browser."
(interactive "e")
(mouse-set-point event)
(popup-menu
(poplife-pwd-easymap (or buffer-file-name default-directory))))
;; (poplife-pwd-menu-open last-nonmenu-event)
(defun poplife-buffer-menu-open (event)
"Open popup-menu that switches current buffer."
(interactive "e")
(mouse-set-point event)
(popup-menu (poplife-buffer-easymap)))
;; (poplife-buffer-menu-open last-nonmenu-event)
\f
;;; Util
(defun poplife-func-switch-to-buffer ()
"Return switch-to-buffer function that corresponds to `poplife-func-find-file'."
(cdr (assoc poplife-func-find-file
'((find-file . switch-to-buffer)
(find-file-other-window . switch-to-buffer-other-window)
(find-file-other-frame . switch-to-buffer-other-frame)))))
;; (let ((foo (poplife-func-switch-to-buffer))) (describe-variable 'foo))
(defun poplife-find-dir (dir &optional submenu)
"Visit directory DIR using `popup-menu'.
When SUBMENU is non-nil, this offers multiple actions."
;; (interactive (list (read-directory-name "Find directory: ")))
(popup-menu (poplife-dir-easymap dir submenu)
(popup-menu-normalize-position last-nonmenu-event)))
(defun poplife-finder-directory ()
"Return directory where Finder is visiting."
(when (eq system-type 'darwin)
(ns-do-applescript
"tell application \"Finder\"
if exists Finder window 1 then
set currentDir to target of Finder window 1 as alias
else
set currentDir to desktop as alias
end if
set thePath to POSIX path of currentDir
end tell")))
;; (let ((foo (poplife-finder-selection))) (describe-variable 'foo))
(defun poplife-find-location (file)
"Visit directory that contains FILE."
;; org-open-file
(cond
((eq system-type 'darwin)
;; Select file in Finder.
(ns-do-applescript ; do-applescript, osascript
(format "tell application \"Finder\"
set thePath to POSIX file \"%s\" as string
activate
reveal thePath
end tell" file)))
((eq system-type 'windows-nt)
;; Select file in File Explorer.
(w32-shell-execute "open" "explorer"
(concat "/e,/select,"
(poplife-convert-w32-filename file))))
((eq system-type 'gnu/linux)
;; Visit dir that contains file by default-app.
;; (start-process "select-file-by-nautilus" nil "nautilus" file)
(start-process "open-dir-by-xdg-open" nil "xdg-open" (file-name-directory file)))
(t
;; Select file in Dired
(dired-other-frame (file-name-directory file)) ; Visit dir that contains file
(dired-goto-file file)))) ; Move point to file
(defun poplife-convert-w32-filename (filename)
"Mirror slash characters in FILENAME into backslashes."
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=24387 (bug#24387)
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=28883 (bug#28883)
;; (setq filename (convert-standard-filename filename))
(let ((start 0))
(while (string-match "/" filename start)
(aset filename (match-beginning 0) ?\\)
(setq start (match-end 0)))
filename))
;;; (poplife-convert-w32-filename "c:/Users/dream/.emacs.d")
(defun poplife-find-file-by-default-app (file)
"Visit FILE by default application or default file browser."
(when (plist-get poplife-mouse-edit-cottager :recentf)
(recentf-push file))
(cond
((eq system-type 'gnu/linux)
(start-process "find-file-by-default-app" nil "xdg-open" file)) ; Visit file by default-app.
((eq system-type 'darwin)
(start-process "find-file-by-default-app" nil "open" file)) ; Visit file by default-app.
((eq system-type 'cygwin)
(start-process "find-file-by-default-app" nil "cygstart" file)) ; Visit file by default-app.
((eq system-type 'windows-nt)
(w32-shell-execute "open" "explorer" (poplife-convert-w32-filename file))) ; Visit file by default-app.
(t
(find-file-other-frame file))))
(provide 'poplife)
;;; poplife.el ends here
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 14:37 ` Thibaut Verron
2020-09-16 15:06 ` Eli Zaretskii
@ 2020-09-17 3:57 ` Richard Stallman
1 sibling, 0 replies; 59+ messages in thread
From: Richard Stallman @ 2020-09-17 3:57 UTC (permalink / raw)
To: thibaut.verron; +Cc: juri, eliz, tkk, emacs-devel
[[[ To any NSA and FBI agents reading my email: please consider ]]]
[[[ whether defending the US Constitution against all enemies, ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]
> > Yes, we do support contextual menus, just press C-mouse-3.
> It is only contextual if "context" just means "major mode".
It would not be hard to make text property bindings for C-mouse-3
depending on where in the buffer the mouse is. Or make a binding that
calculates what menu to use. No special infrastructure is needed --
just figure out what bindings you want.
--
Dr Richard Stallman
Chief GNUisance of the GNU Project (https://gnu.org)
Founder, Free Software Foundation (https://fsf.org)
Internet Hall-of-Famer (https://internethalloffame.org)
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-16 23:49 ` Tak Kunihiro
2020-09-17 2:33 ` Tak Kunihiro
@ 2020-09-17 7:43 ` Juri Linkov
2020-09-17 9:22 ` Robert Pluim
2020-09-17 19:41 ` chad
1 sibling, 2 replies; 59+ messages in thread
From: Juri Linkov @ 2020-09-17 7:43 UTC (permalink / raw)
To: Tak Kunihiro; +Cc: emacs-devel
>>> - Horizontal scroll by wheel is supported.
>>> - Moving text using mouse is supported.
>
>> Please explain how horizontal scroll by wheel is supported.
>> Also please explain how moving text using mouse is supported.
>
> Try something like this.
>
> (setq mouse-wheel-tilt-scroll t)
I tried this, but still it doesn't scroll horizontally
with the mouse wheel. It seems this feature expects the
mouse events mouse-6 and mouse-7, but scrolling the mouse wheel
produces mouse-4 and mouse-5.
> (setq mouse-drag-and-drop-region 'meta)
I tried this with (setq mouse-drag-and-drop-region 'control)
and it moves the text without modifier, and copies the text
when the modifier key is pressed immediately before releasing
the mouse button. In other apps, the modifier key can be pressed
before clicking the mouse button.
>> Thanks, this is a good starting point for adding contextual menu.
>> I tried poplife-mode, and it pops up the menu on mouse-3,
>> but it seems only when there is the selection already,
>> i.e. it doesn't pop up the menu when nothing is selected.
>> Is this intended to work this way?
>
> That’s not intended behavior!
> I found that for unknown reason, poplife-mode does not overwrite
> [mouse-3] started with emacs -Q. I’ll fix it and come back
> soon while the topic is hot.
...
> I fixed it and confirm with Emacs -Q. Can you try again to see
> the approach to start with?
Now this is much better, thanks.
There are still some minor problems. For example, when "Paste"
is selected from the Context menu, it doesn't paste the text
where mouse-3 was clicked. It still pastes at the current cursor
position.
Also the Context menu is not activated immediately after clicking
down-mouse-3. It's activated only after mouse-3 is released.
In other apps, down-mouse-3 can activate the Context menu.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-17 7:43 ` Juri Linkov
@ 2020-09-17 9:22 ` Robert Pluim
2020-09-17 18:59 ` Juri Linkov
2020-09-17 19:41 ` chad
1 sibling, 1 reply; 59+ messages in thread
From: Robert Pluim @ 2020-09-17 9:22 UTC (permalink / raw)
To: Juri Linkov; +Cc: Tak Kunihiro, emacs-devel
>>>>> On Thu, 17 Sep 2020 10:43:59 +0300, Juri Linkov <juri@linkov.net> said:
Juri> There are still some minor problems. For example, when "Paste"
Juri> is selected from the Context menu, it doesn't paste the text
Juri> where mouse-3 was clicked. It still pastes at the current cursor
Juri> position.
What's your value of 'mouse-yank-at-point'?
Robert
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-17 9:22 ` Robert Pluim
@ 2020-09-17 18:59 ` Juri Linkov
0 siblings, 0 replies; 59+ messages in thread
From: Juri Linkov @ 2020-09-17 18:59 UTC (permalink / raw)
To: Robert Pluim; +Cc: Tak Kunihiro, emacs-devel
> What's your value of 'mouse-yank-at-point'?
I use the default value nil of 'mouse-yank-at-point'.
So poplife.el should take onto account mouse-yank-at-point.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-17 7:43 ` Juri Linkov
2020-09-17 9:22 ` Robert Pluim
@ 2020-09-17 19:41 ` chad
2020-09-18 8:23 ` Juri Linkov
1 sibling, 1 reply; 59+ messages in thread
From: chad @ 2020-09-17 19:41 UTC (permalink / raw)
To: Juri Linkov; +Cc: Tak Kunihiro, EMACS development team
[-- Attachment #1: Type: text/plain, Size: 1545 bytes --]
On Thu, Sep 17, 2020 at 1:28 AM Juri Linkov <juri@linkov.net> wrote:
> >>> - Horizontal scroll by wheel is supported.
> >>> - Moving text using mouse is supported.
> >
> >> Please explain how horizontal scroll by wheel is supported.
> >> Also please explain how moving text using mouse is supported.
> >
> > Try something like this.
> >
> > (setq mouse-wheel-tilt-scroll t)
>
> I tried this, but still it doesn't scroll horizontally
> with the mouse wheel. It seems this feature expects the
> mouse events mouse-6 and mouse-7, but scrolling the mouse wheel
> produces mouse-4 and mouse-5.
>
FWIW, emacs uses the "original" (I think they started with XFree86?)
bindings that treat up/down mouse wheel as mouse buttons 4/5, while
left/right are 6&7, so this is working as (at least at one point in the
past) intended. There might be a communication issue around "by wheel"
here: are you using a mouse with a wheel that tilts to the left/right? I
believe that's what the OP had in mind.
(There is some added complication here, in that windows and macos seem to
use mouse-up/down/left/right, whereas the x11 code uses mouse-4/5/6/7. My
current environment is a little odd, but it seems like gtk (ala emacs-pgtk)
ise still using the numbers rather than the names.)
Poking around the internet suggests that the "standard" for horizontal
roll-the-wheel-scrolling is to add shift to rolling the wheel (and this
works in the couple apps that I tried). Emacs' mwheel.el doesn't currently
do this, but it looks like it wouldn't be hard to add.
~Chad
[-- Attachment #2: Type: text/html, Size: 2078 bytes --]
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-17 19:41 ` chad
@ 2020-09-18 8:23 ` Juri Linkov
2020-09-18 18:41 ` chad
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2020-09-18 8:23 UTC (permalink / raw)
To: chad; +Cc: Tak Kunihiro, EMACS development team
> > (setq mouse-wheel-tilt-scroll t)
>
> I tried this, but still it doesn't scroll horizontally
> with the mouse wheel. It seems this feature expects the
> mouse events mouse-6 and mouse-7, but scrolling the mouse wheel
> produces mouse-4 and mouse-5.
>
> FWIW, emacs uses the "original" (I think they started with XFree86?)
> bindings that treat up/down mouse wheel as mouse buttons 4/5, while
> left/right are 6&7, so this is working as (at least at one point in the
> past) intended. There might be a communication issue around "by wheel"
> here: are you using a mouse with a wheel that tilts to the left/right? I
> believe that's what the OP had in mind.
Do you mean a mouse with many additional buttons? I heard such monsters
exist, but never seen them.
> Poking around the internet suggests that the "standard" for horizontal
> roll-the-wheel-scrolling is to add shift to rolling the wheel (and this
> works in the couple apps that I tried). Emacs' mwheel.el doesn't currently
> do this, but it looks like it wouldn't be hard to add.
I recall posting a patch that implements this, maybe I should resubmit it.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-18 8:23 ` Juri Linkov
@ 2020-09-18 18:41 ` chad
0 siblings, 0 replies; 59+ messages in thread
From: chad @ 2020-09-18 18:41 UTC (permalink / raw)
To: Juri Linkov; +Cc: Tak Kunihiro, EMACS development team
[-- Attachment #1: Type: text/plain, Size: 2182 bytes --]
On Fri, Sep 18, 2020 at 1:31 AM Juri Linkov <juri@linkov.net> wrote:
> > > (setq mouse-wheel-tilt-scroll t)
> >
> > I tried this, but still it doesn't scroll horizontally
> > with the mouse wheel. It seems this feature expects the
> > mouse events mouse-6 and mouse-7, but scrolling the mouse wheel
> > produces mouse-4 and mouse-5.
> >
> > FWIW, emacs uses the "original" (I think they started with XFree86?)
> > bindings that treat up/down mouse wheel as mouse buttons 4/5, while
> > left/right are 6&7, so this is working as (at least at one point in the
> > past) intended. There might be a communication issue around "by wheel"
> > here: are you using a mouse with a wheel that tilts to the left/right? I
> > believe that's what the OP had in mind.
>
> Do you mean a mouse with many additional buttons? I heard such monsters
> exist, but never seen them.
>
On one hand, if you believe that mice should have exactly 3 buttons in a
manner perhaps similar to the Spanish Inquisition, then yes.
On the other, for mice with a physical wheel, on X11 and derivatives (but
neither windows nor mac), a physically rolling the wheel appears to the
window system (and thus to emacs) as mouse buttons numbered 4 and 5. If the
wheel also registers a tilt to the left/right, these appear as buttons 6 &
7. The former is very common; the latter seems to be uncommon these days.
Neither of these are the so-called "MMO mouse" (apparently now sometimes
"MOBA mouse") with 6-15 (yes, really) extra buttons that are *relatively*
common amongst hardcore PC gamers. (For example:
https://www.howtogeek.com/403685/how-to-use-an-mmo-or-moba-mouse-for-productivity/)
I've
only tried one of these once, and at the time the extra buttons weren't
supported by X11, so I can't tell you if the mouse equivalent of the space
cadet keyboard is emacs nirvana, instant thumb-sprain, or just a waste of
plastic.
These days, trackpads/touchpads are very common, and most of them have a
scrolling feature that uses gestures to fake those 4 extra buttons. Emacs
master currently does the expected scrolling things for up/down but not
left/right nor shift-up/down.
Hope that helps,
~Chad
[-- Attachment #2: Type: text/html, Size: 2864 bytes --]
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2020-09-15 13:11 ` Stefan Monnier
@ 2021-07-11 23:38 ` Juri Linkov
2021-07-12 11:55 ` Eli Zaretskii
2021-07-12 22:32 ` Stefan Monnier
0 siblings, 2 replies; 59+ messages in thread
From: Juri Linkov @ 2021-07-11 23:38 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, Richard Stallman, spacibba, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
[-- Attachment #1: Type: text/plain, Size: 1777 bytes --]
>> > We don't need to choose, since we can get a context menu on
>> > `down-mouse-3` and 'mouse-save-then-kill' on `mouse-3`.
>
> (defun mouse-maybe-context-menu (event)
> "Bring up a context menu for a long click.
> See `mouse-long-click-time' and `mouse-context-menu-function'."
> (interactive "@e")
> (if (let ((track-mouse t)) (sit-for (/ mouse-long-click-time 1000.0)))
> (push (cons 'context-menu (cdr event)) unread-command-events)))
>
> It's not good enough for `master` (IIRC the sit-for tends to return nil
> immediately in some circumstances), but I didn't pursue this any further
> given the lack of interest at the time. I can't think of any reason why
> it should be hard to fix.
This is a much needed feature and it would nice to finish it one way or another.
I tried to use timers, and the result works well in all test cases:
1. Modes that already bind down-mouse-3 continue working without problems,
so e.g. smerge-popup-context-menu works fine. BTW, while testing it
by clicking down-mouse-3 in non-selected windows, I found a problem,
so a fix for this very rare problem is also included in the patch.
For the same reason 'popup-menu' needs to be called explicitly
to be able to select the right window, as the patch does,
for the case when the mouse is dragged and down-mouse-3
is released in another window.
2. flyspell.el doesn't need to be changed. ibuffer.el works too.
3. cua-rect.el helped to fix one problem and now is supported too.
The new user option mouse-3-down-context-menu is based on mouse-1-click-follows-link.
Currently mouse-context-menu-map shows just the Edit menu - filling the content
of the context menu with e.g. mouse-context-menu-function is a separate task.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: mouse-3-down-context-menu.patch --]
[-- Type: text/x-diff, Size: 4670 bytes --]
diff --git a/lisp/mouse.el b/lisp/mouse.el
index 89e5d7c48a..febda78212 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -156,6 +156,82 @@ key-translation-map
(define-key key-translation-map [double-mouse-1]
#'mouse--click-1-maybe-follows-link)
+\f
+(defcustom mouse-3-down-context-menu 450
+ "Non-nil means that holding down Mouse-3 shows context menu.
+
+With the default setting, holding down the Mouse-3 button
+for more than 450 milliseconds performs the same action as S-Down-Mouse-3
+\(which shows the menu), while an ordinary Mouse-3 click performs the
+original Mouse-3 binding (which typically sets the region where you
+click the mouse).
+
+If value is an integer, the time elapsed between pressing and
+releasing the mouse button determines whether to show the menu
+or perform the normal Mouse-3 action (typically set the region).
+The absolute numeric value specifies the maximum duration of a
+\"short click\" in milliseconds. A positive value means that a
+short click shows the menu, and a longer click performs the
+normal action.
+
+Otherwise, a single Mouse-3 click unconditionally shows the menu."
+ :version "28.1"
+ :type '(choice (const :tag "Disabled" nil)
+ (number :tag "Single click time limit" :value 450)
+ (other :tag "Single click" t)))
+
+(defvar mouse--down-3-timer nil)
+
+(defun mouse-maybe-context-menu (event)
+ (interactive "@e")
+ (cond
+ ;; Delay context menu display.
+ ((numberp mouse-3-down-context-menu)
+ (when (timerp mouse--down-3-timer)
+ (cancel-timer mouse--down-3-timer))
+ (setq mouse--down-3-timer
+ (run-with-timer
+ (/ (abs mouse-3-down-context-menu) 1000.0) nil
+ (lambda ()
+ (setq mouse--down-3-timer t)
+ (unless (eq (posn-window (event-end event)) (selected-window))
+ (select-window (posn-window (event-end event))))
+ (mouse-context-menu event))))
+ nil)
+ ;; Immediately pop up context menu.
+ (mouse-3-down-context-menu
+ (unless (eq (posn-window (event-end event)) (selected-window))
+ (select-window (posn-window (event-end event))))
+ (mouse-context-menu event))))
+
+(defun mouse--click-3-maybe-context-menu (&optional _prompt)
+ (cond
+ ((numberp mouse-3-down-context-menu)
+ (cond
+ ((timerp mouse--down-3-timer)
+ ;; Don't wait for context menu and fall back to mouse-save-then-kill.
+ (cancel-timer mouse--down-3-timer)
+ (setq mouse--down-3-timer nil)
+ nil)
+ (mouse--down-3-timer
+ ;; Context menu was displayed after delay.
+ (setq mouse--down-3-timer nil)
+ [])))
+ ;; Context menu was displayed immediately.
+ (mouse-3-down-context-menu
+ [])))
+
+(define-key key-translation-map [mouse-3]
+ #'mouse--click-3-maybe-context-menu)
+
+(defun mouse-context-menu-map ()
+ (cddr (assq 'edit (lookup-key global-map [menu-bar]))))
+
+(defun mouse-context-menu (event)
+ "Show a context menu for the current buffer."
+ (interactive "@e")
+ (popup-menu (mouse-context-menu-map) event))
+
\f
;; Provide a mode-specific menu on a mouse button.
@@ -1672,7 +1748,7 @@ mouse-save-then-kill
((not (numberp click-pt)) nil)
;; If the user clicked without moving point, kill the region.
;; This also resets `mouse-selection-click-count'.
- ((and (eq last-command 'mouse-save-then-kill)
+ ((and (memq last-command '(mouse-save-then-kill mouse-maybe-context-menu))
(eq click-pt mouse-save-then-kill-posn)
(eq window (selected-window)))
(if mouse-drag-copy-region
@@ -2899,6 +2975,8 @@ function-key-map
;; Allow yanking also when the corresponding cursor is "in the fringe".
(define-key function-key-map [right-fringe mouse-2] 'mouse--strip-first-event)
(define-key function-key-map [left-fringe mouse-2] 'mouse--strip-first-event)
+
+(global-set-key [down-mouse-3] 'mouse-maybe-context-menu)
(global-set-key [mouse-3] 'mouse-save-then-kill)
(define-key function-key-map [right-fringe mouse-3] 'mouse--strip-first-event)
(define-key function-key-map [left-fringe mouse-3] 'mouse--strip-first-event)
diff --git a/lisp/vc/smerge-mode.el b/lisp/vc/smerge-mode.el
index 694d4529b9..3aa7399dd5 100644
--- a/lisp/vc/smerge-mode.el
+++ b/lisp/vc/smerge-mode.el
@@ -385,6 +385,8 @@ smerge-remove-props
(defun smerge-popup-context-menu (event)
"Pop up the Smerge mode context menu under mouse."
(interactive "e")
+ (unless (eq (posn-window (event-end event)) (selected-window))
+ (select-window (posn-window (event-end event))))
(if (and smerge-mode
(save-excursion (posn-set-point (event-end event)) (smerge-check 1)))
(progn
^ permalink raw reply related [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-11 23:38 ` Context menus and mouse-3 Juri Linkov
@ 2021-07-12 11:55 ` Eli Zaretskii
2021-07-12 20:56 ` Juri Linkov
2021-07-12 22:32 ` Stefan Monnier
1 sibling, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-12 11:55 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Date: Mon, 12 Jul 2021 02:38:19 +0300
> Cc: philipk@posteo.net, Richard Stallman <rms@gnu.org>, spacibba@aol.com,
> emacs-devel@gnu.org, arthur.miller@live.com, dgutov@yandex.ru, ghe@sdf.org,
> drew.adams@oracle.com
>
> >> > We don't need to choose, since we can get a context menu on
> >> > `down-mouse-3` and 'mouse-save-then-kill' on `mouse-3`.
> >
> > (defun mouse-maybe-context-menu (event)
> > "Bring up a context menu for a long click.
> > See `mouse-long-click-time' and `mouse-context-menu-function'."
> > (interactive "@e")
> > (if (let ((track-mouse t)) (sit-for (/ mouse-long-click-time 1000.0)))
> > (push (cons 'context-menu (cdr event)) unread-command-events)))
> >
> > It's not good enough for `master` (IIRC the sit-for tends to return nil
> > immediately in some circumstances), but I didn't pursue this any further
> > given the lack of interest at the time. I can't think of any reason why
> > it should be hard to fix.
>
> This is a much needed feature and it would nice to finish it one way or another.
> I tried to use timers, and the result works well in all test cases:
Could you please remind us the problem you are trying to solve here?
Thanks.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-12 11:55 ` Eli Zaretskii
@ 2021-07-12 20:56 ` Juri Linkov
2021-07-13 11:32 ` Eli Zaretskii
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-12 20:56 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
>> >> > We don't need to choose, since we can get a context menu on
>> >> > `down-mouse-3` and 'mouse-save-then-kill' on `mouse-3`.
>> >
>> > (defun mouse-maybe-context-menu (event)
>> > "Bring up a context menu for a long click.
>> > See `mouse-long-click-time' and `mouse-context-menu-function'."
>> > (interactive "@e")
>> > (if (let ((track-mouse t)) (sit-for (/ mouse-long-click-time 1000.0)))
>> > (push (cons 'context-menu (cdr event)) unread-command-events)))
>> >
>> > It's not good enough for `master` (IIRC the sit-for tends to return nil
>> > immediately in some circumstances), but I didn't pursue this any further
>> > given the lack of interest at the time. I can't think of any reason why
>> > it should be hard to fix.
>>
>> This is a much needed feature and it would nice to finish it one way or another.
>> I tried to use timers, and the result works well in all test cases:
>
> Could you please remind us the problem you are trying to solve here?
Nowadays every other app uses down-mouse-3 to pop up a context menu,
but in Emacs mouse-3 operates on the region. So the solution is
similar to mouse-1-click-follows-link: to pop up a context menu
after a delay, otherwise the immediate click operates on the region.
The only thing I don't understand why couldn't the context-menu
implementation be the same as the menu-bar implementation?
The global binding of [menu-bar] is defined in menu-bar.el,
so the same way [context-menu] could be defined globally
with the standard bindings such as all other apps have:
"Cut", "Copy", "Paste", "Select All", "Undo", "Redo".
Then the users will be able to customize the global
[context-menu] applicable to all buffers.
Every mode can define own [context-menu] in their mode-maps,
then users will be able to customize mode's [context-menu]
using mode hooks. Even text-properties could define
the [context-menu] in their local bindings that will be used
by '(current-local-map)' or better '(current-active-maps)'.
To be able to override the [context-menu], local bindings could use
text-property maps with another binding named [overriding-context-menu]
that will replace the whole composite [context-menu], so e.g.
flyspell-mode could put it on misspelled words, etc.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-11 23:38 ` Context menus and mouse-3 Juri Linkov
2021-07-12 11:55 ` Eli Zaretskii
@ 2021-07-12 22:32 ` Stefan Monnier
2021-07-12 23:56 ` Juri Linkov
1 sibling, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-12 22:32 UTC (permalink / raw)
To: Juri Linkov
Cc: Richard Stallman, spacibba, philipk, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
Juri Linkov [2021-07-12 02:38:19] wrote:
> This is a much needed feature and it would nice to finish it one way or another.
> I tried to use timers, and the result works well in all test cases:
[...]
> + (setq mouse--down-3-timer
> + (run-with-timer
> + (/ (abs mouse-3-down-context-menu) 1000.0) nil
> + (lambda ()
> + (setq mouse--down-3-timer t)
> + (unless (eq (posn-window (event-end event)) (selected-window))
> + (select-window (posn-window (event-end event))))
> + (mouse-context-menu event))))
[...]
> (defun mouse-context-menu (event)
> "Show a context menu for the current buffer."
> (interactive "@e")
> (popup-menu (mouse-context-menu-map) event))
I'm a bit busy with other things these days, but just looking at the
above code, one thing I see as a potential problem with this code is
that the context menu commands won't be run "the normal way": the
undo-boundary won't be applied as usual, post/pre-command-hook won't be
run as usual, etc...
> The global binding of [menu-bar] is defined in menu-bar.el,
> so the same way [context-menu] could be defined globally
Agreed, and I think it would address the above problem.
I.e. do something like
(defun mouse-context-menu (event)
(push `(context-menu . ,(cdr event)) unread-command-events))
BTW, the other worry with your code is that if you use an input device
which can fail to pair the down/up mouse-3 events, you might end up
failing to cancel the timer code (that can also happen when the users
set a binding for `drag-mouse-3`, I think), so maybe you should arrange
for the down event to register a `pre-command-hook` that cancels
the timer.
In terms of aesthetics, it would be good to arrange the code such that
`mouse--click-3-maybe-context-menu` doesn't need to duplicate/mimic the
tests performed in `mouse-maybe-context-menu`. Maybe have
`mouse-maybe-context-menu` set some var telling
`mouse--click-3-maybe-context-menu` when the next `mouse-3` should
be dropped?
Stefan
PS: FWIW, the "proof of concept" code I have in my local Emacs is:
(global-set-key [down-mouse-3] #'mouse-maybe-context-menu)
(defun mouse-maybe-context-menu (event)
"Bring up a context menu for a long click."
(interactive "@e")
(if (let* ((track-mouse t)
(time (/ mouse-long-click-time 1000.0))
(result (sit-for time)))
(message "Maybe-context-menu: %S %S" time result)
result)
(push (cons 'context-menu (cdr event)) unread-command-events)))
I think `sit-for` can be made to work OK, but my proof-of-concept
isn't good enough. I think the main issue is that it doesn't
swallow the mouse-3 click, but I haven't really looked into it.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-12 22:32 ` Stefan Monnier
@ 2021-07-12 23:56 ` Juri Linkov
2021-07-13 3:01 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-12 23:56 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, Richard Stallman, spacibba, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>> The global binding of [menu-bar] is defined in menu-bar.el,
>> so the same way [context-menu] could be defined globally
>
> Agreed, and I think it would address the above problem.
> I.e. do something like
>
> (defun mouse-context-menu (event)
> (push `(context-menu . ,(cdr event)) unread-command-events))
OT1H, this avoids an explicit call to 'popup-menu'. But OTOH,
this precludes constructing the context-menu with a user-defined
function mouse-context-menu-function.
I tried to override the global [context-menu] with e.g. menu-bar-custom-menu
(that later could be replaced with the value returned from mouse-context-menu-function):
(let ((map (make-sparse-keymap)))
(define-key map [context-menu] menu-bar-custom-menu)
(set-transient-map map))
(push (cons 'context-menu (cdr event)) unread-command-events)
However, it doesn't override the global [context-menu], but prepends
the transient [context-menu] before the global one.
> PS: FWIW, the "proof of concept" code I have in my local Emacs is:
[...]
> I think `sit-for` can be made to work OK, but my proof-of-concept
> isn't good enough. I think the main issue is that it doesn't
> swallow the mouse-3 click, but I haven't really looked into it.
At least, now we have two implementations, and both could be improved
before selecting the one that works better. Meanwhile, I'd continue
trying to combine [context-menu] with mouse-context-menu-function.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-12 23:56 ` Juri Linkov
@ 2021-07-13 3:01 ` Stefan Monnier
2021-07-13 23:32 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-13 3:01 UTC (permalink / raw)
To: Juri Linkov
Cc: Richard Stallman, spacibba, philipk, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>>> The global binding of [menu-bar] is defined in menu-bar.el,
>>> so the same way [context-menu] could be defined globally
>> Agreed, and I think it would address the above problem.
>> I.e. do something like
>>
>> (defun mouse-context-menu (event)
>> (push `(context-menu . ,(cdr event)) unread-command-events))
>
> OT1H, this avoids an explicit call to 'popup-menu'.
Calling `popup-menu` is not a problem, but not doing the dance usually
done by the command loop is more problematic.
> But OTOH, this precludes constructing the context-menu with
> a user-defined function mouse-context-menu-function.
Actually it doesn't.
You can bind `context-menu` to a command that uses that function and
then passes the result to `popup-menu`, or you can bind it to something like
`(menu-item "Context menu" ignore
:filter (lambda (_)
..call mouse-context-menu-function..))
> I tried to override the global [context-menu] with e.g. menu-bar-custom-menu
> (that later could be replaced with the value returned from mouse-context-menu-function):
>
> (let ((map (make-sparse-keymap)))
> (define-key map [context-menu] menu-bar-custom-menu)
> (set-transient-map map))
> (push (cons 'context-menu (cdr event)) unread-command-events)
>
> However, it doesn't override the global [context-menu], but prepends
> the transient [context-menu] before the global one.
Yes, if the key is bound in various keymaps and all the bindings are to
a keymap, then those keymaps get merged, as usual for prefix keymaps.
Two ways to solve that:
- use `mouse-context-menu-function` instead of adding a new local
key binding for `context-menu`.
- don't bind `context-menu` to a keymap but to a command (that
presumably then uses `popup-menu` internally).
>> PS: FWIW, the "proof of concept" code I have in my local Emacs is:
> [...]
>> I think `sit-for` can be made to work OK, but my proof-of-concept
>> isn't good enough. I think the main issue is that it doesn't
>> swallow the mouse-3 click, but I haven't really looked into it.
>
> At least, now we have two implementations, and both could be improved
> before selecting the one that works better.
FWIW I think yours holds more promise, so I don't intend to try and
improve mine.
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-12 20:56 ` Juri Linkov
@ 2021-07-13 11:32 ` Eli Zaretskii
2021-07-13 23:46 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-13 11:32 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Cc: monnier@iro.umontreal.ca, philipk@posteo.net, rms@gnu.org,
> spacibba@aol.com, emacs-devel@gnu.org, arthur.miller@live.com,
> dgutov@yandex.ru, ghe@sdf.org, drew.adams@oracle.com
> Date: Mon, 12 Jul 2021 23:56:39 +0300
>
> Nowadays every other app uses down-mouse-3 to pop up a context menu,
> but in Emacs mouse-3 operates on the region. So the solution is
> similar to mouse-1-click-follows-link: to pop up a context menu
> after a delay, otherwise the immediate click operates on the region.
Can't we decide which effect is TRT based on where the user clicks?
Context menus are available only in special places, and it would seem
that setting the region in those places doesn't make sense, by and
large.
And if sometimes we cannot dwim there, how about making the defcustom
you introduced to allow the users to express their preferences in
these problematic cases?
If the above makes sense, I think it's a better solution than forcing
this feature on everyone. I would be surprised if holding the mouse
button for several hundreds of milliseconds would suddenly produce an
entirely different and unrelated effect, and I'd probably be annoyed
by the need to hold the button when I _know_ I want the context menu.
So it sounds like this implementation is sub-optimal from the get-go,
and we should try looking for a better one.
We could also consider an even more radical solution: an option to
swap mouse-2 and mouse-3. Because isn't it true that people who
expect context menus to pop up when mouse-3 is pressed generally don't
expect and don't use region-related mouse clicks at all? (We have
such a "swap-buttons" variable specific to MS-Windows, and I've been
using it for eons, because clicking mouse-2 on a wheeled mouse is very
inconvenient.)
Thanks.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-13 3:01 ` Stefan Monnier
@ 2021-07-13 23:32 ` Juri Linkov
2021-07-14 2:14 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-13 23:32 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, Richard Stallman, spacibba, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>> I tried to override the global [context-menu] with e.g. menu-bar-custom-menu
>> (that later could be replaced with the value returned from mouse-context-menu-function):
>>
>> (let ((map (make-sparse-keymap)))
>> (define-key map [context-menu] menu-bar-custom-menu)
>> (set-transient-map map))
>> (push (cons 'context-menu (cdr event)) unread-command-events)
>>
>> However, it doesn't override the global [context-menu], but prepends
>> the transient [context-menu] before the global one.
>
> Yes, if the key is bound in various keymaps and all the bindings are to
> a keymap, then those keymaps get merged, as usual for prefix keymaps.
>
> Two ways to solve that:
> - use `mouse-context-menu-function` instead of adding a new local
> key binding for `context-menu`.
> - don't bind `context-menu` to a keymap but to a command (that
> presumably then uses `popup-menu` internally).
`popup-menu` has the same problems: it doesn't run post/pre-command-hook, etc.
because it uses `call-interactively`. So any code that is based on `popup-menu`
such as `org-mouse-show-context-menu` has the same problems, and any code
that uses `call-interactively` such as `tmm-prompt`.
But it's possible to replace `call-interactively` with something like:
(let ((command (car (x-popup-menu event menu-bar-custom-menu)))
(map (make-sparse-keymap)))
(define-key map [next-command] command)
(set-transient-map map t))
(push (cons 'next-command (cdr event)) unread-command-events)
It works surprisingly well, and I really see no problems with this.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-13 11:32 ` Eli Zaretskii
@ 2021-07-13 23:46 ` Juri Linkov
2021-07-14 4:30 ` Eli Zaretskii
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-13 23:46 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> Can't we decide which effect is TRT based on where the user clicks?
> Context menus are available only in special places, and it would seem
> that setting the region in those places doesn't make sense, by and
> large.
Context menus are useful everywhere, not just in special places.
For example, selecting "Paste" from the context menu makes sense everywhere.
> And if sometimes we cannot dwim there, how about making the defcustom
> you introduced to allow the users to express their preferences in
> these problematic cases?
In the previous patch, the defcustom is named mouse-3-down-context-menu.
When it's customized to nil, then only the current behavior is available
with mouse-save-then-kill. When customized to t, then the context menu
pops up immediately.
> If the above makes sense, I think it's a better solution than forcing
> this feature on everyone. I would be surprised if holding the mouse
> button for several hundreds of milliseconds would suddenly produce an
> entirely different and unrelated effect, and I'd probably be annoyed
> by the need to hold the button when I _know_ I want the context menu.
> So it sounds like this implementation is sub-optimal from the get-go,
> and we should try looking for a better one.
We can add as many options as necessary to cater for all needs,
but the question is about the default behavior. The proposed delay is
a middle ground before the user decides which behavior is more preferable.
> We could also consider an even more radical solution: an option to
> swap mouse-2 and mouse-3. Because isn't it true that people who
> expect context menus to pop up when mouse-3 is pressed generally don't
> expect and don't use region-related mouse clicks at all? (We have
> such a "swap-buttons" variable specific to MS-Windows, and I've been
> using it for eons, because clicking mouse-2 on a wheeled mouse is very
> inconvenient.)
This is not backward-compatibile change of the default behavior.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-13 23:32 ` Juri Linkov
@ 2021-07-14 2:14 ` Stefan Monnier
2021-07-14 23:32 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-14 2:14 UTC (permalink / raw)
To: Juri Linkov
Cc: Richard Stallman, spacibba, philipk, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>>> I tried to override the global [context-menu] with e.g. menu-bar-custom-menu
>>> (that later could be replaced with the value returned from mouse-context-menu-function):
>>>
>>> (let ((map (make-sparse-keymap)))
>>> (define-key map [context-menu] menu-bar-custom-menu)
>>> (set-transient-map map))
>>> (push (cons 'context-menu (cdr event)) unread-command-events)
>>>
>>> However, it doesn't override the global [context-menu], but prepends
>>> the transient [context-menu] before the global one.
>>
>> Yes, if the key is bound in various keymaps and all the bindings are to
>> a keymap, then those keymaps get merged, as usual for prefix keymaps.
>>
>> Two ways to solve that:
>> - use `mouse-context-menu-function` instead of adding a new local
>> key binding for `context-menu`.
>> - don't bind `context-menu` to a keymap but to a command (that
>> presumably then uses `popup-menu` internally).
>
> `popup-menu` has the same problems: it doesn't run post/pre-command-hook, etc.
That doesn't matter: the command loop does that for us before/after calling
the command bound to `context-menu`, so the command bound to
`context-menu` doesn't need to do it. The problem with your original
call is not the use of `popup-menu` but the fact that it runs it from
from a timer.
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-13 23:46 ` Juri Linkov
@ 2021-07-14 4:30 ` Eli Zaretskii
2021-07-14 23:37 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-14 4:30 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Cc: monnier@iro.umontreal.ca, philipk@posteo.net, rms@gnu.org,
> spacibba@aol.com, emacs-devel@gnu.org, arthur.miller@live.com,
> dgutov@yandex.ru, ghe@sdf.org, drew.adams@oracle.com
> Date: Wed, 14 Jul 2021 02:46:27 +0300
>
> > Can't we decide which effect is TRT based on where the user clicks?
> > Context menus are available only in special places, and it would seem
> > that setting the region in those places doesn't make sense, by and
> > large.
>
> Context menus are useful everywhere, not just in special places.
> For example, selecting "Paste" from the context menu makes sense
> everywhere.
Where in Emacs do we have context menus which include "Paste"? I
thought we were talking about existing menus popped by mouse-2 that
you'd like to pop up with mouse-3. If this isn't the case, then what
menus are we talking about here? In particular, if you want to _add_
menus which currently don't exist in contexts where we currently don't
offer menus, that could be an entirely new minor mode, and then the
conflict with current bindings of mouse-3 could be resolved as part of
that mode.
> > And if sometimes we cannot dwim there, how about making the defcustom
> > you introduced to allow the users to express their preferences in
> > these problematic cases?
>
> In the previous patch, the defcustom is named mouse-3-down-context-menu.
> When it's customized to nil, then only the current behavior is available
> with mouse-save-then-kill. When customized to t, then the context menu
> pops up immediately.
I was thinking about something more flexible than an all-or-nothing
setting. A delay is not really intelligent enough, since it again is
a global value. I was thinking about something sensitive to the
context.
> > If the above makes sense, I think it's a better solution than forcing
> > this feature on everyone. I would be surprised if holding the mouse
> > button for several hundreds of milliseconds would suddenly produce an
> > entirely different and unrelated effect, and I'd probably be annoyed
> > by the need to hold the button when I _know_ I want the context menu.
> > So it sounds like this implementation is sub-optimal from the get-go,
> > and we should try looking for a better one.
>
> We can add as many options as necessary to cater for all needs,
> but the question is about the default behavior. The proposed delay is
> a middle ground before the user decides which behavior is more preferable.
The default you suggest strikes me as inappropriate. It is definitely
backward incompatible and confusing.
> > We could also consider an even more radical solution: an option to
> > swap mouse-2 and mouse-3. Because isn't it true that people who
> > expect context menus to pop up when mouse-3 is pressed generally don't
> > expect and don't use region-related mouse clicks at all? (We have
> > such a "swap-buttons" variable specific to MS-Windows, and I've been
> > using it for eons, because clicking mouse-2 on a wheeled mouse is very
> > inconvenient.)
>
> This is not backward-compatibile change of the default behavior.
An opt-in behavior is by definition always backward-compatible.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-14 2:14 ` Stefan Monnier
@ 2021-07-14 23:32 ` Juri Linkov
2021-07-15 1:18 ` Stefan Monnier
2021-07-15 6:24 ` Eli Zaretskii
0 siblings, 2 replies; 59+ messages in thread
From: Juri Linkov @ 2021-07-14 23:32 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, Richard Stallman, spacibba, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
> The problem with your original call is not the use of `popup-menu`
> but the fact that it runs it from from a timer.
So when the user wants the context menu to pop up after a delay then
(push (cons 'context-menu (cdr event)) unread-command-events)
is a less problematic way to do this. But for another case
to pop up the context menu immediately, customizing
mouse-3-down-context-menu to nil and still using the same
translation functions like mouse--click-3-maybe-context-menu
is not the most optimal way.
To pop up it immediately, I see no better way than currently
is implemented for C-down-mouse-3 because then C-h k C-down-mouse-3
works smoothly as well as Undo selected from the menu keeps undoing
on consequent calls, etc.
This means there is a need to create a new mode that when enabled
will bind the context menu directly to the key down-mouse-3
and unbind mouse-3 from mouse-save-then-kill
when the user wants to pop up it immediately.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-14 4:30 ` Eli Zaretskii
@ 2021-07-14 23:37 ` Juri Linkov
2021-07-15 6:22 ` Eli Zaretskii
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-14 23:37 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
>> Context menus are useful everywhere, not just in special places.
>> For example, selecting "Paste" from the context menu makes sense
>> everywhere.
>
> Where in Emacs do we have context menus which include "Paste"?
When the menu-bar is disabled, then C-down-mouse-3 pops up the global
menu where "Edit" contains "Paste". When the menu-bar is enabled,
then some modes include "Paste" (e.g. calculator-paste) in a buffer-local menu.
And some modes like org-mode, flyspell-mode, etc. already redefine
down-mouse-3 to pop up context menus.
> I thought we were talking about existing menus popped by mouse-2 that
I suppose you meant C-down-mouse-3?
> you'd like to pop up with mouse-3. If this isn't the case, then what
> menus are we talking about here? In particular, if you want to _add_
> menus which currently don't exist in contexts where we currently don't
> offer menus, that could be an entirely new minor mode, and then the
> conflict with current bindings of mouse-3 could be resolved as part of
> that mode.
It seems a new minor mode is unavoidable because when the user wants
down-mouse-3 to pop up the context menu immediately, then
for the best effect down-mouse-3 should be bound directly
to the context-map, instead of sending the [context-menu] event.
>> In the previous patch, the defcustom is named mouse-3-down-context-menu.
>> When it's customized to nil, then only the current behavior is available
>> with mouse-save-then-kill. When customized to t, then the context menu
>> pops up immediately.
>
> I was thinking about something more flexible than an all-or-nothing
> setting. A delay is not really intelligent enough, since it again is
> a global value. I was thinking about something sensitive to the
> context.
The users who currently use mouse-3 to operate on the region
might want to continue using it even on context-sensitive regions.
For example, like in all browsers, down-mouse-3 in e.g. Info browser or eww,
will show the menu with such items as "Backward" and "Forward".
But when down-mouse-3 will be clicked on a link, it will display
the context sensitive menu with "Follow" and "Open in New Window".
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-14 23:32 ` Juri Linkov
@ 2021-07-15 1:18 ` Stefan Monnier
2021-07-15 22:31 ` Juri Linkov
2021-07-15 6:24 ` Eli Zaretskii
1 sibling, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-15 1:18 UTC (permalink / raw)
To: Juri Linkov
Cc: Richard Stallman, spacibba, philipk, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
Juri Linkov [2021-07-15 02:32:14] wrote:
>> The problem with your original call is not the use of `popup-menu`
>> but the fact that it runs it from from a timer.
>
> So when the user wants the context menu to pop up after a delay then
>
> (push (cons 'context-menu (cdr event)) unread-command-events)
Yup.
> To pop up it immediately, I see no better way than currently
> is implemented for C-down-mouse-3 because then C-h k C-down-mouse-3
> works smoothly as well as Undo selected from the menu keeps undoing
> on consequent calls, etc.
Indeed. Maybe we can bring them together by (when
`mouse-3-down-context-menu` is t) using a remapping from `down-mouse-3`
to `context-menu`. It seems pretty intrusive, tho :-(
Maybe a better option is to make it so the `context-menu` event is kept
internal (give it a less attractive name), i.e. just an implementation
detail for the case where the menu pops up after a delay.
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-14 23:37 ` Juri Linkov
@ 2021-07-15 6:22 ` Eli Zaretskii
2021-07-15 22:23 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-15 6:22 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Cc: monnier@iro.umontreal.ca, philipk@posteo.net, rms@gnu.org,
> spacibba@aol.com, emacs-devel@gnu.org, arthur.miller@live.com,
> dgutov@yandex.ru, ghe@sdf.org, drew.adams@oracle.com
> Date: Thu, 15 Jul 2021 02:37:11 +0300
>
> >> Context menus are useful everywhere, not just in special places.
> >> For example, selecting "Paste" from the context menu makes sense
> >> everywhere.
> >
> > Where in Emacs do we have context menus which include "Paste"?
>
> When the menu-bar is disabled, then C-down-mouse-3 pops up the global
> menu where "Edit" contains "Paste". When the menu-bar is enabled,
> then some modes include "Paste" (e.g. calculator-paste) in a buffer-local menu.
So you want to have the same menus on mouse-3, including when the menu
bar is not disabled?
> And some modes like org-mode, flyspell-mode, etc. already redefine
> down-mouse-3 to pop up context menus.
But those modes define those context menus on special parts of
display, where pasting etc. makes less sense, no?
> > I thought we were talking about existing menus popped by mouse-2 that
>
> I suppose you meant C-down-mouse-3?
Both, I guess.
> > you'd like to pop up with mouse-3. If this isn't the case, then what
> > menus are we talking about here? In particular, if you want to _add_
> > menus which currently don't exist in contexts where we currently don't
> > offer menus, that could be an entirely new minor mode, and then the
> > conflict with current bindings of mouse-3 could be resolved as part of
> > that mode.
>
> It seems a new minor mode is unavoidable because when the user wants
> down-mouse-3 to pop up the context menu immediately, then
> for the best effect down-mouse-3 should be bound directly
> to the context-map, instead of sending the [context-menu] event.
I'm okay with adding a minor mode for this. Based on its popularity,
we could later discuss whether it should be turned on by default.
> The users who currently use mouse-3 to operate on the region
> might want to continue using it even on context-sensitive regions.
>
> For example, like in all browsers, down-mouse-3 in e.g. Info browser or eww,
> will show the menu with such items as "Backward" and "Forward".
> But when down-mouse-3 will be clicked on a link, it will display
> the context sensitive menu with "Follow" and "Open in New Window".
If this kind of "dwim-ish" heuristic solves the issue, we should use
it. In situations where such heuristic doesn't give good results,
perhaps this new minor mode we are talking about can also decide which
alternative to prefer when it isn't clear-cut.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-14 23:32 ` Juri Linkov
2021-07-15 1:18 ` Stefan Monnier
@ 2021-07-15 6:24 ` Eli Zaretskii
2021-07-15 22:28 ` Juri Linkov
1 sibling, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-15 6:24 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Date: Thu, 15 Jul 2021 02:32:14 +0300
> Cc: philipk@posteo.net, Richard Stallman <rms@gnu.org>, spacibba@aol.com,
> emacs-devel@gnu.org, arthur.miller@live.com, dgutov@yandex.ru, ghe@sdf.org,
> drew.adams@oracle.com
>
> This means there is a need to create a new mode that when enabled
> will bind the context menu directly to the key down-mouse-3
> and unbind mouse-3 from mouse-save-then-kill
> when the user wants to pop up it immediately.
Instead of unbinding mouse-save-then-kill, this new mode could rebind
it to C-down-mouse-3, so that users could still invoke the command.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-15 6:22 ` Eli Zaretskii
@ 2021-07-15 22:23 ` Juri Linkov
2021-07-16 6:49 ` Eli Zaretskii
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-15 22:23 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
>> When the menu-bar is disabled, then C-down-mouse-3 pops up the global
>> menu where "Edit" contains "Paste". When the menu-bar is enabled,
>> then some modes include "Paste" (e.g. calculator-paste) in a buffer-local menu.
>
> So you want to have the same menus on mouse-3, including when the menu
> bar is not disabled?
Exactly.
>> And some modes like org-mode, flyspell-mode, etc. already redefine
>> down-mouse-3 to pop up context menus.
>
> But those modes define those context menus on special parts of
> display, where pasting etc. makes less sense, no?
When the buffer is not read-only, I see no reason to disallow
pasting text because e.g. in flyspell-mode it makes sense
to paste a correction to a misspelled word, etc.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-15 6:24 ` Eli Zaretskii
@ 2021-07-15 22:28 ` Juri Linkov
2021-07-16 6:51 ` Eli Zaretskii
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-15 22:28 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
>> This means there is a need to create a new mode that when enabled
>> will bind the context menu directly to the key down-mouse-3
>> and unbind mouse-3 from mouse-save-then-kill
>> when the user wants to pop up it immediately.
>
> Instead of unbinding mouse-save-then-kill, this new mode could rebind
> it to C-down-mouse-3, so that users could still invoke the command.
Swapping mouse-3 and C-down-mouse-3 would be a good thing. Another
necessary change to emulate the behavior of modern apps is to rebind
mouse-save-then-kill to S-mouse-1.
Maybe it's possible to add a delay so that the current binding [S-down-mouse-1]
could pop up mouse-appearance-menu only after a delay, e.g. 450ms.
But on releasing S-mouse-1 immediately, it could use mouse-save-then-kill
as modern apps do.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-15 1:18 ` Stefan Monnier
@ 2021-07-15 22:31 ` Juri Linkov
2021-07-16 15:46 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-15 22:31 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, Richard Stallman, spacibba, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>> To pop up it immediately, I see no better way than currently
>> is implemented for C-down-mouse-3 because then C-h k C-down-mouse-3
>> works smoothly as well as Undo selected from the menu keeps undoing
>> on consequent calls, etc.
>
> Indeed. Maybe we can bring them together by (when
> `mouse-3-down-context-menu` is t) using a remapping from `down-mouse-3`
> to `context-menu`. It seems pretty intrusive, tho :-(
I don't know what I did wrong but this does nothing:
(global-set-key [down-mouse-3] [context-menu])
(global-set-key [context-menu]
`(menu-item ,(purecopy "Context Menu") ignore
:filter (lambda (_) (mouse-context-menu-map))))
whereas this works fine:
(global-set-key [down-mouse-3]
`(menu-item ,(purecopy "Context Menu") ignore
:filter (lambda (_) (mouse-context-menu-map))))
> Maybe a better option is to make it so the `context-menu` event is kept
> internal (give it a less attractive name), i.e. just an implementation
> detail for the case where the menu pops up after a delay.
A minor mode will also need to remove this binding
(define-key key-translation-map [mouse-3]
#'mouse--click-3-maybe-context-menu)
when `mouse-3-down-context-menu` is t, and to unbind
[mouse-3] from 'mouse-save-then-kill' because sometimes
'mouse-save-then-kill' still gets called, I haven't investigated why.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-15 22:23 ` Juri Linkov
@ 2021-07-16 6:49 ` Eli Zaretskii
2021-07-16 18:59 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-16 6:49 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Cc: monnier@iro.umontreal.ca, philipk@posteo.net, rms@gnu.org,
> spacibba@aol.com, emacs-devel@gnu.org, arthur.miller@live.com,
> dgutov@yandex.ru, ghe@sdf.org, drew.adams@oracle.com
> Date: Fri, 16 Jul 2021 01:23:09 +0300
>
> >> And some modes like org-mode, flyspell-mode, etc. already redefine
> >> down-mouse-3 to pop up context menus.
> >
> > But those modes define those context menus on special parts of
> > display, where pasting etc. makes less sense, no?
>
> When the buffer is not read-only, I see no reason to disallow
> pasting text because e.g. in flyspell-mode it makes sense
> to paste a correction to a misspelled word, etc.
IMO, when mouse-3 is clocked on a misspelled word, it makes much more
sense to assume the user wants to fix that word than that the user
wants to paste.
But here's an idea: how about merging the two menus into one in these
cases? We could add a top-level menu with the two alternatives, each
one would then drop down one of the two possible menus.
Alternatively, just make a long menu by concatenating the contents of
the two original ones.
WDYT?
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-15 22:28 ` Juri Linkov
@ 2021-07-16 6:51 ` Eli Zaretskii
2021-07-16 18:56 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-16 6:51 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Cc: monnier@iro.umontreal.ca, philipk@posteo.net, rms@gnu.org,
> spacibba@aol.com, emacs-devel@gnu.org, arthur.miller@live.com,
> dgutov@yandex.ru, ghe@sdf.org, drew.adams@oracle.com
> Date: Fri, 16 Jul 2021 01:28:30 +0300
>
> Another necessary change to emulate the behavior of modern apps is
> to rebind mouse-save-then-kill to S-mouse-1.
>
> Maybe it's possible to add a delay so that the current binding [S-down-mouse-1]
> could pop up mouse-appearance-menu only after a delay, e.g. 450ms.
> But on releasing S-mouse-1 immediately, it could use mouse-save-then-kill
> as modern apps do.
Once again, I think lumping such different commands on the same mouse
gesture controlled by some more-or-less arbitrary timeout is not a
good UI. We should avoid that as much as we possible can.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-15 22:31 ` Juri Linkov
@ 2021-07-16 15:46 ` Stefan Monnier
2021-07-16 18:50 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-16 15:46 UTC (permalink / raw)
To: Juri Linkov
Cc: Richard Stallman, spacibba, philipk, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>> Indeed. Maybe we can bring them together by (when
>> `mouse-3-down-context-menu` is t) using a remapping from `down-mouse-3`
>> to `context-menu`. It seems pretty intrusive, tho :-(
> I don't know what I did wrong but this does nothing:
> (global-set-key [down-mouse-3] [context-menu])
This is a keyboard macro and the "extra info" (the `posn`) associated
with the `down-mouse-3` will not be passed on to the `context-menu`
event, so that's probably why it "does nothing".
Maybe something like
(global-set-key [down-mouse-3]
(lambda (event)
(interactive "e")
(execute-kbd-macro (vector `(context-menu . ,(cdr event))))))
??
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 15:46 ` Stefan Monnier
@ 2021-07-16 18:50 ` Juri Linkov
2021-07-16 19:25 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-16 18:50 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, Richard Stallman, spacibba, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>> I don't know what I did wrong but this does nothing:
>> (global-set-key [down-mouse-3] [context-menu])
>
> This is a keyboard macro and the "extra info" (the `posn`) associated
> with the `down-mouse-3` will not be passed on to the `context-menu`
> event, so that's probably why it "does nothing".
>
> Maybe something like
>
> (global-set-key [down-mouse-3]
> (lambda (event)
> (interactive "e")
> (execute-kbd-macro (vector `(context-menu . ,(cdr event))))))
>
> ??
Nope, this doesn't fly. Then for debugging tried:
(global-set-key [context-menu]
(lambda (&rest args) (interactive) (message "=> %S" args)))
(global-set-key [down-mouse-3]
(lambda (event)
(interactive "e")
(execute-kbd-macro (vector `(context-menu . ,(cdr event))))))
but it prints => nil
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 6:51 ` Eli Zaretskii
@ 2021-07-16 18:56 ` Juri Linkov
2021-07-16 23:13 ` Stefan Kangas
2021-07-17 6:02 ` Eli Zaretskii
0 siblings, 2 replies; 59+ messages in thread
From: Juri Linkov @ 2021-07-16 18:56 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
>> Another necessary change to emulate the behavior of modern apps is
>> to rebind mouse-save-then-kill to S-mouse-1.
>>
>> Maybe it's possible to add a delay so that the current binding [S-down-mouse-1]
>> could pop up mouse-appearance-menu only after a delay, e.g. 450ms.
>> But on releasing S-mouse-1 immediately, it could use mouse-save-then-kill
>> as modern apps do.
>
> Once again, I think lumping such different commands on the same mouse
> gesture controlled by some more-or-less arbitrary timeout is not a
> good UI. We should avoid that as much as we possible can.
Then maybe it could be enabled by the same mode e.g. 'modern-mouse-mode'.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 6:49 ` Eli Zaretskii
@ 2021-07-16 18:59 ` Juri Linkov
2021-07-18 5:13 ` Tak Kunihiro
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-16 18:59 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
>> >> And some modes like org-mode, flyspell-mode, etc. already redefine
>> >> down-mouse-3 to pop up context menus.
>> >
>> > But those modes define those context menus on special parts of
>> > display, where pasting etc. makes less sense, no?
>>
>> When the buffer is not read-only, I see no reason to disallow
>> pasting text because e.g. in flyspell-mode it makes sense
>> to paste a correction to a misspelled word, etc.
>
> IMO, when mouse-3 is clocked on a misspelled word, it makes much more
> sense to assume the user wants to fix that word than that the user
> wants to paste.
>
> But here's an idea: how about merging the two menus into one in these
> cases? We could add a top-level menu with the two alternatives, each
> one would then drop down one of the two possible menus.
> Alternatively, just make a long menu by concatenating the contents of
> the two original ones.
>
> WDYT?
Tak Kunihiro had a very good idea of using a hook-like variable
that contains a list of functions that return parts of the whole
context-menu, then these parts will be collected into the final menu.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 18:50 ` Juri Linkov
@ 2021-07-16 19:25 ` Stefan Monnier
0 siblings, 0 replies; 59+ messages in thread
From: Stefan Monnier @ 2021-07-16 19:25 UTC (permalink / raw)
To: Juri Linkov
Cc: Richard Stallman, spacibba, philipk, emacs-devel, arthur.miller,
dgutov, ghe, drew.adams
>> (global-set-key [down-mouse-3]
>> (lambda (event)
>> (interactive "e")
>> (execute-kbd-macro (vector `(context-menu . ,(cdr event))))))
>>
>> ??
>
> Nope, this doesn't fly. Then for debugging tried:
>
> (global-set-key [context-menu]
> (lambda (&rest args) (interactive) (message "=> %S" args)))
>
> (global-set-key [down-mouse-3]
> (lambda (event)
> (interactive "e")
> (execute-kbd-macro (vector `(context-menu . ,(cdr event))))))
Then I guess the only way that will work is if the rewrite from `down-mouse-3`
to `context-menu` is performed directly via
`input-decode-map/function-key-map/key-translation-map`.
Of course, you can also forgo rewriting to `context-menu` and
have the `down-mouse-3` event directly bring up the menu.
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 18:56 ` Juri Linkov
@ 2021-07-16 23:13 ` Stefan Kangas
2021-07-17 6:22 ` Eli Zaretskii
2021-07-17 6:02 ` Eli Zaretskii
1 sibling, 1 reply; 59+ messages in thread
From: Stefan Kangas @ 2021-07-16 23:13 UTC (permalink / raw)
To: Juri Linkov
Cc: Ergus, Richard Stallman, Philip K., Emacs developers,
Stefan Monnier, Arthur Miller, Dmitry Gutov, Gregory Heytings,
Eli Zaretskii, Drew Adams
Juri Linkov <juri@linkov.net> writes:
> Then maybe it could be enabled by the same mode e.g. 'modern-mouse-mode'.
But why make this conditional on a timeout? I would have assumed the
"modern" way would be to just show the context menu unconditionally on
mouse-3, and ask for a modifier if you want to manipulate the region.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 18:56 ` Juri Linkov
2021-07-16 23:13 ` Stefan Kangas
@ 2021-07-17 6:02 ` Eli Zaretskii
2021-07-19 17:48 ` Stefan Kangas
1 sibling, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-17 6:02 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, drew.adams
> From: Juri Linkov <juri@linkov.net>
> Cc: monnier@iro.umontreal.ca, philipk@posteo.net, rms@gnu.org,
> spacibba@aol.com, emacs-devel@gnu.org, arthur.miller@live.com,
> dgutov@yandex.ru, ghe@sdf.org, drew.adams@oracle.com
> Date: Fri, 16 Jul 2021 21:56:56 +0300
>
> > Once again, I think lumping such different commands on the same mouse
> > gesture controlled by some more-or-less arbitrary timeout is not a
> > good UI. We should avoid that as much as we possible can.
>
> Then maybe it could be enabled by the same mode e.g. 'modern-mouse-mode'.
IMO, it's not a good idea to use "modern" in variable names.
Something like mouse-friendly-mode or alt-mouse-mode, maybe?
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 23:13 ` Stefan Kangas
@ 2021-07-17 6:22 ` Eli Zaretskii
2021-07-17 21:46 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Eli Zaretskii @ 2021-07-17 6:22 UTC (permalink / raw)
To: Stefan Kangas
Cc: philipk, rms, spacibba, emacs-devel, monnier, arthur.miller,
dgutov, ghe, juri, drew.adams
> From: Stefan Kangas <stefan@marxist.se>
> Date: Sat, 17 Jul 2021 01:13:45 +0200
> Cc: Eli Zaretskii <eliz@gnu.org>, "Philip K." <philipk@posteo.net>, Richard Stallman <rms@gnu.org>,
> Ergus <spacibba@aol.com>, Emacs developers <emacs-devel@gnu.org>,
> Stefan Monnier <monnier@iro.umontreal.ca>, Arthur Miller <arthur.miller@live.com>,
> Dmitry Gutov <dgutov@yandex.ru>, Gregory Heytings <ghe@sdf.org>, Drew Adams <drew.adams@oracle.com>
>
> Juri Linkov <juri@linkov.net> writes:
>
> > Then maybe it could be enabled by the same mode e.g. 'modern-mouse-mode'.
>
> But why make this conditional on a timeout? I would have assumed the
> "modern" way would be to just show the context menu unconditionally on
> mouse-3, and ask for a modifier if you want to manipulate the region.
AFAIU, the suggestion is to use the minor mode to _replace_ the
timeout.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-17 6:22 ` Eli Zaretskii
@ 2021-07-17 21:46 ` Juri Linkov
0 siblings, 0 replies; 59+ messages in thread
From: Juri Linkov @ 2021-07-17 21:46 UTC (permalink / raw)
To: Eli Zaretskii
Cc: philipk, rms, Stefan Kangas, spacibba, emacs-devel, monnier,
arthur.miller, dgutov, ghe, drew.adams
>> > Then maybe it could be enabled by the same mode e.g. 'modern-mouse-mode'.
>>
>> But why make this conditional on a timeout? I would have assumed the
>> "modern" way would be to just show the context menu unconditionally on
>> mouse-3, and ask for a modifier if you want to manipulate the region.
>
> AFAIU, the suggestion is to use the minor mode to _replace_ the
> timeout.
Indeed. And maybe the same mode could still provide an option to
use the timeout if enabling the timeout by default is not an option.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-16 18:59 ` Juri Linkov
@ 2021-07-18 5:13 ` Tak Kunihiro
2021-07-18 15:53 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Tak Kunihiro @ 2021-07-18 5:13 UTC (permalink / raw)
To: Juri Linkov
Cc: spacibba, rms, philipk, emacs-devel, tkk, monnier, arthur.miller,
dgutov, ghe, Eli Zaretskii, drew.adams
>> IMO, when mouse-3 is clocked on a misspelled word, it makes much more
>> sense to assume the user wants to fix that word than that the user
>> wants to paste.
>>
>> But here's an idea: how about merging the two menus into one in these
>> cases? We could add a top-level menu with the two alternatives, each
>> one would then drop down one of the two possible menus.
>> Alternatively, just make a long menu by concatenating the contents of
>> the two original ones.
>>
>> WDYT?
>
> Tak Kunihiro had a very good idea of using a hook-like variable
> that contains a list of functions that return parts of the whole
> context-menu, then these parts will be collected into the final menu.
The idea is to define a variable (something like below;
poplife-menu-candidates) with list of functions that returns nil or
keymap (something like below; poplife-mouse-word-menu). A function
(something like below; poplife-context-menu) will test each of them
until it gets keymap.
In this way, to revise context menu is easy.
(defvar poplife-menu-candidates
'(poplife-mouse-help-menu ; HELP menu
poplife-mouse-info-menu ; INFO menu
poplife-mouse-file-menu ; FILE menu
poplife-mouse-dir-menu ; DIR menu
poplife-mouse-word-menu ; WORD menu
poplife-mouse-url-menu ; URL menu
;; menu-bar-edit-menu ; EDIT menu (default)
poplife-mouse-edit-menu) ; EDIT menu
"List of candidates for context menu.
Candidates are function or keymap. They will be evaluated in the
order of the list. A function should accept mouse EVENT, and
return keymap or nil. The last candidate should return valid
keymap.")
(defun poplife-mouse-word-menu (event)
"Return 'flyspell-correct-word when word under mouse cursor on EVENT is incorrect."
(and
(not (region-active-p))
;; Check face by (what-cursor-position t) or C-u C-x =.
(let ((faces-at-point (mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at (posn-point (event-start event))))))
(when (or (member 'flyspell-incorrect faces-at-point)
(member 'flyspell-duplicate faces-at-point))
#'flyspell-correct-word))))
(defun poplife-context-menu (event)
"Return key's definition depending on thing under mouse click EVENT.
Items in `poplife-menu-candidates' are examined sequentially.
See `define-key' for the key's definition"
;; ~/.emacs.d/init.el ~/.emacs.d/ https://www.gnu.org/software/emacs/
(when (fboundp 'secondary-selection-to-region) ; 26.1
(secondary-selection-to-region)) ; When there is only secondary, turn it to region.
(let ((candidates poplife-menu-candidates)
context-menu)
(while (not context-menu)
(let ((item (car candidates)))
(setq candidates (cdr candidates))
;; See how dired-guess-shell-alist-user is used in dired-guess-default.
(setq context-menu (cond ((fboundp item)
(funcall item event))
((and (symbolp item)
(keymapp (symbol-value item)))
(symbol-value item))
(t ; else
nil)))))
context-menu))
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-18 5:13 ` Tak Kunihiro
@ 2021-07-18 15:53 ` Stefan Monnier
2021-07-19 15:55 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-18 15:53 UTC (permalink / raw)
To: Tak Kunihiro
Cc: Juri Linkov, Eli Zaretskii, philipk, rms, spacibba, emacs-devel,
arthur.miller, dgutov, ghe, drew.adams, tkk
> The idea is to define a variable (something like below;
> poplife-menu-candidates) with list of functions that returns nil or
> keymap (something like below; poplife-mouse-word-menu). A function
> (something like below; poplife-context-menu) will test each of them
> until it gets keymap.
Note: such a variable should have a name that ends in `-hook` or
`-functions` and should be manipulated with the usual hook functions,
e.g. `run-hook-wrapped`.
Note also that the `context-menu-function` already mentioned offers the
same kind of functionality (except you need to use `add-function`
instead of `add-hook` and it offers a bit more flexibility at the cost
of extra work).
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-18 15:53 ` Stefan Monnier
@ 2021-07-19 15:55 ` Juri Linkov
2021-07-19 16:37 ` Stefan Monnier
2021-07-19 19:59 ` Ergus via Emacs development discussions.
0 siblings, 2 replies; 59+ messages in thread
From: Juri Linkov @ 2021-07-19 15:55 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, rms, spacibba, emacs-devel, Tak Kunihiro, tkk,
arthur.miller, dgutov, ghe, Eli Zaretskii, drew.adams
>> The idea is to define a variable (something like below;
>> poplife-menu-candidates) with list of functions that returns nil or
>> keymap (something like below; poplife-mouse-word-menu). A function
>> (something like below; poplife-context-menu) will test each of them
>> until it gets keymap.
>
> Note: such a variable should have a name that ends in `-hook` or
> `-functions` and should be manipulated with the usual hook functions,
> e.g. `run-hook-wrapped`.
>
> Note also that the `context-menu-function` already mentioned offers the
> same kind of functionality (except you need to use `add-function`
> instead of `add-hook` and it offers a bit more flexibility at the cost
> of extra work).
For easier customization, 'context-menu-functions' could be more hook-like,
so e.g. when the default value will be defined as
(defcustom context-menu-functions '(context-menu-region)
then users could easily customize the default value.
More minor modes could add own functions to the global value, e.g.
(add-hook 'context-menu-functions 'bug-reference-context-menu -5)
(add-hook 'context-menu-functions 'goto-address-context-menu -10)
Major modes could modify buffer-local values:
(add-hook 'context-menu-functions 'dired-context-menu nil t)
(add-hook 'context-menu-functions 'info-context-menu -5 t)
(add-hook 'context-menu-functions 'dictionary-context-menu -10 t)
Then the user could have the final say by using another option, e.g.
(defcustom context-menu-filter nil
where a user-defined function (that can be extended using `add-function`)
will accept a menu constructed by context-menu-functions
and return the modified menu with reordered/removed/added items
to user's liking.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-19 15:55 ` Juri Linkov
@ 2021-07-19 16:37 ` Stefan Monnier
2021-07-20 20:52 ` Juri Linkov
2021-07-19 19:59 ` Ergus via Emacs development discussions.
1 sibling, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-19 16:37 UTC (permalink / raw)
To: Juri Linkov
Cc: Tak Kunihiro, Eli Zaretskii, philipk, rms, spacibba, emacs-devel,
arthur.miller, dgutov, ghe, drew.adams, tkk
> For easier customization, 'context-menu-functions' could be more hook-like,
FWIW, I originally thought of using a `-function` because I expect there
are situations where you'll want to override the normal menu with a more
specific one (rather than adding entries only), or move the normal
menu to a submenu.
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-17 6:02 ` Eli Zaretskii
@ 2021-07-19 17:48 ` Stefan Kangas
2021-07-19 18:08 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Stefan Kangas @ 2021-07-19 17:48 UTC (permalink / raw)
To: Eli Zaretskii
Cc: Philip K., Richard Stallman, Ergus, Emacs developers,
Stefan Monnier, Arthur Miller, Dmitry Gutov, Gregory Heytings,
Juri Linkov, Drew Adams
Eli Zaretskii <eliz@gnu.org> writes:
> IMO, it's not a good idea to use "modern" in variable names.
>
> Something like mouse-friendly-mode or alt-mouse-mode, maybe?
Sure, any of those would work.
I personally don't mind the word "modern" too much, as it signals
something along the lines of: "avoid this mostly if you are a veteran
user and are too used to the old way". But I'm all in favour of
finding a better word than "modern" to describe the grab-bag of
features that some of us would like to see under the general umbrella
"making Emacs more similar to contemporary software [unless there is
good reason to be different]".
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-19 17:48 ` Stefan Kangas
@ 2021-07-19 18:08 ` Stefan Monnier
0 siblings, 0 replies; 59+ messages in thread
From: Stefan Monnier @ 2021-07-19 18:08 UTC (permalink / raw)
To: Stefan Kangas
Cc: Eli Zaretskii, Juri Linkov, Philip K., Richard Stallman, Ergus,
Emacs developers, Arthur Miller, Dmitry Gutov, Gregory Heytings,
Drew Adams
Stefan Kangas [2021-07-19 19:48:21] wrote:
> Eli Zaretskii <eliz@gnu.org> writes:
>> IMO, it's not a good idea to use "modern" in variable names.
>> Something like mouse-friendly-mode or alt-mouse-mode, maybe?
> Sure, any of those would work.
To me none of them are any good, because the only useful word there is
`mouse` and it's still pretty vague.
AFAIK the first and most important element is to introduce the concept
of a "context menu" (via `content-menu-function` or something like that)
which modes can use to add there own entries.
Then as a second step comes the choice of how to give access to this
menu, and I wouldn't call that a "mode" because I can't see any reason
why it should be a binary choice. This menu should be available by
default, the only user-config question is which key-binding(s) to use
for it (some of the choices being to override `down-mouse-3` (and hence
`mouse-3`), or to use `down-mouse-3` with a delay, ...).
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-19 15:55 ` Juri Linkov
2021-07-19 16:37 ` Stefan Monnier
@ 2021-07-19 19:59 ` Ergus via Emacs development discussions.
2021-07-20 20:51 ` Juri Linkov
1 sibling, 1 reply; 59+ messages in thread
From: Ergus via Emacs development discussions. @ 2021-07-19 19:59 UTC (permalink / raw)
To: emacs-devel, Juri Linkov, Stefan Monnier
Cc: philipk, rms, Tak Kunihiro, tkk, arthur.miller, dgutov, ghe,
Eli Zaretskii, drew.adams
[-- Attachment #1: Type: text/plain, Size: 2055 bytes --]
Hi Juri:
is there anything to test, a scratch o feature branch? I am very interested in this feature.
On July 19, 2021 5:55:57 PM GMT+02:00, Juri Linkov <juri@linkov.net> wrote:
>>> The idea is to define a variable (something like below;
>>> poplife-menu-candidates) with list of functions that returns nil or
>>> keymap (something like below; poplife-mouse-word-menu). A function
>>> (something like below; poplife-context-menu) will test each of them
>>> until it gets keymap.
>>
>> Note: such a variable should have a name that ends in `-hook` or
>> `-functions` and should be manipulated with the usual hook functions,
>> e.g. `run-hook-wrapped`.
>>
>> Note also that the `context-menu-function` already mentioned offers
>the
>> same kind of functionality (except you need to use `add-function`
>> instead of `add-hook` and it offers a bit more flexibility at the
>cost
>> of extra work).
>
>For easier customization, 'context-menu-functions' could be more
>hook-like,
>so e.g. when the default value will be defined as
>
> (defcustom context-menu-functions '(context-menu-region)
>
>then users could easily customize the default value.
>More minor modes could add own functions to the global value, e.g.
>
> (add-hook 'context-menu-functions 'bug-reference-context-menu -5)
> (add-hook 'context-menu-functions 'goto-address-context-menu -10)
>
>Major modes could modify buffer-local values:
>
> (add-hook 'context-menu-functions 'dired-context-menu nil t)
> (add-hook 'context-menu-functions 'info-context-menu -5 t)
> (add-hook 'context-menu-functions 'dictionary-context-menu -10 t)
>
>Then the user could have the final say by using another option, e.g.
>
> (defcustom context-menu-filter nil
>
>where a user-defined function (that can be extended using
>`add-function`)
>will accept a menu constructed by context-menu-functions
>and return the modified menu with reordered/removed/added items
>to user's liking.
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
[-- Attachment #2: Type: text/html, Size: 2585 bytes --]
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-19 19:59 ` Ergus via Emacs development discussions.
@ 2021-07-20 20:51 ` Juri Linkov
0 siblings, 0 replies; 59+ messages in thread
From: Juri Linkov @ 2021-07-20 20:51 UTC (permalink / raw)
To: Ergus
Cc: philipk, rms, emacs-devel, Tak Kunihiro, tkk, Stefan Monnier,
arthur.miller, dgutov, ghe, Eli Zaretskii, drew.adams
> is there anything to test, a scratch o feature branch? I am very interested
> in this feature.
Now there is a new branch 'feature/context-menu'.
It provides a new mode 'context-menu-mode'
that binds the Context menu to [down-mouse-3].
For testing, it adds the Context menu to the major mode
Info-mode, so clicking anywhere in the Info buffer
pops up the navigation menu, and clicking on links
adds a menu item to open a link.
Also is adds the Context menu to one minor mode
goto-address-mode where clicking on an address
pops up a menu with an item to open that address.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-19 16:37 ` Stefan Monnier
@ 2021-07-20 20:52 ` Juri Linkov
2021-07-20 22:24 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-20 20:52 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, rms, spacibba, emacs-devel, Tak Kunihiro, tkk,
arthur.miller, dgutov, ghe, Eli Zaretskii, drew.adams
>> For easier customization, 'context-menu-functions' could be more hook-like,
>
> FWIW, I originally thought of using a `-function` because I expect there
> are situations where you'll want to override the normal menu with a more
> specific one (rather than adding entries only), or move the normal
> menu to a submenu.
The new branch 'feature/context-menu' now includes also the variable
'context-menu-overriding-function' that can override the normal menu.
But I have no idea on which mode to test it. It makes sense to
override the normal menu in modes that produce too long menus,
such as a long list of candidates in flyspell-mode. But flyspell-mode
already overrides the menu with own [down-mouse-3], and rewriting it to
use context-menu-overriding-function might cause backward-incompatibility bugs.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-20 20:52 ` Juri Linkov
@ 2021-07-20 22:24 ` Stefan Monnier
2021-07-20 23:15 ` Juri Linkov
0 siblings, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-20 22:24 UTC (permalink / raw)
To: Juri Linkov
Cc: Tak Kunihiro, Eli Zaretskii, philipk, rms, spacibba, emacs-devel,
arthur.miller, dgutov, ghe, drew.adams, tkk
> such as a long list of candidates in flyspell-mode. But flyspell-mode
> already overrides the menu with own [down-mouse-3], and rewriting it to
> use context-menu-overriding-function might cause backward-incompatibility bugs.
Yet, it is definitely one of the packages which should use context-menu.
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-20 22:24 ` Stefan Monnier
@ 2021-07-20 23:15 ` Juri Linkov
2021-07-21 4:39 ` Tak Kunihiro
0 siblings, 1 reply; 59+ messages in thread
From: Juri Linkov @ 2021-07-20 23:15 UTC (permalink / raw)
To: Stefan Monnier
Cc: philipk, rms, spacibba, emacs-devel, Tak Kunihiro, tkk,
arthur.miller, dgutov, ghe, Eli Zaretskii, drew.adams
>> such as a long list of candidates in flyspell-mode. But flyspell-mode
>> already overrides the menu with own [down-mouse-3], and rewriting it to
>> use context-menu-overriding-function might cause backward-incompatibility bugs.
>
> Yet, it is definitely one of the packages which should use context-menu.
I realized now that context-menu-overriding-function is not needed
since it's possible just to reverse the priorities, so the
functions at the end of the list in context-menu-functions
will have higher priority. Then for example, flyspell-context-menu
called as the last function can remove all items added by earlier calls
and return a new menu with only own items.
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-20 23:15 ` Juri Linkov
@ 2021-07-21 4:39 ` Tak Kunihiro
2021-07-21 12:45 ` Stefan Monnier
0 siblings, 1 reply; 59+ messages in thread
From: Tak Kunihiro @ 2021-07-21 4:39 UTC (permalink / raw)
To: Juri Linkov
Cc: philipk, rms, spacibba, emacs-devel, tkk, Stefan Monnier,
arthur.miller, dgutov, ghe, Eli Zaretskii, drew.adams
I think most of the time, a thing under mouse cursor matters for
context-menu instead of mode.
For example, when a thing under mouse cursor is with face
'flyspell-incorrect, it is nice to have 'flyspell-correct-word as shown
below.
(defun poplife-mouse-word-menu (event)
"Return 'flyspell-correct-word when word under mouse cursor on EVENT is incorrect."
(and
(not (region-active-p))
(let ((faces-at-point (mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at (posn-point (event-start event))))))
(when (or (member 'flyspell-incorrect faces-at-point)
(member 'flyspell-duplicate faces-at-point))
#'flyspell-correct-word))))
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-21 4:39 ` Tak Kunihiro
@ 2021-07-21 12:45 ` Stefan Monnier
2021-07-21 17:26 ` [External] : " Drew Adams
0 siblings, 1 reply; 59+ messages in thread
From: Stefan Monnier @ 2021-07-21 12:45 UTC (permalink / raw)
To: Tak Kunihiro
Cc: Juri Linkov, Eli Zaretskii, philipk, rms, spacibba, emacs-devel,
arthur.miller, dgutov, ghe, drew.adams, tkk
Tak Kunihiro [2021-07-21 13:39:30] wrote:
> I think most of the time, a thing under mouse cursor matters for
> context-menu instead of mode.
Obviously, but note that the code that decides what "thing under the
cursor" is relevant, and which menu entries should be shown for it is
most likely going to be provided by a mode.
So I guess what I'm saying is that I disagree with "instead of".
Stefan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: Context menus and mouse-3
2021-07-21 17:26 ` [External] : " Drew Adams
@ 2021-07-22 3:49 ` Tak Kunihiro
0 siblings, 0 replies; 59+ messages in thread
From: Tak Kunihiro @ 2021-07-22 3:49 UTC (permalink / raw)
To: emacs-devel@gnu.org
Cc: philipk@posteo.net, rms@gnu.org, spacibba@aol.com, Juri Linkov,
国広卓也, Stefan Monnier,
arthur.miller@live.com, dgutov@yandex.ru, ghe@sdf.org,
Eli Zaretskii, Drew Adams
>>> I think most of the time, a thing under mouse cursor matters for
>>> context-menu instead of mode.
>>
>> Obviously, but note that the code that decides what "thing under the
>> cursor" is relevant, and which menu entries should be shown for it is
>> most likely going to be provided by a mode.
>>
>> So I guess what I'm saying is that I disagree with "instead of".
>
> It's not just modes. It's not just text under the
> mouse pointer. It's not "instead of". Right.
> It's anything you (a user or code) want the menu
> to be.
I think operation double click on icon in file browser is a good
analogue to operation via context menu.
Double click opens an icon.
I think by default, context menu should provide a way to open a
thing under mouse pointer.
What is appropriate open, isn’t obvious.
| thing | open |
|-----------+---------------|
| file | file-file |
| directory | dired |
| url | browse-url |
| function | describe-help |
^ permalink raw reply [flat|nested] 59+ messages in thread
end of thread, other threads:[~2021-07-22 3:49 UTC | newest]
Thread overview: 59+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-09-16 6:28 Context menus and mouse-3 Tak Kunihiro
2020-09-16 14:18 ` Eli Zaretskii
2020-09-16 14:37 ` Thibaut Verron
2020-09-16 15:06 ` Eli Zaretskii
2020-09-16 15:39 ` Thibaut Verron
2020-09-17 3:57 ` Richard Stallman
2020-09-16 19:45 ` Juri Linkov
2020-09-16 23:49 ` Tak Kunihiro
2020-09-17 2:33 ` Tak Kunihiro
2020-09-17 7:43 ` Juri Linkov
2020-09-17 9:22 ` Robert Pluim
2020-09-17 18:59 ` Juri Linkov
2020-09-17 19:41 ` chad
2020-09-18 8:23 ` Juri Linkov
2020-09-18 18:41 ` chad
-- strict thread matches above, loose matches on Subject: below --
2020-09-14 3:06 Context menus and mouse-3 [was: Changes for emacs 28] Drew Adams
2020-09-14 6:11 ` Ergus
2020-09-14 6:28 ` Stefan Monnier
2020-09-15 4:35 ` Richard Stallman
2020-09-15 13:11 ` Stefan Monnier
2021-07-11 23:38 ` Context menus and mouse-3 Juri Linkov
2021-07-12 11:55 ` Eli Zaretskii
2021-07-12 20:56 ` Juri Linkov
2021-07-13 11:32 ` Eli Zaretskii
2021-07-13 23:46 ` Juri Linkov
2021-07-14 4:30 ` Eli Zaretskii
2021-07-14 23:37 ` Juri Linkov
2021-07-15 6:22 ` Eli Zaretskii
2021-07-15 22:23 ` Juri Linkov
2021-07-16 6:49 ` Eli Zaretskii
2021-07-16 18:59 ` Juri Linkov
2021-07-18 5:13 ` Tak Kunihiro
2021-07-18 15:53 ` Stefan Monnier
2021-07-19 15:55 ` Juri Linkov
2021-07-19 16:37 ` Stefan Monnier
2021-07-20 20:52 ` Juri Linkov
2021-07-20 22:24 ` Stefan Monnier
2021-07-20 23:15 ` Juri Linkov
2021-07-21 4:39 ` Tak Kunihiro
2021-07-21 12:45 ` Stefan Monnier
2021-07-21 17:26 ` [External] : " Drew Adams
2021-07-22 3:49 ` Tak Kunihiro
2021-07-19 19:59 ` Ergus via Emacs development discussions.
2021-07-20 20:51 ` Juri Linkov
2021-07-12 22:32 ` Stefan Monnier
2021-07-12 23:56 ` Juri Linkov
2021-07-13 3:01 ` Stefan Monnier
2021-07-13 23:32 ` Juri Linkov
2021-07-14 2:14 ` Stefan Monnier
2021-07-14 23:32 ` Juri Linkov
2021-07-15 1:18 ` Stefan Monnier
2021-07-15 22:31 ` Juri Linkov
2021-07-16 15:46 ` Stefan Monnier
2021-07-16 18:50 ` Juri Linkov
2021-07-16 19:25 ` Stefan Monnier
2021-07-15 6:24 ` Eli Zaretskii
2021-07-15 22:28 ` Juri Linkov
2021-07-16 6:51 ` Eli Zaretskii
2021-07-16 18:56 ` Juri Linkov
2021-07-16 23:13 ` Stefan Kangas
2021-07-17 6:22 ` Eli Zaretskii
2021-07-17 21:46 ` Juri Linkov
2021-07-17 6:02 ` Eli Zaretskii
2021-07-19 17:48 ` Stefan Kangas
2021-07-19 18:08 ` Stefan Monnier
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.