From: Cecilio Pardo <cpardo@imayhem.com>
To: 3468@debbugs.gnu.org
Subject: bug#3468: drag and drop text
Date: Sat, 28 Sep 2024 23:52:45 +0200 [thread overview]
Message-ID: <8ba4e567-550e-4ac2-96f4-c6f7bacd78d0@imayhem.com> (raw)
In-Reply-To: <20090604070321.177690@gmx.net>
[-- Attachment #1: Type: text/plain, Size: 428 bytes --]
This patch implements drag-and-drop for w32 for files and text, using C,
not C++.
This should work from Windows 95, but I can't test it. Because of this,
the prior implementation with WM_DROPFILES has been removed with '#if
0', and can probably be completely removed.
I tested with mingw-w64 and mingw on Windows 11.
Text is inserted at point position, not at drop position. I don't know
how this works on other platforms.
[-- Attachment #2: 0001-Implement-drag-n-drop-for-w32-with-support-for-files.patch --]
[-- Type: text/plain, Size: 13699 bytes --]
From 1712ab3ba109b2beafa418f0b12d09c836590b2c Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Fri, 27 Sep 2024 23:58:02 +0200
Subject: [PATCH] Implement drag-n-drop for w32 with support for files and text
Implement dnd with IDropTarget
* lisp/term/w32-win.el (w32-drag-n-drop): changed to handle files or strings
* src/w32fns.c
(process_dropfiles): new, convert DROPFILES struct to array of strings
(w32_createwindow): assign an IDropTarget to each new frame
(w32_name_of_message): added new messages
(w32_msg_pump): Changed CoInitialize to OleInitialize, needed by the
drag-n-drop functions
(w32_wnd_proc): new struct w32_drop_target, and w32_drop_target_*
functions to implement the IDropTarget interface
* src/w32term.c (w32_read_socket): handle WM_EMACS_DROPFILES,
WM_EMACS_DROPSTRING, skip WM_EMACS_DROPFILES
* src/w32term.h (): add WM_EMACS_DROPFILES, WM_EMACS_DROPSTRING
---
lisp/term/w32-win.el | 19 ++--
src/w32fns.c | 202 ++++++++++++++++++++++++++++++++++++++++++-
src/w32term.c | 46 ++++++++++
src/w32term.h | 4 +-
4 files changed, 260 insertions(+), 11 deletions(-)
diff --git a/lisp/term/w32-win.el b/lisp/term/w32-win.el
index b57b3dd3bef..ac85ec8d945 100644
--- a/lisp/term/w32-win.el
+++ b/lisp/term/w32-win.el
@@ -131,8 +131,11 @@ w32-dropped-file-to-url
file-name))
(defun w32-drag-n-drop (event &optional new-frame)
- "Edit the files listed in the drag-n-drop EVENT.
-Switch to a buffer editing the last file dropped."
+ "If the drag-n-drop EVENT is for a file or files, edit those
+files. Switch to a buffer editing the last file dropped.
+
+If the EVENT is for text, insert the text at point on the buffer shown
+in the target window, or add to kill ring if that buffer is read-only."
(interactive "e")
(save-excursion
;; Make sure the drop target has positive co-ords
@@ -140,6 +143,7 @@ w32-drag-n-drop
;; won't work. <skx@tardis.ed.ac.uk>
(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))
@@ -150,11 +154,12 @@ w32-drag-n-drop
(raise-frame)
(setq window (selected-window))
- (dnd-handle-multiple-urls
- window
- (mapcar #'w32-dropped-file-to-url
- (car (cdr (cdr event))))
- 'private))))
+ (if (stringp arg)
+ (dnd-insert-text window 'copy arg)
+ (dnd-handle-multiple-urls
+ window
+ (mapcar #'w32-dropped-file-to-url arg)
+ 'private)))))
(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 0a3f5c38a58..fdba3524846 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -34,6 +34,12 @@ #define _WIN32_WINNT 0x0600
#include <c-ctype.h>
+#define COBJMACROS /* Ask for C definitions for COM. */
+#include <shlobj.h>
+#include <oleidl.h>
+#include <objidl.h>
+#include <ole2.h>
+
#include "lisp.h"
#include "w32term.h"
#include "frame.h"
@@ -359,6 +365,10 @@ #define WS_EX_NOACTIVATE 0x08000000L
static struct w32_display_info *w32_display_info_for_name (Lisp_Object);
+static void my_post_msg (W32Msg*, HWND, UINT, WPARAM, LPARAM);
+static unsigned int w32_get_modifiers (void);
+
+
/* Let the user specify a display with a frame.
nil stands for the selected frame--or, if that is not a w32 frame,
the first display on the list. */
@@ -2464,6 +2474,168 @@ w32_createhscrollbar (struct frame *f, struct scroll_bar * bar)
return hwnd;
}
+/* From the DROPFILES struct, extract the list of filenames. Returns a
+ NULL terminated malloc array of malloc strings that should be freed
+ by the caller. */
+static char **
+process_dropfiles (DROPFILES *files)
+{
+ char *start_of_files = (char*)files + files->pFiles;
+ int count = 0;
+ char filename[MAX_PATH];
+ char **filenames;
+
+ if (files->fWide)
+ {
+ WCHAR *p = (WCHAR*)start_of_files;
+ for ( ; *p; count ++, p += wcslen (p) + 1)
+ ;
+ filenames = malloc ((count+1) * sizeof (char*));
+ filenames [count] = NULL;
+ p = (WCHAR*)start_of_files;
+ for ( int i = 0; *p; p += wcslen (p) + 1, i++)
+ {
+ filename_from_utf16 (p, filename);
+ filenames [i] = xstrdup (filename);
+ }
+ }
+ else
+ {
+ char *p = start_of_files;
+ for ( ; *p; count ++, p += strlen (p) + 1)
+ ;
+ filenames = malloc ((count + 1) * sizeof (char*));
+ filenames [count] = NULL;
+ p = start_of_files;
+ for ( int i = 0; *p; p += strlen(p) + 1, i++)
+ {
+ filename_from_ansi (p, filename);
+ filenames[i] = xstrdup (filename);
+ }
+ }
+ return filenames;
+}
+
+struct w32_drop_target {
+ /* i_drop_target must be the first member. */
+ IDropTarget i_drop_target;
+ HWND hwnd;
+};
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_QueryInterface (IDropTarget *t, REFIID ri, void **r)
+{
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE
+w32_drop_target_AddRef (IDropTarget *This)
+{
+ return 1;
+}
+
+static ULONG STDMETHODCALLTYPE
+w32_drop_target_Release (IDropTarget *This)
+{
+ struct w32_drop_target *target = (struct w32_drop_target *)This;
+ xfree( target->i_drop_target.lpVtbl );
+ xfree( target );
+ return 0;
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_DragEnter( IDropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_DragOver ( IDropTarget *This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_DragLeave( IDropTarget *This )
+{
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_Drop (IDropTarget *This, IDataObject *pDataObj,
+ DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ struct w32_drop_target *target = (struct w32_drop_target *)This;
+ *pdwEffect = DROPEFFECT_COPY;
+
+ W32Msg msg = {0};
+ msg.dwModifiers = w32_get_modifiers();
+ msg.msg.time = GetMessageTime ();
+ msg.msg.pt.x = pt.x;
+ msg.msg.pt.y = pt.y;
+
+ STGMEDIUM stgmedium;
+
+ /* Try for dropped files (CF_HDROP). */
+ FORMATETC formatetc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ if (SUCCEEDED (IDataObject_GetData (pDataObj, &formatetc, &stgmedium)))
+ {
+ if (stgmedium.tymed == TYMED_HGLOBAL)
+ {
+ DROPFILES *files = (DROPFILES*)GlobalLock (stgmedium.hGlobal);
+ if (files)
+ my_post_msg (&msg, target->hwnd, WM_EMACS_DROPFILES,
+ 0, (LPARAM)process_dropfiles (files) );
+ GlobalUnlock (stgmedium.hGlobal);
+ }
+ ReleaseStgMedium (&stgmedium);
+ return S_OK;
+ }
+
+ formatetc.cfFormat = CF_UNICODETEXT;
+ if (SUCCEEDED (IDataObject_GetData (pDataObj, &formatetc, &stgmedium)))
+ {
+ if (stgmedium.tymed == TYMED_HGLOBAL)
+ {
+ WCHAR *text = (WCHAR*)GlobalLock (stgmedium.hGlobal);
+ Lisp_Object text_string = from_unicode_buffer (text);
+ char *utf8 = xstrdup (SSDATA (ENCODE_UTF_8 (text_string)));
+ my_post_msg (&msg, target->hwnd, WM_EMACS_DROPSTRING,
+ 0, (LPARAM)utf8 );
+ GlobalUnlock (stgmedium.hGlobal);
+ }
+ ReleaseStgMedium (&stgmedium);
+ return S_OK;
+ }
+
+ formatetc.cfFormat = CF_TEXT;
+ if (SUCCEEDED (IDataObject_GetData (pDataObj, &formatetc, &stgmedium)))
+ {
+ if (stgmedium.tymed == TYMED_HGLOBAL)
+ {
+ char *text = (char*)GlobalLock (stgmedium.hGlobal);
+
+ int l = strlen (text);
+ WCHAR *text_utf16 = xmalloc (sizeof (WCHAR) * l + 1 );;
+ if (MultiByteToWideChar (CP_ACP, 0, text, l, text_utf16, l + 1))
+ {
+ Lisp_Object text_string = from_unicode_buffer (text_utf16);
+ char *utf8 = xstrdup (SSDATA (ENCODE_UTF_8 (text_string)));
+ my_post_msg (&msg, target->hwnd, WM_EMACS_DROPSTRING,
+ 0, (LPARAM)utf8 );
+ }
+ xfree (text_utf16);
+ GlobalUnlock (stgmedium.hGlobal);
+ }
+ ReleaseStgMedium (&stgmedium);
+ return S_OK;
+ }
+
+ return S_OK;
+}
+
static void
w32_createwindow (struct frame *f, int *coords)
{
@@ -2548,7 +2720,23 @@ w32_createwindow (struct frame *f, int *coords)
SetWindowLong (hwnd, WND_BACKGROUND_INDEX, FRAME_BACKGROUND_PIXEL (f));
/* Enable drag-n-drop. */
+ struct w32_drop_target *drop_target = xmalloc (sizeof (struct w32_drop_target));
+ drop_target-> hwnd = hwnd;
+
+ IDropTargetVtbl *vtbl = xmalloc (sizeof (IDropTargetVtbl));
+ drop_target->i_drop_target.lpVtbl = vtbl;
+ vtbl->QueryInterface = w32_drop_target_QueryInterface;
+ vtbl->AddRef = w32_drop_target_AddRef;
+ vtbl->Release = w32_drop_target_Release;
+ vtbl->DragEnter = w32_drop_target_DragEnter;
+ vtbl->DragOver = w32_drop_target_DragOver;
+ vtbl->DragLeave = w32_drop_target_DragLeave;
+ vtbl->Drop = w32_drop_target_Drop;
+ RegisterDragDrop (hwnd, &drop_target->i_drop_target);
+
+#if 0
DragAcceptFiles (hwnd, TRUE);
+#endif
/* Enable system light/dark theme. */
w32_applytheme (hwnd);
@@ -3399,6 +3587,8 @@ #define M(msg) { msg, # msg }
M (WM_EMACS_PAINT),
M (WM_EMACS_IME_STATUS),
M (WM_CHAR),
+ M (WM_EMACS_DROPFILES),
+ M (WM_EMACS_DROPSTRING),
#undef M
{ 0, 0 }
};
@@ -3465,13 +3655,14 @@ w32_msg_pump (deferred_msg * msg_buf)
/* Produced by complete_deferred_msg; just ignore. */
break;
case WM_EMACS_CREATEWINDOW:
- /* Initialize COM for this window. Even though we don't use it,
- some third party shell extensions can cause it to be used in
+ /* Initialize COM for this window. Needed for RegisterDragDrop.
+ Some third party shell extensions can cause it to be used in
system dialogs, which causes a crash if it is not initialized.
This is a known bug in Windows, which was fixed long ago, but
the patch for XP is not publicly available until XP SP3,
and older versions will never be patched. */
- CoInitialize (NULL);
+ OleInitialize (NULL);
+
w32_createwindow ((struct frame *) msg.wParam,
(int *) msg.lParam);
if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
@@ -5106,7 +5297,9 @@ #define MOUSEEVENTF_FROMTOUCH 0xFF515700
return 0;
case WM_MOUSEWHEEL:
+#if 0
case WM_DROPFILES:
+#endif
wmsg.dwModifiers = w32_get_modifiers ();
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
signal_user_input ();
@@ -5597,7 +5790,10 @@ #define WM_TOUCH 576
}
case WM_EMACS_DESTROYWINDOW:
+ RevokeDragDrop ((HWND) wParam);
+#if 0
DragAcceptFiles ((HWND) wParam, FALSE);
+#endif
return DestroyWindow ((HWND) wParam);
case WM_EMACS_HIDE_CARET:
diff --git a/src/w32term.c b/src/w32term.c
index 62037e3b2cd..e1ff4112a7b 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -3576,6 +3576,7 @@ w32_construct_mouse_wheel (struct input_event *result, W32Msg *msg,
return Qnil;
}
+#if 0
static Lisp_Object
w32_construct_drag_n_drop (struct input_event *result, W32Msg *msg,
struct frame *f)
@@ -3651,6 +3652,7 @@ w32_construct_drag_n_drop (struct input_event *result, W32Msg *msg,
return Qnil;
}
+#endif
\f
#if HAVE_W32NOTIFY
@@ -5682,12 +5684,56 @@ w32_read_socket (struct terminal *terminal,
}
break;
+#if 0
case WM_DROPFILES:
f = w32_window_to_frame (dpyinfo, msg.msg.hwnd);
if (f)
w32_construct_drag_n_drop (&inev, &msg, f);
break;
+#endif
+
+ case WM_EMACS_DROPSTRING:
+ 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);
+ inev.arg = make_string ((char*)msg.msg.lParam, strlen((char*)msg.msg.lParam));
+ xfree ((void*)msg.msg.lParam);
+ break;
+
+ case WM_EMACS_DROPFILES:
+ {
+ 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);
+
+ Lisp_Object files = Qnil;
+ char **filenames = (char**)msg.msg.lParam;
+ for (int n = 0; filenames[n]; n++ )
+ {
+ files = Fcons (DECODE_FILE (build_string (filenames[n])), files );
+ xfree (filenames[n]);
+ }
+ xfree (filenames);
+ inev.arg = files;
+ break;
+ }
case WM_HSCROLL:
{
diff --git a/src/w32term.h b/src/w32term.h
index 47be542f570..c1253af47c4 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -710,7 +710,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_END (WM_EMACS_START + 27)
+#define WM_EMACS_DROPFILES (WM_EMACS_START + 27)
+#define WM_EMACS_DROPSTRING (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
next prev parent reply other threads:[~2024-09-28 21:52 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-06-04 7:03 bug#3468: drag and drop text Erdkern Erdkern
2019-09-30 15:29 ` Lars Ingebrigtsen
2019-09-30 15:49 ` Eli Zaretskii
2024-09-28 21:52 ` Cecilio Pardo [this message]
2024-09-29 7:19 ` Eli Zaretskii
2024-09-29 11:17 ` Cecilio Pardo
2024-09-29 11:36 ` Eli Zaretskii
2024-09-29 11:45 ` Cecilio Pardo
2024-09-30 21:20 ` Cecilio Pardo
2024-10-05 11:07 ` Eli Zaretskii
2024-10-05 12:07 ` Cecilio Pardo
2024-10-05 12:30 ` Eli Zaretskii
2024-10-05 12:34 ` Eli Zaretskii
2024-10-05 21:41 ` Cecilio Pardo
2024-10-05 21:43 ` Cecilio Pardo
2024-10-06 6:10 ` Eli Zaretskii
2024-10-07 10:28 ` Cecilio Pardo
2024-10-07 12:00 ` Eli Zaretskii
2024-10-08 13:15 ` Eli Zaretskii
2024-10-08 18:51 ` Cecilio Pardo
2024-10-23 17:16 ` Cecilio Pardo
2024-10-24 7:37 ` Eli Zaretskii
2024-10-24 7:56 ` Cecilio Pardo
2024-10-24 8:16 ` Eli Zaretskii
2024-10-24 8:46 ` Cecilio Pardo
2024-10-24 9:40 ` Eli Zaretskii
2024-10-24 16:52 ` Cecilio Pardo
2024-10-25 10:43 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8ba4e567-550e-4ac2-96f4-c6f7bacd78d0@imayhem.com \
--to=cpardo@imayhem.com \
--cc=3468@debbugs.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.