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