[SHELL32]
[reactos.git] / dll / win32 / shell32 / folders / desktop.cpp
index 14e1ec4..4311e12 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()
@@ -289,7 +300,7 @@ HRESULT WINAPI CDesktopFolder::ParseDisplayName(
     LPBC pbc,
     LPOLESTR lpszDisplayName,
     DWORD *pchEaten,
-    LPITEMIDLIST *ppidl,
+    PIDLIST_RELATIVE *ppidl,
     DWORD *pdwAttributes)
 {
     WCHAR szElement[MAX_PATH];
@@ -410,48 +421,16 @@ HRESULT WINAPI CDesktopFolder::ParseDisplayName(
 /**************************************************************************
  *        CDesktopFolder::EnumObjects
  */
-HRESULT WINAPI CDesktopFolder::EnumObjects(
-    HWND hwndOwner,
-    DWORD dwFlags,
-    LPENUMIDLIST *ppEnumIDList)
+HRESULT WINAPI CDesktopFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
 {
-    CComObject<CDesktopFolderEnum>            *theEnumerator;
-    CComPtr<IEnumIDList>                    result;
-    HRESULT                                    hResult;
-
-    TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", this, hwndOwner, dwFlags, ppEnumIDList);
-
-    if (ppEnumIDList == NULL)
-        return E_POINTER;
-    *ppEnumIDList = NULL;
-
-    ATLTRY (theEnumerator = new CComObject<CDesktopFolderEnum>);
-
-    if (theEnumerator == NULL)
-        return E_OUTOFMEMORY;
-
-    hResult = theEnumerator->QueryInterface (IID_IEnumIDList, (void **)&result);
-    if (FAILED (hResult))
-    {
-        delete theEnumerator;
-        return hResult;
-    }
-
-    hResult = theEnumerator->Initialize (this, hwndOwner, dwFlags);
-    if (FAILED (hResult))
-        return hResult;
-    *ppEnumIDList = result.Detach ();
-
-    TRACE ("-- (%p)->(new ID List: %p)\n", this, *ppEnumIDList);
-
-    return S_OK;
+    return ShellObjectCreatorInit<CDesktopFolderEnum>(this, hwndOwner, dwFlags, IID_IEnumIDList, ppEnumIDList);
 }
 
 /**************************************************************************
  *        CDesktopFolder::BindToObject
  */
 HRESULT WINAPI CDesktopFolder::BindToObject(
-    LPCITEMIDLIST pidl,
+    PCUIDLIST_RELATIVE pidl,
     LPBC pbcReserved,
     REFIID riid,
     LPVOID *ppvOut)
@@ -466,7 +445,7 @@ HRESULT WINAPI CDesktopFolder::BindToObject(
  *    CDesktopFolder::BindToStorage
  */
 HRESULT WINAPI CDesktopFolder::BindToStorage(
-    LPCITEMIDLIST pidl,
+    PCUIDLIST_RELATIVE pidl,
     LPBC pbcReserved,
     REFIID riid,
     LPVOID *ppvOut)
@@ -481,7 +460,7 @@ HRESULT WINAPI CDesktopFolder::BindToStorage(
 /**************************************************************************
  *     CDesktopFolder::CompareIDs
  */
-HRESULT WINAPI CDesktopFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
+HRESULT WINAPI CDesktopFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
 {
     int nReturn;
 
@@ -512,8 +491,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))
     {
@@ -535,7 +513,7 @@ HRESULT WINAPI CDesktopFolder::CreateViewObject(
  */
 HRESULT WINAPI CDesktopFolder::GetAttributesOf(
     UINT cidl,
-    LPCITEMIDLIST *apidl,
+    PCUITEMID_CHILD_ARRAY apidl,
     DWORD *rgfInOut)
 {
     HRESULT hr = S_OK;
@@ -599,7 +577,7 @@ HRESULT WINAPI CDesktopFolder::GetAttributesOf(
 HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
     HWND hwndOwner,
     UINT cidl,
-    LPCITEMIDLIST *apidl,
+    PCUITEMID_CHILD_ARRAY apidl,
     REFIID riid,
     UINT *prgfInOut,
     LPVOID *ppvOut)
@@ -627,20 +605,26 @@ HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
     else if (IsEqualIID (riid, IID_IExtractIconA) && (cidl == 1))
     {
         pidl = ILCombine (pidlRoot, apidl[0]);
-        pObj = (LPUNKNOWN) IExtractIconA_Constructor (pidl);
+        pObj = IExtractIconA_Constructor (pidl);
         SHFree (pidl);
         hr = S_OK;
     }
     else if (IsEqualIID (riid, IID_IExtractIconW) && (cidl == 1))
     {
         pidl = ILCombine (pidlRoot, apidl[0]);
-        pObj = (LPUNKNOWN) IExtractIconW_Constructor (pidl);
+        pObj = IExtractIconW_Constructor (pidl);
         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 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
+        {
+            IDropTarget * pDt = NULL;
+            hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
+            pObj = pDt;
+        }
     }
     else if ((IsEqualIID(riid, IID_IShellLinkW) ||
               IsEqualIID(riid, IID_IShellLinkA)) && (cidl == 1))
@@ -666,7 +650,7 @@ HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
  * NOTES
  *    special case: pidl = null gives desktop-name back
  */
-HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
+HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
 {
     HRESULT hr = S_OK;
     LPWSTR pszPath;
@@ -695,7 +679,7 @@ HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlag
 
         if ((clsid = _ILGetGUIDPointer (pidl)))
         {
-            if (GET_SHGDN_FOR (dwFlags) & SHGDN_FORPARSING)
+            if (GET_SHGDN_FOR (dwFlags) == SHGDN_FORPARSING)
             {
                 int bWantsForParsing;
 
@@ -841,10 +825,10 @@ HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlag
  */
 HRESULT WINAPI CDesktopFolder::SetNameOf(
     HWND hwndOwner,
-    LPCITEMIDLIST pidl,    /* simple pidl */
+    PCUITEMID_CHILD pidl,    /* simple pidl */
     LPCOLESTR lpName,
     DWORD dwFlags,
-    LPITEMIDLIST *pPidlOut)
+    PITEMID_CHILD *pPidlOut)
 {
     CComPtr<IShellFolder2>                psf;
     HRESULT hr;
@@ -857,7 +841,7 @@ HRESULT WINAPI CDesktopFolder::SetNameOf(
 
     if (_ILGetGUIDPointer(pidl))
     {
-        if (SUCCEEDED(BindToObject(pidl, NULL, IID_IShellFolder2, (LPVOID *)&psf)))
+        if (SUCCEEDED(BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder2, &psf))))
         {
             hr = psf->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
             return hr;
@@ -950,7 +934,7 @@ HRESULT WINAPI CDesktopFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFla
 }
 
 HRESULT WINAPI CDesktopFolder::GetDetailsEx(
-    LPCITEMIDLIST pidl,
+    PCUITEMID_CHILD pidl,
     const SHCOLUMNID *pscid,
     VARIANT *pv)
 {
@@ -960,7 +944,7 @@ HRESULT WINAPI CDesktopFolder::GetDetailsEx(
 }
 
 HRESULT WINAPI CDesktopFolder::GetDetailsOf(
-    LPCITEMIDLIST pidl,
+    PCUITEMID_CHILD pidl,
     UINT iColumn,
     SHELLDETAILS *psd)
 {
@@ -1205,7 +1189,7 @@ HRESULT WINAPI CDesktopFolder::DeleteItems(UINT cidl, LPCITEMIDLIST *apidl)
     return ret;
 }
 
-HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCITEMIDLIST *apidl)
+HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCITEMIDLIST *apidl, bool bCopy)
 {
     CComPtr<IPersistFolder2> ppf2;
     WCHAR szSrcPath[MAX_PATH];
@@ -1218,7 +1202,7 @@ HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCIT
 
     TRACE ("(%p)->(%p,%u,%p)\n", this, pSFFrom, cidl, apidl);
 
-    pSFFrom->QueryInterface(IID_IPersistFolder2, (LPVOID *)&ppf2);
+    pSFFrom->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
     if (ppf2)
     {
         if (FAILED(ppf2->GetCurFolder(&pidl)))
@@ -1283,14 +1267,16 @@ HRESULT WINAPI CDesktopFolder::CopyItems(IShellFolder *pSFFrom, UINT cidl, LPCIT
             }
 
             op.pTo = szTargetPath;
+            op.fFlags = 0;
         }
         else
         {
             op.pTo = pszTargetList;
+            op.fFlags = FOF_MULTIDESTFILES;
         }
         op.hwnd = GetActiveWindow();
-        op.wFunc = FO_COPY;
-        op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
+        op.wFunc = bCopy ? FO_COPY : FO_MOVE;
+        op.fFlags |= FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
 
         res = SHFileOperationW(&op);
 
@@ -1324,3 +1310,201 @@ 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)
+{
+    TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
+    FORMATETC fmt;
+    FORMATETC fmt2;
+    fAcceptFmt = FALSE;
+
+    InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
+    InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
+
+    if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
+        fAcceptFmt = TRUE;
+    else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
+        fAcceptFmt = TRUE;
+
+    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;
+    bool passthroughtofs = FALSE;
+    FORMATETC formatetc;
+    InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
+    
+    HRESULT hr = pDataObject->GetData(&formatetc, &medium);
+    if (SUCCEEDED(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;
+        }
+        passthroughtofs = !_ILIsDesktop(pidl) || (dwKeyState & MK_CONTROL);
+        SHFree(pidl);
+        _ILFreeaPidl(apidl, lpcida->cidl);
+        ReleaseStgMedium(&medium);
+    }
+    else
+    {
+        InitFormatEtc (formatetc, CF_HDROP, TYMED_HGLOBAL);
+        if (SUCCEEDED(pDataObject->QueryGetData(&formatetc)))
+        {
+            passthroughtofs = TRUE;
+        }
+    }
+    /* We only want to really move files around if they don't already
+       come from the desktop, or we're linking or copying */
+    if (passthroughtofs)
+    {
+        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))
+        {
+            CComPtr<IDropTarget> pDT;
+            hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IDropTarget, &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. */
+    return hr;
+}
+
+HRESULT WINAPI CDesktopFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut) {
+    HRESULT hr;
+
+    TRACE("CFSFolder::_GetDropTarget entered\n");
+
+    if (_ILGetGUIDPointer (pidl) || _ILIsFolder (pidl))
+        return this->BindToObject(pidl, NULL, IID_IDropTarget, ppvOut);
+
+    LPITEMIDLIST pidlNext = NULL;
+
+    STRRET strFile;
+    hr = this->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strFile);
+    if (SUCCEEDED(hr))
+    {
+        WCHAR wszPath[MAX_PATH];
+        hr = StrRetToBufW(&strFile, pidl, wszPath, _countof(wszPath));
+
+        if (SUCCEEDED(hr))
+        {
+            PathRemoveFileSpecW (wszPath);
+            hr = this->ParseDisplayName(NULL, NULL, wszPath, NULL, &pidlNext, NULL);
+
+            if (SUCCEEDED(hr))
+            {
+                CComPtr<IShellFolder> psf;
+                hr = this->BindToObject(pidlNext, NULL, IID_PPV_ARG(IShellFolder, &psf));
+                CoTaskMemFree(pidlNext);
+                if (SUCCEEDED(hr))
+                {
+                    hr = psf->GetUIObjectOf(NULL, 1, &pidl, IID_IDropTarget, NULL, ppvOut);
+                    if (FAILED(hr))
+                        ERR("FS GetUIObjectOf failed: %x\n", hr);
+                }
+                else 
+                    ERR("BindToObject failed: %x\n", hr);
+            }
+            else
+                ERR("ParseDisplayName failed: %x\n", hr);
+        }
+        else
+            ERR("StrRetToBufW failed: %x\n", hr);
+    }    
+    else
+        ERR("GetDisplayNameOf failed: %x\n", hr);
+
+    return hr;
+}
\ No newline at end of file