emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: mouse-drag-and-drop-region


From: Tak Kunihiro
Subject: Re: mouse-drag-and-drop-region
Date: Mon, 27 Nov 2017 22:11:26 +0900 (JST)

I re-revised mouse-drag-and-drop-region.  I'm sending replay to
https://lists.gnu.org/archive/html/emacs-devel/2017-11/msg00589.html,
ChangeLog, and patch.

* ChangeLog

2017-11-27 Tak Kunihiro <address@hidden>

        Improve comments and have three new options

        * lisp/mouse.el (mouse-drag-and-drop-region): Make usage of tooltip 
option.  Have option to make dragging cut among buffers.
        (mouse-drag-and-drop-region-cut-when-buffers-differ): New variable.  If 
non-nil, text is cut instead of copied when dragged among buffers.
        (mouse-drag-and-drop-region-show-tooltip): New variable.  If non-nil, 
text is shown by tooltip in a graphic display.
        (mouse-drag-and-drop-region-face): New variable for face.  The face is 
used to highlight the original text.

* Replay

> instead of perusing the secondary overlay, you don't use your own
> overlay to indicate start and end of the text you cut or copy.  When
> done with the operation or when an error occurs during dragging, you
> simply remove that overlay.  In addition, you can define a face the
> user can customize in order to highlight that overlay.

OK.  That is a good idea.  I got rid of usage of secondary-overlay.  I
define a new variable mouse-drag-and-drop-region-face.

