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)
10 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
13 CCopyToMenu::CCopyToMenu() :
18 m_bIgnoreTextBoxChange(FALSE
)
22 CCopyToMenu::~CCopyToMenu()
26 static LRESULT CALLBACK
27 WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
29 WCHAR szPath
[MAX_PATH
];
31 reinterpret_cast<CCopyToMenu
*>(GetWindowLongPtr(hwnd
, GWLP_USERDATA
));
37 switch (LOWORD(wParam
))
39 case IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT
:
41 if (HIWORD(wParam
) == EN_CHANGE
)
43 if (!this_
->m_bIgnoreTextBoxChange
)
46 GetDlgItemTextW(hwnd
, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT
, szPath
, _countof(szPath
));
47 StrTrimW(szPath
, L
" \t");
50 BOOL bValid
= !PathIsRelative(szPath
) && PathIsDirectoryW(szPath
);
51 SendMessageW(hwnd
, BFFM_ENABLEOK
, 0, bValid
);
57 this_
->m_bIgnoreTextBoxChange
= FALSE
;
65 return CallWindowProcW(this_
->m_fnOldWndProc
, hwnd
, uMsg
, wParam
, lParam
);
69 BrowseCallbackProc(HWND hwnd
, UINT uMsg
, LPARAM lParam
, LPARAM lpData
)
72 reinterpret_cast<CCopyToMenu
*>(GetWindowLongPtr(hwnd
, GWLP_USERDATA
));
76 case BFFM_INITIALIZED
:
78 SetWindowLongPtr(hwnd
, GWLP_USERDATA
, lpData
);
79 this_
= reinterpret_cast<CCopyToMenu
*>(lpData
);
81 // Select initial directory
82 SendMessageW(hwnd
, BFFM_SETSELECTION
, FALSE
,
83 reinterpret_cast<LPARAM
>(static_cast<LPCITEMIDLIST
>(this_
->m_pidlFolder
)));
86 CString
strCaption(MAKEINTRESOURCEW(IDS_COPYITEMS
));
87 SetWindowTextW(hwnd
, strCaption
);
90 CString
strCopy(MAKEINTRESOURCEW(IDS_COPYBUTTON
));
91 SetDlgItemText(hwnd
, IDOK
, strCopy
);
94 this_
->m_fnOldWndProc
=
95 reinterpret_cast<WNDPROC
>(
96 SetWindowLongPtr(hwnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(WindowProc
)));
99 PostMessageW(hwnd
, BFFM_ENABLEOK
, 0, FALSE
);
102 case BFFM_SELCHANGED
:
107 WCHAR szPath
[MAX_PATH
];
108 LPCITEMIDLIST pidl
= reinterpret_cast<LPCITEMIDLIST
>(lParam
);
111 SHGetPathFromIDListW(pidl
, szPath
);
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
);
118 PostMessageW(hwnd
, BFFM_ENABLEOK
, 0, FALSE
);
120 // the text box will be updated later soon, ignore it
121 this_
->m_bIgnoreTextBoxChange
= TRUE
;
129 HRESULT
CCopyToMenu::DoRealCopy(LPCMINVOKECOMMANDINFO lpici
, LPCITEMIDLIST pidl
)
131 CDataObjectHIDA
pCIDA(m_pDataObject
);
132 if (FAILED_UNEXPECTEDLY(pCIDA
.hr()))
135 PCUIDLIST_ABSOLUTE pidlParent
= HIDA_GetPIDLFolder(pCIDA
);
138 ERR("HIDA_GetPIDLFolder failed\n");
143 WCHAR szPath
[MAX_PATH
];
144 for (UINT n
= 0; n
< pCIDA
->cidl
; ++n
)
146 PCUIDLIST_RELATIVE pidlRelative
= HIDA_GetPIDLItem(pCIDA
, n
);
150 CComHeapPtr
<ITEMIDLIST
> pidlCombine(ILCombine(pidlParent
, pidlRelative
));
154 SHGetPathFromIDListW(pidlCombine
, szPath
);
161 strFiles
+= L
'|'; // double null-terminated
162 strFiles
.Replace(L
'|', L
'\0');
164 if (_ILIsDesktop(pidl
))
165 SHGetSpecialFolderPathW(NULL
, szPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
167 SHGetPathFromIDListW(pidl
, szPath
);
168 INT cchPath
= lstrlenW(szPath
);
169 if (cchPath
+ 1 < MAX_PATH
)
171 szPath
[cchPath
+ 1] = 0; // double null-terminated
175 ERR("Too long path\n");
179 SHFILEOPSTRUCTW op
= { lpici
->hwnd
};
183 op
.fFlags
= FOF_ALLOWUNDO
;
184 int res
= SHFileOperationW(&op
);
187 ERR("SHFileOperationW failed with 0x%x\n", res
);
193 CStringW
CCopyToMenu::DoGetFileTitle()
195 CStringW ret
= L
"(file)";
197 CDataObjectHIDA
pCIDA(m_pDataObject
);
198 if (FAILED_UNEXPECTEDLY(pCIDA
.hr()))
201 PCUIDLIST_ABSOLUTE pidlParent
= HIDA_GetPIDLFolder(pCIDA
);
204 ERR("HIDA_GetPIDLFolder failed\n");
208 WCHAR szPath
[MAX_PATH
];
209 PCUIDLIST_RELATIVE pidlRelative
= HIDA_GetPIDLItem(pCIDA
, 0);
212 ERR("HIDA_GetPIDLItem failed\n");
216 CComHeapPtr
<ITEMIDLIST
> pidlCombine(ILCombine(pidlParent
, pidlRelative
));
218 if (SHGetPathFromIDListW(pidlCombine
, szPath
))
219 ret
= PathFindFileNameW(szPath
);
221 ERR("Cannot get path\n");
229 HRESULT
CCopyToMenu::DoCopyToFolder(LPCMINVOKECOMMANDINFO lpici
)
231 WCHAR wszPath
[MAX_PATH
];
234 TRACE("DoCopyToFolder(%p)\n", lpici
);
236 if (!SHGetPathFromIDListW(m_pidlFolder
, wszPath
))
238 ERR("SHGetPathFromIDListW failed\n");
242 CStringW strFileTitle
= DoGetFileTitle();
244 strTitle
.Format(IDS_COPYTOTITLE
, static_cast<LPCWSTR
>(strFileTitle
));
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
));
255 hr
= DoRealCopy(lpici
, pidl
);
262 CCopyToMenu::QueryContextMenu(HMENU hMenu
,
271 TRACE("CCopyToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n",
272 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
274 m_idCmdFirst
= m_idCmdLast
= idCmdFirst
;
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
)
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
))
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
))
305 m_idCmdCopyTo
= m_idCmdLast
++;
310 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, Count
);
314 CCopyToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
317 TRACE("CCopyToMenu::InvokeCommand(%p)\n", lpici
);
319 if (HIWORD(lpici
->lpVerb
) == 0)
321 if (m_idCmdFirst
+ LOWORD(lpici
->lpVerb
) == m_idCmdCopyTo
)
323 hr
= DoCopyToFolder(lpici
);
328 if (::lstrcmpiA(lpici
->lpVerb
, "copyto") == 0)
330 hr
= DoCopyToFolder(lpici
);
338 CCopyToMenu::GetCommandString(UINT_PTR idCmd
,
344 FIXME("%p %lu %u %p %p %u\n", this,
345 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
351 CCopyToMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
353 TRACE("This %p uMsg %x\n", this, uMsg
);
358 CCopyToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder
,
359 IDataObject
*pdtobj
, HKEY hkeyProgID
)
361 m_pidlFolder
.Attach(ILClone(pidlFolder
));
362 m_pDataObject
= pdtobj
;
366 HRESULT WINAPI
CCopyToMenu::SetSite(IUnknown
*pUnkSite
)
372 HRESULT WINAPI
CCopyToMenu::GetSite(REFIID riid
, void **ppvSite
)
377 return m_pSite
->QueryInterface(riid
, ppvSite
);