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