add back ghost/guardedalloc from trunk
[blender-staging.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
37 #ifdef GHOST_DEBUG
38 // utility
39 void printLastError(void);
40 #endif // GHOST_DEBUG
41
42
43 GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 * window, GHOST_SystemWin32 * system)
44 :
45 m_window(window),
46 m_system(system)
47 {
48         m_cRef = 1;
49         m_hWnd = window->getHWND();
50         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
51         
52         // register our window as drop target
53         ::RegisterDragDrop(m_hWnd, this);
54 }
55
56 GHOST_DropTargetWin32::~GHOST_DropTargetWin32()
57 {
58         ::RevokeDragDrop(m_hWnd);
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         {
74                 AddRef();
75                 *ppvObj = (void*)this;
76                 return S_OK;
77         }
78         else
79         {
80                 *ppvObj = 0;
81                 return E_NOINTERFACE;
82         }
83 }
84
85
86 /* 
87  *      IUnknown::AddRef 
88  */
89
90 ULONG __stdcall GHOST_DropTargetWin32::AddRef(void)
91 {
92         return ::InterlockedIncrement(&m_cRef);
93 }
94
95 /* 
96  * IUnknown::Release
97  */
98 ULONG __stdcall GHOST_DropTargetWin32::Release(void)
99 {
100         ULONG refs = ::InterlockedDecrement(&m_cRef);
101                 
102         if(refs == 0)
103         {
104                 delete this;
105                 return 0;
106         }
107         else
108         {
109                 return refs;
110         }
111 }
112
113 /* 
114  * Implementation of IDropTarget::DragEnter
115  */
116 HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
117 {
118         // we accept all drop by default
119         m_window->setAcceptDragOperation(true);
120         *pdwEffect = DROPEFFECT_NONE;
121         
122         m_draggedObjectType = getGhostType(pDataObject);
123         m_system->pushDragDropEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
124         return S_OK;
125 }
126
127 /* 
128  * Implementation of IDropTarget::DragOver
129  */
130 HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
131 {
132         if(m_window->canAcceptDragOperation())
133         {
134                 *pdwEffect = allowedDropEffect(*pdwEffect);
135         }
136         else
137         {
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         {
169                 *pdwEffect = DROPEFFECT_NONE;
170         }
171         if (data)
172                 m_system->pushDragDropEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data );
173                 
174         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
175         return S_OK;
176 }
177
178 /* 
179  * Helpers
180  */
181  
182 DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed)
183 {
184         DWORD dwEffect = DROPEFFECT_NONE;
185         if(dwAllowed & DROPEFFECT_COPY) 
186                 dwEffect = DROPEFFECT_COPY;
187
188         return dwEffect;
189 }
190
191 GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject * pDataObject)
192 {
193         /* Text
194          * Note: Unicode text is aviable as CF_TEXT too, the system can do the 
195          * conversion, but we do the conversion ourself with WC_NO_BEST_FIT_CHARS.
196          */
197         FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
198         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
199         {
200                 return GHOST_kDragnDropTypeString;
201         }
202
203         // Filesnames
204         fmtetc.cfFormat = CF_HDROP;
205         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
206         {
207                 return GHOST_kDragnDropTypeFilenames;
208         }
209
210         return GHOST_kDragnDropTypeUnknown;
211 }
212
213 void * GHOST_DropTargetWin32::getGhostData(IDataObject * pDataObject)
214 {
215         GHOST_TDragnDropTypes type = getGhostType(pDataObject);
216         switch(type)
217         {
218                 case GHOST_kDragnDropTypeFilenames:
219                         return getDropDataAsFilenames(pDataObject);
220                         break;
221                 case GHOST_kDragnDropTypeString:
222                         return getDropDataAsString(pDataObject);
223                         break;
224                 case GHOST_kDragnDropTypeBitmap:
225                         //return getDropDataAsBitmap(pDataObject);
226                         break;
227                 default:
228 #ifdef GHOST_DEBUG
229                         ::printf("\nGHOST_kDragnDropTypeUnknown");
230 #endif // GHOST_DEBUG
231                         return NULL;
232                         break;
233         }
234         return NULL;
235 }
236
237 void * GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject * pDataObject)
238 {
239         UINT  totfiles, nvalid=0;
240         WCHAR fpath [MAX_PATH]; 
241         char * temp_path;
242         GHOST_TStringArray *strArray = NULL;
243         FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
244         STGMEDIUM stgmed;
245         HDROP hdrop;
246
247         // Check if dataobject supplies the format we want.
248         // Double checking here, first in getGhostType.
249         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
250         {
251                 if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
252                 {
253                         hdrop = (HDROP)::GlobalLock(stgmed.hGlobal);
254
255                         totfiles = ::DragQueryFileW ( hdrop, -1, NULL, 0 );
256                         if (!totfiles)
257                         {
258                                 ::GlobalUnlock(stgmed.hGlobal);
259                                 return NULL;
260                         }
261
262                         strArray = (GHOST_TStringArray*) ::malloc(sizeof(GHOST_TStringArray));
263                         strArray->count = 0;
264                         strArray->strings = (GHOST_TUns8**) ::malloc(totfiles*sizeof(GHOST_TUns8*));
265
266                         for ( UINT nfile = 0; nfile < totfiles; nfile++ )
267                         {
268                                 if ( ::DragQueryFileW ( hdrop, nfile, fpath, MAX_PATH ) > 0 )
269                                 {
270                                         if ( !WideCharToANSI(fpath, temp_path) )
271                                         {
272                                                 continue;
273                                         } 
274                                         // Just ignore paths that could not be converted verbatim.
275                                         if (strpbrk(temp_path, "?"))
276                                         {
277 #ifdef GHOST_DEBUG
278                                                 ::printf("\ndiscarding path that contains illegal characters: %s", temp_path);
279 #endif // GHOST_DEBUG
280                                                 ::free(temp_path);
281                                                 temp_path = NULL;
282                                                 continue;
283                                         }
284                                         strArray->strings[nvalid] = (GHOST_TUns8*) temp_path;
285                                         strArray->count = nvalid+1;
286                                         nvalid++;
287                                 }
288                         }
289                         // Free up memory.
290                         ::GlobalUnlock(stgmed.hGlobal);
291                         ::ReleaseStgMedium(&stgmed);
292                         
293                         return strArray;
294                 }
295         }
296         return NULL;
297 }
298
299 void * GHOST_DropTargetWin32::getDropDataAsString(IDataObject * pDataObject)
300 {
301         char* tmp_string;
302         FORMATETC fmtetc = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
303         STGMEDIUM stgmed;
304
305         // Try unicode first.
306         // Check if dataobject supplies the format we want.
307         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
308         {
309                 if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
310                 {
311                         LPCWSTR wstr = (LPCWSTR)::GlobalLock(stgmed.hGlobal);
312                         if ( !WideCharToANSI(wstr, tmp_string) )
313                         {
314                                 ::GlobalUnlock(stgmed.hGlobal);
315                                 return NULL;
316                         }
317                         // Free memory
318                         ::GlobalUnlock(stgmed.hGlobal);
319                         ::ReleaseStgMedium(&stgmed);
320 #ifdef GHOST_DEBUG
321                         ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n",tmp_string);
322 #endif // GHOST_DEBUG
323                         return tmp_string;
324                 }
325         }
326
327         fmtetc.cfFormat = CF_TEXT;
328
329         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
330         {
331                 if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
332                 {
333                         char * str = (char*)::GlobalLock(stgmed.hGlobal);
334                         
335                         tmp_string = (char*)::malloc(::strlen(str)+1);
336                         if ( !tmp_string )
337                         {
338                                 ::GlobalUnlock(stgmed.hGlobal);
339                                 return NULL;
340                         }
341
342                         if ( !::strcpy(tmp_string, str) )
343                         {
344                                 ::free(tmp_string);
345                                 ::GlobalUnlock(stgmed.hGlobal);
346                                 return NULL;
347                         }
348                         // Free memory
349                         ::GlobalUnlock(stgmed.hGlobal);
350                         ::ReleaseStgMedium(&stgmed);
351
352                         return tmp_string;
353                 }
354         }
355         
356         return NULL;
357 }
358
359 int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char * &out)
360 {
361         int size;
362         out = NULL; //caller should free if != NULL
363
364         // Get the required size.
365         size = ::WideCharToMultiByte(CP_ACP,            //System Default Codepage
366                                                                 0x00000400,             // WC_NO_BEST_FIT_CHARS
367                                                                 in,
368                                                                 -1,                             //-1 null terminated, makes output null terminated too.
369                                                                 NULL,
370                                                                 0,
371                                                                 NULL,NULL
372                         );
373         
374         if(!size) 
375         {
376 #ifdef GHOST_DEBUG
377                 ::printLastError();
378 #endif // GHOST_DEBUG
379                 return 0;
380         }
381
382         out = (char*)::malloc(size);
383         if (!out)
384         {
385                 ::printf("\nmalloc failed!!!");
386                 return 0;
387         }
388
389         size = ::WideCharToMultiByte(CP_ACP,
390                                                                 0x00000400,
391                                                                 in,
392                                                                 -1,
393                                                                 (LPSTR) out,
394                                                                 size,
395                                                                 NULL,NULL
396                         );
397
398         if(!size)
399         {
400 #ifdef GHOST_DEBUG
401                 ::printLastError();
402 #endif //GHOST_DEBUG
403                 ::free(out);
404                 out = NULL;
405         }
406         return size;
407 }
408
409 #ifdef GHOST_DEBUG
410 void printLastError(void)
411 {
412         LPTSTR s;
413         DWORD err;
414
415         err = GetLastError();
416         if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
417                 FORMAT_MESSAGE_FROM_SYSTEM,
418                 NULL,
419                 err,
420                 0,
421                 (LPTSTR)&s,
422                 0,
423                 NULL)
424         )
425         {
426                 printf("\nLastError: (%d) %s\n", (int)err, s);
427                 LocalFree(s);
428         }
429 }
430 #endif // GHOST_DEBUG
431