[SHELL32]
authorAmine Khaldi <amine.khaldi@reactos.org>
Thu, 19 Dec 2013 12:51:32 +0000 (12:51 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Thu, 19 Dec 2013 12:51:32 +0000 (12:51 +0000)
* Initial go at the Drag & Drop support. Work is still ongoing here. Brought to you by Huw Campbell.
CORE-3760

svn path=/trunk/; revision=61294

reactos/dll/win32/shell32/defcontextmenu.cpp
reactos/dll/win32/shell32/folders/desktop.cpp
reactos/dll/win32/shell32/folders/desktop.h
reactos/dll/win32/shell32/folders/fs.cpp
reactos/dll/win32/shell32/folders/fs.h
reactos/dll/win32/shell32/shell32_main.h
reactos/dll/win32/shell32/shlview.cpp

index 1cc23dc..6f0e20f 100644 (file)
@@ -58,8 +58,7 @@ class CDefaultContextMenu :
         UINT BuildBackgroundContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
         UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT IndexMenu);
         UINT BuildShellItemContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
-        HRESULT DoPaste(LPCMINVOKECOMMANDINFO lpcmi);
-        HRESULT DoPasteLink(LPCMINVOKECOMMANDINFO lpcmi);
+        HRESULT DoPaste(LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink);
         HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi);
         HRESULT DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi);
         HRESULT DoDelete(LPCMINVOKECOMMANDINFO lpcmi);
@@ -947,7 +946,7 @@ NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi, BOOL bRefresh)
 
 HRESULT
 CDefaultContextMenu::DoPaste(
-    LPCMINVOKECOMMANDINFO lpcmi)
+    LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink)
 {
     HRESULT hr;
 
@@ -956,56 +955,12 @@ CDefaultContextMenu::DoPaste(
     if (FAILED(hr))
         return hr;
 
-    STGMEDIUM medium;
-    FORMATETC formatetc;
-    InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
-    hr = pda->GetData(&formatetc, &medium);
-    if (FAILED(hr))
-        return hr;
-
-    /* lock the handle */
-    LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
-    if (!lpcida)
-    {
-        ReleaseStgMedium(&medium);
-        return E_FAIL;
-    }
-
-    /* convert the data into pidl */
-    LPITEMIDLIST pidl;
-    LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
-    if (!apidl)
-    {
-        ReleaseStgMedium(&medium);
-        return E_FAIL;
-    }
-
     CComPtr<IShellFolder> psfDesktop;
-    CComPtr<IShellFolder> psfFrom = NULL;
     CComPtr<IShellFolder> psfTarget = NULL;
-    CComPtr<ISFHelper> psfhlpdst;
-    CComPtr<ISFHelper> psfhlpsrc;
-    bool bCopy = TRUE;
 
     hr = SHGetDesktopFolder(&psfDesktop);
     if (FAILED(hr))
-        goto cleanup;
-
-    /* Find source folder */
-    if (_ILIsDesktop(pidl))
-    {
-        /* use desktop shellfolder */
-        psfFrom = psfDesktop;
-    }
-    else 
-    {
-        hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
-        if (FAILED(hr)) 
-        {
-            ERR("no IShellFolder\n");
-            goto cleanup;
-        }
-    }
+        return hr;
 
     /* Find target folder */
     if (m_Dcm.cidl)
@@ -1043,267 +998,49 @@ CDefaultContextMenu::DoPaste(
     if (FAILED(hr))
     {
         ERR("no IShellFolder\n");
-        goto cleanup;
-    }
-
-    /* get source and destination shellfolder */
-    hr = psfTarget->QueryInterface(IID_PPV_ARG(ISFHelper, &psfhlpdst));
-    if (FAILED(hr))
-    {
-        ERR("no IID_ISFHelper for destination\n");
-        goto cleanup;
-    }
-
-    hr = psfFrom->QueryInterface(IID_PPV_ARG(ISFHelper, &psfhlpsrc));
-    if (FAILED(hr))
-    {
-        ERR("no IID_ISFHelper for source\n");
-        goto cleanup;
+        return hr;
     }
 
     FORMATETC formatetc2;
     STGMEDIUM medium2;
     InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
+    
+    DWORD dwKey= 0;
 
-    if SUCCEEDED(pda->GetData(&formatetc2, &medium2))
+    if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
     {
         DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
         if (pdwFlag)
         {
-            TRACE("Current drop effect flag %i\n", *pdwFlag);
-            if (*pdwFlag & DROPEFFECT_MOVE)
-                bCopy = FALSE;
+            if (*pdwFlag == DROPEFFECT_COPY)
+                dwKey = MK_CONTROL;
+            else
+                dwKey = MK_SHIFT;
+        }
+        else {
+            ERR("No drop effect obtained");
         }
         GlobalUnlock(medium2.hGlobal);
     }
 
-    hr = psfhlpdst->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
-
-    NotifyShellViewWindow(lpcmi, TRUE);
-
-cleanup:
-    SHFree(pidl);
-    _ILFreeaPidl(apidl, lpcida->cidl);
-    ReleaseStgMedium(&medium);
-
-    TRACE("CP result %x\n", hr);
-    return S_OK;
-}
-
-BOOL
-GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut)
-{
-    WCHAR wszLink[40];
-
-    if (!bShortcut)
+    if (bLink)
     {
-        if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink)))
-            wszLink[0] = L'\0';
+        dwKey = MK_CONTROL|MK_SHIFT;
     }
 
