summaryrefslogtreecommitdiff
path: root/src/modules/clist/clcfiledrop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/clist/clcfiledrop.cpp')
-rw-r--r--src/modules/clist/clcfiledrop.cpp278
1 files changed, 278 insertions, 0 deletions
diff --git a/src/modules/clist/clcfiledrop.cpp b/src/modules/clist/clcfiledrop.cpp
new file mode 100644
index 0000000000..aae299d7b8
--- /dev/null
+++ b/src/modules/clist/clcfiledrop.cpp
@@ -0,0 +1,278 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2009 Miranda ICQ/IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "commonheaders.h"
+#include "clc.h"
+#include <shlobj.h>
+
+struct CDropTarget : IDropTarget
+{
+ LONG refCount;
+ IDropTargetHelper *pDropTargetHelper;
+
+ ULONG STDMETHODCALLTYPE AddRef(void);
+ ULONG STDMETHODCALLTYPE Release(void);
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject);
+
+ HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+ HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+ HRESULT STDMETHODCALLTYPE DragLeave(void);
+ HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
+}
+static dropTarget;
+
+static HWND hwndCurrentDrag = NULL;
+static int originalSelection;
+
+HRESULT CDropTarget::QueryInterface(REFIID riid, LPVOID * ppvObj)
+{
+ if (riid == IID_IDropTarget) {
+ *ppvObj = this;
+ AddRef();
+ return S_OK;
+ }
+ *ppvObj = NULL;
+ return E_NOINTERFACE;
+}
+
+ULONG CDropTarget::AddRef(void)
+{
+ return InterlockedIncrement(&refCount);
+}
+
+ULONG CDropTarget::Release(void)
+{
+ if (refCount == 1) {
+ if (pDropTargetHelper)
+ pDropTargetHelper->Release();
+ }
+ return InterlockedDecrement(&refCount);
+}
+
+static HANDLE HContactFromPoint(HWND hwnd, struct ClcData *dat, int x, int y, int *hitLine)
+{
+ int hit;
+ struct ClcContact *contact;
+ DWORD hitFlags;
+ char *szProto;
+ DWORD protoCaps;
+
+ hit = cli.pfnHitTest(hwnd, dat, x, y, &contact, NULL, &hitFlags);
+ if (hit == -1 || !(hitFlags & (CLCHT_ONITEMLABEL | CLCHT_ONITEMICON)) || contact->type != CLCIT_CONTACT)
+ return NULL;
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) contact->hContact, 0);
+ if (szProto == NULL)
+ return NULL;
+ protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (!(protoCaps & PF1_FILESEND))
+ return NULL;
+ if (ID_STATUS_OFFLINE == DBGetContactSettingWord(contact->hContact, szProto, "Status", ID_STATUS_OFFLINE))
+ return NULL;
+ if (hitLine)
+ *hitLine = hit;
+ return contact->hContact;
+}
+
+HRESULT CDropTarget::DragOver(DWORD /*grfKeyState*/, POINTL pt, DWORD * pdwEffect)
+{
+ POINT shortPt;
+ struct ClcData *dat;
+ RECT clRect;
+ int hit;
+ HANDLE hContact;
+
+ if (pDropTargetHelper && hwndCurrentDrag)
+ pDropTargetHelper->DragOver((POINT*)&pt, *pdwEffect);
+
+ *pdwEffect = 0;
+ if (hwndCurrentDrag == NULL) {
+ *pdwEffect = DROPEFFECT_NONE;
+ return S_OK;
+ }
+ CallService(MS_CLIST_PAUSEAUTOHIDE, 0, 0);
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+ shortPt.x = pt.x;
+ shortPt.y = pt.y;
+ ScreenToClient(hwndCurrentDrag, &shortPt);
+ GetClientRect(hwndCurrentDrag, &clRect);
+
+ if (shortPt.y < dat->dragAutoScrollHeight || shortPt.y >= clRect.bottom - dat->dragAutoScrollHeight) {
+ *pdwEffect |= DROPEFFECT_SCROLL;
+ cli.pfnScrollTo(hwndCurrentDrag, dat, dat->yScroll + (shortPt.y < dat->dragAutoScrollHeight ? -1 : 1) * dat->rowHeight * 2, 0);
+ }
+ hContact = HContactFromPoint(hwndCurrentDrag, dat, shortPt.x, shortPt.y, &hit);
+ if (hContact == NULL) {
+ hit = -1;
+ *pdwEffect |= DROPEFFECT_NONE;
+ }
+ else
+ *pdwEffect |= DROPEFFECT_COPY;
+
+ if (dat->selection != hit) {
+ dat->selection = hit;
+ cli.pfnInvalidateRect(hwndCurrentDrag, NULL, FALSE);
+ if (pDropTargetHelper) pDropTargetHelper->Show(FALSE);
+ UpdateWindow(hwndCurrentDrag);
+ if (pDropTargetHelper) pDropTargetHelper->Show(TRUE);
+ }
+
+ return S_OK;
+}
+
+HRESULT CDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ HWND hwnd;
+ TCHAR szWindowClass[64];
+ POINT shortPt;
+
+ shortPt.x = pt.x;
+ shortPt.y = pt.y;
+ hwnd = WindowFromPoint(shortPt);
+ GetClassName(hwnd, szWindowClass, SIZEOF(szWindowClass));
+ if (!lstrcmp(szWindowClass, CLISTCONTROL_CLASS)) {
+ struct ClcData *dat;
+ hwndCurrentDrag = hwnd;
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+ originalSelection = dat->selection;
+ dat->showSelAlways = 1;
+ }
+ if (pDropTargetHelper && hwndCurrentDrag)
+ pDropTargetHelper->DragEnter(hwndCurrentDrag, pDataObj, (POINT*)&pt, *pdwEffect);
+ return DragOver(grfKeyState, pt, pdwEffect);
+}
+
+HRESULT CDropTarget::DragLeave(void)
+{
+ if (hwndCurrentDrag) {
+ struct ClcData *dat;
+ if (pDropTargetHelper)
+ pDropTargetHelper->DragLeave();
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+ dat->showSelAlways = 0;
+ dat->selection = originalSelection;
+ cli.pfnInvalidateRect(hwndCurrentDrag, NULL, FALSE);
+ }
+ hwndCurrentDrag = NULL;
+ return S_OK;
+}
+
+static void AddToFileList(TCHAR ***pppFiles, int *totalCount, const TCHAR *szFilename)
+{
+ *pppFiles = (TCHAR **) mir_realloc(*pppFiles, (++*totalCount + 1) * sizeof(TCHAR *));
+ (*pppFiles)[*totalCount] = NULL;
+ (*pppFiles)[*totalCount - 1] = mir_tstrdup(szFilename);
+ if (GetFileAttributes(szFilename) & FILE_ATTRIBUTE_DIRECTORY) {
+ WIN32_FIND_DATA fd;
+ HANDLE hFind;
+ TCHAR szPath[MAX_PATH];
+ lstrcpy(szPath, szFilename);
+ lstrcat(szPath, _T("\\*"));
+ if (hFind = FindFirstFile(szPath, &fd)) {
+ do {
+ if (!lstrcmp(fd.cFileName, _T(".")) || !lstrcmp(fd.cFileName, _T("..")))
+ continue;
+ lstrcpy(szPath, szFilename);
+ lstrcat(szPath, _T("\\"));
+ lstrcat(szPath, fd.cFileName);
+ AddToFileList(pppFiles, totalCount, szPath);
+ } while (FindNextFile(hFind, &fd));
+ FindClose(hFind);
+ }
+ }
+}
+
+HRESULT CDropTarget::Drop(IDataObject * pDataObj, DWORD /*fKeyState*/, POINTL pt, DWORD * pdwEffect)
+{
+ FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stg;
+ HDROP hDrop;
+ POINT shortPt;
+ struct ClcData *dat;
+ HANDLE hContact;
+
+ if (pDropTargetHelper && hwndCurrentDrag)
+ pDropTargetHelper->Drop(pDataObj, (POINT*)&pt, *pdwEffect);
+
+ *pdwEffect = DROPEFFECT_NONE;
+ if (hwndCurrentDrag == NULL || S_OK != pDataObj->GetData(&fe, &stg))
+ return S_OK;
+ hDrop = (HDROP) stg.hGlobal;
+ dat = (struct ClcData *) GetWindowLongPtr(hwndCurrentDrag, 0);
+
+ shortPt.x = pt.x;
+ shortPt.y = pt.y;
+ ScreenToClient(hwndCurrentDrag, &shortPt);
+ hContact = HContactFromPoint(hwndCurrentDrag, dat, shortPt.x, shortPt.y, NULL);
+ if (hContact != NULL) {
+ TCHAR **ppFiles = NULL;
+ TCHAR szFilename[MAX_PATH];
+ int fileCount, totalCount = 0, i;
+
+ fileCount = DragQueryFile(hDrop, -1, NULL, 0);
+ ppFiles = NULL;
+ for (i = 0; i < fileCount; i++) {
+ DragQueryFile(hDrop, i, szFilename, SIZEOF(szFilename));
+ AddToFileList(&ppFiles, &totalCount, szFilename);
+ }
+
+ if (!CallService(MS_FILE_SENDSPECIFICFILEST, (WPARAM) hContact, (LPARAM) ppFiles))
+ *pdwEffect = DROPEFFECT_COPY;
+
+ for (i = 0; ppFiles[i]; i++)
+ mir_free(ppFiles[i]);
+ mir_free(ppFiles);
+ }
+
+ if (stg.pUnkForRelease)
+ stg.pUnkForRelease->Release();
+ else
+ GlobalFree(stg.hGlobal);
+
+ DragLeave();
+ return S_OK;
+}
+
+static VOID CALLBACK CreateDropTargetHelperTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD)
+{
+ KillTimer(hwnd, idEvent);
+ //This is a ludicrously slow function (~200ms) so we delay load it a bit.
+ if (S_OK != CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
+ IID_IDropTargetHelper, (LPVOID*)&dropTarget.pDropTargetHelper))
+ dropTarget.pDropTargetHelper = NULL;
+}
+
+void InitFileDropping(void)
+{
+ // Disabled as this function loads tons of dlls for no apparenet reason
+ // we will se what the reaction will be
+// SetTimer(NULL, 1, 1000, CreateDropTargetHelperTimerProc);
+}
+
+void fnRegisterFileDropping(HWND hwnd)
+{
+ RegisterDragDrop(hwnd, (IDropTarget *) & dropTarget);
+}
+
+void fnUnregisterFileDropping(HWND hwnd)
+{
+ RevokeDragDrop(hwnd);
+}