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