Blender  V2.59
GHOST_DropTargetWin32.cpp
Go to the documentation of this file.
00001 /*
00002  * $Id: GHOST_DropTargetWin32.cpp 38908 2011-08-02 04:28:05Z merwin $
00003  * ***** BEGIN GPL LICENSE BLOCK *****
00004  *
00005  * This program is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * as published by the Free Software Foundation; either version 2
00008  * of the License, or (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software Foundation,
00017  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00020  * All rights reserved.
00021  *
00022  * The Original Code is: all of this file.
00023  *
00024  * Contributor(s): none yet.
00025  *
00026  * ***** END GPL LICENSE BLOCK *****
00027  */
00028 
00034 #include "GHOST_Debug.h"
00035 #include "GHOST_DropTargetWin32.h"
00036 #include <ShellApi.h>
00037 
00038 #ifdef GHOST_DEBUG
00039 // utility
00040 void printLastError(void);
00041 #endif // GHOST_DEBUG
00042 
00043 
00044 GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 * window, GHOST_SystemWin32 * system)
00045 :
00046 m_window(window),
00047 m_system(system)
00048 {
00049         m_cRef = 1;
00050         m_hWnd = window->getHWND();
00051         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
00052         
00053         // register our window as drop target
00054         ::RegisterDragDrop(m_hWnd, this);
00055 }
00056 
00057 GHOST_DropTargetWin32::~GHOST_DropTargetWin32()
00058 {
00059         ::RevokeDragDrop(m_hWnd);
00060 }
00061 
00062 
00063 /* 
00064  *      IUnknown::QueryInterface
00065  */
00066 HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface (REFIID riid, void ** ppvObj)
00067 {
00068 
00069         if (!ppvObj)
00070                 return E_INVALIDARG;
00071         *ppvObj = NULL;
00072 
00073         if(riid == IID_IUnknown || riid == IID_IDropTarget)
00074         {
00075                 AddRef();
00076                 *ppvObj = (void*)this;
00077                 return S_OK;
00078         }
00079         else
00080         {
00081                 *ppvObj = 0;
00082                 return E_NOINTERFACE;
00083         }
00084 }
00085 
00086 
00087 /* 
00088  *      IUnknown::AddRef 
00089  */
00090 
00091 ULONG __stdcall GHOST_DropTargetWin32::AddRef(void)
00092 {
00093         return ::InterlockedIncrement(&m_cRef);
00094 }
00095 
00096 /* 
00097  * IUnknown::Release
00098  */
00099 ULONG __stdcall GHOST_DropTargetWin32::Release(void)
00100 {
00101         ULONG refs = ::InterlockedDecrement(&m_cRef);
00102                 
00103         if(refs == 0)
00104         {
00105                 delete this;
00106                 return 0;
00107         }
00108         else
00109         {
00110                 return refs;
00111         }
00112 }
00113 
00114 /* 
00115  * Implementation of IDropTarget::DragEnter
00116  */
00117 HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
00118 {
00119         // we accept all drop by default
00120         m_window->setAcceptDragOperation(true);
00121         *pdwEffect = DROPEFFECT_NONE;
00122         
00123         m_draggedObjectType = getGhostType(pDataObject);
00124         m_system->pushDragDropEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
00125         return S_OK;
00126 }
00127 
00128 /* 
00129  * Implementation of IDropTarget::DragOver
00130  */
00131 HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
00132 {
00133         if(m_window->canAcceptDragOperation())
00134         {
00135                 *pdwEffect = allowedDropEffect(*pdwEffect);
00136         }
00137         else
00138         {
00139                 *pdwEffect = DROPEFFECT_NONE;
00140                 //*pdwEffect = DROPEFFECT_COPY; // XXX Uncomment to test drop. Drop will not be called if pdwEffect == DROPEFFECT_NONE.
00141         }
00142         m_system->pushDragDropEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
00143         return S_OK;
00144 }
00145 
00146 /* 
00147  * Implementation of IDropTarget::DragLeave
00148  */
00149 HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
00150 {
00151         m_system->pushDragDropEvent(GHOST_kEventDraggingExited, m_draggedObjectType, m_window, 0, 0, NULL);
00152         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
00153         return S_OK;
00154 }
00155 
00156 /* Implementation of IDropTarget::Drop
00157  * This function will not be called if pdwEffect is set to DROPEFFECT_NONE in 
00158  * the implementation of IDropTarget::DragOver
00159  */
00160 HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
00161 {
00162         void * data = getGhostData(pDataObject);
00163         if(m_window->canAcceptDragOperation())
00164         {
00165                 *pdwEffect = allowedDropEffect(*pdwEffect);
00166 
00167         }
00168         else
00169         {
00170                 *pdwEffect = DROPEFFECT_NONE;
00171         }
00172         if (data)
00173                 m_system->pushDragDropEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data );
00174                 
00175         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
00176         return S_OK;
00177 }
00178 
00179 /* 
00180  * Helpers
00181  */
00182  
00183 DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed)
00184 {
00185         DWORD dwEffect = DROPEFFECT_NONE;
00186         if(dwAllowed & DROPEFFECT_COPY) 
00187                 dwEffect = DROPEFFECT_COPY;
00188 
00189         return dwEffect;
00190 }
00191 
00192 GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject * pDataObject)
00193 {
00194         /* Text
00195          * Note: Unicode text is aviable as CF_TEXT too, the system can do the 
00196          * conversion, but we do the conversion ourself with WC_NO_BEST_FIT_CHARS.
00197          */
00198         FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
00199         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
00200         {
00201                 return GHOST_kDragnDropTypeString;
00202         }
00203 
00204         // Filesnames
00205         fmtetc.cfFormat = CF_HDROP;
00206         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
00207         {
00208                 return GHOST_kDragnDropTypeFilenames;
00209         }
00210 
00211         return GHOST_kDragnDropTypeUnknown;
00212 }
00213 
00214 void * GHOST_DropTargetWin32::getGhostData(IDataObject * pDataObject)
00215 {
00216         GHOST_TDragnDropTypes type = getGhostType(pDataObject);
00217         switch(type)
00218         {
00219                 case GHOST_kDragnDropTypeFilenames:
00220                         return getDropDataAsFilenames(pDataObject);
00221                         break;
00222                 case GHOST_kDragnDropTypeString:
00223                         return getDropDataAsString(pDataObject);
00224                         break;
00225                 case GHOST_kDragnDropTypeBitmap:
00226                         //return getDropDataAsBitmap(pDataObject);
00227                         break;
00228                 default:
00229 #ifdef GHOST_DEBUG
00230                         ::printf("\nGHOST_kDragnDropTypeUnknown");
00231 #endif // GHOST_DEBUG
00232                         return NULL;
00233                         break;
00234         }
00235         return NULL;
00236 }
00237 
00238 void * GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject * pDataObject)
00239 {
00240         UINT  totfiles, nvalid=0;
00241         WCHAR fpath [MAX_PATH]; 
00242         char * temp_path;
00243         GHOST_TStringArray *strArray = NULL;
00244         FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
00245         STGMEDIUM stgmed;
00246         HDROP hdrop;
00247 
00248         // Check if dataobject supplies the format we want.
00249         // Double checking here, first in getGhostType.
00250         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
00251         {
00252                 if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
00253                 {
00254                         hdrop = (HDROP)::GlobalLock(stgmed.hGlobal);
00255 
00256                         totfiles = ::DragQueryFileW ( hdrop, -1, NULL, 0 );
00257                         if (!totfiles)
00258                         {
00259                                 ::GlobalUnlock(stgmed.hGlobal);
00260                                 return NULL;
00261                         }
00262 
00263                         strArray = (GHOST_TStringArray*) ::malloc(sizeof(GHOST_TStringArray));
00264                         strArray->count = 0;
00265                         strArray->strings = (GHOST_TUns8**) ::malloc(totfiles*sizeof(GHOST_TUns8*));
00266 
00267                         for ( UINT nfile = 0; nfile < totfiles; nfile++ )
00268                         {
00269                                 if ( ::DragQueryFileW ( hdrop, nfile, fpath, MAX_PATH ) > 0 )
00270                                 {
00271                                         if ( !WideCharToANSI(fpath, temp_path) )
00272                                         {
00273                                                 continue;
00274                                         } 
00275                                         // Just ignore paths that could not be converted verbatim.
00276                                         if (strpbrk(temp_path, "?"))
00277                                         {
00278 #ifdef GHOST_DEBUG
00279                                                 ::printf("\ndiscarding path that contains illegal characters: %s", temp_path);
00280 #endif // GHOST_DEBUG
00281                                                 ::free(temp_path);
00282                                                 temp_path = NULL;
00283                                                 continue;
00284                                         }
00285                                         strArray->strings[nvalid] = (GHOST_TUns8*) temp_path;
00286                                         strArray->count = nvalid+1;
00287                                         nvalid++;
00288                                 }
00289                         }
00290                         // Free up memory.
00291                         ::GlobalUnlock(stgmed.hGlobal);
00292                         ::ReleaseStgMedium(&stgmed);
00293                         
00294                         return strArray;
00295                 }
00296         }
00297         return NULL;
00298 }
00299 
00300 void * GHOST_DropTargetWin32::getDropDataAsString(IDataObject * pDataObject)
00301 {
00302         char* tmp_string;
00303         FORMATETC fmtetc = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
00304         STGMEDIUM stgmed;
00305 
00306         // Try unicode first.
00307         // Check if dataobject supplies the format we want.
00308         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
00309         {
00310                 if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
00311                 {
00312                         LPCWSTR wstr = (LPCWSTR)::GlobalLock(stgmed.hGlobal);
00313                         if ( !WideCharToANSI(wstr, tmp_string) )
00314                         {
00315                                 ::GlobalUnlock(stgmed.hGlobal);
00316                                 return NULL;
00317                         }
00318                         // Free memory
00319                         ::GlobalUnlock(stgmed.hGlobal);
00320                         ::ReleaseStgMedium(&stgmed);
00321 #ifdef GHOST_DEBUG
00322                         ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n",tmp_string);
00323 #endif // GHOST_DEBUG
00324                         return tmp_string;
00325                 }
00326         }
00327 
00328         fmtetc.cfFormat = CF_TEXT;
00329 
00330         if(pDataObject->QueryGetData(&fmtetc) == S_OK)
00331         {
00332                 if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
00333                 {
00334                         char * str = (char*)::GlobalLock(stgmed.hGlobal);
00335                         
00336                         tmp_string = (char*)::malloc(::strlen(str)+1);
00337                         if ( !tmp_string )
00338                         {
00339                                 ::GlobalUnlock(stgmed.hGlobal);
00340                                 return NULL;
00341                         }
00342 
00343                         if ( !::strcpy(tmp_string, str) )
00344                         {
00345                                 ::free(tmp_string);
00346                                 ::GlobalUnlock(stgmed.hGlobal);
00347                                 return NULL;
00348                         }
00349                         // Free memory
00350                         ::GlobalUnlock(stgmed.hGlobal);
00351                         ::ReleaseStgMedium(&stgmed);
00352 
00353                         return tmp_string;
00354                 }
00355         }
00356         
00357         return NULL;
00358 }
00359 
00360 int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char * &out)
00361 {
00362         int size;
00363         out = NULL; //caller should free if != NULL
00364 
00365         // Get the required size.
00366         size = ::WideCharToMultiByte(CP_ACP,            //System Default Codepage
00367                                                                 0x00000400,             // WC_NO_BEST_FIT_CHARS
00368                                                                 in,
00369                                                                 -1,                             //-1 null terminated, makes output null terminated too.
00370                                                                 NULL,
00371                                                                 0,
00372                                                                 NULL,NULL
00373                         );
00374         
00375         if(!size) 
00376         {
00377 #ifdef GHOST_DEBUG
00378                 ::printLastError();
00379 #endif // GHOST_DEBUG
00380                 return 0;
00381         }
00382 
00383         out = (char*)::malloc(size);
00384         if (!out)
00385         {
00386                 ::printf("\nmalloc failed!!!");
00387                 return 0;
00388         }
00389 
00390         size = ::WideCharToMultiByte(CP_ACP,
00391                                                                 0x00000400,
00392                                                                 in,
00393                                                                 -1,
00394                                                                 (LPSTR) out,
00395                                                                 size,
00396                                                                 NULL,NULL
00397                         );
00398 
00399         if(!size)
00400         {
00401 #ifdef GHOST_DEBUG
00402                 ::printLastError();
00403 #endif //GHOST_DEBUG
00404                 ::free(out);
00405                 out = NULL;
00406         }
00407         return size;
00408 }
00409 
00410 #ifdef GHOST_DEBUG
00411 void printLastError(void)
00412 {
00413         LPTSTR s;
00414         DWORD err;
00415 
00416         err = GetLastError();
00417         if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
00418                 FORMAT_MESSAGE_FROM_SYSTEM,
00419                 NULL,
00420                 err,
00421                 0,
00422                 (LPTSTR)&s,
00423                 0,
00424                 NULL)
00425         )
00426         {
00427                 printf("\nLastError: (%d) %s\n", (int)err, s);
00428                 LocalFree(s);
00429         }
00430 }
00431 #endif // GHOST_DEBUG
00432