>> How to show insert point?
>
> I think that by default the insert point should be shown by the
> mouse cursor alone.  Optionally, I see no problem showing the normal
> cursor just as `mouse-drag-region' does, maybe with some
> customizable cursor type (I think "bar" would give a better feeling
> than "box").

OK.  Only set point by mouse-set-point when
mouse-drag-and-drop-region-show-tooltip is non-nil with changing
cursor type.  I also changed the default of
mouse-drag-and-drop-region-show-tooltip to nil to be more stable
against accident during dragging.

> More precisely, you should wrap 'track-mouse' and whatever you do
> after it terminates in a 'condition-case' form and in the error part
> restore everything to the saved values.  Like so:
>
> (... ; save the state here
>  (condition-case nil
>      (progn
>        (track-mouse
>        )
>        )
>    (error ... ; restore the state here
>         )))
>
> If you have any problems coding that, please ask.

Even track-mouse is sandwiched by condition-case, I cannot catch a
error typing during dragging.  However, as inferred already, when
mouse-drag-and-drop-region-show-tooltip is nil, it is more stable even
typing key during dragging.


* Patch

diff --git a/lisp/mouse.el b/lisp/mouse.el
old mode 100644
new mode 100755
index 17d1732..b81d56e
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -2361,6 +2361,29 @@ text is copied instead of being cut."
   :version "26.1"
   :group 'mouse)
 
+(defcustom mouse-drag-and-drop-region-cut-when-buffers-differ nil
+  "If non-nil, cut text also when source and destination buffers differ.
+If this option is nil, `mouse-drag-and-drop-region' will leave
+the text in the source buffer alone when dropping it in a
+different buffer.  If this is non-nil, it will cut the text just
+as it does when dropping text in the source buffer."
+  :type 'boolean
+  :version "26.1"
+  :group 'mouse)
+
+(defcustom mouse-drag-and-drop-region-show-tooltip nil
+  "If non-nil, text is shown by a tooltip in a graphic display.
+If this option is non-nil, point is set to where the mouse cursor
+is during dragging and the original text is highlighted by face
+`mouse-drag-and-drop-region-face'."
+  :type 'boolean
+  :version "26.1"
+  :group 'mouse)
+
+(defvar mouse-drag-and-drop-region-face 'region
+  "Face to highlight the original text during dragging.
+See also `mouse-drag-and-drop-region-show-tooltip'.")
+
 (defun mouse-drag-and-drop-region (event)
   "Move text in the region to point where mouse is dragged to.
 The transportation of text is also referred as `drag and drop'.
@@ -2369,66 +2392,171 @@ modifier key was pressed when dropping, and the value 
of the
 variable `mouse-drag-and-drop-region' is that modifier, the text
 is copied instead of being cut."
   (interactive "e")
-  (require 'tooltip)
-  (let ((start (region-beginning))
-        (end (region-end))
-        (point (point))
-        (buffer (current-buffer))
-        (window (selected-window))
-        value-selection)
+  (let* ((mouse-drag-and-drop-region-show-tooltip
+          (and mouse-drag-and-drop-region-show-tooltip
+               (display-multi-frame-p)
+               (require 'tooltip)))
+         (start (region-beginning))
+         (end (region-end))
+         (point (point))
+         (buffer (current-buffer))
+         (window (selected-window))
+         (cursor-type cursor-type)
+         (text-from-read-only buffer-read-only)
+         (mouse-drag-and-drop-overlay
+          (make-overlay start end))
+         point-to-paste
+         point-to-paste-read-only
+         window-to-paste
+         no-modifier-on-drop
+         drag-but-negligible
+         clicked
+         value-selection)   ; This remains nil when event was "click".
+
+    ;; FIXME: Handle error such as hitting C-g while dragging.  See
+    ;; https://lists.gnu.org/archive/html/emacs-devel/2017-11/msg00486.html
     (track-mouse
-      ;; When event was click instead of drag, skip loop
+      ;; When event was "click" instead of "drag", skip loop.
       (while (progn
                (setq event (read-event))
                (or (mouse-movement-p event)
                    ;; Handle `mouse-autoselect-window'.
                    (eq (car-safe event) 'select-window)))
-        (unless value-selection ; initialization
-          (delete-overlay mouse-secondary-overlay)
+        ;; Obtain the dragged text in region.  When the loop was
+        ;; skipped, value-selection remains nil.
+        (unless value-selection
           (setq value-selection (buffer-substring start end))
-          (move-overlay mouse-secondary-overlay start end)) ; (deactivate-mark)
-        (ignore-errors (deactivate-mark) ; care existing region in other window
-                       (mouse-set-point event)
-                       (tooltip-show value-selection)))
-      (tooltip-hide))
-    ;; Do not modify buffer under mouse when "event was click",
-    ;;                                       "drag negligible", or
-    ;;                                       "drag to read-only".
-    (if (or (equal (mouse-posn-property (event-end event) 'face) 'region) ; 
"event was click"
-            (member 'secondary-selection ; "drag negligible"
-                    (mapcar (lambda (xxx) (overlay-get xxx 'face))
-                            (overlays-at (posn-point (event-end event)))))
-            buffer-read-only)
-        ;; Do not modify buffer under mouse.
-        (cond
-         ;; "drag negligible" or "drag to read-only", restore region.
-         (value-selection
-          (select-window window) ; In case miss drag to other window
-          (goto-char point)
+
+          ;; Check if selected text is read-only.
+          (dolist (index (number-sequence start end))
+            ;; (add-text-properties (region-beginning) (region-end) 
'(read-only t))
+            (setq text-from-read-only (or text-from-read-only
+                                          (get-text-property index 
'read-only)))))
+        (ignore-errors
+          (setq window-to-paste (posn-window (event-end event)))
+          (setq point-to-paste (posn-point (event-end event)))
+
+          ;; Check if point under mouse is read-only.
+          (save-window-excursion
+            (select-window window-to-paste)
+            ;; (add-text-properties (region-beginning) (region-end) 
'(read-only t))
+            (setq point-to-paste-read-only
+                  (or buffer-read-only
+                      (get-text-property point-to-paste 'read-only))))
+
+          ;; Check if "drag but negligible".  Operation "drag but
+          ;; negligible" is defined as drag-and-drop the text to
+          ;; the original region.  When modifier is pressed, the
+          ;; text will be inserted to inside of the original
+          ;; region.
+          (setq drag-but-negligible
+                (member mouse-drag-and-drop-region-face
+                        (mapcar (lambda (xxx) (overlay-get xxx 'face))
+                                (overlays-at point-to-paste))))
+
+          ;; Show tooltip.
+          (when mouse-drag-and-drop-region-show-tooltip
+            (overlay-put mouse-drag-and-drop-overlay
+                         'face mouse-drag-and-drop-region-face)
+            (deactivate-mark)   ; Maintain region in other window.
+            (mouse-set-point event)
+            (setq cursor-type (if (or point-to-paste-read-only
+                                      drag-but-negligible)
+                                  'hollow
+                                'bar))
+            (if (and (not drag-but-negligible)
+                     (not point-to-paste-read-only))
+                (tooltip-show value-selection)
+              (tooltip-hide)))))
+      ;; Hide a tooltip.
+      (when mouse-drag-and-drop-region-show-tooltip (tooltip-hide)))
+    
+
+    ;; Check if modifier was pressed on drop.
+    (setq no-modifier-on-drop
+          (not (member mouse-drag-and-drop-region (event-modifiers event))))
+
+    ;; Check if event was "click".
+    ;; (setq clicked (equal (mouse-posn-property (event-end event) 'face) 
'region))
+    (setq clicked (not value-selection))
+
+    ;; Do not modify any buffers when event is "click",
+    ;; "drag but negligible", or "drag to read-only".
+    (let* ((mouse-drag-and-drop-region-cut-when-buffers-differ
+            (if no-modifier-on-drop
+                mouse-drag-and-drop-region-cut-when-buffers-differ
+              (not mouse-drag-and-drop-region-cut-when-buffers-differ)))
+           (wanna-paste-to-same-buffer (equal (window-buffer window-to-paste) 
buffer))
+           (wanna-cut-on-same-buffer (and wanna-paste-to-same-buffer
+                                          no-modifier-on-drop))
+           (wanna-cut-on-other-buffer (and (not wanna-paste-to-same-buffer)
+                                           
mouse-drag-and-drop-region-cut-when-buffers-differ))
+           (cannot-paste (or point-to-paste-read-only
+                             (when (or wanna-cut-on-same-buffer
+                                       wanna-cut-on-other-buffer)
+                               text-from-read-only))))
+
+      (cond
+       ;; Move point within region.
+       (clicked
+        (deactivate-mark)
+        (mouse-set-point event))
+       ;; Undo operation. Set back the original text as region.
+       ((or (and drag-but-negligible
+                 no-modifier-on-drop)
+            cannot-paste)
+        ;; Inform user either source or destination buffer cannot be modified.
+        (when (and (not drag-but-negligible)
+                   cannot-paste)
+          (message "Buffer is read-only"))
+
+        ;; Select source window back and restore region.
+        ;; (set-window-point window point)
+        (select-window window)
+        (goto-char point)
+        (setq deactivate-mark nil)
+        (activate-mark))
+       ;; Modify buffers.
+       (t
+        ;; * DESTINATION BUFFER::
+        ;; Insert the text to destination buffer under mouse.
+        (select-window window-to-paste)
+        (goto-char point-to-paste)
+        (push-mark)
+        (insert value-selection)
+        ;; On success, set the text as region on destination buffer.
+        (when (not (equal (mark) (point)))
           (setq deactivate-mark nil)
           (activate-mark))
-         ;; "event was click"
-         (t
-          (deactivate-mark)
-          (mouse-set-point event)))
-      ;; Modify buffer under mouse by inserting text.
-      (push-mark)
-      (insert value-selection)
-      (when (not (equal (mark) (point))) ; on success insert
-        (setq deactivate-mark nil)
-        (activate-mark)) ; have region on destination
-      ;; Take care of initial region on source.
-      (if (equal (current-buffer) buffer) ; when same buffer
-          (let (deactivate-mark) ; remove text
-            (unless (member mouse-drag-and-drop-region (event-modifiers event))
-              (kill-region (overlay-start mouse-secondary-overlay)
-                           (overlay-end mouse-secondary-overlay))))
-        (let ((window1 (selected-window))) ; when beyond buffer
-          (select-window window)
-          (goto-char point) ; restore point on source window
-          (activate-mark) ; restore region
-          (select-window window1))))
-    (delete-overlay mouse-secondary-overlay)))
+
+        ;; * SOURCE BUFFER::
+        ;; Set back the original text as region or delete the original
+        ;; text, on source buffer.
+        (if wanna-paste-to-same-buffer
+            ;; When source buffer and destination buffer are the same,
+            ;; remove the original text.
+            (when no-modifier-on-drop
+              (let (deactivate-mark)
+                (delete-region (overlay-start mouse-drag-and-drop-overlay)
+                               (overlay-end mouse-drag-and-drop-overlay))))
+          ;; When source buffer and destination buffer are different,
+          ;; keep (set back the original text as region) or remove the
+          ;; original text.
+          (select-window window)   ; Select window with source buffer.
+          (goto-char point) ; Move point to the original text on source buffer.
+
+          (if mouse-drag-and-drop-region-cut-when-buffers-differ
+              ;; Remove the dragged text from source buffer like
+              ;; operation `cut'.
+              (delete-region (overlay-start mouse-drag-and-drop-overlay)
+                             (overlay-end mouse-drag-and-drop-overlay))
+            ;; Set back the dragged text as region on source buffer
+            ;; like operation `copy'.
+            (activate-mark))
+          (select-window window-to-paste)))))
+
+    ;; Clean up.
+    (delete-overlay mouse-drag-and-drop-overlay)))
 
 
 ;;; Bindings for mouse commands.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]