Subject: bug#3468: drag and drop text

From: Cecilio Pardo <[email-masked]>
Date: Wed, 23 Oct 2024 19:16:51 +0200

On 08/10/2024 15:15, Eli Zaretskii wrote:

> I'm not closing the bug, because you said you want to add support for
> the dnd-* variables.

This add support for dnd-scroll-margin and dnd-indicate-insertion-point 
on MS-Windows.

BTW, I think this variables should be enabled by default. I'd say that 
is the expected behaviour for drag and drop.

From 5c0f2a1b40931824df75a620d2494f2031bf0f83 Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <[email-masked]>
Date: Wed, 23 Oct 2024 14:41:24 +0200
Subject: [PATCH] Improve drag and drop on MS-Windows (bug#3468)

Add support for dnd-scroll-margin and dnd-indicate-insertion-point by
calling dnd-handle-movement when dragging the mouse.

* lisp/term/w32-win.el (w32-drag-n-drop): Call dnd-handle-movement
when applicable.
* src/w32fns.c (w32_handle_drag_movement): New function, sends a
WM_EMACS_DRAGOVER message.
(w32_drop_target_DragEnter): Call w32_handle_drag_movement.
(w32_drop_target_DragOver): Call w32_handle_drag_movement.
* src/w32term.c: (w32_read_socket): Handle WM_EMACS_DRAGOVER,
sending an drag-n-drop event.
* src/w32term.h (): Define new WM_EMACS_DRAGOVER message.
---
 lisp/term/w32-win.el | 54 ++++++++++++++++++++++++--------------------
 src/w32fns.c         | 17 +++++++++++++-
 src/w32term.c        | 18 +++++++++++++++
 src/w32term.h        |  5 ++--
 4 files changed, 66 insertions(+), 28 deletions(-)

diff --git a/lisp/term/w32-win.el b/lisp/term/w32-win.el
index 541fef2ced3..b47b5f21f05 100644
--- a/lisp/term/w32-win.el
+++ b/lisp/term/w32-win.el
@@ -137,35 +137,39 @@ w32-drag-n-drop
 If EVENT is for text, insert that text at point into the buffer
 shown in the window that is the target of the drop; if that buffer is
 read-only, add the dropped text to kill-ring.
+If EVENT payload is nil, then this is a drag event.
 If the optional argument NEW-FRAME is non-nil, perform the
 drag-n-drop action in a newly-created frame using its selected-window
 and that window's buffer."
   (interactive "e")
-  (save-excursion
-    ;; Make sure the drop target has positive co-ords
-    ;; before setting the selected frame - otherwise it
-    ;; won't work.  <[email-masked]>
-    (let* ((window (posn-window (event-start event)))
-	   (coords (posn-x-y (event-start event)))
-           (arg (car (cdr (cdr event))))
-	   (x (car coords))
-	   (y (cdr coords)))
-      (if (and (> x 0) (> y 0))
-	  (set-frame-selected-window nil window))
-
-      (when new-frame
-        (select-frame (make-frame)))
-      (raise-frame)
-      (setq window (selected-window))
-
-      ;; arg (the payload of the event) is a string when the drop is
-      ;; text, and a list of strings when the drop is one or more files.
-      (if (stringp arg)
-          (dnd-insert-text window 'copy arg)
-        (dnd-handle-multiple-urls
-         window
-         (mapcar #'w32-dropped-file-to-url arg)
-         'private)))))
+  ;; Make sure the drop target has positive co-ords
+  ;; before setting the selected frame - otherwise it
+  ;; won't work.  <[email-masked]>
+  (let* ((window (posn-window (event-start event)))
+	 (coords (posn-x-y (event-start event)))
+         (arg (car (cdr (cdr event))))
+	 (x (car coords))
+	 (y (cdr coords)))
+
+    (if (and (> x 0) (> y 0) (window-live-p window))
+        (set-frame-selected-window nil window))
+    ;; Don't create new frame if we are just dragging
+    (and arg new-frame
+         (select-frame (make-frame)))
+    (raise-frame)
+    (setq window (selected-window))
+
+    ;; arg (the payload of the event) is a string when the drop is
+    ;; text, and a list of strings when the drop is one or more files.
+    ;; It is nil if the event is a drag event.
+    (if arg
+        (if (stringp arg)
+            (dnd-insert-text window 'copy arg)
+          (dnd-handle-multiple-urls
+           window
+           (mapcar #'w32-dropped-file-to-url arg)
+           'private))
+      (dnd-handle-movement (event-start event)))))
 
 (defun w32-drag-n-drop-other-frame (event)
   "Edit the files listed in the drag-n-drop EVENT, in other frames.
diff --git a/src/w32fns.c b/src/w32fns.c
index 3ee13dcbbdd..eb42d3b61b2 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -2505,7 +2505,6 @@ process_dropfiles (DROPFILES *files)
   return lisp_files;
 }
 
-
 /* This function can be called ONLY between calls to
    block_input/unblock_input.  It is used in w32_read_socket.  */
 Lisp_Object
@@ -2572,6 +2571,19 @@ w32_drop_target_Release (IDropTarget *This)
   return 0;
 }
 
+static void
+w32_handle_drag_movement (IDropTarget *This, POINTL pt)
+{
+  struct w32_drop_target *target = (struct w32_drop_target *)This;
+
+  W32Msg msg = {0};
+  msg.dwModifiers = w32_get_modifiers ();
+  msg.msg.time = GetMessageTime ();
+  msg.msg.pt.x = pt.x;
+  msg.msg.pt.y = pt.y;
+  my_post_msg (&msg, target->hwnd, WM_EMACS_DRAGOVER, 0, 0 );
+}
+
 static HRESULT STDMETHODCALLTYPE
 w32_drop_target_DragEnter (IDropTarget *This, IDataObject *pDataObj,
 			   DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
@@ -2581,6 +2593,7 @@ w32_drop_target_DragEnter (IDropTarget *This, IDataObject *pDataObj,
      happen on drop.  We send COPY because our use cases don't modify
      or link to the original data.  */
   *pdwEffect = DROPEFFECT_COPY;
+  w32_handle_drag_movement (This, pt);
   return S_OK;
 }
 
@@ -2590,6 +2603,7 @@ w32_drop_target_DragOver (IDropTarget *This, DWORD grfKeyState, POINTL pt,
 {
   /* See comment in w32_drop_target_DragEnter.  */
   *pdwEffect = DROPEFFECT_COPY;
+  w32_handle_drag_movement (This, pt);
   return S_OK;
 }
 
@@ -3607,6 +3621,7 @@ #define M(msg) { msg, # msg }
       M (WM_EMACS_PAINT),
       M (WM_EMACS_IME_STATUS),
       M (WM_CHAR),
+      M (WM_EMACS_DRAGOVER),
       M (WM_EMACS_DROP),
 #undef M
       { 0, 0 }
diff --git a/src/w32term.c b/src/w32term.c
index 3a627308137..88622700386 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -5629,6 +5629,24 @@ w32_read_socket (struct terminal *terminal,
 	  }
 	  break;
 
+	case WM_EMACS_DRAGOVER:
+	  {
+	    f = w32_window_to_frame (dpyinfo, msg.msg.hwnd);
+	    if (!f)
+	      break;
+	    XSETFRAME (inev.frame_or_window, f);
+	    inev.kind = DRAG_N_DROP_EVENT;
+	    inev.code = 0;
+	    inev.timestamp = msg.msg.time;
+	    inev.modifiers = msg.dwModifiers;
+	    ScreenToClient (msg.msg.hwnd, &msg.msg.pt);
+	    XSETINT (inev.x, msg.msg.pt.x);
+	    XSETINT (inev.y, msg.msg.pt.y);
+	    /* This is a drag movement.  */
+	    inev.arg = Qnil;
+	    break;
+	  }
+
 	case WM_HSCROLL:
 	  {
 	    struct scroll_bar *bar =
diff --git a/src/w32term.h b/src/w32term.h
index 39e2262e2a8..cad9fcf8cb1 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -711,8 +711,9 @@ #define WM_EMACS_BRINGTOTOP            (WM_EMACS_START + 23)
 #define WM_EMACS_INPUT_READY           (WM_EMACS_START + 24)
 #define WM_EMACS_FILENOTIFY            (WM_EMACS_START + 25)
 #define WM_EMACS_IME_STATUS            (WM_EMACS_START + 26)
-#define WM_EMACS_DROP                  (WM_EMACS_START + 27)
-#define WM_EMACS_END                   (WM_EMACS_START + 28)
+#define WM_EMACS_DRAGOVER              (WM_EMACS_START + 27)
+#define WM_EMACS_DROP                  (WM_EMACS_START + 28)
+#define WM_EMACS_END                   (WM_EMACS_START + 29)
 
 #define WND_FONTWIDTH_INDEX    (0)
 #define WND_LINEHEIGHT_INDEX   (4)
-- 
2.35.1.windows.2