-    if (!bShortcut)
-        swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt);
-    else
-        swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt);
-
-    for (UINT i = 2; PathFileExistsW(pwszTarget); ++i)
-    {
-        if (!bShortcut)
-            swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt);
-        else
-            swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt);
-    }
-
-    return TRUE;
-}
-
-HRESULT
-CDefaultContextMenu::DoPasteLink(
-    LPCMINVOKECOMMANDINFO lpcmi)
-{
-    HRESULT hr;
-    WCHAR wszPath[MAX_PATH];
-    WCHAR wszTarget[MAX_PATH];
-
-    CComPtr<IDataObject> pda;
-    hr = OleGetClipboard(&pda);
-    if (FAILED(hr))
-        return hr;
-
-    STGMEDIUM medium;
-    FORMATETC formatetc;
-    InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
-    hr = pda->GetData(&formatetc, &medium);
+    IDropTarget *pdrop;
+    hr = psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pdrop));
     if (FAILED(hr))
-        return hr;
-
-    /* lock the handle */
-    LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
-    if (!lpcida)
-    {
-        ReleaseStgMedium(&medium);
-        return E_FAIL;
-    }
-
-    /* convert the clipboard data into pidl (pointer to id list) */
-    LPITEMIDLIST pidl;
-    LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
-    if (!apidl)
-    {
-        ReleaseStgMedium(&medium);
-        return E_FAIL;
-    }
-
-    CComPtr<IShellFolder> psfDesktop;
-    CComPtr<IPersistFolder2> ppf2 = NULL;
-    CComPtr<IShellFolder> psfFrom = NULL;
-
-    /* Grab the desktop shell folder */
-    hr = SHGetDesktopFolder(&psfDesktop);
-    if (FAILED(hr))
-    {
-        ERR("SHGetDesktopFolder failed\n");
-        goto cleanup;
-    }
-
-    /* Find source folder, this is where the clipboard data was copied from */
-    if (_ILIsDesktop(pidl))
-    {
-        /* use desktop shell folder */
-        psfFrom = psfDesktop;
-    }
-    else 
-    {
-        hr = psfDesktop->BindToObject(pidl, NULL, IID_IShellFolder, (LPVOID*)&psfFrom);
-        if (FAILED(hr))
-        {
-            ERR("no IShellFolder\n");
-            goto cleanup;
-        }
-    }
-    
-    /* Find the target path, where we will be saving the new links (where the user right clicked) */
-    STRRET strFile;
-    WCHAR wszTargetPath[MAX_PATH];
-
-    LPITEMIDLIST targetpidl;
-
-    hr = m_Dcm.psf->QueryInterface(IID_IPersistFolder2, (LPVOID *) &ppf2);
-    if (SUCCEEDED(hr))
     {
-        hr = ppf2->GetCurFolder(&targetpidl);
-        if (SUCCEEDED(hr))
-        {
-            hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
-            ILFree(targetpidl);
-            if (SUCCEEDED(hr)) 
-            {
-                hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
-            }
-        }
-    }
-
-    if (FAILED(hr)) 
-    {
-        ERR("Error obtaining target path");
-        goto cleanup;
-    }
-    TRACE("target path = %s", debugstr_w(wszTargetPath));
-
-    /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
-    for (UINT i = 0; i < lpcida->cidl; i++)
-    {
-        //Find out which file we're copying
-        STRRET strFile;
-        hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
-        if (FAILED(hr)) 
-        {
-            ERR("Error source obtaining path");
-            break;
-        }
-
-        hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
-        if (FAILED(hr)) 
-        {
-            ERR("Error putting source path into buffer");
-            break;
-        }
-        TRACE("source path = %s", debugstr_w(wszPath));
-
-        // Creating a buffer to hold the combined path
-        WCHAR buffer_1[MAX_PATH] = L"";
-        WCHAR *lpStr1;
-        lpStr1 = buffer_1;
-
-        LPWSTR pwszFileName = PathFindFileNameW(wszPath);
-        LPWSTR pwszExt = PathFindExtensionW(wszPath);
-        LPWSTR placementPath = PathCombineW(lpStr1, wszTargetPath, pwszFileName);
-        CComPtr<IPersistFile> ppf;
-
-        //Check to see if it's already a link. 
-        if (!wcsicmp(pwszExt, L".lnk"))
-        {
-            //It's a link so, we create a new one which copies the old.
-            if(!GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE)) 
-            {
-                ERR("Error getting unique file name");
-                hr = E_FAIL;
-                break;
-            }
-            hr = IShellLink_ConstructFromFile(NULL, IID_IPersistFile, ILCombine(pidl, apidl[i]), (LPVOID*)&ppf);
-            if (FAILED(hr)) {
-                ERR("Error constructing link from file");
-                break;
-            }
-
-            hr = ppf->Save(wszTarget, FALSE);
-        }
-        else
-        {
-            //It's not a link, so build a new link using the creator class and fill it in.
-            //Create a file name for the link
-            if (!GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE))
-            {
-                ERR("Error creating unique file name");
-                hr = E_FAIL;
-                break;
-            }
-
-            CComPtr<IShellLinkW> pLink;
-            hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
-            if (FAILED(hr)) {
-                ERR("Error instantiating IShellLinkW");
-                break;
-            }
-
-            WCHAR szDirPath[MAX_PATH], *pwszFile;
-            GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
-            if (pwszFile) pwszFile[0] = 0;
-
-            hr = pLink->SetPath(wszPath);
-            if(FAILED(hr))
-                break;
-
-            hr = pLink->SetWorkingDirectory(szDirPath);
-            if(FAILED(hr))
-                break;
-
-            hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
-            if(FAILED(hr))
-                break;
-
-            hr = ppf->Save(wszTarget, TRUE);
-        }
+        ERR("Error getting IDropTarget interface\n");
+        return hr;
     }
 
