Blender V5.0
GHOST_DropTargetWin32.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
10#include "GHOST_Debug.hh"
11#include <shellapi.h>
12
13#include "utf_winfunc.hh"
14#include "utfconv.hh"
15
16#ifdef WITH_GHOST_DEBUG
17/* utility */
18void printLastError(void);
19#endif /* WITH_GHOST_DEBUG */
20
22 : window_(window), system_(system)
23{
24 c_ref_ = 1;
25 h_wnd_ = window->getHWND();
26 dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
27}
28
30
31/*
32 * IUnknown::QueryInterface
33 */
34HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppv_obj)
35{
36
37 if (!ppv_obj) {
38 return E_INVALIDARG;
39 }
40 *ppv_obj = nullptr;
41
42 if (riid == IID_IUnknown || riid == IID_IDropTarget) {
43 AddRef();
44 *ppv_obj = (void *)this;
45 return S_OK;
46 }
47 *ppv_obj = nullptr;
48 return E_NOINTERFACE;
49}
50
51/*
52 * IUnknown::AddRef
53 */
54
55ULONG __stdcall GHOST_DropTargetWin32::AddRef(void)
56{
57 return ::InterlockedIncrement(&c_ref_);
58}
59
60/*
61 * IUnknown::Release
62 */
63ULONG __stdcall GHOST_DropTargetWin32::Release(void)
64{
65 ULONG refs = ::InterlockedDecrement(&c_ref_);
66
67 if (refs == 0) {
68 delete this;
69 return 0;
70 }
71 else {
72 return refs;
73 }
74}
75
76/*
77 * Implementation of IDropTarget::DragEnter
78 */
79HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object,
80 DWORD /*grf_key_state*/,
81 POINTL pt,
82 DWORD *pdw_effect)
83{
84 /* We accept all drop by default. */
85 window_->setAcceptDragOperation(true);
86 *pdw_effect = DROPEFFECT_NONE;
87
88 dragged_object_type_ = getGhostType(p_data_object);
89 system_->pushDragDropEvent(
90 GHOST_kEventDraggingEntered, dragged_object_type_, window_, pt.x, pt.y, nullptr);
91 return S_OK;
92}
93
94/*
95 * Implementation of IDropTarget::DragOver
96 */
97HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD /*grf_key_state*/,
98 POINTL pt,
99 DWORD *pdw_effect)
100{
101 if (window_->canAcceptDragOperation()) {
102 *pdw_effect = allowedDropEffect(*pdw_effect);
103 }
104 else {
105 *pdw_effect = DROPEFFECT_NONE;
106 /* XXX Uncomment to test drop. Drop will not be called if `pdw_effect == DROPEFFECT_NONE`. */
107 // *pdw_effect = DROPEFFECT_COPY;
108 }
109 system_->pushDragDropEvent(
110 GHOST_kEventDraggingUpdated, dragged_object_type_, window_, pt.x, pt.y, nullptr);
111 return S_OK;
112}
113
114/*
115 * Implementation of IDropTarget::DragLeave
116 */
117HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
118{
119 system_->pushDragDropEvent(
120 GHOST_kEventDraggingExited, dragged_object_type_, window_, 0, 0, nullptr);
121 dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
122 return S_OK;
123}
124
125/* Implementation of IDropTarget::Drop
126 * This function will not be called if pdw_effect is set to DROPEFFECT_NONE in
127 * the implementation of IDropTarget::DragOver
128 */
129HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *p_data_object,
130 DWORD /*grf_key_state*/,
131 POINTL pt,
132 DWORD *pdw_effect)
133{
134 void *data = getGhostData(p_data_object);
135 if (window_->canAcceptDragOperation()) {
136 *pdw_effect = allowedDropEffect(*pdw_effect);
137 }
138 else {
139 *pdw_effect = DROPEFFECT_NONE;
140 }
141 if (data) {
142 system_->pushDragDropEvent(
143 GHOST_kEventDraggingDropDone, dragged_object_type_, window_, pt.x, pt.y, data);
144 }
145 dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
146 return S_OK;
147}
148
149/*
150 * Helpers
151 */
152
153DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dw_allowed)
154{
155 DWORD dw_effect = DROPEFFECT_NONE;
156 if (dw_allowed & DROPEFFECT_COPY) {
157 dw_effect = DROPEFFECT_COPY;
158 }
159 return dw_effect;
160}
161
162GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *p_data_object)
163{
164 /* File-names. Prefer looking for the CF_HDROP data format first as certain file-manager
165 * applications send CF_TEXT as well. See issue #135672. */
166 FORMATETC fmtetc = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
167 if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
169 }
170
171 /* Text
172 * NOTE: Unlike the clipboard, Windows will not synthesize missing text formats for drag and
173 * drop operations, so we need to check for both CF_UNICODETEXT and CF_TEXT.
174 */
175 fmtetc.cfFormat = CF_UNICODETEXT;
176 if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
178 }
179
180 fmtetc.cfFormat = CF_TEXT;
181 if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
183 }
184
186}
187
188void *GHOST_DropTargetWin32::getGhostData(IDataObject *p_data_object)
189{
190 GHOST_TDragnDropTypes type = getGhostType(p_data_object);
191 switch (type) {
193 return getDropDataAsFilenames(p_data_object);
195 return getDropDataAsString(p_data_object);
197 // return getDropDataAsBitmap(p_data_object);
198 break;
199 default:
200#ifdef WITH_GHOST_DEBUG
201 ::printf("\nGHOST_kDragnDropTypeUnknown");
202#endif /* WITH_GHOST_DEBUG */
203 return nullptr;
204 }
205 return nullptr;
206}
207
208void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *p_data_object)
209{
210 GHOST_TStringArray *str_array = nullptr;
211 FORMATETC fmtetc = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
212
213 /* Check if data-object supplies the format we want.
214 * Double checking here, first in #getGhostType. */
215 if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
216 STGMEDIUM stgmed;
217 if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
218 const HDROP hdrop = (HDROP)::GlobalLock(stgmed.hGlobal);
219
220 const uint totfiles = ::DragQueryFileW(hdrop, -1, nullptr, 0);
221 if (totfiles) {
222 str_array = (GHOST_TStringArray *)::malloc(sizeof(GHOST_TStringArray));
223 str_array->count = 0;
224 str_array->strings = (uint8_t **)::malloc(totfiles * sizeof(uint8_t *));
225
226 for (uint nfile = 0; nfile < totfiles; nfile++) {
227 WCHAR fpath[MAX_PATH];
228 if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) {
229 char *temp_path;
230 if (!(temp_path = alloc_utf_8_from_16(fpath, 0))) {
231 /* Just ignore paths that could not be converted verbatim. */
232 continue;
233 }
234 str_array->strings[str_array->count++] = (uint8_t *)temp_path;
235 }
236 }
237 }
238 /* Free up memory. */
239 ::GlobalUnlock(stgmed.hGlobal);
240 ::ReleaseStgMedium(&stgmed);
241 }
242 }
243 return str_array;
244}
245
246void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *p_data_object)
247{
248 char *tmp_string;
249 FORMATETC fmtetc = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
250 STGMEDIUM stgmed;
251
252 /* Try unicode first.
253 * Check if data-object supplies the format we want. */
254 if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
255 if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
256 LPCWSTR wstr = (LPCWSTR)::GlobalLock(stgmed.hGlobal);
257
258 tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0);
259
260 /* Free memory. */
261 ::GlobalUnlock(stgmed.hGlobal);
262 ::ReleaseStgMedium(&stgmed);
263
264#ifdef WITH_GHOST_DEBUG
265 if (tmp_string) {
266 ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n",
267 tmp_string);
268 }
269#endif /* WITH_GHOST_DEBUG */
270 return tmp_string;
271 }
272 }
273
274 fmtetc.cfFormat = CF_TEXT;
275
276 if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
277 if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
278 char *str = (char *)::GlobalLock(stgmed.hGlobal);
279 int str_size = ::strlen(str) + 1;
280
281 tmp_string = (char *)::malloc(str_size);
282 if (tmp_string) {
283 ::memcpy(tmp_string, str, str_size);
284 }
285 /* Free memory. */
286 ::GlobalUnlock(stgmed.hGlobal);
287 ::ReleaseStgMedium(&stgmed);
288
289 return tmp_string;
290 }
291 }
292
293 return nullptr;
294}
295
296int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out)
297{
298 int size;
299 out = nullptr; /* caller should free if != nullptr */
300
301 /* Get the required size. */
302 size = ::WideCharToMultiByte(CP_ACP, /* System Default Codepage */
303 0x00000400, /* WC_NO_BEST_FIT_CHARS */
304 in,
305 -1, /* -1 null terminated, makes output null terminated too. */
306 nullptr,
307 0,
308 nullptr,
309 nullptr);
310
311 if (!size) {
312#ifdef WITH_GHOST_DEBUG
313 ::printLastError();
314#endif /* WITH_GHOST_DEBUG */
315 return 0;
316 }
317
318 out = (char *)::malloc(size);
319 if (!out) {
320 ::printf("\nmalloc failed!!!");
321 return 0;
322 }
323
324 size = ::WideCharToMultiByte(CP_ACP, 0x00000400, in, -1, (LPSTR)out, size, nullptr, nullptr);
325
326 if (!size) {
327#ifdef WITH_GHOST_DEBUG
328 ::printLastError();
329#endif /* WITH_GHOST_DEBUG */
330 ::free(out);
331 out = nullptr;
332 }
333 return size;
334}
335
336#ifdef WITH_GHOST_DEBUG
337void printLastError(void)
338{
339 LPTSTR s;
340 DWORD err;
341
342 err = GetLastError();
343 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
344 nullptr,
345 err,
346 0,
347 (LPTSTR)&s,
348 0,
349 nullptr))
350 {
351 printf("\nLastError: (%d) %s\n", int(err), s);
352 LocalFree(s);
353 }
354}
355#endif /* WITH_GHOST_DEBUG */
void BLI_kdtree_nd_ free(KDTree *tree)
unsigned int uint
@ GHOST_kEventDraggingDropDone
@ GHOST_kEventDraggingExited
@ GHOST_kEventDraggingUpdated
@ GHOST_kEventDraggingEntered
GHOST_TDragnDropTypes
@ GHOST_kDragnDropTypeUnknown
@ GHOST_kDragnDropTypeFilenames
@ GHOST_kDragnDropTypeBitmap
@ GHOST_kDragnDropTypeString
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv_obj)
HRESULT __stdcall DragEnter(IDataObject *p_data_object, DWORD grf_key_state, POINTL pt, DWORD *pdw_effect)
HRESULT __stdcall DragLeave()
HRESULT __stdcall Drop(IDataObject *p_data_object, DWORD grf_key_state, POINTL pt, DWORD *pdw_effect)
GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system)
HRESULT __stdcall DragOver(DWORD grf_key_state, POINTL pt, DWORD *pdw_effect)
#define str(s)
#define in
#define out
#define printf(...)
char * alloc_utf_8_from_16(const wchar_t *in16, size_t add)
Definition utfconv.cc:282