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 | |
| 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.
| -rw-r--r-- | doc/emacs/frames.texi | 4 | ||||
| -rw-r--r-- | doc/emacs/killing.texi | 3 | ||||
| -rw-r--r-- | doc/lispref/commands.texi | 6 | ||||
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/mouse.el | 113 | ||||
| -rw-r--r-- | lisp/rect.el | 8 | ||||
| -rw-r--r-- | src/xdisp.c | 12 |
7 files changed, 146 insertions, 3 deletions
diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index 091c011fb96..f6c2d239132 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi | |||
| @@ -91,6 +91,10 @@ If the region is active, move the nearer end of the region to the | |||
| 91 | click position; otherwise, set mark at the current value of point and | 91 | click position; otherwise, set mark at the current value of point and |
| 92 | point at the click position. Save the resulting region in the kill | 92 | point at the click position. Save the resulting region in the kill |
| 93 | ring; on a second click, kill it (@code{mouse-save-then-kill}). | 93 | ring; on a second click, kill it (@code{mouse-save-then-kill}). |
| 94 | |||
| 95 | @item C-M-mouse-1 | ||
| 96 | Activate a rectangular region around the text selected by dragging. | ||
| 97 | @xref{Rectangles}. | ||
| 94 | @end table | 98 | @end table |
| 95 | 99 | ||
| 96 | @findex mouse-set-point | 100 | @findex mouse-set-point |
diff --git a/doc/emacs/killing.texi b/doc/emacs/killing.texi index 80e2868908a..ce00cb38a74 100644 --- a/doc/emacs/killing.texi +++ b/doc/emacs/killing.texi | |||
| @@ -732,6 +732,9 @@ region is controlled. But remember that a given combination of point | |||
| 732 | and mark values can be interpreted either as a region or as a | 732 | and mark values can be interpreted either as a region or as a |
| 733 | rectangle, depending on the command that uses them. | 733 | rectangle, depending on the command that uses them. |
| 734 | 734 | ||
| 735 | A rectangular region can also be marked using the mouse: click and drag | ||
| 736 | @kbd{C-M-mouse-1} from one corner of the rectangle to the opposite. | ||
| 737 | |||
| 735 | @table @kbd | 738 | @table @kbd |
| 736 | @item C-x r k | 739 | @item C-x r k |
| 737 | Kill the text of the region-rectangle, saving its contents as the | 740 | Kill the text of the region-rectangle, saving its contents as the |
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 0c848a80257..032f005e9c4 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi | |||
| @@ -1661,6 +1661,12 @@ events within its body. Outside of @code{track-mouse} forms, Emacs | |||
| 1661 | does not generate events for mere motion of the mouse, and these | 1661 | does not generate events for mere motion of the mouse, and these |
| 1662 | events do not appear. @xref{Mouse Tracking}. | 1662 | events do not appear. @xref{Mouse Tracking}. |
| 1663 | 1663 | ||
| 1664 | @defvar mouse-fine-grained-tracking | ||
| 1665 | When non-@code{nil}, mouse motion events are generated even for very | ||
| 1666 | small movements. Otherwise, motion events are not generated as long | ||
| 1667 | as the mouse cursor remains pointing to the same glyph in the text. | ||
| 1668 | @end defvar | ||
| 1669 | |||
| 1664 | @node Focus Events | 1670 | @node Focus Events |
| 1665 | @subsection Focus Events | 1671 | @subsection Focus Events |
| 1666 | @cindex focus event | 1672 | @cindex focus event |
| @@ -614,6 +614,9 @@ arguments mitigating performance issues when operating on huge | |||
| 614 | buffers. | 614 | buffers. |
| 615 | 615 | ||
| 616 | +++ | 616 | +++ |
| 617 | ** Dragging 'C-M-mouse-1' now marks rectangular regions. | ||
| 618 | |||
| 619 | +++ | ||
| 617 | ** The command 'delete-indentation' now operates on the active region. | 620 | ** The command 'delete-indentation' now operates on the active region. |
| 618 | If the region is active, the command joins all the lines in the | 621 | If the region is active, the command joins all the lines in the |
| 619 | region. When there's no active region, the command works on the | 622 | region. When there's no active region, the command works on the |
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) |
diff --git a/src/xdisp.c b/src/xdisp.c index 2b4dda27157..c4d23be4cde 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -2491,6 +2491,12 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) | |||
| 2491 | enum glyph_row_area area; | 2491 | enum glyph_row_area area; |
| 2492 | int x, y, width, height; | 2492 | int x, y, width, height; |
| 2493 | 2493 | ||
| 2494 | if (mouse_fine_grained_tracking) | ||
| 2495 | { | ||
| 2496 | STORE_NATIVE_RECT (*rect, gx, gy, 1, 1); | ||
| 2497 | return; | ||
| 2498 | } | ||
| 2499 | |||
| 2494 | /* Try to determine frame pixel position and size of the glyph under | 2500 | /* Try to determine frame pixel position and size of the glyph under |
| 2495 | frame pixel coordinates X/Y on frame F. */ | 2501 | frame pixel coordinates X/Y on frame F. */ |
| 2496 | 2502 | ||
| @@ -34946,6 +34952,12 @@ The default is to use octal format (\200) whereas hexadecimal (\x80) | |||
| 34946 | may be more familiar to users. */); | 34952 | may be more familiar to users. */); |
| 34947 | display_raw_bytes_as_hex = false; | 34953 | display_raw_bytes_as_hex = false; |
| 34948 | 34954 | ||
| 34955 | DEFVAR_BOOL ("mouse-fine-grained-tracking", mouse_fine_grained_tracking, | ||
| 34956 | doc: /* Non-nil for pixel-wise mouse-movement. | ||
| 34957 | When nil, mouse-movement events will not be generated as long as the | ||
| 34958 | mouse stays within the extent of a single glyph (except for images). */); | ||
| 34959 | mouse_fine_grained_tracking = false; | ||
| 34960 | |||
| 34949 | } | 34961 | } |
| 34950 | 34962 | ||
| 34951 | 34963 | ||