Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

Robert Weiner-2
Presently the function mouse-drag-and-drop-region works only within a single frame.  The code attached below fixes that and allows for drops in windows of other frames.  Please try it out and send feedback with any issues.

There is one remaining issue that I see in the last 3 lines of the function.  I would like to have focus shift to the frame of the drop since typically one might be doing more editing there (maybe this could be an option), but under the macOS windowing system, focus reverts to the source window and frame rather than the one the code selects.  Is there a way to make that work.

Bob

------

(setq mouse-drag-and-drop-region 'shift) 

(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'.
When text is dragged over to a different buffer, or if a
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))
        (source-window (selected-window))
event-handler
release-pos
        value-selection)
    (track-mouse
      ;; When event was click instead of drag, skip loop
      (while (or (mouse-movement-p (setq event (read-event)))
(setq event-handler (and (consp event)
  (intern-soft (concat "handle-"
       (symbol-name
(event-basic-type event)))))))
(if (fboundp event-handler)
    (progn (funcall event-handler event) (setq event-handler nil))
  (setq event-handler nil))
          (unless value-selection ; initialization
            (delete-overlay mouse-secondary-overlay)
            (setq value-selection (buffer-substring start end))
            (move-overlay mouse-secondary-overlay start end)) ; (deactivate-mark)
          (ignore-errors (deactivate-mark) ; deactivate any 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"
    (and (setq release-point (posn-point (event-end event)))
(member 'secondary-selection ; "drag negligible"
(mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at release-point))))
            buffer-read-only)
        ;; Do not modify buffer under mouse.
        (cond
         ;; "drag negligible" or "drag to read-only", restore region.
         (value-selection
          (select-window source-window) ; In case miss drag to other window
          (goto-char 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 successful insert
        (setq deactivate-mark nil)
        (activate-mark)) ; have region on destination
        (let ((dest-window (selected-window))) ; when beyond buffer
  ;; 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))))
            (select-window source-window)
            (goto-char point) ; restore point in source-window
            (activate-mark)) ; restore region
  ;; Ensure change focus to any new frame; typically edits
  ;; would continue in its selected window where the text was
  ;; dropped.
          (select-frame (window-frame dest-window))
  (select-window dest-window)))
    (delete-overlay mouse-secondary-overlay)))
Reply | Threaded
Open this post in threaded view
|

Re: Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

Robert Weiner-2
2 small fixes; release-point was misnamed in the let and indentation was wrong in the latter part of the code.
Use this version.  -- Bob

(setq mouse-drag-and-drop-region 'shift) 

(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'.
When text is dragged over to a different buffer, or if a
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))
        (source-window (selected-window))
event-handler
release-point
        value-selection)
    (track-mouse
      ;; When event was click instead of drag, skip loop
      (while (or (mouse-movement-p (setq event (read-event)))
(setq event-handler (and (consp event)
  (intern-soft (concat "handle-"
       (symbol-name
(event-basic-type event)))))))
(if (fboundp event-handler)
    (progn (funcall event-handler event) (setq event-handler nil))
  (setq event-handler nil))
        (unless value-selection ; initialization
          (delete-overlay mouse-secondary-overlay)
          (setq value-selection (buffer-substring start end))
          (move-overlay mouse-secondary-overlay start end)) ; (deactivate-mark)
        (ignore-errors (deactivate-mark) ; deactivate any 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"
    (and (setq release-point (posn-point (event-end event)))
(member 'secondary-selection ; "drag negligible"
(mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at release-point))))
            buffer-read-only)
        ;; Do not modify buffer under mouse.
        (cond
         ;; "drag negligible" or "drag to read-only", restore region.
         (value-selection
          (select-window source-window) ; In case miss drag to other window
          (goto-char 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 successful insert
        (setq deactivate-mark nil)
        (activate-mark)) ; have region on destination
      (let ((dest-window (selected-window))) ; when beyond buffer
;; 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))))
          (select-window source-window)
          (goto-char point)       ; restore point in source-window
          (activate-mark))       ; restore region
;; Ensure change focus to any new frame; typically edits
;; would continue in its selected window where the text was
;; dropped.
        (select-frame (window-frame dest-window))
(select-window dest-window)))
    (delete-overlay mouse-secondary-overlay)))
Reply | Threaded
Open this post in threaded view
|

Re: Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

Robert Weiner-2
I rewrote the doc strings for clarity.  -- Bob

