c0e1c83a54d8c6b915a14f122923183ed89561a6
[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 if (!this_)
104 break;
105
106 WCHAR szPath[MAX_PATH];
107 LPCITEMIDLIST pidl = reinterpret_cast<LPCITEMIDLIST>(lParam);
108
109 szPath[0] = 0;
110 SHGetPathFromIDListW(pidl, szPath);
111
112 if (ILIsEqual(pidl, this_->m_pidlFolder))
113 PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE);
114 else if (PathFileExistsW(szPath) || _ILIsDesktop(pidl))
115 PostMessageW(hwnd, BFFM_ENABLEOK, 0, TRUE);
116 else
117 PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE);
118
119 // the text box will be updated later soon, ignore it
120 this_->m_bIgnoreTextBoxChange = TRUE;
121 break;
122 }
123 }
124
125 return FALSE;
126 }
127
128 HRESULT CMoveToMenu::DoRealMove(LPCMINVOKECOMMANDINFO lpici, LPCITEMIDLIST pidl)
129 {
130 CDataObjectHIDA pCIDA(m_pDataObject);
131 if (FAILED_UNEXPECTEDLY(pCIDA.hr()))
132 return pCIDA.hr();
133
134 PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pCIDA);
135 if (!pidlParent)
136 {
137 ERR("HIDA_GetPIDLFolder failed\n");
138 return E_FAIL;
139 }
140
141 CStringW strFiles;
142 WCHAR szPath[MAX_PATH];
143 for (UINT n = 0; n < pCIDA->cidl; ++n)
144 {
145 PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(pCIDA, n);
146 if (!pidlRelative)
147 continue;
148
149 CComHeapPtr<ITEMIDLIST> pidlCombine(ILCombine(pidlParent, pidlRelative));
150 if (!pidl)
151 return E_FAIL;
152
153 SHGetPathFromIDListW(pidlCombine, szPath);
154
155 if (n > 0)
156 strFiles += L'|';
157 strFiles += szPath;
158 }
159
160 strFiles += L'|'; // double null-terminated
161 strFiles.Replace(L'|', L'\0');
162
163 if (_ILIsDesktop(pidl))
164 SHGetSpecialFolderPathW(NULL, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
165 else
166 SHGetPathFromIDListW(pidl, szPath);
167 INT cchPath = lstrlenW(szPath);
168 if (cchPath + 1 < MAX_PATH)
169 {
170 szPath[cchPath + 1] = 0; // double null-terminated
171 }
172 else
173 {
174 ERR("Too long path\n");
175 return E_FAIL;
176 }
177
178 SHFILEOPSTRUCTW op = { lpici->hwnd };
179 op.wFunc = FO_MOVE;
180 op.pFrom = strFiles;
181 op.pTo = szPath;
182 op.fFlags = FOF_ALLOWUNDO;
183 int res = SHFileOperationW(&op);
184 if (res)
185 {
186 ERR("SHFileOperationW failed with 0x%x\n", res);
187 return E_FAIL;
188 }
189 return S_OK;
190 }
191
192 CStringW CMoveToMenu::DoGetFileTitle()
193 {
194 CStringW ret = L"(file)";
195
196 CDataObjectHIDA pCIDA(m_pDataObject);
197 if (FAILED_UNEXPECTEDLY(pCIDA.hr()))
198 return ret;
199
200 PCUIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pCIDA);
201 if (!pidlParent)
202 {
203 ERR("HIDA_GetPIDLFolder failed\n");
204 return ret;
205 }
206
207 WCHAR szPath[MAX_PATH];
208 PCUIDLIST_RELATIVE pidlRelative = HIDA_GetPIDLItem(pCIDA, 0);
209 if (!pidlRelative)
210 {
211 ERR("HIDA_GetPIDLItem failed\n");
212 return ret;
213 }
214
215 CComHeapPtr<ITEMIDLIST> pidlCombine(ILCombine(pidlParent, pidlRelative));
216
217 if (SHGetPathFromIDListW(pidlCombine, szPath))
218 ret = PathFindFileNameW(szPath);
219 else
220 ERR("Cannot get path\n");
221
222 if (pCIDA->cidl > 1)
223 ret += L" ...";
224
225 return ret;
226 }
227
228 HRESULT CMoveToMenu::DoMoveToFolder(LPCMINVOKECOMMANDINFO lpici)
229 {
230 WCHAR wszPath[MAX_PATH];
231 HRESULT hr = E_FAIL;
232
233 TRACE("DoMoveToFolder(%p)\n", lpici);
234
235 if (!SHGetPathFromIDListW(m_pidlFolder, wszPath))
236 {
237 ERR("SHGetPathFromIDListW failed\n");
238 return hr;
239 }
240
241 CStringW strFileTitle = DoGetFileTitle();
242 CStringW strTitle;
243 strTitle.Format(IDS_MOVETOTITLE, static_cast<LPCWSTR>(strFileTitle));
244
245 BROWSEINFOW info = { lpici->hwnd };
246 info.pidlRoot = NULL;
247 info.lpszTitle = strTitle;
248 info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
249 info.lpfn = BrowseCallbackProc;
250 info.lParam = reinterpret_cast<LPARAM>(this);
251 CComHeapPtr<ITEMIDLIST> pidl(SHBrowseForFolder(&info));
252 if (pidl)
253 {
254 hr = DoRealMove(lpici, pidl);
255 }
256
257 return hr;
258 }
259
260 HRESULT WINAPI
261 CMoveToMenu::QueryContextMenu(HMENU hMenu,
262 UINT indexMenu,
263 UINT idCmdFirst,
264 UINT idCmdLast,
265 UINT uFlags)
266 {
267 MENUITEMINFOW mii;
268 UINT Count = 0;
269
270 TRACE("CMoveToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n",
271 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
272
273 m_idCmdFirst = m_idCmdLast = idCmdFirst;
274
275 // insert separator if necessary
276 CStringW strCopyTo(MAKEINTRESOURCEW(IDS_COPYTOMENU));
277 WCHAR szBuff[128];
278 ZeroMemory(&mii, sizeof(mii));
279 mii.cbSize = sizeof(mii);
280 mii.fMask = MIIM_TYPE;
281 mii.dwTypeData = szBuff;
282 mii.cch = _countof(szBuff);
283 if (GetMenuItemInfoW(hMenu, indexMenu - 1, TRUE, &mii) &&
284 mii.fType != MFT_SEPARATOR &&
285 !(mii.fType == MFT_STRING && CStringW(szBuff) == strCopyTo))
286 {
287 ZeroMemory(&mii, sizeof(mii));
288 mii.cbSize = sizeof(mii);
289 mii.fMask = MIIM_TYPE;
290 mii.fType = MFT_SEPARATOR;
291 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
292 {
293 ++indexMenu;
294 ++Count;
295 }
296 }
297
298 // insert "Move to folder..."
299 CStringW strText(MAKEINTRESOURCEW(IDS_MOVETOMENU));
300 ZeroMemory(&mii, sizeof(mii));
301 mii.cbSize = sizeof(mii);
302 mii.fMask = MIIM_ID | MIIM_TYPE;
303 mii.fType = MFT_STRING;
304 mii.dwTypeData = strText.GetBuffer();
305 mii.cch = wcslen(mii.dwTypeData);
306 mii.wID = m_idCmdLast;
307 if (InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
308 {
309 m_idCmdMoveTo = m_idCmdLast++;
310 ++indexMenu;
311 ++Count;
312 }
313
314 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
315 }
316
317 HRESULT WINAPI
318 CMoveToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
319 {
320 HRESULT hr = E_FAIL;
321 TRACE("CMoveToMenu::InvokeCommand(%p)\n", lpici);
322
323 if (HIWORD(lpici->lpVerb) == 0)
324 {
325 if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdMoveTo)
326 {
327 hr = DoMoveToFolder(lpici);
328 }
329 }
330 else
331 {
332 if (::lstrcmpiA(lpici->lpVerb, "moveto") == 0)
333 {
334 hr = DoMoveToFolder(lpici);
335 }
336 }
337
338 return hr;
339 }
340
341 HRESULT WINAPI
342 CMoveToMenu::GetCommandString(UINT_PTR idCmd,
343 UINT uType,
344 UINT *pwReserved,
345 LPSTR pszName,
346 UINT cchMax)
347 {
348 FIXME("%p %lu %u %p %p %u\n", this,
349 idCmd, uType, pwReserved, pszName, cchMax);
350
351 return E_NOTIMPL;
352 }
353
354 HRESULT WINAPI
355 CMoveToMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
356 {
357 TRACE("This %p uMsg %x\n", this, uMsg);
358 return E_NOTIMPL;
359 }
360
361 HRESULT WINAPI
362 CMoveToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder,
363 IDataObject *pdtobj, HKEY hkeyProgID)
364 {
365 m_pidlFolder.Attach(ILClone(pidlFolder));
366 m_pDataObject = pdtobj;
367 return S_OK;
368 }
369
370 HRESULT WINAPI CMoveToMenu::SetSite(IUnknown *pUnkSite)
371 {
372 m_pSite = pUnkSite;
373 return S_OK;
374 }
375
376 HRESULT WINAPI CMoveToMenu::GetSite(REFIID riid, void **ppvSite)
377 {
378 if (!m_pSite)
379 return E_FAIL;
380
381 return m_pSite->QueryInterface(riid, ppvSite);
382 }