[USER32] Follow-up of #4262 (a47590c) (#4312)
[reactos.git] / dll / win32 / shell32 / CMoveToMenu.cpp
1 /*
2 * PROJECT: shell32
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: MoveTo implementation
5 * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6 */
7
8 #include "precomp.h"
9
10 WINE_DEFAULT_DEBUG_CHANNEL(shell);
11
12 CMoveToMenu::CMoveToMenu() :
13 m_idCmdFirst(0),
14 m_idCmdLast(0),
15 m_idCmdMoveTo(-1),
16 m_fnOldWndProc(NULL),
17 m_bIgnoreTextBoxChange(FALSE)
18 {
19 }
20
21 CMoveToMenu::~CMoveToMenu()
22 {
23 }
24
25 static LRESULT CALLBACK
26 WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
27 {
28 WCHAR szPath[MAX_PATH];
29 CMoveToMenu *this_ =
30 reinterpret_cast<CMoveToMenu *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
31
32 switch (uMsg)
33 {
34 case WM_COMMAND:
35 {
36 switch (LOWORD(wParam))
37 {
38 case IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT:
39 {
40 if (HIWORD(wParam) == EN_CHANGE)
41 {
42 if (!this_->m_bIgnoreTextBoxChange)
43 {
44 // get the text
45 GetDlgItemTextW(hwnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT, szPath, _countof(szPath));
46 StrTrimW(szPath, L" \t");
47
48 // update OK button
49 BOOL bValid = !PathIsRelative(szPath) && PathIsDirectoryW(szPath);
50 SendMessageW(hwnd, BFFM_ENABLEOK, 0, bValid);
51
52 return 0;
53 }
54
55 // reset flag
56 this_->m_bIgnoreTextBoxChange = FALSE;
57 }
58 break;
59 }
60 }
61 break;
62 }
63 }
64 return CallWindowProcW(this_->m_fnOldWndProc, hwnd, uMsg, wParam, lParam);
65 }
66
67 static int CALLBACK
68 BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
69 {
70 CMoveToMenu *this_ =
71 reinterpret_cast<CMoveToMenu *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
72
73 switch (uMsg)
74 {
75 case BFFM_INITIALIZED:
76 {
77 SetWindowLongPtr(hwnd, GWLP_USERDATA, lpData);
78 this_ = reinterpret_cast<CMoveToMenu *>(lpData);
79
80 // Select initial directory
81 SendMessageW(hwnd, BFFM_SETSELECTION, FALSE,
82 reinterpret_cast<LPARAM>(static_cast<LPCITEMIDLIST>(this_->m_pidlFolder)));
83
84 // Set caption
85 CString strCaption(MAKEINTRESOURCEW(IDS_MOVEITEMS));
86 SetWindowTextW(hwnd, strCaption);
87
88 // Set OK button text
89 CString strMove(MAKEINTRESOURCEW(IDS_MOVEBUTTON));
90 SetDlgItemText(hwnd, IDOK, strMove);
91
92 // Subclassing
93 this_->m_fnOldWndProc =
94 reinterpret_cast<WNDPROC>(
95 SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WindowProc)));
96
97 // Disable OK
98 PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE);
99 break;
100 }
101 case BFFM_SELCHANGED:
102 {
103 WCHAR szPath[MAX_PATH];
104 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(lParam);
105
106 szPath[0] = 0;
107 SHGetPathFromIDListW(pidl, szPath);
108
109 if (ILIsEqual(pidl, this_->m_pidlFolder))
110 PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE);
111 else if (PathFileExistsW(szPath) || _ILIsDesktop(pidl))
112 PostMessageW(hwnd, BFFM_ENABLEOK, 0, TRUE);
113 else
114 PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE);
115
116 // the text box will be updated later soon, ignore it
117 this_->m_bIgnoreTextBoxChange = TRUE;
118 break;
119 }
120 }
121
122 return FALSE;
123 }
124
125 HRESULT CMoveToMenu::DoRealMove(LPCMINVOKECOMMANDINFO lpici, LPCITEMIDLIST pidl)
126 {
127 CDataObjectHIDA pCIDA(m_pDataObject);
128 if (FAILED_UNEXPECTEDLY(pCIDA.hr()))
129 return pCIDA.hr();
130
131 PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pCIDA);
132 if (!pidlParent)
133 {
134 ERR("HIDA_GetPIDLFolder failed\n");
135 return E_FAIL;
136 }
137
138 CStringW strFiles;
139 WCHAR szPath[MAX_PATH];
140 for (UINT n = 0; n < pCIDA->cidl; ++n)
141 {
142 PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(pCIDA, n);
143 if (!pidlRelative)
144 continue;
145
146 CComHeapPtr<ITEMIDLIST> pidlCombine(ILCombine(pidlParent, pidlRelative));
147 if (!pidl)
148 return E_FAIL;
149
150 SHGetPathFromIDListW(pidlCombine, szPath);
151
152 if (n > 0)
153 strFiles += L'|';
154 strFiles += szPath;
155 }
156
157 strFiles += L'|'; // double null-terminated
158 strFiles.Replace(L'|', L'\0');
159
160 if (_ILIsDesktop(pidl))
161 SHGetSpecialFolderPathW(NULL, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
162 else
163 SHGetPathFromIDListW(pidl, szPath);
164 INT cchPath = lstrlenW(szPath);
165 if (cchPath + 1 < MAX_PATH)
166 {
167 szPath[cchPath + 1] = 0; // double null-terminated
168 }
169 else
170 {
171 ERR("Too long path\n");
172 return E_FAIL;
173 }
174
175 SHFILEOPSTRUCTW op = { lpici->hwnd };
176 op.wFunc = FO_MOVE;
177 op.pFrom = strFiles;
178 op.pTo = szPath;
179 op.fFlags = FOF_ALLOWUNDO;
180 int res = SHFileOperationW(&op);
181 if (res)
182 {
183 ERR("SHFileOperationW failed with 0x%x\n", res);
184 return E_FAIL;
185 }
186 return S_OK;
187 }
188
189 CStringW CMoveToMenu::DoGetFileTitle()
190 {
191 CStringW ret = L"(file)";
192
193 CDataObjectHIDA pCIDA(m_pDataObject);
194 if (FAILED_UNEXPECTEDLY(pCIDA.hr()))
195 return ret;
196
197 PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pCIDA);
198 if (!pidlParent)
199 {
200 ERR("HIDA_GetPIDLFolder failed\n");
201 return ret;
202 }
203
204 WCHAR szPath[MAX_PATH];
205 PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(pCIDA, 0);
206 if (!pidlRelative)
207 {
208 ERR("HIDA_GetPIDLItem failed\n");
209 return ret;
210 }
211
212 CComHeapPtr<ITEMIDLIST> pidlCombine(ILCombine(pidlParent, pidlRelative));
213
214 if (SHGetPathFromIDListW(pidlCombine, szPath))
215 ret = PathFindFileNameW(szPath);
216 else
217 ERR("Cannot get path\n");
218
219 if (pCIDA->cidl > 1)
220 ret += L" ...";
221
222 return ret;
223 }
224
225 HRESULT CMoveToMenu::DoMoveToFolder(LPCMINVOKECOMMANDINFO lpici)
226 {
227 WCHAR wszPath[MAX_PATH];
228 HRESULT hr = E_FAIL;
229
230 TRACE("DoMoveToFolder(%p)\n", lpici);
231
232 if (!SHGetPathFromIDListW(m_pidlFolder, wszPath))
233 {
234 ERR("SHGetPathFromIDListW failed\n");
235 return hr;
236 }
237
238 CStringW strFileTitle = DoGetFileTitle();
239 CStringW strTitle;
240 strTitle.Format(IDS_MOVETOTITLE, static_cast<LPCWSTR>(strFileTitle));
241
242 BROWSEINFOW info = { lpici->hwnd };
243 info.pidlRoot = NULL;
244 info.lpszTitle = strTitle;
245 info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
246 info.lpfn = BrowseCallbackProc;
247 info.lParam = reinterpret_cast<LPARAM>(this);
248 CComHeapPtr<ITEMIDLIST> pidl(SHBrowseForFolder(&info));
249 if (pidl)
250 {
251 hr = DoRealMove(lpici, pidl);
252 }
253
254 return hr;
255 }
256
257 HRESULT WINAPI
258 CMoveToMenu::QueryContextMenu(HMENU hMenu,
259 UINT indexMenu,
260 UINT idCmdFirst,
261 UINT idCmdLast,
262 UINT uFlags)
263 {
264 MENUITEMINFOW mii;
265 UINT Count = 0;
266
267 TRACE("CMoveToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n",
268 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
269
270 m_idCmdFirst = m_idCmdLast = idCmdFirst;
271
272 // insert separator if necessary
273 CStringW strCopyTo(MAKEINTRESOURCEW(IDS_COPYTOMENU));
274 WCHAR szBuff[128];
275 ZeroMemory(&mii, sizeof(mii));
276 mii.cbSize = sizeof(mii);
277 mii.fMask = MIIM_TYPE;
278 mii.dwTypeData = szBuff;
279 mii.cch = _countof(szBuff);
280 if (GetMenuItemInfoW(hMenu, indexMenu - 1, TRUE, &mii) &&
281 mii.fType != MFT_SEPARATOR &&
282 !(mii.fType == MFT_STRING && CStringW(szBuff) == strCopyTo))
283 {
284 ZeroMemory(&mii, sizeof(mii));
285 mii.cbSize = sizeof(mii);
286 mii.fMask = MIIM_TYPE;
287 mii.fType = MFT_SEPARATOR;
288 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
289 {
290 ++indexMenu;
291 ++Count;
292 }
293 }
294
295 // insert "Move to folder..."
296 CStringW strText(MAKEINTRESOURCEW(IDS_MOVETOMENU));
297 ZeroMemory(&mii, sizeof(mii));
298 mii.cbSize = sizeof(mii);
299 mii.fMask = MIIM_ID | MIIM_TYPE;
300 mii.fType = MFT_STRING;
301 mii.dwTypeData = strText.GetBuffer();
302 mii.cch = wcslen(mii.dwTypeData);
303 mii.wID = m_idCmdLast;
304 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
305 {
306 m_idCmdMoveTo = m_idCmdLast++;
307 ++indexMenu;
308 ++Count;
309 }
310
311 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
312 }
313
314 HRESULT WINAPI
315 CMoveToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
316 {
317 HRESULT hr = E_FAIL;
318 TRACE("CMoveToMenu::InvokeCommand(%p)\n", lpici);
319
320 if (HIWORD(lpici->lpVerb) == 0)
321 {
322 if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdMoveTo)
323 {
324 hr = DoMoveToFolder(lpici);
325 }
326 }
327 else
328 {
329 if (::lstrcmpiA(lpici->lpVerb, "moveto") == 0)
330 {
331 hr = DoMoveToFolder(lpici);
332 }
333 }
334
335 return hr;
336 }
337
338 HRESULT WINAPI
339 CMoveToMenu::GetCommandString(UINT_PTR idCmd,
340 UINT uType,
341 UINT *pwReserved,
342 LPSTR pszName,
343 UINT cchMax)
344 {
345 FIXME("%p %lu %u %p %p %u\n", this,
346 idCmd, uType, pwReserved, pszName, cchMax);
347
348 return E_NOTIMPL;
349 }
350
351 HRESULT WINAPI
352 CMoveToMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
353 {
354 TRACE("This %p uMsg %x\n", this, uMsg);
355 return E_NOTIMPL;
356 }
357
358 HRESULT WINAPI
359 CMoveToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder,
360 IDataObject *pdtobj, HKEY hkeyProgID)
361 {
362 m_pidlFolder.Attach(ILClone(pidlFolder));
363 m_pDataObject = pdtobj;
364 return S_OK;
365 }
366
367 HRESULT WINAPI CMoveToMenu::SetSite(IUnknown *pUnkSite)
368 {
369 m_pSite = pUnkSite;
370 return S_OK;
371 }
372
373 HRESULT WINAPI CMoveToMenu::GetSite(REFIID riid, void **ppvSite)
374 {
375 if (!m_pSite)
376 return E_FAIL;
377
378 return m_pSite->QueryInterface(riid, ppvSite);
379 }