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