/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ /** \file ghost/intern/GHOST_DropTargetWin32.cpp * \ingroup GHOST */ #include "GHOST_Debug.h" #include "GHOST_DropTargetWin32.h" #include #include "utf_winfunc.h" #include "utfconv.h" #ifdef GHOST_DEBUG // utility void printLastError(void); #endif // GHOST_DEBUG GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system) : m_window(window), m_system(system) { m_cRef = 1; m_hWnd = window->getHWND(); m_draggedObjectType = GHOST_kDragnDropTypeUnknown; } GHOST_DropTargetWin32::~GHOST_DropTargetWin32() { } /* * IUnknown::QueryInterface */ HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppvObj) { if (!ppvObj) return E_INVALIDARG; *ppvObj = NULL; if (riid == IID_IUnknown || riid == IID_IDropTarget) { AddRef(); *ppvObj = (void *)this; return S_OK; } else { *ppvObj = 0; return E_NOINTERFACE; } } /* * IUnknown::AddRef */ ULONG __stdcall GHOST_DropTargetWin32::AddRef(void) { return ::InterlockedIncrement(&m_cRef); } /* * IUnknown::Release */ ULONG __stdcall GHOST_DropTargetWin32::Release(void) { ULONG refs = ::InterlockedDecrement(&m_cRef); if (refs == 0) { delete this; return 0; } else { return refs; } } /* * Implementation of IDropTarget::DragEnter */ HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { // we accept all drop by default m_window->setAcceptDragOperation(true); *pdwEffect = DROPEFFECT_NONE; m_draggedObjectType = getGhostType(pDataObject); m_system->pushDragDropEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL); return S_OK; } /* * Implementation of IDropTarget::DragOver */ HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { if (m_window->canAcceptDragOperation()) { *pdwEffect = allowedDropEffect(*pdwEffect); } else { *pdwEffect = DROPEFFECT_NONE; // *pdwEffect = DROPEFFECT_COPY; // XXX Uncomment to test drop. Drop will not be called if pdwEffect == DROPEFFECT_NONE. } m_system->pushDragDropEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, m_window, pt.x, pt.y, NULL); return S_OK; } /* * Implementation of IDropTarget::DragLeave */ HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void) { m_system->pushDragDropEvent(GHOST_kEventDraggingExited, m_draggedObjectType, m_window, 0, 0, NULL); m_draggedObjectType = GHOST_kDragnDropTypeUnknown; return S_OK; } /* Implementation of IDropTarget::Drop * This function will not be called if pdwEffect is set to DROPEFFECT_NONE in * the implementation of IDropTarget::DragOver */ HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { void *data = getGhostData(pDataObject); if (m_window->canAcceptDragOperation()) { *pdwEffect = allowedDropEffect(*pdwEffect); } else { *pdwEffect = DROPEFFECT_NONE; } if (data) m_system->pushDragDropEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data); m_draggedObjectType = GHOST_kDragnDropTypeUnknown; return S_OK; } /* * Helpers */ DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed) { DWORD dwEffect = DROPEFFECT_NONE; if (dwAllowed & DROPEFFECT_COPY) dwEffect = DROPEFFECT_COPY; return dwEffect; } GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *pDataObject) { /* Text * Note: Unicode text is aviable as CF_TEXT too, the system can do the * conversion, but we do the conversion ourself with WC_NO_BEST_FIT_CHARS. */ FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; if (pDataObject->QueryGetData(&fmtetc) == S_OK) { return GHOST_kDragnDropTypeString; } // Filesnames fmtetc.cfFormat = CF_HDROP; if (pDataObject->QueryGetData(&fmtetc) == S_OK) { return GHOST_kDragnDropTypeFilenames; } return GHOST_kDragnDropTypeUnknown; } void *GHOST_DropTargetWin32::getGhostData(IDataObject *pDataObject) { GHOST_TDragnDropTypes type = getGhostType(pDataObject); switch (type) { case GHOST_kDragnDropTypeFilenames: return getDropDataAsFilenames(pDataObject); break; case GHOST_kDragnDropTypeString: return getDropDataAsString(pDataObject); break; case GHOST_kDragnDropTypeBitmap: //return getDropDataAsBitmap(pDataObject); break; default: #ifdef GHOST_DEBUG ::printf("\nGHOST_kDragnDropTypeUnknown"); #endif // GHOST_DEBUG return NULL; break; } return NULL; } void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *pDataObject) { UINT totfiles, nvalid = 0; WCHAR fpath[MAX_PATH]; char *temp_path; GHOST_TStringArray *strArray = NULL; FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgmed; HDROP hdrop; // Check if dataobject supplies the format we want. // Double checking here, first in getGhostType. if (pDataObject->QueryGetData(&fmtetc) == S_OK) { if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) { hdrop = (HDROP) ::GlobalLock(stgmed.hGlobal); totfiles = ::DragQueryFileW(hdrop, -1, NULL, 0); if (!totfiles) { ::GlobalUnlock(stgmed.hGlobal); return NULL; } strArray = (GHOST_TStringArray *) ::malloc(sizeof(GHOST_TStringArray)); strArray->count = 0; strArray->strings = (GHOST_TUns8 **) ::malloc(totfiles * sizeof(GHOST_TUns8 *)); for (UINT nfile = 0; nfile < totfiles; nfile++) { if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) { if (!(temp_path = alloc_utf_8_from_16(fpath, 0)) ) { continue; } // Just ignore paths that could not be converted verbatim. strArray->strings[nvalid] = (GHOST_TUns8 *) temp_path; strArray->count = nvalid + 1; nvalid++; } } // Free up memory. ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); return strArray; } } return NULL; } void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject) { char *tmp_string; FORMATETC fmtetc = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgmed; // Try unicode first. // Check if dataobject supplies the format we want. if (pDataObject->QueryGetData(&fmtetc) == S_OK) { if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) { LPCWSTR wstr = (LPCWSTR) ::GlobalLock(stgmed.hGlobal); if (!(tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0)) ) { ::GlobalUnlock(stgmed.hGlobal); return NULL; } // Free memory ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); #ifdef GHOST_DEBUG ::printf("\n\n%s\n\n", tmp_string); #endif // GHOST_DEBUG return tmp_string; } } fmtetc.cfFormat = CF_TEXT; if (pDataObject->QueryGetData(&fmtetc) == S_OK) { if (pDataObject->GetData(&fmtetc, &stgmed) == S_OK) { char *str = (char *)::GlobalLock(stgmed.hGlobal); tmp_string = (char *)::malloc(::strlen(str) + 1); if (!tmp_string) { ::GlobalUnlock(stgmed.hGlobal); return NULL; } if (!::strcpy(tmp_string, str) ) { ::free(tmp_string); ::GlobalUnlock(stgmed.hGlobal); return NULL; } // Free memory ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); return tmp_string; } } return NULL; } int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char * &out) { int size; out = NULL; //caller should free if != NULL // Get the required size. size = ::WideCharToMultiByte(CP_ACP, //System Default Codepage 0x00000400, // WC_NO_BEST_FIT_CHARS in, -1, //-1 null terminated, makes output null terminated too. NULL, 0, NULL, NULL ); if (!size) { #ifdef GHOST_DEBUG ::printLastError(); #endif // GHOST_DEBUG return 0; } out = (char *)::malloc(size); if (!out) { ::printf("\nmalloc failed!!!"); return 0; } size = ::WideCharToMultiByte(CP_ACP, 0x00000400, in, -1, (LPSTR) out, size, NULL, NULL ); if (!size) { #ifdef GHOST_DEBUG ::printLastError(); #endif //GHOST_DEBUG ::free(out); out = NULL; } return size; } #ifdef GHOST_DEBUG void printLastError(void) { LPTSTR s; DWORD err; err = GetLastError(); if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, (LPTSTR)&s, 0, NULL) ) { printf("\nLastError: (%d) %s\n", (int)err, s); LocalFree(s); } } #endif // GHOST_DEBUG