Index: dll/win32/shell32/defcontextmenu.cpp =================================================================== --- dll/win32/shell32/defcontextmenu.cpp (revision 61280) +++ dll/win32/shell32/defcontextmenu.cpp (working copy) @@ -58,8 +58,7 @@ 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 @@ HRESULT CDefaultContextMenu::DoPaste( - LPCMINVOKECOMMANDINFO lpcmi) + LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink) { HRESULT hr; @@ -956,57 +955,13 @@ 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 psfDesktop; - CComPtr psfFrom = NULL; CComPtr psfTarget = NULL; - CComPtr psfhlpdst; - CComPtr psfhlpsrc; - bool bCopy = TRUE; hr = SHGetDesktopFolder(&psfDesktop); if (FAILED(hr)) - goto cleanup; + return hr; - /* 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; - } - } - /* Find target folder */ if (m_Dcm.cidl) { @@ -1043,267 +998,49 @@ if (FAILED(hr)) { ERR("no IShellFolder\n"); - goto cleanup; + return hr; } - /* 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; - } - 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) + IDropTarget *pdrop; + hr = psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pdrop)); + if (FAILED(hr)) { - 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 pda; - hr = OleGetClipboard(&pda); - if (FAILED(hr)) + ERR("Error getting IDropTarget interface\n"); 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 clipboard data into pidl (pointer to id list) */ - LPITEMIDLIST pidl; - LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida); - if (!apidl) - { - ReleaseStgMedium(&medium); - return E_FAIL; - } - - CComPtr psfDesktop; - CComPtr ppf2 = NULL; - CComPtr 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 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 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); - } - } - + 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 @@ CDefaultContextMenu::DoCreateLink( LPCMINVOKECOMMANDINFO lpcmi) { - WCHAR wszTarget[MAX_PATH]; - IPersistFile *ppf; + LPDATAOBJECT pDataObj; + IDropTarget *pDT; HRESULT hr; - STRRET strFile; + CComPtr ppf2 = NULL; + LPITEMIDLIST pidl; + CComPtr psfDesktop; + CComPtr psfTarget = NULL; - hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strFile); + hr = SHGetDesktopFolder(&psfDesktop); if (FAILED(hr)) - { - ERR("IShellFolder_GetDisplayNameOf failed for apidl\n"); return hr; - } - WCHAR wszPath[MAX_PATH]; - hr = StrRetToBufW(&strFile, m_Dcm.apidl[0], wszPath, _countof(wszPath)); - 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); + } + + if (FAILED(hr)) + { + ERR("no IShellFolder\n"); return hr; } - else + + psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pDT)); + if (FAILED(hr)) { - 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 (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); + 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 @@ 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: dll/win32/shell32/folders/desktop.cpp =================================================================== --- dll/win32/shell32/folders/desktop.cpp (revision 61280) +++ dll/win32/shell32/folders/desktop.cpp (working copy) @@ -254,10 +254,21 @@ 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 @@ 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 @@ 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 @@ } 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)) || (*pdwEffect &= DROPEFFECT_COPY)) + { + 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: dll/win32/shell32/folders/desktop.h =================================================================== --- dll/win32/shell32/folders/desktop.h (revision 61280) +++ dll/win32/shell32/folders/desktop.h (working copy) @@ -28,6 +28,7 @@ public CComObjectRootEx, public IShellFolder2, public IPersistFolder2, + public IDropTarget, public ISFHelper { private: @@ -34,9 +35,12 @@ /* 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 fDesktopFolder; -// CComPtr fCommonDesktopFolder; public: CDesktopFolder(); ~CDesktopFolder(); @@ -72,6 +76,13 @@ // *** 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 @@ 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: dll/win32/shell32/folders/fs.cpp =================================================================== --- dll/win32/shell32/folders/fs.cpp (revision 61280) +++ dll/win32/shell32/folders/fs.cpp (working copy) @@ -79,6 +79,7 @@ sPathTarget = NULL; pidlRoot = NULL; cfShellIDList = 0; + SF_RegisterClipFmt(); fAcceptFmt = FALSE; } @@ -498,10 +499,15 @@ 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 @@ { /*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 @@ { 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 @@ 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 @@ 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,12 @@ 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); - return S_OK; } @@ -1376,7 +1411,213 @@ 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 psfDesktop; + CComPtr psfFrom = NULL; + CComPtr 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 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 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 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; } Index: dll/win32/shell32/folders/fs.h =================================================================== --- dll/win32/shell32/folders/fs.h (revision 61280) +++ dll/win32/shell32/folders/fs.h (working copy) @@ -43,11 +43,14 @@ 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); + 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 +110,7 @@ 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: dll/win32/shell32/shell32_main.cpp =================================================================== --- dll/win32/shell32/shell32_main.cpp (revision 61280) +++ dll/win32/shell32/shell32_main.cpp (working copy) @@ -584,7 +584,7 @@ TRACE("szExt=%s\n", debugstr_w(szExt)); if ( szExt && HCR_MapTypeToValueW(szExt, sTemp, MAX_PATH, TRUE) && - HCR_GetDefaultIconW(sTemp, sTemp, MAX_PATH, &psfi->iIcon)) + HCR_GetIconW(sTemp, sTemp, NULL, MAX_PATH, &psfi->iIcon)) { if (lstrcmpW(p1W, sTemp)) wcscpy(psfi->szDisplayName, sTemp); @@ -640,7 +640,7 @@ szExt = PathFindExtensionW(sTemp); if ( szExt && HCR_MapTypeToValueW(szExt, sTemp, MAX_PATH, TRUE) && - HCR_GetDefaultIconW(sTemp, sTemp, MAX_PATH, &icon_idx)) + HCR_GetIconW(sTemp, sTemp, NULL, MAX_PATH, &icon_idx)) { if (!lstrcmpW(p1W,sTemp)) /* icon is in the file */ wcscpy(sTemp, szFullPath); Index: dll/win32/shell32/shell32_main.h =================================================================== --- dll/win32/shell32/shell32_main.h (revision 61280) +++ dll/win32/shell32/shell32_main.h (working copy) @@ -95,8 +95,9 @@ #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: dll/win32/shell32/shlview.cpp =================================================================== --- dll/win32/shell32/shlview.cpp (revision 61280) +++ dll/win32/shell32/shlview.cpp (working copy) @@ -937,7 +937,10 @@ } 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 @@ { DWORD dwEffect2; DoDragDrop(pda, pds, dwEffect, &dwEffect2); + if ((dwEffect2 & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) + this->Refresh(); } pda->Release(); } @@ -2482,6 +2487,8 @@ { m_pCurDropTarget->Drop(pDataObject, grfKeyState, pt, pdwEffect); m_pCurDropTarget.Release(); + + this->Refresh(); } m_pCurDataObject.Release();