Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401/* * PROJECT: ReactOS shell32 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: CopyTo and MoveTo implementation * COPYRIGHT: Copyright 2020-2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */
#include "precomp.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
enum { IDC_ACTION = 0 };
CCopyMoveToMenu::CCopyMoveToMenu() : m_fnOldWndProc(NULL), m_bIgnoreTextBoxChange(FALSE){}
static LRESULT CALLBACKWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ WCHAR szPath[MAX_PATH]; CCopyMoveToMenu *this_ = reinterpret_cast<CCopyMoveToMenu *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
switch (uMsg) { case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT: { if (HIWORD(wParam) == EN_CHANGE) { if (!this_->m_bIgnoreTextBoxChange) { // get the text GetDlgItemTextW(hwnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT, szPath, _countof(szPath)); StrTrimW(szPath, L" \t");
// update OK button BOOL bValid = !PathIsRelative(szPath) && PathIsDirectoryW(szPath); SendMessageW(hwnd, BFFM_ENABLEOK, 0, bValid);
return 0; }
// reset flag this_->m_bIgnoreTextBoxChange = FALSE; } break; } } break; } } return CallWindowProcW(this_->m_fnOldWndProc, hwnd, uMsg, wParam, lParam);}
static INT CALLBACKBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData){ CCopyMoveToMenu *this_ = reinterpret_cast<CCopyMoveToMenu *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
switch (uMsg) { case BFFM_INITIALIZED: { SetWindowLongPtr(hwnd, GWLP_USERDATA, lpData); this_ = reinterpret_cast<CCopyMoveToMenu *>(lpData);
// Select initial directory SendMessageW(hwnd, BFFM_SETSELECTION, FALSE, reinterpret_cast<LPARAM>(static_cast<LPCITEMIDLIST>(this_->m_pidlFolder)));
// Set caption CString strCaption(MAKEINTRESOURCEW(this_->GetCaptionStringID())); SetWindowTextW(hwnd, strCaption);
// Set OK button text CString strCopyOrMove(MAKEINTRESOURCEW(this_->GetButtonStringID())); SetDlgItemText(hwnd, IDOK, strCopyOrMove);
// Subclassing this_->m_fnOldWndProc = reinterpret_cast<WNDPROC>( SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WindowProc)));
// Disable OK PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE); break; } case BFFM_SELCHANGED: { if (!this_) break;
WCHAR szPath[MAX_PATH]; LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(lParam);
szPath[0] = 0; SHGetPathFromIDListW(pidl, szPath);
if (ILIsEqual(pidl, this_->m_pidlFolder)) PostMessageW(hwnd, BFFM_ENABLEOK, 0, this_->GetFileOp() == FO_COPY); else if (PathFileExistsW(szPath) || _ILIsDesktop(pidl)) PostMessageW(hwnd, BFFM_ENABLEOK, 0, TRUE); else PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE);
// the text box will be updated later soon, ignore it this_->m_bIgnoreTextBoxChange = TRUE; break; } }
return FALSE;}
HRESULTCCopyMoveToMenu::DoRealFileOp(const CIDA *pCIDA, LPCMINVOKECOMMANDINFO lpici, PCUIDLIST_ABSOLUTE pidlDestination){ CStringW strFiles; WCHAR szPath[MAX_PATH]; for (UINT n = 0; n < pCIDA->cidl; ++n) { CComHeapPtr<ITEMIDLIST> pidlCombine(SHELL_CIDA_ILCloneFull(pCIDA, n)); if (!pidlCombine) return E_FAIL;
if (!SHGetPathFromIDListW(pidlCombine, szPath)) return E_FAIL;
if (n > 0) strFiles += L'|'; strFiles += szPath; }
strFiles += L'|'; // double null-terminated strFiles.Replace(L'|', L'\0');
if (_ILIsDesktop(pidlDestination)) SHGetSpecialFolderPathW(lpici->hwnd, szPath, CSIDL_DESKTOPDIRECTORY, TRUE); else SHGetPathFromIDListW(pidlDestination, szPath); INT cchPath = lstrlenW(szPath); if (cchPath + 1 < MAX_PATH) { szPath[cchPath + 1] = 0; // double null-terminated } else { ERR("Too long path\n"); return E_FAIL; }
SHFILEOPSTRUCTW op = { lpici->hwnd, GetFileOp(), strFiles, szPath }; op.fFlags = FOF_ALLOWUNDO; int res = SHFileOperationW(&op); if (res) { ERR("SHFileOperationW failed with 0x%x\n", res); return E_FAIL; } return S_OK;}
static HRESULTDoGetFileTitle(const CIDA *pCIDA, CStringW& strTitle){ CComHeapPtr<ITEMIDLIST> pidlCombine(SHELL_CIDA_ILCloneFull(pCIDA, 0)); if (!pidlCombine) return E_OUTOFMEMORY;
WCHAR szPath[MAX_PATH]; HRESULT hr = SHGetNameAndFlagsW(pidlCombine, SHGDN_INFOLDER, szPath, _countof(szPath), NULL); strTitle = SUCCEEDED(hr) ? szPath : L""; if (strTitle.IsEmpty()) return E_FAIL;
if (pCIDA->cidl > 1) strTitle += L" ..."; return S_OK;}
HRESULT CCopyMoveToMenu::DoAction(LPCMINVOKECOMMANDINFO lpici){ WCHAR wszPath[MAX_PATH]; TRACE("(%p)\n", lpici);
CDataObjectHIDA pCIDA(m_pDataObject); HRESULT hr = pCIDA.hr(); if (FAILED_UNEXPECTEDLY(hr)) { ERR("Failed to get CIDA, %#x\n", hr); return hr; }
if (!SHGetPathFromIDListW(m_pidlFolder, wszPath)) { ERR("SHGetPathFromIDListW failed\n"); return E_FAIL; }
CStringW strFileTitle; hr = DoGetFileTitle(pCIDA, strFileTitle); if (FAILED(hr)) return hr;
CStringW strTitle; strTitle.Format(GetActionTitleStringID(), static_cast<LPCWSTR>(strFileTitle));
BROWSEINFOW info = { lpici->hwnd }; info.pidlRoot = NULL; info.lpszTitle = strTitle; info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; info.lpfn = BrowseCallbackProc; info.lParam = reinterpret_cast<LPARAM>(this); CComHeapPtr<ITEMIDLIST> pidl(SHBrowseForFolder(&info)); if (pidl) hr = DoRealFileOp(pCIDA, lpici, pidl);
return hr;}
static BOOLGetPreviousMenuItemInfo(HMENU hMenu, UINT iItem, LPMENUITEMINFOW lpmii){ BOOL bSuccess = FALSE;
while (iItem > 0) { bSuccess = GetMenuItemInfoW(hMenu, --iItem, TRUE, lpmii); if (bSuccess || (!bSuccess && GetLastError() != ERROR_MENU_ITEM_NOT_FOUND)) break; }
return bSuccess;}
STDMETHODIMPCCopyToMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags){ TRACE("CCopyToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); return QueryContextMenuImpl(TRUE, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);}
STDMETHODIMPCMoveToMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags){ TRACE("CMoveToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); return QueryContextMenuImpl(FALSE, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);}
STDMETHODIMPCCopyMoveToMenu::QueryContextMenuImpl(BOOL IsCopyOp, HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags){ if (uFlags & (CMF_NOVERBS | CMF_VERBSONLY)) return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
UINT idHighest = 0; CStringW strCopyTo(MAKEINTRESOURCEW(IDS_COPYTOMENU)), strMoveTo; LPWSTR itemText = strCopyTo.GetBuffer(); if (!IsCopyOp) { strMoveTo.LoadString(IDS_MOVETOMENU); itemText = strMoveTo.GetBuffer(); }
// Insert separator if necessary WCHAR szBuff[128]; MENUITEMINFOW mii = { sizeof(mii), MIIM_TYPE }; mii.dwTypeData = szBuff; mii.cch = _countof(szBuff); if (GetPreviousMenuItemInfo(hMenu, indexMenu, &mii) && mii.fType != MFT_SEPARATOR && (IsCopyOp || !(mii.fType == MFT_STRING && !wcscmp(szBuff, strCopyTo.GetBuffer())))) { mii.fMask = MIIM_TYPE; mii.fType = MFT_SEPARATOR; mii.dwTypeData = NULL; mii.cch = 0; if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) { ++indexMenu; } }
// Insert the menu item mii.fMask = MIIM_ID | MIIM_TYPE; mii.fType = MFT_STRING; mii.dwTypeData = itemText; mii.wID = idCmdFirst + IDC_ACTION; if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii)) { idHighest = max(idHighest, mii.wID); ++indexMenu; } return idHighest >= idCmdFirst ? MAKE_HRESULT(SEVERITY_SUCCESS, 0, idHighest - idCmdFirst + 1) : E_FAIL;}
STDMETHODIMPCCopyMoveToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici){ HRESULT hr = E_FAIL; TRACE("CCopyMoveToMenu::InvokeCommand(%p)\n", lpici);
if (IS_INTRESOURCE(lpici->lpVerb)) { if (LOWORD(lpici->lpVerb) == IDC_ACTION) hr = DoAction(lpici); } else { if (::lstrcmpiA(lpici->lpVerb, GetVerb()) == 0) hr = DoAction(lpici); }
return hr;}
STDMETHODIMPCCopyMoveToMenu::GetCommandString( UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax){ if ((uType | GCS_UNICODE) == GCS_VALIDATEW) return idCmd == IDC_ACTION ? S_OK : S_FALSE;
if (uType == GCS_VERBW && idCmd == IDC_ACTION) return SHAnsiToUnicode(GetVerb(), (LPWSTR)pszName, cchMax);
FIXME("%p %lu %u %p %p %u\n", this, idCmd, uType, pwReserved, pszName, cchMax);
return E_NOTIMPL;}
STDMETHODIMPCCopyMoveToMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam){ TRACE("This %p uMsg %x\n", this, uMsg); return E_NOTIMPL;}
STDMETHODIMPCCopyMoveToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID){ m_pDataObject = pdtobj; HRESULT hr = E_FAIL; if (pidlFolder) { hr = SHILClone(pidlFolder, &pidlFolder); } else { pidlFolder = SHELL_DataObject_ILCloneFullItem(pdtobj, 0); if (pidlFolder) { ILRemoveLastID((LPITEMIDLIST)pidlFolder); hr = S_OK; } }
if (SUCCEEDED(hr)) m_pidlFolder.Attach(const_cast<PIDLIST_ABSOLUTE>(pidlFolder)); return hr;}
STDMETHODIMPCCopyMoveToMenu::SetSite(IUnknown *pUnkSite){ m_pSite = pUnkSite; return S_OK;}
STDMETHODIMPCCopyMoveToMenu::GetSite(REFIID riid, void **ppvSite){ if (!m_pSite) return E_FAIL;
return m_pSite->QueryInterface(riid, ppvSite);}