(defcustom mouse-drag-and-drop-region nil
  "If non-nil, then text regions can be dragged and dropped to new locations.
Normally, drops within the same buffer move the text of the region; drops
to a different buffer copy the region.  However, if this value is a modifier
symbol, such as `control' or `shift' or `meta', and the matching modifier
key is pressed when dropping the region, then the region text is always
copied.  The drop location must be within the text area of an Emacs window."
  :type 'symbol
  :version "26.1"
  :group 'mouse)

(defun mouse-drag-and-drop-region (event)
  "Drag and drop the active region text to the point of mouse button release.
Normally, drops within the same buffer move the region text and drops
to a different buffer copy it.  However, if a modifier key is pressed
when dropping, and the value of the variable `mouse-drag-and-drop-region'
is the symbol for that modifier, then the text is always copied.  The drop
location must be within the text area of an Emacs window."


On Thu, Oct 12, 2017 at 12:42 PM, Robert Weiner <[hidden email]> wrote:
2 small fixes; release-point was misnamed in the let and indentation was wrong in the latter part of the code.
Use this version.  -- Bob

(setq mouse-drag-and-drop-region 'shift) 

(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'.
When text is dragged over to a different buffer, or if a
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))
        (source-window (selected-window))
event-handler
release-point
        value-selection)
    (track-mouse
      ;; When event was click instead of drag, skip loop
      (while (or (mouse-movement-p (setq event (read-event)))
(setq event-handler (and (consp event)
  (intern-soft (concat "handle-"
       (symbol-name
(event-basic-type event)))))))
(if (fboundp event-handler)
    (progn (funcall event-handler event) (setq event-handler nil))
  (setq event-handler nil))
        (unless value-selection ; initialization
          (delete-overlay mouse-secondary-overlay)
          (setq value-selection (buffer-substring start end))
          (move-overlay mouse-secondary-overlay start end)) ; (deactivate-mark)
        (ignore-errors (deactivate-mark) ; deactivate any 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"
    (and (setq release-point (posn-point (event-end event)))
(member 'secondary-selection ; "drag negligible"
(mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at release-point))))
            buffer-read-only)
        ;; Do not modify buffer under mouse.
        (cond
         ;; "drag negligible" or "drag to read-only", restore region.
         (value-selection
          (select-window source-window) ; In case miss drag to other window
          (goto-char 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 successful insert
        (setq deactivate-mark nil)
        (activate-mark)) ; have region on destination
      (let ((dest-window (selected-window))) ; when beyond buffer
;; 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))))
          (select-window source-window)
          (goto-char point)       ; restore point in source-window
          (activate-mark))       ; restore region
;; Ensure change focus to any new frame; typically edits
;; would continue in its selected window where the text was
;; dropped.
        (select-frame (window-frame dest-window))
(select-window dest-window)))
    (delete-overlay mouse-secondary-overlay)))

Reply | Threaded
Open this post in threaded view
|

Re: Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

Tak Kunihiro-3
In reply to this post by Robert Weiner-2
I tried the revised `mouse-drag-and-drop-region' on Emacs -Q 26.0.90 on
macOS 10.9.5.  I can drag text within a frame but cannot do so among
frames.  I think I miss something.  Do you have idea?

Reply | Threaded
Open this post in threaded view
|

Re: Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

Robert Weiner-2
On Fri, Oct 13, 2017 at 5:57 AM, Tak Kunihiro <[hidden email]> wrote:
I tried the revised `mouse-drag-and-drop-region' on Emacs -Q 26.0.90 on
macOS 10.9.5.  I can drag text within a frame but cannot do so among
frames.  I think I miss something.  Do you have idea?

​The yellow visual rectangle of the text being dragged seems to get stuck at the source frame but if you continue your drag and release in another frame with a non-read-only buffer, your text should be properly dropped there.  Is this what you did?  Try it a couple different times to ensure there is not some initialization warm up problem.

For clarity, here is the latest code:

(defcustom mouse-drag-and-drop-region nil
  "If non-nil, then text regions can be dragged and dropped to new locations.
Normally, drops within the same buffer move the text of the region; drops
to a different buffer copy the region.  However, if this value is a modifier
symbol, such as `control' or `shift' or `meta', and the matching modifier
key is pressed when dropping the region, then the region text is always
copied.  The drop location must be within the text area of an Emacs window."
  :type 'symbol
  :version "26.1"
  :group 'mouse)

(defun mouse-drag-and-drop-region (event)
  "Drag and drop the active region text to the point of mouse button release.
Normally, drops within the same buffer move the region text and drops
to a different buffer copy it.  However, if a modifier key is pressed
when dropping, and the value of the variable `mouse-drag-and-drop-region'
is the symbol for that modifier, then the text is always copied.  The drop
location must be within the text area of an Emacs window."
  (interactive "e")
  (require 'tooltip)
  (let ((start (region-beginning))
        (end (region-end))
        (point (point))
        (buffer (current-buffer))
        (source-window (selected-window))
event-handler
release-point
        value-selection)
    (track-mouse
      ;; When event was click instead of drag, skip loop
      (while (or (mouse-movement-p (setq event (read-event)))
(setq event-handler (and (consp event)
  (intern-soft (concat "handle-"
       (symbol-name
(event-basic-type event)))))))
(if (fboundp event-handler)
    (progn (funcall event-handler event) (setq event-handler nil))
  (setq event-handler nil))
        (unless value-selection ; initialization
          (delete-overlay mouse-secondary-overlay)
          (setq value-selection (buffer-substring start end))
          (move-overlay mouse-secondary-overlay start end)) ; (deactivate-mark)
        (ignore-errors (deactivate-mark) ; deactivate any 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"
    (and (setq release-point (posn-point (event-end event)))
(member 'secondary-selection ; "drag negligible"
(mapcar (lambda (xxx) (overlay-get xxx 'face))
(overlays-at release-point))))
            buffer-read-only)
        ;; Do not modify buffer under mouse.
        (cond
         ;; "drag negligible" or "drag to read-only", restore region.
         (value-selection
          (select-window source-window) ; In case miss drag to other window
          (goto-char 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 successful insert
        (setq deactivate-mark nil)
        (activate-mark)) ; have region on destination
      (let ((dest-window (selected-window))) ; when beyond buffer
;; 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))))
          (select-window source-window)
          (goto-char point)       ; restore point in source-window
          (activate-mark))       ; restore region
;; Ensure change focus to any new frame; typically edits
;; would continue in its selected window where the text was
;; dropped.
        (select-frame (window-frame dest-window))
(select-window dest-window)))
    (delete-overlay mouse-secondary-overlay)))


Reply | Threaded
Open this post in threaded view
|

Re: Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

Robert Weiner-2
On Mon, Oct 16, 2017 at 9:45 AM, Robert Weiner <[hidden email]> wrote:
On Fri, Oct 13, 2017 at 5:57 AM, Tak Kunihiro <[hidden email]> wrote:
I tried the revised `mouse-drag-and-drop-region' on Emacs -Q 26.0.90 on
macOS 10.9.5.  I can drag text within a frame but cannot do so among
frames.  I think I miss something.  Do you have idea?

​The yellow visual rectangle of the text being dragged seems to get stuck at the source frame but if you continue your drag and release in another frame with a non-read-only buffer, your text should be properly dropped there.  Is this what you did?  Try it a couple different times to ensure there is not some initialization warm up problem.

​I see the issue now; the standard Emacs code does not handle cross-frame drags well.  Replace the mouse-set-point function from mouse.el with the version below and then the yellow rectangle of text will follow you to the drag release frame and your drag-and-drops should work as you expect (with the mouse-drag-and-drop-region changes from before).  -- Bob​

(defun mouse-set-point (event &optional promote-to-region)
  "Move point to the position clicked on with the mouse.
This should be bound to a mouse click event type.
If PROMOTE-TO-REGION is non-nil and event is a multiple-click,
select the corresponding element around point, with the resulting position of
point determined by `mouse-select-region-move-to-beginning'."
  (interactive "e\np")
  (let ((start-w-or-f (posn-window (event-start event)))
(end-w-or-f   (posn-window (event-end event))))
    (if (framep start-w-or-f)
(with-selected-frame start-w-or-f (setq start-w-or-f (selected-window))))
    (if (framep end-w-or-f)
(with-selected-frame end-w-or-f (setq end-w-or-f (selected-window))))
    (if (and (window-minibuffer-p start-w-or-f)
     (not (minibuffer-window-active-p start-w-or-f)))
;; Select the ending frame only, not the window pressed within.
(select-frame (window-frame end-w-or-f))
      ;; Give temporary modes such as isearch a chance to turn off.
      (run-hooks 'mouse-leave-buffer-hook)
      (if (and promote-to-region (> (event-click-count event) 1))
  (progn (mouse-set-region event)
(when (and (boundp 'mouse-select-region-move-to-beginning)
    mouse-select-region-move-to-beginning)
   (when (> (posn-point (event-start event)) (region-beginning))
     (exchange-point-and-mark))))
;; Use event-end in case called from mouse-drag-region.
;; If EVENT is a click, event-end and event-start give same value.
(if (and (window-minibuffer-p end-w-or-f)
(not (minibuffer-window-active-p end-w-or-f)))
    ;; Select the ending frame only, not the window pressed within.
    (select-frame (window-frame end-w-or-f))
  (condition-case ()
      (posn-set-point (event-end event))
    (error (select-frame (window-frame end-w-or-f)))))))))

Reply | Threaded
Open this post in threaded view
|

Re: Emacs 26: Code that fixes mouse-drag-and-drop-region to work across frames

Tak Kunihiro-3
>>>  I tried the revised `mouse-drag-and-drop-region' on Emacs -Q
>>>  26.0.90 on macOS 10.9.5. I can drag text within a frame but cannot
>>>  do so among frames. I think I miss something. Do you have idea?
>>
> ​I see the issue now; the standard Emacs code does not handle
> cross-frame drags well. Replace the mouse-set-point function from
> mouse.el with the version below

GNU Emacs 27.0.50 (build 1, x86_64-apple-darwin13.4.0, NS appkit-1265.21 Version 10.9.5 (Build 13F1911)) of 2017-10-17
Emacs -Q

I cannot drag text among frames yet.  I think I still miss something.
Can you show recipe that starts from emacs -Q?