diff options
| author | Mattias EngdegÄrd | 2019-10-25 11:16:39 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2019-11-27 13:40:29 +0100 |
| commit | 4eb7db5d4b84708912c63a77569c8adeeff6c640 (patch) | |
| tree | e4f014f3b2b2407ec74e875a8ef2ab3bc37b2225 /lisp | |
| parent | e7b4c248a6d2a2eca19a2a362103a7f24cfe30fc (diff) | |
| download | emacs-4eb7db5d4b84708912c63a77569c8adeeff6c640.tar.gz emacs-4eb7db5d4b84708912c63a77569c8adeeff6c640.zip | |
Mouse rectangular region selection (bug#38013)
Make it possible to select a rectangular region using the mouse.
The standard binding is C-M-mouse-1.
* lisp/mouse.el (mouse-scroll-subr): Add ADJUST argument.
(mouse-drag-region-rectangle): New.
* lisp/rect.el (rectangle--reset-point-crutches): New.
(rectangle--reset-crutches): Use 'rectangle--reset-point-crutches'.
* src/xdisp.c (remember_mouse_glyph, syms_of_xdisp):
Add 'mouse-fine-grained-tracking'.
* doc/lispref/commands.texi (Motion Events):
Document 'mouse-fine-grained-tracking'.
* doc/emacs/frames.texi (Mouse Commands):
* doc/emacs/killing.texi (Rectangles):
* etc/NEWS: Document rectangular selection with the mouse.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/mouse.el | 113 | ||||
| -rw-r--r-- | lisp/rect.el | 8 |
2 files changed, 118 insertions, 3 deletions
diff --git a/lisp/mouse.el b/lisp/mouse.el index c91760a7348..f076e90bd93 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el | |||
| @@ -1045,10 +1045,12 @@ the mouse has moved. However, it always scrolls at least the number | |||
| 1045 | of lines specified by this variable." | 1045 | of lines specified by this variable." |
| 1046 | :type 'integer) | 1046 | :type 'integer) |
| 1047 | 1047 | ||
| 1048 | (defun mouse-scroll-subr (window jump &optional overlay start) | 1048 | (defun mouse-scroll-subr (window jump &optional overlay start adjust) |
| 1049 | "Scroll the window WINDOW, JUMP lines at a time, until new input arrives. | 1049 | "Scroll the window WINDOW, JUMP lines at a time, until new input arrives. |
| 1050 | If OVERLAY is an overlay, let it stretch from START to the far edge of | 1050 | If OVERLAY is an overlay, let it stretch from START to the far edge of |
| 1051 | the newly visible text. | 1051 | the newly visible text. |
| 1052 | ADJUST, if non-nil, is a function, without arguments, to call after | ||
| 1053 | setting point. | ||
| 1052 | Upon exit, point is at the far edge of the newly visible text." | 1054 | Upon exit, point is at the far edge of the newly visible text." |
| 1053 | (cond | 1055 | (cond |
| 1054 | ((and (> jump 0) (< jump mouse-scroll-min-lines)) | 1056 | ((and (> jump 0) (< jump mouse-scroll-min-lines)) |
| @@ -1077,6 +1079,8 @@ Upon exit, point is at the far edge of the newly visible text." | |||
| 1077 | ;; so that we don't mess up the selected window. | 1079 | ;; so that we don't mess up the selected window. |
| 1078 | (or (eq window (selected-window)) | 1080 | (or (eq window (selected-window)) |
| 1079 | (goto-char opoint)) | 1081 | (goto-char opoint)) |
| 1082 | (when adjust | ||
| 1083 | (funcall adjust)) | ||
| 1080 | (sit-for mouse-scroll-delay))))) | 1084 | (sit-for mouse-scroll-delay))))) |
| 1081 | (or (eq window (selected-window)) | 1085 | (or (eq window (selected-window)) |
| 1082 | (goto-char opoint)))) | 1086 | (goto-char opoint)))) |
| @@ -1960,6 +1964,113 @@ When there is no region, this function does nothing." | |||
| 1960 | (move-overlay mouse-secondary-overlay (region-beginning) (region-end)))) | 1964 | (move-overlay mouse-secondary-overlay (region-beginning) (region-end)))) |
| 1961 | 1965 | ||
| 1962 | 1966 | ||
| 1967 | (defun mouse-drag-region-rectangle (start-event) | ||
| 1968 | "Set the region to the rectangle that the mouse is dragged over. | ||
| 1969 | This must be bound to a button-down mouse event." | ||
| 1970 | (interactive "e") | ||
| 1971 | (let* ((scroll-margin 0) | ||
| 1972 | (start-pos (event-start start-event)) | ||
| 1973 | (start-posn (event-start start-event)) | ||
| 1974 | (start-point (posn-point start-posn)) | ||
| 1975 | (start-window (posn-window start-posn)) | ||
| 1976 | (start-hscroll (window-hscroll start-window)) | ||
| 1977 | (start-col (+ (car (posn-col-row start-pos)) start-hscroll)) | ||
| 1978 | (bounds (window-edges start-window)) | ||
| 1979 | (top (nth 1 bounds)) | ||
| 1980 | (bottom (if (window-minibuffer-p start-window) | ||
| 1981 | (nth 3 bounds) | ||
| 1982 | (1- (nth 3 bounds)))) | ||
| 1983 | (dragged nil) | ||
| 1984 | (old-track-mouse track-mouse) | ||
| 1985 | (old-mouse-fine-grained-tracking mouse-fine-grained-tracking) | ||
| 1986 | ;; For right-to-left text, columns are counted from the right margin; | ||
| 1987 | ;; translate from mouse events, which always count from the left. | ||
| 1988 | (adjusted-col (lambda (col) | ||
| 1989 | (if (eq (current-bidi-paragraph-direction) | ||
| 1990 | 'right-to-left) | ||
| 1991 | (- (frame-text-cols) col -1) | ||
| 1992 | col))) | ||
| 1993 | (map (make-sparse-keymap))) | ||
| 1994 | (define-key map [switch-frame] #'ignore) | ||
| 1995 | (define-key map [select-window] #'ignore) | ||
| 1996 | (define-key map [mouse-movement] | ||
| 1997 | (lambda (event) | ||
| 1998 | (interactive "e") | ||
| 1999 | (unless dragged | ||
| 2000 | ;; This is actually a drag. | ||
| 2001 | (setq dragged t) | ||
| 2002 | (mouse-minibuffer-check start-event) | ||
| 2003 | (deactivate-mark) | ||
| 2004 | (posn-set-point start-pos) | ||
| 2005 | (rectangle-mark-mode) | ||
| 2006 | ;; Only tell rectangle about the exact column if we are possibly | ||
| 2007 | ;; beyond end-of-line or in a tab, since the column we got from | ||
| 2008 | ;; the mouse position isn't necessarily accurate for use in | ||
| 2009 | ;; specifying a rectangle (which uses the `move-to-column' | ||
| 2010 | ;; measure). | ||
| 2011 | (when (or (eolp) (eq (following-char) ?\t)) | ||
| 2012 | (let ((col (funcall adjusted-col start-col))) | ||
| 2013 | (rectangle--col-pos col 'mark) | ||
| 2014 | (rectangle--col-pos col 'point)))) | ||
| 2015 | |||
| 2016 | (let* ((posn (event-end event)) | ||
| 2017 | (window (posn-window posn)) | ||
| 2018 | (hscroll (if (window-live-p window) | ||
| 2019 | (window-hscroll window) | ||
| 2020 | 0)) | ||
| 2021 | (mouse-pos (mouse-position)) | ||
| 2022 | (mouse-col (+ (cadr mouse-pos) hscroll)) | ||
| 2023 | (mouse-row (cddr mouse-pos)) | ||
| 2024 | (set-col (lambda () | ||
| 2025 | (if (or (eolp) (eq (following-char) ?\t)) | ||
| 2026 | (rectangle--col-pos | ||
| 2027 | (funcall adjusted-col mouse-col) 'point) | ||
| 2028 | (rectangle--reset-point-crutches))))) | ||
| 2029 | (if (and (eq window start-window) | ||
| 2030 | mouse-row | ||
| 2031 | (<= top mouse-row (1- bottom))) | ||
| 2032 | ;; Drag inside the same window. | ||
| 2033 | (progn | ||
| 2034 | (posn-set-point posn) | ||
| 2035 | (funcall set-col)) | ||
| 2036 | ;; Drag outside the window: scroll. | ||
| 2037 | (cond | ||
| 2038 | ((null mouse-row)) | ||
| 2039 | ((< mouse-row top) | ||
| 2040 | (mouse-scroll-subr | ||
| 2041 | start-window (- mouse-row top) nil start-point | ||
| 2042 | set-col)) | ||
| 2043 | ((>= mouse-row bottom) | ||
| 2044 | (mouse-scroll-subr | ||
| 2045 | start-window (1+ (- mouse-row bottom)) nil start-point | ||
| 2046 | set-col))))))) | ||
| 2047 | (condition-case err | ||
| 2048 | (progn | ||
| 2049 | (setq track-mouse t) | ||
| 2050 | (setq mouse-fine-grained-tracking t) | ||
| 2051 | (set-transient-map | ||
| 2052 | map t | ||
| 2053 | (lambda () | ||
| 2054 | (setq track-mouse old-track-mouse) | ||
| 2055 | (setq mouse-fine-grained-tracking old-mouse-fine-grained-tracking) | ||
| 2056 | (when (or (not dragged) | ||
| 2057 | (not (mark)) | ||
| 2058 | (equal (rectangle-dimensions (mark) (point)) '(0 . 1))) | ||
| 2059 | ;; No nontrivial region selected; deactivate rectangle mode. | ||
| 2060 | (deactivate-mark))))) | ||
| 2061 | ;; Clean up in case something went wrong. | ||
| 2062 | (error (setq track-mouse old-track-mouse) | ||
| 2063 | (setq mouse-fine-grained-tracking old-mouse-fine-grained-tracking) | ||
| 2064 | (signal (car err) (cdr err)))))) | ||
| 2065 | |||
| 2066 | ;; The drag event must be bound to something but does not need any effect, | ||
| 2067 | ;; as everything takes place in `mouse-drag-region-rectangle'. | ||
| 2068 | ;; The click event can be anything; `mouse-set-point' is just a convenience. | ||
| 2069 | (global-set-key [C-M-down-mouse-1] #'mouse-drag-region-rectangle) | ||
| 2070 | (global-set-key [C-M-drag-mouse-1] #'ignore) | ||
| 2071 | (global-set-key [C-M-mouse-1] #'mouse-set-point) | ||
| 2072 | |||
| 2073 | |||
| 1963 | (defcustom mouse-buffer-menu-maxlen 20 | 2074 | (defcustom mouse-buffer-menu-maxlen 20 |
| 1964 | "Number of buffers in one pane (submenu) of the buffer menu. | 2075 | "Number of buffers in one pane (submenu) of the buffer menu. |
| 1965 | If we have lots of buffers, divide them into groups of | 2076 | If we have lots of buffers, divide them into groups of |
diff --git a/lisp/rect.el b/lisp/rect.el index 4d4d6146f21..1109786fc5b 100644 --- a/lisp/rect.el +++ b/lisp/rect.el | |||
| @@ -133,11 +133,15 @@ Point is at the end of the segment of this line within the rectangle." | |||
| 133 | (defun rectangle--crutches () | 133 | (defun rectangle--crutches () |
| 134 | (cons rectangle--mark-crutches | 134 | (cons rectangle--mark-crutches |
| 135 | (window-parameter nil 'rectangle--point-crutches))) | 135 | (window-parameter nil 'rectangle--point-crutches))) |
| 136 | (defun rectangle--reset-crutches () | 136 | |
| 137 | (kill-local-variable 'rectangle--mark-crutches) | 137 | (defun rectangle--reset-point-crutches () |
| 138 | (if (window-parameter nil 'rectangle--point-crutches) | 138 | (if (window-parameter nil 'rectangle--point-crutches) |
| 139 | (setf (window-parameter nil 'rectangle--point-crutches) nil))) | 139 | (setf (window-parameter nil 'rectangle--point-crutches) nil))) |
| 140 | 140 | ||
| 141 | (defun rectangle--reset-crutches () | ||
| 142 | (kill-local-variable 'rectangle--mark-crutches) | ||
| 143 | (rectangle--reset-point-crutches)) | ||
| 144 | |||
| 141 | ;;; Rectangle operations. | 145 | ;;; Rectangle operations. |
| 142 | 146 | ||
| 143 | (defun apply-on-rectangle (function start end &rest args) | 147 | (defun apply-on-rectangle (function start end &rest args) |