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