OSDN Git Service

Improve drag&drop support (from zip folder, ftp folder, thunderbird, winscp, firefox...
authorsdottaka <sdottaka@users.sourceforge.net>
Tue, 24 Feb 2015 14:16:00 +0000 (23:16 +0900)
committersdottaka <sdottaka@users.sourceforge.net>
Tue, 24 Feb 2015 14:16:00 +0000 (23:16 +0900)
--HG--
branch : stable

16 files changed:
Src/Common/DragDrop.cpp [deleted file]
Src/Common/DragDrop.h [deleted file]
Src/Common/SuperComboBox.cpp
Src/Common/SuperComboBox.h
Src/DropHandler.cpp [new file with mode: 0644]
Src/DropHandler.h [new file with mode: 0644]
Src/MainFrm.cpp
Src/MainFrm.h
Src/Merge.cpp
Src/Merge.rc
Src/Merge.vcxproj
Src/Merge.vcxproj.filters
Src/OpenView.cpp
Src/OpenView.h
Src/paths.cpp
Src/paths.h

diff --git a/Src/Common/DragDrop.cpp b/Src/Common/DragDrop.cpp
deleted file mode 100644 (file)
index 65f4b08..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "DragDrop.h"
-#include <memory>
-#include "paths.h"
-
-//
-//     OnDropFiles code from CDropEdit
-//     Copyright 1997 Chris Losinger
-//
-//     shortcut expansion code modified from :
-//     CShortcut, 1996 Rob Warner
-//
-
-bool GetDroppedFiles(HDROP dropInfo, std::vector<String>& files)
-{
-// Get the number of pathnames that have been dropped
-       UINT wNumFilesDropped = DragQueryFile(dropInfo, 0xFFFFFFFF, NULL, 0);
-       UINT fileCount = 0;
-
-       // get all file names. but we'll only need the first one.
-       for (WORD x = 0 ; x < wNumFilesDropped; x++)
-       {
-               // Get the number of bytes required by the file's full pathname
-               UINT wPathnameSize = DragQueryFile(dropInfo, x, NULL, 0);
-
-               // Allocate memory to contain full pathname & zero byte
-               wPathnameSize += 1;
-               std::unique_ptr<TCHAR[]> npszFile(new TCHAR[wPathnameSize]);
-
-               // Copy the pathname into the buffer
-               DragQueryFile(dropInfo, x, npszFile.get(), wPathnameSize);
-
-               if (x < 3)
-               {
-                       files.resize(x + 1);
-                       files[x] = npszFile.get();
-                       fileCount++;
-               }
-       }
-
-       // Free the memory block containing the dropped-file information
-       DragFinish(dropInfo);
-
-       for (UINT i = 0; i < fileCount; i++)
-       {
-               if (paths_IsShortcut(files[i]))
-               {
-                       // if this was a shortcut, we need to expand it to the target path
-                       String expandedFile = ExpandShortcut(files[i]);
-
-                       // if that worked, we should have a real file name
-                       if (!expandedFile.empty())
-                               files[i] = expandedFile;
-               }
-       }
-       return true;
-}
diff --git a/Src/Common/DragDrop.h b/Src/Common/DragDrop.h
deleted file mode 100644 (file)
index f7ea2c5..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-#include "UnicodeString.h"
-#include <windows.h>
-#include <vector>
-
-bool GetDroppedFiles(HDROP dropInfo, std::vector<String>& files);
index ed1802a..8ce444c 100644 (file)
@@ -5,7 +5,7 @@
 #include "SuperComboBox.h"
 #include <shlwapi.h>
 #include <vector>
-#include "DragDrop.h"
+#include "DropHandler.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -49,6 +49,7 @@ static char THIS_FILE[] = __FILE__;
 HIMAGELIST CSuperComboBox::m_himlSystem = NULL;
 
 CSuperComboBox::CSuperComboBox(BOOL bAdd /*= TRUE*/, UINT idstrAddText /*= 0*/)
+       : m_pDropHandler(NULL)
 {
        m_bEditChanged=FALSE;
        m_bDoComplete = FALSE;
@@ -93,7 +94,7 @@ BEGIN_MESSAGE_MAP(CSuperComboBox, CComboBoxEx)
        ON_CONTROL_REFLECT_EX(CBN_EDITCHANGE, OnEditchange)
        ON_CONTROL_REFLECT_EX(CBN_SELCHANGE, OnSelchange)
        ON_WM_CREATE()
-       ON_WM_DROPFILES()
+       ON_WM_DESTROY()
        ON_NOTIFY_REFLECT(CBEN_GETDISPINFO, OnGetDispInfo)
        //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
@@ -101,6 +102,13 @@ END_MESSAGE_MAP()
 /////////////////////////////////////////////////////////////////////////////
 // CSuperComboBox message handlers
 
+void CSuperComboBox::PreSubclassWindow()
+{
+       CComboBoxEx::PreSubclassWindow();
+       m_pDropHandler = new DropHandler(std::bind(&CSuperComboBox::OnDropFiles, this, std::placeholders::_1));
+       RegisterDragDrop(m_hWnd, m_pDropHandler);
+}
+
 /**
  * @brief Returns whether the window associated with this object is ComboBoxEx.
  */
@@ -420,14 +428,20 @@ void CSuperComboBox::ResetContent()
 
 int CSuperComboBox::OnCreate(LPCREATESTRUCT lpCreateStruct) 
 {
-       if (CComboBox::OnCreate(lpCreateStruct) == -1)
+       if (CComboBoxEx::OnCreate(lpCreateStruct) == -1)
                return -1;
        
        SetAutoAdd(!m_strAutoAdd.IsEmpty());
-       DragAcceptFiles(TRUE);
+       m_pDropHandler = new DropHandler(std::bind(&CSuperComboBox::OnDropFiles, this, std::placeholders::_1));
+       RegisterDragDrop(m_hWnd, m_pDropHandler);
        return 0;
 }
 
+void CSuperComboBox::OnDestroy(void)
+{
+       if (m_pDropHandler)
+               RevokeDragDrop(m_hWnd);
+}
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -437,13 +451,8 @@ int CSuperComboBox::OnCreate(LPCREATESTRUCT lpCreateStruct)
 //     shortcut expansion code modified from :
 //     CShortcut, 1996 Rob Warner
 //
-void CSuperComboBox::OnDropFiles(HDROP dropInfo)
+void CSuperComboBox::OnDropFiles(const std::vector<String>& files)
 {
-       std::vector<String> files;
-       GetDroppedFiles(dropInfo, files);
-       if (files.size() == 0)
-               return;
-
        GetParent()->SendMessage(WM_COMMAND, GetDlgCtrlID() +
                (CBN_EDITUPDATE << 16), (LPARAM)m_hWnd);
        SetWindowText(files[0].c_str());
index ee498c2..b526274 100644 (file)
@@ -3,6 +3,11 @@
 // ComboBoxEx.h : header file
 //
 
+#include <vector>
+#include "UnicodeString.h"
+
+class DropHandler;
+
 /////////////////////////////////////////////////////////////////////////////
 // CSuperComboBox window
 
@@ -61,14 +66,17 @@ public:
 protected:
        CString m_strCurSel;
        virtual BOOL OnAddTemplate();
+       virtual void PreSubclassWindow();
        CString m_strAutoAdd;
        BOOL m_bMustUninitOLE;
+       DropHandler *m_pDropHandler;
        //{{AFX_MSG(CSuperComboBox)
        afx_msg BOOL OnEditchange();
        afx_msg BOOL OnSelchange();
        afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
-       afx_msg void OnDropFiles(HDROP dropInfo);
+       afx_msg void OnDropFiles(const std::vector<String>& files);
        afx_msg void OnGetDispInfo(NMHDR *pNotifyStruct, LRESULT *pResult);
+       afx_msg void OnDestroy();
        //}}AFX_MSG
 
        DECLARE_MESSAGE_MAP()
diff --git a/Src/DropHandler.cpp b/Src/DropHandler.cpp
new file mode 100644 (file)
index 0000000..bfb62bf
--- /dev/null
@@ -0,0 +1,366 @@
+#include <StdAfx.h>
+#include "DropHandler.h"
+#include <memory>
+#include <ShlObj.h>
+#include <comip.h>
+#include "paths.h"
+#include "Environment.h"
+#include "unicoder.h"
+#include "WaitStatusCursor.h"
+
+namespace
+{
+       struct HandleDeleter {
+               typedef HANDLE pointer;
+               void operator()(HANDLE h) { if (h && h != INVALID_HANDLE_VALUE) ::CloseHandle(h); }
+       };
+
+       typedef std::unique_ptr<HANDLE, HandleDeleter> unique_handle;
+       typedef _com_ptr_t<_com_IIID<IFileOperation, &__uuidof(IFileOperation)>> IFileOperationPtr;
+       typedef _com_ptr_t<_com_IIID<IShellItem, &__uuidof(IShellItem)>> IShellItemPtr;
+
+       bool CopyFileOrFolder(const String& src, const String& dst)
+       {
+               std::vector<TCHAR> srcpath(src.length() + 2, 0);
+               std::vector<TCHAR> dstpath(dst.length() + 2, 0);
+               memcpy(&srcpath[0], src.c_str(), src.length() * sizeof(TCHAR));
+               memcpy(&dstpath[0], dst.c_str(), dst.length() * sizeof(TCHAR));
+               SHFILEOPSTRUCT fileop = { 0, FO_COPY, &srcpath[0], &dstpath[0], FOF_NOCONFIRMATION, 0, 0, 0 };
+               return SHFileOperation(&fileop) == 0;
+       }
+
+       //
+       //      OnDropFiles code from CDropEdit
+       //      Copyright 1997 Chris Losinger
+       //
+       //      shortcut expansion code modified from :
+       //      CShortcut, 1996 Rob Warner
+       //
+
+       std::vector<String> GetDroppedFiles(HDROP dropInfo)
+       {
+               std::vector<String> files;
+               // Get the number of pathnames that have been dropped
+               UINT wNumFilesDropped = DragQueryFile(dropInfo, 0xFFFFFFFF, NULL, 0);
+               UINT fileCount = 0;
+
+               // get all file names. but we'll only need the first one.
+               for (WORD x = 0; x < wNumFilesDropped; x++)
+               {
+                       // Get the number of bytes required by the file's full pathname
+                       UINT wPathnameSize = DragQueryFile(dropInfo, x, NULL, 0);
+
+                       // Allocate memory to contain full pathname & zero byte
+                       wPathnameSize += 1;
+                       std::unique_ptr<TCHAR[]> npszFile(new TCHAR[wPathnameSize]);
+
+                       // Copy the pathname into the buffer
+                       DragQueryFile(dropInfo, x, npszFile.get(), wPathnameSize);
+
+                       files.push_back(npszFile.get());
+               }
+               return files;
+       }
+
+       std::vector<String> FilterFiles(const std::vector<String>& files_src)
+       {
+               std::vector<String> files(files_src);
+               TCHAR szTempPath[MAX_PATH];
+               TCHAR szTempPathShort[MAX_PATH];
+               GetTempPath(sizeof(szTempPath) / sizeof(szTempPath[0]), szTempPath);
+               GetShortPathName(szTempPath, szTempPathShort, sizeof(szTempPathShort) / sizeof(szTempPathShort[0]));
+
+               for (UINT i = 0; i < files.size(); i++)
+               {
+                       if (paths_IsShortcut(files[i]))
+                       {
+                               // if this was a shortcut, we need to expand it to the target path
+                               String expandedFile = ExpandShortcut(files[i]);
+
+                               // if that worked, we should have a real file name
+                               if (!expandedFile.empty())
+                                       files[i] = expandedFile;
+                       }
+                       else if (paths_IsDecendant(files[i], szTempPath) || paths_IsDecendant(files[i], szTempPathShort))
+                       {
+                               String tmpdir = env_GetTempChildPath();
+                               CopyFileOrFolder(files[i], tmpdir);
+                               files[i] = paths_ConcatPath(tmpdir, paths_FindFileName(files[i]));
+                       }
+               }
+               return files;
+       }
+
+       HRESULT IStream_WriteToFile(IStream *pStream, const String& filename)
+       {
+               unique_handle hFile(CreateFile(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
+               if (hFile.get() == INVALID_HANDLE_VALUE)
+                       return E_FAIL;
+               for (;;)
+               {
+                       char buf[65536];
+                       DWORD dwWritten;
+                       ULONG size = 0;
+                       HRESULT hr = pStream->Read(buf, sizeof(buf), &size);
+                       if (FAILED(hr))
+                               return hr;
+                       if (size == 0)
+                               break;
+                       WriteFile(hFile.get(), buf, size, &dwWritten, NULL);
+               }
+               return S_OK;
+       }
+
+       HRESULT HGLOBAL_WriteToFile(HGLOBAL hGlobal, const String& filename)
+       {
+               unique_handle hFile(CreateFile(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
+               if (hFile.get() == INVALID_HANDLE_VALUE)
+                       return E_FAIL;
+               char *p = static_cast<char *>(GlobalLock(hGlobal));
+               if (!p)
+                       return E_FAIL;
+               SIZE_T size = GlobalSize(hGlobal);
+               while (size > 0)
+               {
+                       DWORD dwWritten;
+                       if (WriteFile(hFile.get(), p, (size > INT_MAX) ? INT_MAX : static_cast<DWORD>(size), &dwWritten, NULL) == FALSE)
+                       {
+                               GlobalUnlock(hGlobal);
+                               return E_FAIL;
+                       }
+                       p += dwWritten;
+                       size -= dwWritten;
+               }
+               GlobalUnlock(hGlobal);
+               return S_OK;
+       }
+
+       HRESULT SetFileWriteTime(const String& filename, const FILETIME& writetime)
+       {
+               unique_handle hFile(CreateFile(filename.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+               if (hFile.get() == INVALID_HANDLE_VALUE)
+                       return E_FAIL;
+               return SetFileTime(hFile.get(), NULL, NULL, &writetime) ? S_OK : E_FAIL;
+       }
+
+       HRESULT GetFileItemsFromIDataObject_CF_HDROP(IDataObject *pDataObj, std::vector<String>& files)
+       {
+               FORMATETC fmtetc_cf_hdrop = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+               STGMEDIUM medium = { 0 };
+               HRESULT hr;
+               if ((hr = pDataObj->GetData(&fmtetc_cf_hdrop, &medium)) == S_OK)
+               {
+                       HDROP hDrop = (HDROP)GlobalLock(medium.hGlobal);
+                       if (hDrop)
+                       {
+                               files = FilterFiles(GetDroppedFiles(hDrop));
+                               GlobalUnlock(medium.hGlobal);
+                       }
+                       ReleaseStgMedium(&medium);
+               }
+               return hr;
+       }
+
+#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
+#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
+
+       HRESULT GetFileItemsFromIDataObject_ShellIDList(IDataObject *pDataObj, std::vector<String>& root_files)
+       {
+               String tmpdir;
+               FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+               STGMEDIUM medium = { 0 };
+               HRESULT hr;
+               if ((hr = pDataObj->GetData(&fmtetc_filedescriptor, &medium)) == S_OK)
+               {
+                       CIDA *pcida = (CIDA *)GlobalLock(medium.hGlobal);
+                       if (pcida)
+                       {
+                               LPCITEMIDLIST pidlParent = HIDA_GetPIDLFolder(pcida);
+                               for (unsigned i = 0; i < pcida->cidl; ++i)
+                               {
+                                       IShellItemPtr pShellItem;
+                                       if (SUCCEEDED(hr = SHCreateShellItem(pidlParent, NULL, HIDA_GetPIDLItem(pcida, i), &pShellItem)))
+                                       {
+                                               SFGAOF sfgaof = 0;
+                                               if (SUCCEEDED(hr = pShellItem->GetAttributes(SFGAO_FOLDER, &sfgaof)) && (sfgaof & SFGAO_FOLDER))
+                                               {
+                                                       // Folder item
+                                                       wchar_t *pPath = NULL;
+                                                       if (SUCCEEDED(hr = pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEEDITING, &pPath)))
+                                                       {
+                                                               root_files.push_back(ucr::toTString(pPath));
+                                                               CoTaskMemFree(pPath);
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       // File item
+                                                       IFileOperationPtr pFileOperation;
+                                                       if (SUCCEEDED(hr = pFileOperation.CreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL)))
+                                                       {
+                                                               if (tmpdir.empty())
+                                                                       tmpdir = env_GetTempChildPath();
+                                                               pFileOperation->SetOperationFlags(0);
+                                                               PIDLIST_ABSOLUTE pidlDest;
+                                                               if (SUCCEEDED(hr = SHParseDisplayName(ucr::toUTF16(tmpdir).c_str(), NULL, &pidlDest, 0, NULL)))
+                                                               {
+                                                                       IShellItemPtr pShellItemDest;
+                                                                       SHCreateShellItem(NULL, NULL, pidlDest, &pShellItemDest);
+                                                                       pFileOperation->CopyItem(pShellItem, pShellItemDest, NULL, NULL);
+                                                                       if (SUCCEEDED(hr = pFileOperation->PerformOperations()))
+                                                                       {
+                                                                               wchar_t *pName;
+                                                                               if (SUCCEEDED(hr = pShellItem->GetDisplayName(SIGDN_PARENTRELATIVE, &pName)))
+                                                                               {
+                                                                                       root_files.push_back(paths_ConcatPath(tmpdir, ucr::toTString(pName)));
+                                                                                       CoTaskMemFree(pName);
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               GlobalUnlock(medium.hGlobal);
+                       }
+                       ReleaseStgMedium(&medium);
+               }
+               return hr;
+       }
+
+       HRESULT ExtractFileItemFromIDataObject_FileContents(IDataObject *pDataObj, int lindex, const String& filepath)
+       {
+               FORMATETC fmtetc_filecontents = { RegisterClipboardFormat(CFSTR_FILECONTENTS), NULL, DVASPECT_CONTENT, lindex, TYMED_HGLOBAL | TYMED_ISTREAM };
+               STGMEDIUM medium = { 0 };
+               HRESULT hr;
+               if ((hr = pDataObj->GetData(&fmtetc_filecontents, &medium)) == S_OK)
+               {
+                       hr = E_FAIL;
+                       if (medium.tymed == TYMED_HGLOBAL)
+                               hr = HGLOBAL_WriteToFile(medium.hGlobal, filepath);
+                       else if (medium.tymed == TYMED_ISTREAM)
+                               hr = IStream_WriteToFile(medium.pstm, filepath);
+                       ReleaseStgMedium(&medium);
+               }
+               return hr;
+       }
+
+       HRESULT GetFileItemsFromIDataObject_FileDescriptor(IDataObject *pDataObj, std::vector<String>& root_files)
+       {
+               String tmpdir = env_GetTempChildPath();
+               FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+               STGMEDIUM medium = { 0 };
+               HRESULT hr;
+               if ((hr = pDataObj->GetData(&fmtetc_filedescriptor, &medium)) == S_OK)
+               {
+                       FILEGROUPDESCRIPTOR *file_group_descriptor = (FILEGROUPDESCRIPTOR *)GlobalLock(medium.hGlobal);
+                       if (file_group_descriptor)
+                       {
+                               for (unsigned i = 0; i < file_group_descriptor->cItems; ++i)
+                               {
+                                       String filename = file_group_descriptor->fgd[i].cFileName;
+                                       String filepath = paths_ConcatPath(tmpdir, filename);
+                                       if (file_group_descriptor->fgd[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                                               paths_CreateIfNeeded(filepath);
+                                       else
+                                       {
+                                               ExtractFileItemFromIDataObject_FileContents(pDataObj, i, filepath);
+                                               if (file_group_descriptor->fgd[i].dwFlags & FD_WRITESTIME)
+                                                       SetFileWriteTime(filepath, file_group_descriptor->fgd[i].ftLastWriteTime);
+                                       }
+                                       if (filename.find('\\') == String::npos)
+                                               root_files.push_back(filepath);
+                               }
+                               GlobalUnlock(medium.hGlobal);
+                       }
+                       ReleaseStgMedium(&medium);
+               }
+               return hr;
+       }
+
+}
+
+DropHandler::DropHandler(std::function<void(const std::vector<String>&)> callback) 
+       : m_cRef(0), m_callback(callback)
+{
+}
+
+DropHandler::~DropHandler()
+{
+}
+
+HRESULT STDMETHODCALLTYPE DropHandler::QueryInterface(REFIID riid, void **ppvObject)
+{
+       if (!IsEqualIID(riid, IID_IUnknown) && !IsEqualIID(riid, IID_IDropTarget))
+       {
+               *ppvObject = NULL;
+               return E_NOINTERFACE;
+       }
+       *ppvObject = static_cast<IDropTarget *>(this);
+       AddRef();
+       return S_OK;
+}
+
+ULONG STDMETHODCALLTYPE DropHandler::AddRef(void)
+{
+       return InterlockedIncrement(&m_cRef);
+}
+
+ULONG STDMETHODCALLTYPE DropHandler::Release(void)
+{
+       if (InterlockedDecrement(&m_cRef) == 0) {
+               delete this;
+               return 0;
+       }
+       return m_cRef;
+}
+
+HRESULT STDMETHODCALLTYPE DropHandler::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
+{
+       FORMATETC fmtetc_cf_hdrop = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       FORMATETC fmtetc_shellidlist = { RegisterClipboardFormat(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       if (pDataObj->QueryGetData(&fmtetc_cf_hdrop) == S_OK ||
+           pDataObj->QueryGetData(&fmtetc_shellidlist) == S_OK ||
+           pDataObj->QueryGetData(&fmtetc_filedescriptor) == S_OK)
+       {
+               *pdwEffect = DROPEFFECT_COPY;
+               return S_OK;
+       }
+       *pdwEffect = DROPEFFECT_NONE;
+       return DRAGDROP_S_CANCEL;
+}
+
+HRESULT STDMETHODCALLTYPE DropHandler::DragOver(DWORD, POINTL, DWORD *)
+{
+       return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE DropHandler::DragLeave(void)
+{
+       return S_OK;
+}
+
+HRESULT DropHandler::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
+{
+       bool ok = false;
+       WaitStatusCursor waitstatus(IDS_STATUS_COPYFILES);
+       std::vector<String> files;
+       FORMATETC fmtetc_cf_hdrop = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       FORMATETC fmtetc_shellidlist = { RegisterClipboardFormat(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       if (pDataObj->QueryGetData(&fmtetc_cf_hdrop) == S_OK &&
+               GetFileItemsFromIDataObject_CF_HDROP(pDataObj, files) == S_OK && files.size() > 0)
+               ok = true;
+       else if (pDataObj->QueryGetData(&fmtetc_shellidlist) == S_OK &&
+               GetFileItemsFromIDataObject_ShellIDList(pDataObj, files) == S_OK && files.size() > 0)
+               ok = true;
+       else if (pDataObj->QueryGetData(&fmtetc_filedescriptor) == S_OK &&
+               GetFileItemsFromIDataObject_FileDescriptor(pDataObj, files) == S_OK && files.size() > 0)
+               ok = true;
+       if (files.size() > 3)
+               files.resize(3);
+       if (!files.empty())
+               m_callback(files);
+       return S_OK;
+}
diff --git a/Src/DropHandler.h b/Src/DropHandler.h
new file mode 100644 (file)
index 0000000..492f7e3
--- /dev/null
@@ -0,0 +1,26 @@
+#pragma once\r
+\r
+#include <windows.h>\r
+#include <functional>\r
+#include <vector>\r
+#include "UnicodeString.h"\r
+\r
+class DropHandler : public IDropTarget\r
+{\r
+public:\r
+       HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);\r
+       ULONG STDMETHODCALLTYPE AddRef();\r
+       ULONG STDMETHODCALLTYPE Release();\r
+       \r
+       HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);\r
+       HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);\r
+       HRESULT STDMETHODCALLTYPE DragLeave(void);\r
+       HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);\r
+\r
+       DropHandler(std::function<void(const std::vector<String>&)> callback);\r
+       ~DropHandler();\r
+\r
+private:\r
+       LONG m_cRef;\r
+       std::function<void(const std::vector<String>&)> m_callback;\r
+};\r
index a4b3832..d08f194 100644 (file)
@@ -74,7 +74,7 @@
 #include "OptionsFont.h"
 #include "TFile.h"
 #include "JumpList.h"
-#include "DragDrop.h"
+#include "DropHandler.h"
 #include "LanguageSelect.h"
 
 using std::vector;
@@ -197,7 +197,7 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
        ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace)
        ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace)
        ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
-       ON_WM_DROPFILES()
+       ON_WM_DESTROY()
        ON_WM_SETCURSOR()
        ON_COMMAND_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnPluginUnpackMode)
        ON_UPDATE_COMMAND_UI_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnUpdatePluginUnpackMode)
@@ -296,6 +296,7 @@ static const CPtrList &GetDocList(const CMultiDocTemplate *pTemplate)
 CMainFrame::CMainFrame()
 : m_bFlashing(FALSE)
 , m_bFirstTime(TRUE)
+, m_pDropHandler(NULL)
 {
        ZeroMemory(&m_pMenus[0], sizeof(m_pMenus));
 }
@@ -395,9 +396,18 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
        myStatusDisplay.SetFrame(this);
        CustomStatusCursor::SetStatusDisplay(&myStatusDisplay);
 
+       m_pDropHandler = new DropHandler(std::bind(&CMainFrame::OnDropFiles, this, std::placeholders::_1));
+       RegisterDragDrop(m_hWnd, m_pDropHandler);
+
        return 0;
 }
 
+void CMainFrame::OnDestroy(void)
+{
+       if (m_pDropHandler)
+               RevokeDragDrop(m_hWnd);
+}
+
 static HMENU GetSubmenu(HMENU mainMenu, UINT nIDFirstMenuItem, bool bFirstSubmenu)
 {
        int i;
@@ -1795,10 +1805,8 @@ void CMainFrame::OnToolsGeneratePatch()
        }
 }
 
-void CMainFrame::OnDropFiles(HDROP dropInfo)
+void CMainFrame::OnDropFiles(const std::vector<String>& dropped_files)
 {
-       std::vector<String> dropped_files;
-       GetDroppedFiles(dropInfo, dropped_files);
        PathContext files(dropped_files);
        const size_t fileCount = files.GetSize();
 
index 9d85607..08b667a 100644 (file)
@@ -42,6 +42,7 @@ class SyntaxColors;
 class LineFiltersList;
 class TempFile;
 struct FileLocation;
+class DropHandler;
 
 typedef std::shared_ptr<TempFile> TempFilePtr;
 
@@ -203,6 +204,7 @@ protected:
 
        std::unique_ptr<BCMenu> m_pMenus[MENU_COUNT]; /**< Menus for different views */
        std::vector<TempFilePtr> m_tempFiles; /**< List of possibly needed temp files. */
+       DropHandler *m_pDropHandler;
 
 // Generated message map functions
 protected:
@@ -237,7 +239,7 @@ protected:
        afx_msg void OnViewWhitespace();
        afx_msg void OnUpdateViewWhitespace(CCmdUI* pCmdUI);
        afx_msg void OnToolsGeneratePatch();
-       afx_msg void OnDropFiles(HDROP dropInfo);
+       afx_msg void OnDropFiles(const std::vector<String>& files);
        afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
        afx_msg void OnUpdatePluginUnpackMode(CCmdUI* pCmdUI);
        afx_msg void OnPluginUnpackMode(UINT nID);
@@ -294,6 +296,7 @@ protected:
        afx_msg void OnUpdateCompareMethod(CCmdUI* pCmdUI);
        afx_msg void OnMRUs(UINT nID);
        afx_msg void OnUpdateNoMRUs(CCmdUI* pCmdUI);
+       afx_msg void OnDestroy();
        //}}AFX_MSG
        DECLARE_MESSAGE_MAP()
 
index 2c3cc30..0915cd8 100644 (file)
@@ -450,8 +450,6 @@ BOOL CMergeApp::InitInstance()
                return FALSE;
        }
        m_pMainWnd = pMainFrame;
-       // Enable drag&drop files
-       pMainFrame->ModifyStyleEx(NULL, WS_EX_ACCEPTFILES);
 
        // Init menus -- hMenuDefault is for MainFrame
        pMainFrame->m_hMenuDefault = pMainFrame->NewDefaultMenu();
index 350e7d3..ee2377d 100644 (file)
@@ -843,26 +843,25 @@ END
 
 IDD_OPEN DIALOGEX 0, 0, 460, 260
 STYLE DS_CENTER | DS_SETFONT | DS_FIXEDSYS | WS_CHILD
-EXSTYLE WS_EX_ACCEPTFILES
 CAPTION "Select Files or Folders"
 FONT 8, "MS Shell Dlg", 0, 0, 0x1
 BEGIN
     RTEXT           "&1:",IDC_STATIC,13,84,65,8
     CONTROL         "",IDC_PATH0_COMBO,"ComboBoxEx32",CBS_DROPDOWN |
                     CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP,
-                    81,82,367,94,WS_EX_ACCEPTFILES
+                    81,82,367,94
     PUSHBUTTON      "",IDC_SWAP01_BUTTON,384,98,15,14
     PUSHBUTTON      "&Browse...",IDC_PATH0_BUTTON,402,98,50,14
     RTEXT           "&2:",IDC_STATIC,13,116,65,8
     CONTROL         "",IDC_PATH1_COMBO,"ComboBoxEx32",CBS_DROPDOWN |
                     CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
-                    ,81,114,367,95,WS_EX_ACCEPTFILES
+                    ,81,114,367,95
     PUSHBUTTON      "",IDC_SWAP12_BUTTON,384,130,15,14
     PUSHBUTTON      "B&rowse...",IDC_PATH1_BUTTON,402,130,50,14
     RTEXT           "&3(Optional):",IDC_STATIC,13,148,65,8
     CONTROL         "",IDC_PATH2_COMBO,"ComboBoxEx32",CBS_DROPDOWN |
                     CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP,
-                    81,146,367,95,WS_EX_ACCEPTFILES
+                    81,146,367,95
     PUSHBUTTON      "",IDC_SWAP02_BUTTON,384,162,15,14
     PUSHBUTTON      "Br&owse...",IDC_PATH2_BUTTON,402,162,50,14
     RTEXT           "Fi&lter:",IDC_STATIC,13,178,65,8
index 0a68522..82e58ff 100644 (file)
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UnicodeRelease|x64'">NotUsing</PrecompiledHeader>\r
     </ClCompile>\r
     <ClCompile Include="Common\ColorButton.cpp" />\r
-    <ClCompile Include="Common\DragDrop.cpp">\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UnicodeDebug|Win32'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode + Code Analysis|Win32'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UnicodeRelease|Win32'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UnicodeDebug|x64'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode + Code Analysis|x64'">NotUsing</PrecompiledHeader>\r
-      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UnicodeRelease|x64'">NotUsing</PrecompiledHeader>\r
-    </ClCompile>\r
     <ClCompile Include="Common\ExConverter.cpp">\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>\r
     </ClCompile>\r
     <ClCompile Include="Common\CSubclass.cpp">\r
     </ClCompile>\r
+    <ClCompile Include="DropHandler.cpp">\r
+    </ClCompile>\r
     <ClCompile Include="ImgMergeFrm.cpp" />\r
     <ClCompile Include="Merge7zFormatMergePluginImpl.cpp">\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>\r
     <ClInclude Include="codepage.h" />\r
     <ClInclude Include="codepage_detect.h" />\r
     <ClInclude Include="Common\ColorButton.h" />\r
-    <ClInclude Include="Common\DragDrop.h" />\r
     <ClInclude Include="Common\ExConverter.h" />\r
     <ClInclude Include="CompareEngines\BinaryCompare.h" />\r
     <ClInclude Include="CompareOptions.h" />\r
     <ClInclude Include="Common\coretypes.h" />\r
     <ClInclude Include="Common\CSubclass.h" />\r
     <ClInclude Include="HexMergeView.h" />\r
+    <ClInclude Include="DropHandler.h" />\r
     <ClInclude Include="IMergeDoc.h" />\r
     <ClInclude Include="ImgMergeFrm.h" />\r
     <ClInclude Include="Merge7zFormatMergePluginImpl.h" />\r
index d0f3669..ea935af 100644 (file)
     <ClCompile Include="JumpList.cpp">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
-    <ClCompile Include="Common\DragDrop.cpp">\r
-      <Filter>Source Files</Filter>\r
-    </ClCompile>\r
     <ClCompile Include="Common\ClipBoard.cpp">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
     <ClCompile Include="Common\ColorButton.cpp">\r
       <Filter>MFCGui\Common</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="DropHandler.cpp">\r
+      <Filter>MFCGui\Source Files</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="charsets.h">\r
     <ClInclude Include="JumpList.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="Common\DragDrop.h">\r
-      <Filter>Header Files</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="Common\ClipBoard.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
     <ClInclude Include="Common\Picture.h">\r
       <Filter>MFCGui\Common</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="DropHandler.h">\r
+      <Filter>MFCGui\Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <None Include="res\binarydiff.ico">\r
index 2834457..598cf01 100644 (file)
@@ -42,7 +42,7 @@
 #include "7zCommon.h"
 #include "Constants.h"
 #include "Picture.h"
-#include "DragDrop.h"
+#include "DropHandler.h"
 #include "FileFilterHelper.h"
 #include "Plugins.h"
 
@@ -93,7 +93,6 @@ BEGIN_MESSAGE_MAP(COpenView, CFormView)
        ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
        ON_COMMAND(ID_EDIT_CUT, OnEditCut)
        ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
-       ON_WM_DROPFILES()
        ON_MESSAGE(WM_USER + 1, OnUpdateStatus)
        ON_WM_PAINT()
        ON_WM_LBUTTONDOWN()
@@ -109,6 +108,7 @@ COpenView::COpenView()
        : CFormView(COpenView::IDD)
        , m_pUpdateButtonStatusThread(NULL)
        , m_bRecurse(FALSE)
+       , m_pDropHandler(NULL)
 {
        m_bAutoCompleteReady[0] = false;
        m_bAutoCompleteReady[1] = false;
@@ -282,6 +282,9 @@ void COpenView::OnInitialUpdate()
        UpdateData(FALSE);
        SetStatus(IDS_OPEN_FILESDIRS);
        SetUnpackerStatus(IDS_OPEN_UNPACKERDISABLED);
+
+       m_pDropHandler = new DropHandler(std::bind(&COpenView::OnDropFiles, this, std::placeholders::_1));
+       RegisterDragDrop(m_hWnd, m_pDropHandler);
 }
 
 // COpenView diagnostics
@@ -355,6 +358,9 @@ void COpenView::OnWindowPosChanged(WINDOWPOS* lpwndpos)
 
 void COpenView::OnDestroy()
 {
+       if (m_pDropHandler)
+               RevokeDragDrop(m_hWnd);
+
        m_constraint.Persist(true, false);
 
        CFormView::OnDestroy();
@@ -1066,10 +1072,8 @@ void COpenView::OnHelp()
  *    - overwrite both paths, empty or not
  * @param [in] dropInfo Dropped data, including paths.
  */
-void COpenView::OnDropFiles(HDROP dropInfo)
+void COpenView::OnDropFiles(const std::vector<String>& files)
 {
-       std::vector<String> files;
-       GetDroppedFiles(dropInfo, files);
        const size_t fileCount = files.size();
 
        // Add dropped paths to the dialog
index bd3e215..5de0184 100644 (file)
@@ -36,6 +36,7 @@
 
 class ProjectFile;
 class COpenDoc;
+class DropHandler;
 
 /**
  * @brief The Open-View class.
@@ -92,6 +93,7 @@ private:
        prdlg::CMoveConstraint m_constraint;
        CFont m_fontSwapButton;
        bool m_bAutoCompleteReady[3];
+       DropHandler *m_pDropHandler;
 // Overrides
        public:
 virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
@@ -143,7 +145,7 @@ protected:
        afx_msg void OnEditPaste();
        afx_msg void OnEditUndo();
        afx_msg void OnHelp();
-       afx_msg void OnDropFiles(HDROP dropInfo);
+       afx_msg void OnDropFiles(const std::vector<String>& files);
        afx_msg LRESULT OnUpdateStatus(WPARAM wParam, LPARAM lParam);
        afx_msg void OnPaint();
        afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
index d9c50bd..296d967 100644 (file)
@@ -741,3 +741,9 @@ bool paths_IsURLorCLSID(const String& path)
 {
        return (path.find(_T("://")) != String::npos || path.find(_T("::{")) != String::npos);
 }
+
+bool paths_IsDecendant(const String& path, const String& ancestor)
+{
+       return path.length() > ancestor.length() && 
+                  string_compare_nocase(String(path.c_str(), path.c_str() + ancestor.length()), ancestor) == 0;
+}
index c3437fd..fb3d050 100644 (file)
@@ -40,3 +40,4 @@ void paths_SplitFilename(const String& s, String * path, String * name, String *
 void paths_SplitViewName(const TCHAR *s, String * path, String * name, String * ext);
 String paths_GetPathOnly(const String& fullpath);
 bool paths_IsURLorCLSID(const String& path);
+bool paths_IsDecendant(const String& path, const String& ancestor);