Merge branch 'blender2.7'
[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(
47         GHOST_WindowWin32 *window,
48         GHOST_SystemWin32 *system)
49     : m_window(window),
50       m_system(system)
51 {
52         m_cRef = 1;
53         m_hWnd = window->getHWND();
54         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
55 }
56
57 GHOST_DropTargetWin32::~GHOST_DropTargetWin32()
58 {
59 }
60
61
62 /*
63  * IUnknown::QueryInterface
64  */
65 HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppvObj)
66 {
67
68         if (!ppvObj)
69                 return E_INVALIDARG;
70         *ppvObj = NULL;
71
72         if (riid == IID_IUnknown || riid == IID_IDropTarget) {
73                 AddRef();
74                 *ppvObj = (void *)this;
75                 return S_OK;
76         }
77         else {
78                 *ppvObj = NULL;
79                 return E_NOINTERFACE;
80         }
81 }
82
83
84 /*
85  * IUnknown::AddRef
86  */
87
88 ULONG __stdcall GHOST_DropTargetWin32::AddRef(void)
89 {
90         return ::InterlockedIncrement(&m_cRef);
91 }
92
93 /*
94  * IUnknown::Release
95  */
96 ULONG __stdcall GHOST_DropTargetWin32::Release(void)
97 {
98         ULONG refs = ::InterlockedDecrement(&m_cRef);
99
100         if (refs == 0) {
101                 delete this;
102                 return 0;
103         }
104         else {
105                 return refs;
106         }
107 }
108
109 /*
110  * Implementation of IDropTarget::DragEnter
111  */
112 HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
113 {
114         // we accept all drop by default
115         m_window->setAcceptDragOperation(true);
116         *pdwEffect = DROPEFFECT_NONE;
117
118         m_draggedObjectType = getGhostType(pDataObject);
119         m_system->pushDragDropEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
120         return S_OK;
121 }
122
123 /*
124  * Implementation of IDropTarget::DragOver
125  */
126 HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
127 {
128         if (m_window->canAcceptDragOperation()) {
129                 *pdwEffect = allowedDropEffect(*pdwEffect);
130         }
131         else {
132                 *pdwEffect = DROPEFFECT_NONE;
133                 // *pdwEffect = DROPEFFECT_COPY; // XXX Uncomment to test drop. Drop will not be called if pdwEffect == DROPEFFECT_NONE.
134         }
135         m_system->pushDragDropEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
136         return S_OK;
137 }
138
139 /*
140  * Implementation of IDropTarget::DragLeave
141  */
142 HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
143 {
144         m_system->pushDragDropEvent(GHOST_kEventDraggingExited, m_draggedObjectType, m_window, 0, 0, NULL);
145         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
146         return S_OK;
147 }
148
149 /* Implementation of IDropTarget::Drop
150  * This function will not be called if pdwEffect is set to DROPEFFECT_NONE in
151  * the implementation of IDropTarget::DragOver
152  */
153 HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
154 {
155         void *data = getGhostData(pDataObject);
156         if (m_window->canAcceptDragOperation()) {
157                 *pdwEffect = allowedDropEffect(*pdwEffect);
158
159         }
160         else {
161                 *pdwEffect = DROPEFFECT_NONE;
162         }
163         if (data)
164                 m_system->pushDragDropEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data);
165
166         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
167         return S_OK;
168 }
169
170 /*
171  * Helpers
172  */
173
174 DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed)
175 {
176         DWORD dwEffect = DROPEFFECT_NONE;
177         if (dwAllowed & DROPEFFECT_COPY)
178                 dwEffect = DROPEFFECT_COPY;
179
180         return dwEffect;
181 }
182
183 GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *pDataObject)
184 {
185         /* Text
186          * Note: Unicode text is available as CF_TEXT too, the system can do the
187          * conversion, but we do the conversion ourself with WC_NO_BEST_FIT_CHARS.
188          */
189         FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
190         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
191                 return GHOST_kDragnDropTypeString;
192         }
193
194         // Filesnames
195         fmtetc.cfFormat = CF_HDROP;
196         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
197                 return GHOST_kDragnDropTypeFilenames;
198         }
199
200         return GHOST_kDragnDropTypeUnknown;
201 }
202
203 void *GHOST_DropTargetWin32::getGhostData(IDataObject *pDataObject)
204 {
205         GHOST_TDragnDropTypes type = getGhostType(pDataObject);
206         switch (type) {
207                 case GHOST_kDragnDropTypeFilenames:
208                         return getDropDataAsFilenames(pDataObject);
209                         break;
210                 case GHOST_kDragnDropTypeString:
211                         return getDropDataAsString(pDataObject);
212                         break;
213                 case GHOST_kDragnDropTypeBitmap:
214                         //return getDropDataAsBitmap(pDataObject);
215                         break;
216                 default:
217 #ifdef GHOST_DEBUG
218                         ::printf("\nGHOST_kDragnDropTypeUnknown");
219 #endif // GHOST_DEBUG
220                         return NULL;
221                         break;
222         }
223         return NULL;
224 }
225
226 void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *pDataObject)
227 {
228         UINT totfiles, nvalid = 0;
229         WCHAR fpath[MAX_PATH];
230         char *temp_path;
231         GHOST_TStringArray *strArray = NULL;
232         FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
233         STGMEDIUM stgmed;
234         HDROP hdrop;
235
236         // Check if dataobject supplies the format we want.
237         // Double checking here, first in getGhostType.
238         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
239                 if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
240                         hdrop = (HDROP) ::GlobalLock(stgmed.hGlobal);
241
242                         totfiles = ::DragQueryFileW(hdrop, -1, NULL, 0);
243                         if (!totfiles) {
244                                 ::GlobalUnlock(stgmed.hGlobal);
245                                 return NULL;
246                         }
247
248                         strArray = (GHOST_TStringArray *) ::malloc(sizeof(GHOST_TStringArray));
249                         strArray->count = 0;
250                         strArray->strings = (GHOST_TUns8 **) ::malloc(totfiles * sizeof(GHOST_TUns8 *));
251
252                         for (UINT nfile = 0; nfile < totfiles; nfile++) {
253                                 if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) {
254                                         if (!(temp_path = alloc_utf_8_from_16(fpath, 0)) ) {
255                                                 continue;
256                                         }
257                                         // Just ignore paths that could not be converted verbatim.
258
259                                         strArray->strings[nvalid] = (GHOST_TUns8 *) temp_path;
260                                         strArray->count = nvalid + 1;
261                                         nvalid++;
262                                 }
263                         }
264                         // Free up memory.
265                         ::GlobalUnlock(stgmed.hGlobal);
266                         ::ReleaseStgMedium(&stgmed);
267
268                         return strArray;
269                 }
270         }
271         return NULL;
272 }
273
274 void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject)
275 {
276         char *tmp_string;
277         FORMATETC fmtetc = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
278         STGMEDIUM stgmed;
279
280         // Try unicode first.
281         // Check if dataobject supplies the format we want.
282         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
283                 if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
284                         LPCWSTR wstr = (LPCWSTR) ::GlobalLock(stgmed.hGlobal);
285                         if (!(tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0)) ) {
286                                 ::GlobalUnlock(stgmed.hGlobal);
287                                 return NULL;
288                         }
289                         // Free memory
290                         ::GlobalUnlock(stgmed.hGlobal);
291                         ::ReleaseStgMedium(&stgmed);
292 #ifdef GHOST_DEBUG
293                         ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n", tmp_string);
294 #endif // GHOST_DEBUG
295                         return tmp_string;
296                 }
297         }
298
299         fmtetc.cfFormat = CF_TEXT;
300
301         if (pDataObject->QueryGetData(&fmtetc) == S_OK) {
302                 if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) {
303                         char *str = (char *)::GlobalLock(stgmed.hGlobal);
304
305                         tmp_string = (char *)::malloc(::strlen(str) + 1);
306                         if (!tmp_string) {
307                                 ::GlobalUnlock(stgmed.hGlobal);
308                                 return NULL;
309                         }
310
311                         if (!::strcpy(tmp_string, str) ) {
312                                 ::free(tmp_string);
313                                 ::GlobalUnlock(stgmed.hGlobal);
314                                 return NULL;
315                         }
316                         // Free memory
317                         ::GlobalUnlock(stgmed.hGlobal);
318                         ::ReleaseStgMedium(&stgmed);
319
320                         return tmp_string;
321                 }
322         }
323
324         return NULL;
325 }
326
327 int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char * &out)
328 {
329         int size;
330         out = NULL; //caller should free if != NULL
331
332         // Get the required size.
333         size = ::WideCharToMultiByte(CP_ACP,        //System Default Codepage
334                                      0x00000400,    // WC_NO_BEST_FIT_CHARS
335                                      in,
336                                      -1,            //-1 null terminated, makes output null terminated too.
337                                      NULL,
338                                      0,
339                                      NULL, NULL
340                                      );
341
342         if (!size) {
343 #ifdef GHOST_DEBUG
344                 ::printLastError();
345 #endif // GHOST_DEBUG
346                 return 0;
347         }
348
349         out = (char *)::malloc(size);
350         if (!out) {
351                 ::printf("\nmalloc failed!!!");
352                 return 0;
353         }
354
355         size = ::WideCharToMultiByte(CP_ACP,
356                                      0x00000400,
357                                      in,
358                                      -1,
359                                      (LPSTR) out,
360                                      size,
361                                      NULL, NULL
362                                      );
363
364         if (!size) {
365 #ifdef GHOST_DEBUG
366                 ::printLastError();
367 #endif //GHOST_DEBUG
368                 ::free(out);
369                 out = NULL;
370         }
371         return size;
372 }
373
374 #ifdef GHOST_DEBUG
375 void printLastError(void)
376 {
377         LPTSTR s;
378         DWORD err;
379
380         err = GetLastError();
381         if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
382                           FORMAT_MESSAGE_FROM_SYSTEM,
383                           NULL,
384                           err,
385                           0,
386                           (LPTSTR)&s,
387                           0,
388                           NULL))
389         {
390                 printf("\nLastError: (%d) %s\n", (int)err, s);
391                 LocalFree(s);
392         }
393 }
394 #endif // GHOST_DEBUG