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
:
104 WCHAR szPath
[MAX_PATH
];
105 LPCITEMIDLIST pidl
= reinterpret_cast<LPCITEMIDLIST
>(lParam
);
108 SHGetPathFromIDListW(pidl
, szPath
);
110 if (ILIsEqual(pidl
, this_
->m_pidlFolder
))
111 PostMessageW(hwnd
, BFFM_ENABLEOK
, 0, FALSE
);
112 else if (PathFileExistsW(szPath
) || _ILIsDesktop(pidl
))
113 PostMessageW(hwnd
, BFFM_ENABLEOK
, 0, TRUE
);
115 PostMessageW(hwnd
, BFFM_ENABLEOK
, 0, FALSE
);
117 // the text box will be updated later soon, ignore it
118 this_
->m_bIgnoreTextBoxChange
= TRUE
;
126 HRESULT
CCopyToMenu::DoRealCopy(LPCMINVOKECOMMANDINFO lpici
, LPCITEMIDLIST pidl
)
128 CDataObjectHIDA
pCIDA(m_pDataObject
);
129 if (FAILED_UNEXPECTEDLY(pCIDA
.hr()))
132 PCUIDLIST_ABSOLUTE pidlParent
= HIDA_GetPIDLFolder(pCIDA
);
135 ERR("HIDA_GetPIDLFolder failed\n");
140 WCHAR szPath
[MAX_PATH
];
141 for (UINT n
= 0; n
< pCIDA
->cidl
; ++n
)
143 PCUIDLIST_RELATIVE pidlRelative
= HIDA_GetPIDLItem(pCIDA
, n
);
147 CComHeapPtr
<ITEMIDLIST
> pidlCombine(ILCombine(pidlParent
, pidlRelative
));
151 SHGetPathFromIDListW(pidlCombine
, szPath
);
158 strFiles
+= L
'|'; // double null-terminated
159 strFiles
.Replace(L
'|', L
'\0');
161 if (_ILIsDesktop(pidl
))
162 SHGetSpecialFolderPathW(NULL
, szPath
, CSIDL_DESKTOPDIRECTORY
, FALSE
);
164 SHGetPathFromIDListW(pidl
, szPath
);
165 INT cchPath
= lstrlenW(szPath
);
166 if (cchPath
+ 1 < MAX_PATH
)
168 szPath
[cchPath
+ 1] = 0; // double null-terminated
172 ERR("Too long path\n");
176 SHFILEOPSTRUCTW op
= { lpici
->hwnd
};
180 op
.fFlags
= FOF_ALLOWUNDO
;
181 int res
= SHFileOperationW(&op
);
184 ERR("SHFileOperationW failed with 0x%x\n", res
);
190 CStringW
CCopyToMenu::DoGetFileTitle()
192 CStringW ret
= L
"(file)";
194 CDataObjectHIDA
pCIDA(m_pDataObject
);
195 if (FAILED_UNEXPECTEDLY(pCIDA
.hr()))
198 PCUIDLIST_ABSOLUTE pidlParent
= HIDA_GetPIDLFolder(pCIDA
);
201 ERR("HIDA_GetPIDLFolder failed\n");
205 WCHAR szPath
[MAX_PATH
];
206 PCUIDLIST_RELATIVE pidlRelative
= HIDA_GetPIDLItem(pCIDA
, 0);
209 ERR("HIDA_GetPIDLItem failed\n");
213 CComHeapPtr
<ITEMIDLIST
> pidlCombine(ILCombine(pidlParent
, pidlRelative
));
215 if (SHGetPathFromIDListW(pidlCombine
, szPath
))
216 ret
= PathFindFileNameW(szPath
);
218 ERR("Cannot get path\n");
226 HRESULT
CCopyToMenu::DoCopyToFolder(LPCMINVOKECOMMANDINFO lpici
)
228 WCHAR wszPath
[MAX_PATH
];
231 TRACE("DoCopyToFolder(%p)\n", lpici
);
233 if (!SHGetPathFromIDListW(m_pidlFolder
, wszPath
))
235 ERR("SHGetPathFromIDListW failed\n");
239 CStringW strFileTitle
= DoGetFileTitle();
241 strTitle
.Format(IDS_COPYTOTITLE
, static_cast<LPCWSTR
>(strFileTitle
));
243 BROWSEINFOW info
= { lpici
->hwnd
};
244 info
.pidlRoot
= NULL
;
245 info
.lpszTitle
= strTitle
;
246 info
.ulFlags
= BIF_RETURNONLYFSDIRS
| BIF_USENEWUI
;
247 info
.lpfn
= BrowseCallbackProc
;
248 info
.lParam
= reinterpret_cast<LPARAM
>(this);
249 CComHeapPtr
<ITEMIDLIST
> pidl(SHBrowseForFolder(&info
));
252 hr
= DoRealCopy(lpici
, pidl
);
259 CCopyToMenu::QueryContextMenu(HMENU hMenu
,
268 TRACE("CCopyToMenu::QueryContextMenu(%p, %u, %u, %u, %u)\n",
269 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
271 m_idCmdFirst
= m_idCmdLast
= idCmdFirst
;
273 // insert separator if necessary
274 ZeroMemory(&mii
, sizeof(mii
));
275 mii
.cbSize
= sizeof(mii
);
276 mii
.fMask
= MIIM_TYPE
;
277 if (GetMenuItemInfoW(hMenu
, indexMenu
- 1, TRUE
, &mii
) &&
278 mii
.fType
!= MFT_SEPARATOR
)
280 ZeroMemory(&mii
, sizeof(mii
));
281 mii
.cbSize
= sizeof(mii
);
282 mii
.fMask
= MIIM_TYPE
;
283 mii
.fType
= MFT_SEPARATOR
;
284 if (InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
291 // insert "Copy to folder..."
292 CStringW
strText(MAKEINTRESOURCEW(IDS_COPYTOMENU
));
293 ZeroMemory(&mii
, sizeof(mii
));
294 mii
.cbSize
= sizeof(mii
);
295 mii
.fMask
= MIIM_ID
| MIIM_TYPE
;
296 mii
.fType
= MFT_STRING
;
297 mii
.dwTypeData
= strText
.GetBuffer();
298 mii
.cch
= wcslen(mii
.dwTypeData
);
299 mii
.wID
= m_idCmdLast
;
300 if (InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
302 m_idCmdCopyTo
= m_idCmdLast
++;
307 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, Count
);
311 CCopyToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
314 TRACE("CCopyToMenu::InvokeCommand(%p)\n", lpici
);
316 if (HIWORD(lpici
->lpVerb
) == 0)
318 if (m_idCmdFirst
+ LOWORD(lpici
->lpVerb
) == m_idCmdCopyTo
)
320 hr
= DoCopyToFolder(lpici
);
325 if (::lstrcmpiA(lpici
->lpVerb
, "copyto") == 0)
327 hr
= DoCopyToFolder(lpici
);
335 CCopyToMenu::GetCommandString(UINT_PTR idCmd
,
341 FIXME("%p %lu %u %p %p %u\n", this,
342 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
348 CCopyToMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
350 TRACE("This %p uMsg %x\n", this, uMsg
);
355 CCopyToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder
,
356 IDataObject
*pdtobj
, HKEY hkeyProgID
)
358 m_pidlFolder
.Attach(ILClone(pidlFolder
));
359 m_pDataObject
= pdtobj
;
363 HRESULT WINAPI
CCopyToMenu::SetSite(IUnknown
*pUnkSite
)
369 HRESULT WINAPI
CCopyToMenu::GetSite(REFIID riid
, void **ppvSite
)
374 return m_pSite
->QueryInterface(riid
, ppvSite
);