+    SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
     NotifyShellViewWindow(lpcmi, TRUE);
 
-cleanup:
-    SHFree(pidl);
-    _ILFreeaPidl(apidl, lpcida->cidl);
-    ReleaseStgMedium(&medium);
-
-    return hr;
+    TRACE("CP result %x\n", hr);
+    return S_OK;
 }
 
 HRESULT
@@ -1318,75 +1055,60 @@ HRESULT
 CDefaultContextMenu::DoCreateLink(
     LPCMINVOKECOMMANDINFO lpcmi)
 {
-    WCHAR wszTarget[MAX_PATH];
-    IPersistFile *ppf;
+    LPDATAOBJECT pDataObj;
+    IDropTarget *pDT;
     HRESULT hr;
-    STRRET strFile;
-
-    hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strFile);
-    if (FAILED(hr))
-    {
-        ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
-        return hr;
-    }
+    CComPtr<IPersistFolder2> ppf2 = NULL;
+    LPITEMIDLIST pidl;
+    CComPtr<IShellFolder> psfDesktop;
+    CComPtr<IShellFolder> psfTarget = NULL;
 
-    WCHAR wszPath[MAX_PATH];
-    hr = StrRetToBufW(&strFile, m_Dcm.apidl[0], wszPath, _countof(wszPath));
+    hr = SHGetDesktopFolder(&psfDesktop);
     if (FAILED(hr))
         return hr;
 
-    LPWSTR pwszExt = PathFindExtensionW(wszPath);
-
-    if (!wcsicmp(pwszExt, L".lnk"))
+    if (SUCCEEDED(hr = SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
     {
-        if (!GetUniqueFileName(wszPath, pwszExt, wszTarget, TRUE))
-            return E_FAIL;
-
-        IPersistFolder2 *ppf2 = NULL;
-        LPITEMIDLIST pidl;
         hr = m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
         if (SUCCEEDED(hr))
         {
             hr = ppf2->GetCurFolder(&pidl);
-            ppf2->Release();
             if (SUCCEEDED(hr))
-                hr = IShellLink_ConstructFromFile(NULL, IID_IPersistFile, ILCombine(pidl, m_Dcm.apidl[0]), (LPVOID*)&ppf);
+            {
+                if (_ILIsDesktop(pidl))
+                {
+                    /* use desktop shellfolder */
+                    psfTarget = psfDesktop;
+                }
+                else
+                {
+                    /* retrieve target desktop folder */
+                    hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
+                }
+                TRACE("psfTarget %x %p, Desktop %u\n", hr, psfTarget.p, _ILIsDesktop(pidl));
+                ILFree(pidl);
+            }
         }
-        if (FAILED(hr))
-            return hr;
 
-        hr = ppf->Save(wszTarget, FALSE);
-        ppf->Release();
-        NotifyShellViewWindow(lpcmi, TRUE);
-        return hr;
     }
-    else
-    {
-        if (!GetUniqueFileName(wszPath, L".lnk", wszTarget, TRUE))
-            return E_FAIL;
-
-        IShellLinkW *pLink;
-        hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
-        if (hr != S_OK)
-            return hr;
 
-        WCHAR szDirPath[MAX_PATH], *pwszFile;
-        GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
-        if (pwszFile) pwszFile[0] = 0;
+    if (FAILED(hr))
+    {
+        ERR("no IShellFolder\n");
+        return hr;
+    }
 
-        if (SUCCEEDED(pLink->SetPath(wszPath)) &&
-            SUCCEEDED(pLink->SetWorkingDirectory(szDirPath)))
-        {
-            if (SUCCEEDED(pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf))))
-            {
-                hr = ppf->Save(wszTarget, TRUE);
-                ppf->Release();
-            }
-        }
-        pLink->Release();
-        NotifyShellViewWindow(lpcmi, TRUE);
+    psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pDT));
+    if (FAILED(hr))
+    {
+        ERR("no IDropTarget Interface\n");
         return hr;
     }
