[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / folders / desktop.cpp
index 3fa66df..9ddbb7a 100644 (file)
@@ -87,7 +87,7 @@ CDesktopFolderEnum::~CDesktopFolderEnum()
 }
 
 static const WCHAR ClassicStartMenuW[] = L"SOFTWARE\\Microsoft\\Windows\\"
-    "CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu";
+    L"CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu";
 
 static INT
 IsNamespaceExtensionHidden(const WCHAR *iid)
@@ -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()
@@ -430,7 +441,7 @@ HRESULT WINAPI CDesktopFolder::EnumObjects(
     if (theEnumerator == NULL)
         return E_OUTOFMEMORY;
 
-    hResult = theEnumerator->QueryInterface (IID_IEnumIDList, (void **)&result);
+    hResult = theEnumerator->QueryInterface(IID_PPV_ARG(IEnumIDList, &result));
     if (FAILED (hResult))
     {
         delete theEnumerator;
@@ -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))
     {
@@ -540,43 +550,46 @@ HRESULT WINAPI CDesktopFolder::GetAttributesOf(
 {
     HRESULT hr = S_OK;
     static const DWORD dwDesktopAttributes =
-        SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
-        SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
+        SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
+        SFGAO_STORAGEANCESTOR | SFGAO_HASPROPSHEET | SFGAO_STORAGE | SFGAO_CANLINK;
     static const DWORD dwMyComputerAttributes =
-        SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
-        SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
+        SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
+        SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
+    static DWORD dwMyNetPlacesAttributes =
+        SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
+        SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
 
-    TRACE ("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
-           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
+    TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
+          this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
 
-    if (!rgfInOut)
-        return E_INVALIDARG;
     if (cidl && !apidl)
         return E_INVALIDARG;
 
     if (*rgfInOut == 0)
         *rgfInOut = ~0;
 
-    if(cidl == 0) {
+    if(cidl == 0)
         *rgfInOut &= dwDesktopAttributes;
-    } else {
-        while (cidl > 0 && *apidl) {
-            pdump (*apidl);
-            if (_ILIsDesktop(*apidl)) {
+    else
+    {
+        /* TODO: always add SFGAO_CANLINK */
+        for (UINT i = 0; i < cidl; ++i)
+        {
+            pdump(*apidl);
+            if (_ILIsDesktop(*apidl))
                 *rgfInOut &= dwDesktopAttributes;
-            } else if (_ILIsMyComputer(*apidl)) {
+            else if (_ILIsMyComputer(apidl[i]))
                 *rgfInOut &= dwMyComputerAttributes;
-            } else {
-                SHELL32_GetItemAttributes ((IShellFolder *)this, *apidl, rgfInOut);
-            }
-            apidl++;
-            cidl--;
+            else if (_ILIsNetHood(apidl[i]))
+                *rgfInOut &= dwMyNetPlacesAttributes;
+            else
+                SHELL32_GetItemAttributes((IShellFolder *)this, apidl[i], rgfInOut);
         }
     }
     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
     *rgfInOut &= ~SFGAO_VALIDATE;
 
-    TRACE ("-- result=0x%08x\n", *rgfInOut);
+    TRACE("-- result=0x%08x\n", *rgfInOut);
 
     return hr;
 }
@@ -635,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))
@@ -854,7 +870,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;
@@ -1141,7 +1157,7 @@ HRESULT WINAPI CDesktopFolder::DeleteItems(UINT cidl, LPCITEMIDLIST *apidl)
         wszCaption[(sizeof(wszCaption)/sizeof(WCHAR))-1] = 0;
 
         res = SHELL_ConfirmMsgBox(GetActiveWindow(), wszPath, wszCaption, NULL, cidl > 1);
-        if (res == IDD_YESTOALL || res == IDYES)
+        if (res == IDC_YESTOALL || res == IDYES)
         {
             for(i = 0; i < cidl; i++)
             {
@@ -1202,7 +1218,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];
@@ -1215,7 +1231,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)))
@@ -1280,14 +1296,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);
 
@@ -1321,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