doxygen: add newline after \file
[blender.git] / intern / ghost / intern / GHOST_DropTargetWin32.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup GHOST
22  */
23
24
25 #include "GHOST_Debug.h"
26 #include "GHOST_DropTargetWin32.h"
27 #include <shellapi.h>
28
29 #include "utf_winfunc.h"
30 #include "utfconv.h"
31
32 #ifdef GHOST_DEBUG
33 // utility
34 void printLastError(void);
35 #endif // GHOST_DEBUG
36
37
38 GHOST_DropTargetWin32::GHOST_DropTargetWin32(
39         GHOST_WindowWin32 *window,
40         GHOST_SystemWin32 *system)
41     : m_window(window),
42       m_system(system)
43 {
44         m_cRef = 1;
45         m_hWnd = window->getHWND();
46         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
47 }
48
49 GHOST_DropTargetWin32::~GHOST_DropTargetWin32()
50 {
51 }
52
53
54 /*
55  * IUnknown::QueryInterface
56  */
57 HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppvObj)
58 {
59
60         if (!ppvObj)
61                 return E_INVALIDARG;
62         *ppvObj = NULL;
63
64         if (riid == IID_IUnknown || riid == IID_IDropTarget) {
65                 AddRef();
66                 *ppvObj = (void *)this;
67                 return S_OK;
68         }
69         else {
70                 *ppvObj = NULL;
71                 return E_NOINTERFACE;
72         }
73 }
74
75
76 /*
77  * IUnknown::AddRef
78  */
79
80 ULONG __stdcall GHOST_DropTargetWin32::AddRef(void)
81 {
82         return ::InterlockedIncrement(&m_cRef);
83 }
84
85 /*
86  * IUnknown::Release
87  */
88 ULONG __stdcall GHOST_DropTargetWin32::Release(void)
89 {
90         ULONG refs = ::InterlockedDecrement(&m_cRef);
91
92         if (refs == 0) {
93                 delete this;
94                 return 0;
95         }
96         else {
97                 return refs;
98         }
99 }
100
101 /*
102  * Implementation of IDropTarget::DragEnter
103  */
104 HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
105 {
106         // we accept all drop by default
107         m_window->setAcceptDragOperation(true);
108         *pdwEffect = DROPEFFECT_NONE;
109
110         m_draggedObjectType = getGhostType(pDataObject);
111         m_system->pushDragDropEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
112         return S_OK;
113 }
114
115 /*
116  * Implementation of IDropTarget::DragOver
117  */
118 HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
119 {
120         if (m_window->canAcceptDragOperation()) {
121                 *pdwEffect = allowedDropEffect(*pdwEffect);
122         }
123         else {
124                 *pdwEffect = DROPEFFECT_NONE;
125                 // *pdwEffect = DROPEFFECT_COPY; // XXX Uncomment to test drop. Drop will not be called if pdwEffect == DROPEFFECT_NONE.
126         }
127         m_system->pushDragDropEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
128         return S_OK;
129 }
130
131 /*
132  * Implementation of IDropTarget::DragLeave
133  */
134 HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
135 {
136         m_system->pushDragDropEvent(GHOST_kEventDraggingExited, m_draggedObjectType, m_window, 0, 0, NULL);
137         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
138         return S_OK;
139 }
140
141 /* Implementation of IDropTarget::Drop
142  * This function will not be called if pdwEffect is set to DROPEFFECT_NONE in
143  * the implementation of IDropTarget::DragOver
144  */
145 HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
146 {
147         void *data = getGhostData(pDataObject);
148         if (m_window->canAcceptDragOperation()) {
149                 *pdwEffect = allowedDropEffect(*pdwEffect);
150
151         }
152         else {
153                 *pdwEffect = DROPEFFECT_NONE;
154         }
155         if (data)
156                 m_system->pushDragDropEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data);
157
158         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
159         return S_OK;
160 }
161
162 /*
163  * Helpers
164  */
165
166 DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed)
167 {
168         DWORD dwEffect = DROPEFFECT_NONE;
169         if (dwAllowed & DROPEFFECT_COPY)
170                 dwEffect = DROPEFFECT_COPY;
171
172         return dwEffect;
173 }
174
175 GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *pDataObject)
176 {
177         /* Text
178          * Note: Unicode text is available as CF_TEXT too, the system can do the
179          * conversion, but we do the conversion ourself with WC_NO_BEST_FIT_CHARS.
180          */
181         FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
182         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
183                 return GHOST_kDragnDropTypeString;
184         }
185
186         // Filesnames
187         fmtetc.cfFormat = CF_HDROP;
188         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
189                 return GHOST_kDragnDropTypeFilenames;
190         }
191
192         return GHOST_kDragnDropTypeUnknown;
193 }
194
195 void *GHOST_DropTargetWin32::getGhostData(IDataObject *pDataObject)
196 {
197         GHOST_TDragnDropTypes type = getGhostType(pDataObject);
198         switch (type) {
199                 case GHOST_kDragnDropTypeFilenames:
200                         return getDropDataAsFilenames(pDataObject);
201                         break;
202                 case GHOST_kDragnDropTypeString:
203                         return getDropDataAsString(pDataObject);
204                         break;
205                 case GHOST_kDragnDropTypeBitmap:
206                         //return getDropDataAsBitmap(pDataObject);
207                         break;
208                 default:
209 #ifdef GHOST_DEBUG
210                         ::printf("\nGHOST_kDragnDropTypeUnknown");
211 #endif // GHOST_DEBUG
212                         return NULL;
213                         break;
214         }
215         return NULL;
216 }
217
218 void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *pDataObject)
219 {
220         UINT totfiles, nvalid = 0;
221         WCHAR fpath[MAX_PATH];
222         char *temp_path;
223         GHOST_TStringArray *strArray = NULL;
224         FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
225         STGMEDIUM stgmed;
226         HDROP hdrop;
227
228         // Check if dataobject supplies the format we want.
229         // Double checking here, first in getGhostType.
230         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
231                 if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
232                         hdrop = (HDROP) ::GlobalLock(stgmed.hGlobal);
233
234                         totfiles = ::DragQueryFileW(hdrop, -1, NULL, 0);
235                         if (!totfiles) {
236                                 ::GlobalUnlock(stgmed.hGlobal);
237                                 return NULL;
238                         }
239
240                         strArray = (GHOST_TStringArray *) ::malloc(sizeof(GHOST_TStringArray));
241                         strArray->count = 0;
242                         strArray->strings = (GHOST_TUns8 **) ::malloc(totfiles * sizeof(GHOST_TUns8 *));
243
244                         for (UINT nfile = 0; nfile < totfiles; nfile++) {
245                                 if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) {
246                                         if (!(temp_path = alloc_utf_8_from_16(fpath, 0)) ) {
247                                                 continue;
248                                         }
249                                         // Just ignore paths that could not be converted verbatim.
250
251                                         strArray->strings[nvalid] = (GHOST_TUns8 *) temp_path;
252                                         strArray->count = nvalid + 1;
253                                         nvalid++;
254                                 }
255                         }
256                         // Free up memory.
257                         ::GlobalUnlock(stgmed.hGlobal);
258                         ::ReleaseStgMedium(&stgmed);
259
260                         return strArray;
261                 }
262         }
263         return NULL;
264 }
265
266 void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject)
267 {
268         char *tmp_string;
269         FORMATETC fmtetc = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
270         STGMEDIUM stgmed;
271
272         // Try unicode first.
273         // Check if dataobject supplies the format we want.
274         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
275                 if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
276                         LPCWSTR wstr = (LPCWSTR) ::GlobalLock(stgmed.hGlobal);
277                         if (!(tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0)) ) {
278                                 ::GlobalUnlock(stgmed.hGlobal);
279                                 return NULL;
280                         }
281                         // Free memory
282                         ::GlobalUnlock(stgmed.hGlobal);
283                         ::ReleaseStgMedium(&stgmed);
284 #ifdef GHOST_DEBUG
285                         ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n", tmp_string);
286 #endif // GHOST_DEBUG
287                         return tmp_string;
288                 }
289         }
290
291         fmtetc.cfFormat = CF_TEXT;
292
293         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
294                 if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
295                         char *str = (char *)::GlobalLock(stgmed.hGlobal);
296
297                         tmp_string = (char *)::malloc(::strlen(str) + 1);
298                         if (!tmp_string) {
299                                 ::GlobalUnlock(stgmed.hGlobal);
300                                 return NULL;
301                         }
302
303                         if (!::strcpy(tmp_string, str) ) {
304                                 ::free(tmp_string);
305                                 ::GlobalUnlock(stgmed.hGlobal);
306                                 return NULL;
307                         }
308                         // Free memory
309                         ::GlobalUnlock(stgmed.hGlobal);
310                         ::ReleaseStgMedium(&stgmed);
311
312                         return tmp_string;
313                 }
314         }
315
316         return NULL;
317 }
318
319 int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char * &out)
320 {
321         int size;
322         out = NULL; //caller should free if != NULL
323
324         // Get the required size.
325         size = ::WideCharToMultiByte(CP_ACP,        //System Default Codepage
326                                      0x00000400,    // WC_NO_BEST_FIT_CHARS
327                                      in,
328                                      -1,            //-1 null terminated, makes output null terminated too.
329                                      NULL,
330                                      0,
331                                      NULL, NULL
332                                      );
333
334         if (!size) {
335 #ifdef GHOST_DEBUG
336                 ::printLastError();
337 #endif // GHOST_DEBUG
338                 return 0;
339         }
340
341         out = (char *)::malloc(size);
342         if (!out) {
343                 ::printf("\nmalloc failed!!!");
344                 return 0;
345         }
346
347         size = ::WideCharToMultiByte(CP_ACP,
348                                      0x00000400,
349                                      in,
350                                      -1,
351                                      (LPSTR) out,
352                                      size,
353                                      NULL, NULL
354                                      );
355
356         if (!size) {
357 #ifdef GHOST_DEBUG
358                 ::printLastError();
359 #endif //GHOST_DEBUG
360                 ::free(out);
361                 out = NULL;
362         }
363         return size;
364 }
365
366 #ifdef GHOST_DEBUG
367 void printLastError(void)
368 {
369         LPTSTR s;
370         DWORD err;
371
372         err = GetLastError();
373         if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
374                           FORMAT_MESSAGE_FROM_SYSTEM,
375                           NULL,
376                           err,
377                           0,
378                           (LPTSTR)&s,
379                           0,
380                           NULL))
381         {
382                 printf("\nLastError: (%d) %s\n", (int)err, s);
383                 LocalFree(s);
384         }
385 }
386 #endif // GHOST_DEBUG