+    DWORD link = DROPEFFECT_LINK;
+    SHSimulateDrop(pDT, pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
+    NotifyShellViewWindow(lpcmi, TRUE);
+
+    return S_OK;
 }
 
 HRESULT
@@ -1843,9 +1565,9 @@ CDefaultContextMenu::InvokeCommand(
         case FCIDM_SHVIEW_REFRESH:
             return NotifyShellViewWindow(lpcmi, FALSE);
         case FCIDM_SHVIEW_INSERT:
-            return DoPaste(lpcmi);
+            return DoPaste(lpcmi, FALSE);
         case FCIDM_SHVIEW_INSERTLINK:
-            return DoPasteLink(lpcmi);
+            return DoPaste(lpcmi, TRUE);
         case FCIDM_SHVIEW_OPEN:
         case FCIDM_SHVIEW_EXPLORE:
             return DoOpenOrExplore(lpcmi);
index 934fdd5..9ddbb7a 100644 (file)
@@ -254,10 +254,21 @@ HRESULT WINAPI CDesktopFolderEnum::Initialize(CDesktopFolder *desktopFolder, HWN
     return ret ? S_OK : E_FAIL;
 }
 
+void CDesktopFolder::SF_RegisterClipFmt()
+{
+    TRACE ("(%p)\n", this);
+
+    if (!cfShellIDList)
+        cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
+}
+
 CDesktopFolder::CDesktopFolder()
 {
     pidlRoot = NULL;
     sPathTarget = NULL;
+    cfShellIDList = 0;
+    SF_RegisterClipFmt();
+    fAcceptFmt = FALSE;
 }
 
 CDesktopFolder::~CDesktopFolder()
@@ -512,8 +523,7 @@ HRESULT WINAPI CDesktopFolder::CreateViewObject(
 
     if (IsEqualIID (riid, IID_IDropTarget))
     {
-        WARN ("IDropTarget not implemented\n");
-        hr = E_NOTIMPL;
+        hr = this->QueryInterface (IID_IDropTarget, ppvOut);
     }
     else if (IsEqualIID (riid, IID_IContextMenu))
     {
@@ -638,9 +648,12 @@ HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
         SHFree (pidl);
         hr = S_OK;
     }
-    else if (IsEqualIID (riid, IID_IDropTarget) && (cidl >= 1))
+    else if (IsEqualIID (riid, IID_IDropTarget))
     {
-        hr = this->QueryInterface (IID_IDropTarget, (LPVOID *)&pObj);
+        /* only interested in attempting to bind to shell folders, not files, semicolon intentionate */
+        if (cidl == 1 && SUCCEEDED(this->BindToObject(apidl[0], NULL, IID_IDropTarget, (LPVOID*)&pObj)));
+        else
+            hr = this->QueryInterface(IID_IDropTarget, (LPVOID*)&pObj);
     }
     else if ((IsEqualIID(riid, IID_IShellLinkW) ||
               IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
@@ -1326,3 +1339,137 @@ HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCIT
     }
     return E_FAIL;
 }
+
+/****************************************************************************
+ * IDropTarget implementation
+ *
+ * This should allow two somewhat separate things, copying files to the users directory,
+ * as well as allowing icons to be moved anywhere and updating the registry to save.
+ *
+ * The first thing I think is best done using fs.cpp to prevent WET code. So we'll simulate
+ * a drop to the user's home directory. The second will look at the pointer location and
+ * set sensible places for the icons to live.
+ *
+ */
+BOOL CDesktopFolder::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
+{
+    /* TODO Windows does different drop effects if dragging across drives. 
+    i.e., it will copy instead of move if the directories are on different disks. */
+
+    DWORD dwEffect = DROPEFFECT_MOVE;
+
+    *pdwEffect = DROPEFFECT_NONE;
+
+    if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
+        *pdwEffect = KeyStateToDropEffect (dwKeyState);
+
+        if (*pdwEffect == DROPEFFECT_NONE)
+            *pdwEffect = dwEffect;
+
+        /* ... matches the desired effect ? */
+        if (dwEffect & *pdwEffect) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+HRESULT WINAPI CDesktopFolder::DragEnter(IDataObject *pDataObject,
+                                    DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
+{
+    FORMATETC fmt;
+
+    InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
+    fAcceptFmt = (S_OK == pDataObject->QueryGetData(&fmt)) ?
+                 TRUE : FALSE;
+
+    QueryDrop(dwKeyState, pdwEffect);
+    return S_OK;
+}
+
+HRESULT WINAPI CDesktopFolder::DragOver(DWORD dwKeyState, POINTL pt,
+                                   DWORD *pdwEffect)
+{
+    TRACE("(%p)\n", this);
+
+    if (!pdwEffect)
+        return E_INVALIDARG;
+
+    QueryDrop(dwKeyState, pdwEffect);
+
+    return S_OK;
+}
+
+HRESULT WINAPI CDesktopFolder::DragLeave()
+{
+    TRACE("(%p)\n", this);
+    fAcceptFmt = FALSE;
+    return S_OK;
+}
+
+HRESULT WINAPI CDesktopFolder::Drop(IDataObject *pDataObject,
+                               DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
+{
+    TRACE("(%p) object dropped desktop\n", this);
+
+    STGMEDIUM medium;
+    FORMATETC formatetc;
+    InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
+    HRESULT hr = pDataObject->GetData(&formatetc, &medium);
+    if (FAILED(hr))
+        return hr;
+
+    /* lock the handle */
+    LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
+    if (!lpcida)
+    {
+        ReleaseStgMedium(&medium);
+        return E_FAIL;
+    }
+
+    /* convert the clipboard data into pidl (pointer to id list) */
+    LPITEMIDLIST pidl;
+    LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
+    if (!apidl)
+    {
+        ReleaseStgMedium(&medium);
+        return E_FAIL;
+    }
+
+    /* We only want to really move files around if they don't already 
+       come from the desktop, or we're linking or copying */
+    if ((!_ILIsDesktop(pidl)) || (dwKeyState & MK_CONTROL))
+    {
+        LPITEMIDLIST pidl = NULL;
+
+        WCHAR szPath[MAX_PATH];
+        LPWSTR pathPtr;
+
+        /* build a complete path to create a simple pidl */
+        lstrcpynW(szPath, sPathTarget, MAX_PATH);
+        pathPtr = PathAddBackslashW(szPath);
+        //hr = _ILCreateFromPathW(szPath, &pidl);
+        hr = this->ParseDisplayName(NULL, NULL, szPath, NULL, &pidl, NULL);
+
+        if (SUCCEEDED(hr))
+        {
+            IDropTarget *pDT; 
+            hr = this->BindToObject(pidl, NULL, IID_IDropTarget, (LPVOID*)&pDT);
+            CoTaskMemFree(pidl);
+            if (SUCCEEDED(hr))
+                SHSimulateDrop(pDT, pDataObject, dwKeyState, NULL, pdwEffect);
+            else
+                ERR("Error Binding");
+        }
+        else
+            ERR("Error creating from %s\n", debugstr_w(szPath));
+    }
+
+    /* Todo, rewrite the registry such that the icons are well placed. 
+    Blocked by no bags implementation. */
+
+    SHFree(pidl);
+    _ILFreeaPidl(apidl, lpcida->cidl);
+    ReleaseStgMedium(&medium);
+    return hr;
+}
\ No newline at end of file
index 3ab15ac..16f47a1 100644 (file)
@@ -28,15 +28,19 @@ class CDesktopFolder :
     public CComObjectRootEx<CComMultiThreadModelNoCS>,
     public IShellFolder2,
     public IPersistFolder2,
+    public IDropTarget,
     public ISFHelper
 {
     private:
         /* both paths are parsible from the desktop */
         LPWSTR sPathTarget;     /* complete path to target used for enumeration and ChangeNotify */
         LPITEMIDLIST pidlRoot;  /* absolute pidl */
+        
+        UINT cfShellIDList;    /* clipboardformat for IDropTarget */
+        BOOL fAcceptFmt;       /* flag for pending Drop */
+        BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect);
+        void SF_RegisterClipFmt();
 
-//  CComPtr<IShellFolder2>              fDesktopFolder;
-//  CComPtr<IShellFolder2>              fCommonDesktopFolder;
     public:
         CDesktopFolder();
         ~CDesktopFolder();
@@ -72,6 +76,13 @@ class CDesktopFolder :
         // *** IPersistFolder2 methods ***
         virtual HRESULT WINAPI GetCurFolder(LPITEMIDLIST * pidl);
 
+        // IDropTarget
+        virtual HRESULT WINAPI DragEnter(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect);
+        virtual HRESULT WINAPI DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect);
+        virtual HRESULT WINAPI DragLeave();
+        virtual HRESULT WINAPI Drop(IDataObject *pDataObject, DWORD dwKeyState, POINTL pt, DWORD *pdwEffect);
+
+
         // *** ISFHelper methods ***
         virtual HRESULT WINAPI GetUniqueName(LPWSTR pwszName, UINT uLen);
         virtual HRESULT WINAPI AddFolder(HWND hwnd, LPCWSTR pwszName, LPITEMIDLIST *ppidlOut);
@@ -89,6 +100,7 @@ class CDesktopFolder :
         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
         COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
+        COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
         COM_INTERFACE_ENTRY_IID(IID_ISFHelper, ISFHelper)
         END_COM_MAP()
 };
index ffcec3c..6aee02f 100644 (file)
@@ -79,6 +79,7 @@ CFSFolder::CFSFolder()
     sPathTarget = NULL;
     pidlRoot = NULL;
     cfShellIDList = 0;
+    SF_RegisterClipFmt();
     fAcceptFmt = FALSE;
 }
 
@@ -498,10 +499,15 @@ HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
             SHFree (pidl);
             hr = S_OK;
         }
-        else if (IsEqualIID (riid, IID_IDropTarget) && (cidl >= 1))
-            hr = this->QueryInterface(IID_IDropTarget, (LPVOID*)&pObj);
+        else if (IsEqualIID (riid, IID_IDropTarget))
+        {
+            /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
+            if (cidl == 1 && SUCCEEDED(hr = this->BindToObject(apidl[0], NULL, IID_IDropTarget, (LPVOID*)&pObj)));
+            else
+                hr = this->QueryInterface(IID_IDropTarget, (LPVOID*)&pObj);
+        }
         else if ((IsEqualIID(riid, IID_IShellLinkW) ||
-                    IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
+            IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
         {
             pidl = ILCombine (pidlRoot, apidl[0]);
             hr = IShellLink_ConstructFromFile(NULL, riid, pidl, (LPVOID*)&pObj);
@@ -571,9 +577,9 @@ void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
 {
     /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
     if (!(dwFlags & SHGDN_FORPARSING) &&
-            ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
-        if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
-            PathRemoveExtensionW(szPath);
+        ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
+            if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
+                PathRemoveExtensionW(szPath);
     }
 }
 
@@ -808,7 +814,7 @@ HRESULT WINAPI CFSFolder::GetDetailsOf(LPCITEMIDLIST pidl,
         {
             case 0:                /* name */
                 hr = GetDisplayNameOf (pidl,
-                                       SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
+                    SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
                 break;
             case 1:                /* size */
                 _ILGetFileSize(pidl, psd->str.cStr, MAX_PATH);
@@ -931,7 +937,7 @@ HRESULT WINAPI CFSFolder::AddFolder(HWND hwnd, LPCWSTR pwszName,
         LoadStringW(shell32_hInstance, IDS_CREATEFOLDER_DENIED, wszTempText,
                     _countof(wszTempText));
         LoadStringW(shell32_hInstance, IDS_CREATEFOLDER_CAPTION, wszCaption,
-                     _countof(wszCaption));
+                    _countof(wszCaption));
         swprintf(wszText, wszTempText, wszNewDir);
         MessageBoxW(hwnd, wszText, wszCaption, MB_OK | MB_ICONEXCLAMATION);
     }
@@ -1314,18 +1320,51 @@ HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti
     return E_NOTIMPL;
 }
 
+BOOL
+CFSFolder::GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut)
+{
+    WCHAR wszLink[40];
+
+    if (!bShortcut)
+    {
+        if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink)))
+            wszLink[0] = L'\0';
+    }
+
+    if (!bShortcut)
+        swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt);
+    else
+        swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt);
+
+    for (UINT i = 2; PathFileExistsW(pwszTarget); ++i)
+    {
+        if (!bShortcut)
+            swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt);
+        else
+            swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt);
+    }
+
+    return TRUE;
+}
+
 /****************************************************************************
- * ISFDropTarget implementation
+ * IDropTarget implementation
  */
 BOOL CFSFolder::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
 {
-    DWORD dwEffect = *pdwEffect;
+    /* TODO Windows does different drop effects if dragging across drives. 
+    i.e., it will copy instead of move if the directories are on different disks. */
+
+    DWORD dwEffect = DROPEFFECT_MOVE;
 
     *pdwEffect = DROPEFFECT_NONE;
 
     if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
         *pdwEffect = KeyStateToDropEffect (dwKeyState);
 
+        if (*pdwEffect == DROPEFFECT_NONE)
+            *pdwEffect = dwEffect;
+
         /* ... matches the desired effect ? */
         if (dwEffect & *pdwEffect) {
             return TRUE;
@@ -1338,16 +1377,18 @@ HRESULT WINAPI CFSFolder::DragEnter(IDataObject *pDataObject,
                                     DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
 {
     FORMATETC fmt;
-
     TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
-
     InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
 
-    fAcceptFmt = (S_OK == pDataObject->QueryGetData(&fmt)) ?
-                 TRUE : FALSE;
+    fAcceptFmt = (S_OK == pDataObject->QueryGetData(&fmt)) ? TRUE : FALSE;
 
-    QueryDrop(dwKeyState, pdwEffect);
+    if (!fAcceptFmt) 
+    {
+        InitFormatEtc(fmt, RegisterClipboardFormatW(CFSTR_FILEDESCRIPTOR), TYMED_HGLOBAL);
+        fAcceptFmt = (S_OK == pDataObject->QueryGetData(&fmt)) ? TRUE : FALSE;
+    }
 
+    QueryDrop(dwKeyState, pdwEffect);
     return S_OK;
 }
 
@@ -1376,7 +1417,219 @@ HRESULT WINAPI CFSFolder::DragLeave()
 HRESULT WINAPI CFSFolder::Drop(IDataObject *pDataObject,
                                DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
 {
-    FIXME("(%p) object dropped\n", this);
+    TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
 
-    return E_NOTIMPL;
+    HRESULT hr;
+    bool bCopy = TRUE;
+    bool bLinking = FALSE;
+
+    STGMEDIUM medium;
+    FORMATETC formatetc;
+    InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
+    hr = pDataObject->GetData(&formatetc, &medium);
+    if (FAILED(hr))
+        return hr;
+
+    /* lock the handle */
+    LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
+    if (!lpcida)
+    {
+        ReleaseStgMedium(&medium);
+        return E_FAIL;
+    }
+
+    /* convert the data into pidl */
+    LPITEMIDLIST pidl;
+    LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
+    if (!apidl)
+    {
+        ReleaseStgMedium(&medium);
+        return E_FAIL;
+    }
+
+    /* Figure out what drop operation we're doing */
+    if (pdwEffect)
+    {
+        TRACE("Current drop effect flag %i\n", *pdwEffect);
+        if (*pdwEffect & DROPEFFECT_MOVE)
+            bCopy = FALSE;
+        if (*pdwEffect & DROPEFFECT_LINK)
+            bLinking = TRUE;
+    }
+
+    CComPtr<IShellFolder> psfDesktop;
+    CComPtr<IShellFolder> psfFrom = NULL;
+    CComPtr<IShellFolder> psfTarget = NULL;
+
+    hr = this->QueryInterface(IID_PPV_ARG(IShellFolder, &psfTarget));
+    if (FAILED(hr))
+    {
+        ERR("psfTarget setting failed\n");
+        SHFree(pidl);
+        _ILFreeaPidl(apidl, lpcida->cidl);
+        ReleaseStgMedium(&medium);
+        return E_FAIL;
+    }
+
+    /* Grab the desktop shell folder */
+    hr = SHGetDesktopFolder(&psfDesktop);
+    if (FAILED(hr))
+    {
+        ERR("SHGetDesktopFolder failed\n");
+        SHFree(pidl);
+        _ILFreeaPidl(apidl, lpcida->cidl);
+        ReleaseStgMedium(&medium);
+        return E_FAIL;
+    }
+
+    /* Find source folder, this is where the clipboard data was copied from */
+    if (_ILIsDesktop(pidl))
+    {
+        /* use desktop shell folder */
+        psfFrom = psfDesktop;
+    }
+    else 
+    {
+        hr = psfDesktop->BindToObject(pidl, NULL, IID_IShellFolder, (LPVOID*)&psfFrom);
+        if (FAILED(hr))
+        {
+            ERR("no IShellFolder\n");
+            SHFree(pidl);
+            _ILFreeaPidl(apidl, lpcida->cidl);
+            ReleaseStgMedium(&medium);
+            return E_FAIL;
+        }
+    }
+
+    if (bLinking)
+    {
+        CComPtr<IPersistFolder2> ppf2 = NULL;
+        STRRET strFile;
+        WCHAR wszTargetPath[MAX_PATH];
+        LPITEMIDLIST targetpidl;
+        WCHAR wszPath[MAX_PATH];
+        WCHAR wszTarget[MAX_PATH];
+
+        hr = this->QueryInterface(IID_IPersistFolder2, (LPVOID *) &ppf2);
+        if (SUCCEEDED(hr))
+        {
+            hr = ppf2->GetCurFolder(&targetpidl);
+            if (SUCCEEDED(hr))
+            {
+                hr = psfDesktop->GetDisplayNameOf(targetpidl, SHGDN_FORPARSING, &strFile);
+                ILFree(targetpidl);
+                if (SUCCEEDED(hr)) 
+                {
+                    hr = StrRetToBufW(&strFile, NULL, wszTargetPath, _countof(wszTargetPath));
+                }
+            }
+        }
+
+        if (FAILED(hr)) 
+        {
+            ERR("Error obtaining target path");
+            
+        }
+        TRACE("target path = %s", debugstr_w(wszTargetPath));
+
+        /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
+        for (UINT i = 0; i < lpcida->cidl; i++)
+        {
+            //Find out which file we're copying
+            STRRET strFile;
+            hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
+            if (FAILED(hr)) 
+            {
+                ERR("Error source obtaining path");
+                break;
+            }
+
+            hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
+            if (FAILED(hr)) 
+            {
+                ERR("Error putting source path into buffer");
+                break;
+            }
+            TRACE("source path = %s", debugstr_w(wszPath));
+
+            // Creating a buffer to hold the combined path
+            WCHAR buffer_1[MAX_PATH] = L"";
+            WCHAR *lpStr1;
+            lpStr1 = buffer_1;
+
+            LPWSTR pwszFileName = PathFindFileNameW(wszPath);
+            LPWSTR pwszExt = PathFindExtensionW(wszPath);
+            LPWSTR placementPath = PathCombineW(lpStr1, wszTargetPath, pwszFileName);
+            CComPtr<IPersistFile> ppf;
+
+            //Check to see if it's already a link. 
+            if (!wcsicmp(pwszExt, L".lnk"))
+            {
+                //It's a link so, we create a new one which copies the old.
+                if(!GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE)) 
+                {
+                    ERR("Error getting unique file name");
+                    hr = E_FAIL;
+                    break;
+                }
+                hr = IShellLink_ConstructFromFile(NULL, IID_IPersistFile, ILCombine(pidl, apidl[i]), (LPVOID*)&ppf);
+                if (FAILED(hr)) {
+                    ERR("Error constructing link from file");
+                    break;
+                }
+
+                hr = ppf->Save(wszTarget, FALSE);
+            }
+            else
+            {
+                //It's not a link, so build a new link using the creator class and fill it in.
+                //Create a file name for the link
+                if (!GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE))
+                {
+                    ERR("Error creating unique file name");
+                    hr = E_FAIL;
+                    break;
+                }
+
+                CComPtr<IShellLinkW> pLink;
+                hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
+                if (FAILED(hr)) {
+                    ERR("Error instantiating IShellLinkW");
+                    break;
+                }
+
+                WCHAR szDirPath[MAX_PATH], *pwszFile;
+                GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
+                if (pwszFile) pwszFile[0] = 0;
+
+                hr = pLink->SetPath(wszPath);
+                if(FAILED(hr))
+                    break;
+
+                hr = pLink->SetWorkingDirectory(szDirPath);
+                if(FAILED(hr))
+                    break;
+
+                hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
+                if(FAILED(hr))
+                    break;
+
+                hr = ppf->Save(wszTarget, TRUE);
+            }
+        }
+    }
+    else 
+    {
+        hr = this->CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
+    }
+
+    SHFree(pidl);
+    _ILFreeaPidl(apidl, lpcida->cidl);
+    ReleaseStgMedium(&medium);
+
+    return hr;
 }
+
+DWORD CFSFolder::_DoDropThreadProc(LPVOID lpParameter) {
+    return 0;
+}
\ No newline at end of file
index fc88e35..191fafa 100644 (file)
@@ -43,11 +43,15 @@ class CFSFolder :
 
         UINT cfShellIDList;    /* clipboardformat for IDropTarget */
         BOOL fAcceptFmt;       /* flag for pending Drop */
+        BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect);
+        void SF_RegisterClipFmt();
+        BOOL GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut);
+        DWORD _DoDropThreadProc(LPVOID lpParameter);
+
     public:
         CFSFolder();
         ~CFSFolder();
