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