-        void SF_RegisterClipFmt();
-        BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect);
+        
 
         // IShellFolder
         virtual HRESULT WINAPI ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, DWORD *pchEaten, LPITEMIDLIST *ppidl, DWORD *pdwAttributes);
@@ -107,6 +111,7 @@ class CFSFolder :
         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder3, IPersistFolder3)
         COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
+        COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
         COM_INTERFACE_ENTRY_IID(IID_ISFHelper, ISFHelper)
         END_COM_MAP()
 };
index 569be9b..0a3d823 100644 (file)
@@ -94,8 +94,9 @@ LPEXTRACTICONW        IExtractIconW_Constructor(LPCITEMIDLIST);
 
 #define KeyStateToDropEffect(kst)\
     ((((kst)&(MK_CONTROL|MK_SHIFT))==(MK_CONTROL|MK_SHIFT)) ? DROPEFFECT_LINK :\
-    (((kst)&(MK_CONTROL|MK_SHIFT)) ? DROPEFFECT_COPY :\
-    DROPEFFECT_MOVE))
+    (((kst)&(MK_CONTROL)) ? DROPEFFECT_COPY :\
+    (((kst)&(MK_SHIFT)) ? DROPEFFECT_MOVE :\
+    DROPEFFECT_NONE)))
 
 
 HGLOBAL RenderHDROP(LPITEMIDLIST pidlRoot, LPITEMIDLIST * apidl, UINT cidl);
index f4ca004..0f051ed 100644 (file)
@@ -937,7 +937,10 @@ LRESULT CDefView::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
     }
 
     if (SUCCEEDED(QueryInterface(IID_PPV_ARG(IDropTarget, &pdt))))
-        RegisterDragDrop(m_hWnd, pdt);
+    {
+        if (FAILED(RegisterDragDrop(m_hWnd, pdt)))
+            ERR("Registering Drag Drop Failed");
+    }
 
     /* register for receiving notifications */
     m_pSFParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
@@ -1688,6 +1691,8 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
                     {
                         DWORD dwEffect2;
                         DoDragDrop(pda, pds, dwEffect, &dwEffect2);
+                        if ((dwEffect2 & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
+                            this->Refresh();
                     }
                     pda->Release();
                 }
@@ -2482,10 +2487,11 @@ HRESULT WINAPI CDefView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINT
     {
         m_pCurDropTarget->Drop(pDataObject, grfKeyState, pt, pdwEffect);
         m_pCurDropTarget.Release();
+
+        this->Refresh();
     }
 
-    m_pCurDataObject.Release();
-    m_iDragOverItem = 0;
+    m_pCurDataObject.Release();    m_iDragOverItem = 0;
 
     return S_OK;
 }