2 * provides SendTo shell item service
4 * Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define MAX_ITEM_COUNT 64
27 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
29 DEFINE_GUID(CLSID_SendToMenu
, 0x7BA4C740, 0x9E81, 0x11CF,
30 0x99, 0xD3, 0x00, 0xAA, 0x00, 0x4A, 0xE8, 0x37);
32 CSendToMenu::CSendToMenu()
37 SHGetDesktopFolder(&m_pDesktop
);
38 m_pSendTo
= GetSpecialFolder(NULL
, CSIDL_SENDTO
);
41 CSendToMenu::~CSendToMenu()
47 DestroyMenu(m_hSubMenu
);
52 HRESULT
CSendToMenu::DoDrop(IDataObject
*pDataObject
, IDropTarget
*pDropTarget
)
54 DWORD dwEffect
= DROPEFFECT_MOVE
| DROPEFFECT_COPY
| DROPEFFECT_LINK
;
56 BOOL bShift
= (GetAsyncKeyState(VK_SHIFT
) < 0);
57 BOOL bCtrl
= (GetAsyncKeyState(VK_CONTROL
) < 0);
59 // THIS CODE IS NOT HUMAN-FRIENDLY. SORRY.
60 // (We have to translate a SendTo action to a Drop action)
61 DWORD dwKeyState
= MK_LBUTTON
;
63 dwKeyState
|= MK_SHIFT
| MK_CONTROL
;
65 dwKeyState
|= MK_CONTROL
;
67 dwKeyState
|= MK_SHIFT
;
69 POINTL ptl
= { 0, 0 };
70 HRESULT hr
= pDropTarget
->DragEnter(pDataObject
, dwKeyState
, ptl
, &dwEffect
);
71 if (SUCCEEDED(hr
) && dwEffect
!= DROPEFFECT_NONE
)
73 // THIS CODE IS NOT HUMAN-FRIENDLY. SORRY.
74 // (We have to translate a SendTo action to a Drop action)
76 dwEffect
= DROPEFFECT_LINK
;
78 dwEffect
= DROPEFFECT_MOVE
;
80 dwEffect
= DROPEFFECT_COPY
;
82 hr
= pDropTarget
->Drop(pDataObject
, dwKeyState
, ptl
, &dwEffect
);
86 ERR("DragEnter: %08lX\n", hr
);
87 pDropTarget
->DragLeave();
93 // get an IShellFolder from CSIDL
95 CSendToMenu::GetSpecialFolder(HWND hwnd
, int csidl
, LPITEMIDLIST
*ppidl
)
102 SHGetDesktopFolder(&m_pDesktop
);
105 ERR("SHGetDesktopFolder\n");
110 LPITEMIDLIST pidl
= NULL
;
111 HRESULT hr
= SHGetSpecialFolderLocation(hwnd
, csidl
, &pidl
);
114 ERR("SHGetSpecialFolderLocation: %08lX\n", hr
);
118 IShellFolder
*pFolder
= NULL
;
119 hr
= m_pDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &pFolder
));
129 ERR("BindToObject: %08lX\n", hr
);
133 // get a UI object from PIDL
134 HRESULT
CSendToMenu::GetUIObjectFromPidl(HWND hwnd
, LPITEMIDLIST pidl
,
135 REFIID riid
, LPVOID
*ppvOut
)
139 LPCITEMIDLIST pidlLast
;
140 CComPtr
<IShellFolder
> pFolder
;
141 HRESULT hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &pFolder
), &pidlLast
);
144 ERR("SHBindToParent: %08lX\n", hr
);
148 hr
= pFolder
->GetUIObjectOf(hwnd
, 1, &pidlLast
, riid
, NULL
, ppvOut
);
151 ERR("GetUIObjectOf: %08lX\n", hr
);
156 void CSendToMenu::UnloadItem(SENDTO_ITEM
*pItem
)
161 CoTaskMemFree(pItem
->pidlChild
);
162 CoTaskMemFree(pItem
->pszText
);
163 DestroyIcon(pItem
->hIcon
);
164 HeapFree(GetProcessHeap(), 0, pItem
);
167 void CSendToMenu::UnloadAllItems()
169 SENDTO_ITEM
*pItems
= m_pItems
;
173 SENDTO_ITEM
*pCurItem
= pItems
;
174 pItems
= pItems
->pNext
;
175 UnloadItem(pCurItem
);
179 BOOL
CSendToMenu::LoadAllItems(HWND hwnd
)
183 LPITEMIDLIST pidlSendTo
;
184 m_pSendTo
= GetSpecialFolder(hwnd
, CSIDL_SENDTO
, &pidlSendTo
);
187 ERR("GetSpecialFolder\n");
192 CComPtr
<IEnumIDList
> pEnumIDList
;
193 hr
= m_pSendTo
->EnumObjects(hwnd
,
194 SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
,
198 ERR("EnumObjects: %08lX\n", hr
);
204 LPITEMIDLIST pidlChild
;
206 while (pEnumIDList
->Next(1, &pidlChild
, NULL
) == S_OK
)
208 SENDTO_ITEM
*pNewItem
= (SENDTO_ITEM
*)HeapAlloc(GetProcessHeap(),
210 sizeof(SENDTO_ITEM
));
215 CoTaskMemFree(pidlChild
);
220 hr
= m_pSendTo
->GetDisplayNameOf(pidlChild
, SHGDN_NORMAL
, &strret
);
223 LPWSTR pszText
= NULL
;
224 hr
= StrRetToStrW(&strret
, pidlChild
, &pszText
);
227 LPITEMIDLIST pidlAbsolute
= ILCombine(pidlSendTo
, pidlChild
);
229 SHFILEINFOW fi
= { NULL
};
230 const UINT uFlags
= SHGFI_PIDL
| SHGFI_TYPENAME
|
231 SHGFI_ICON
| SHGFI_SMALLICON
;
232 SHGetFileInfoW((LPCWSTR
)pidlAbsolute
, 0, &fi
, sizeof(fi
), uFlags
);
234 ILFree(pidlAbsolute
);
236 pNewItem
->pidlChild
= pidlChild
;
237 pNewItem
->pszText
= pszText
;
238 pNewItem
->hIcon
= fi
.hIcon
;
241 pNewItem
->pNext
= m_pItems
;
247 if (nCount
>= MAX_ITEM_COUNT
)
255 ERR("StrRetToStrW: %08lX\n", hr
);
260 ERR("GetDisplayNameOf: %08lX\n", hr
);
263 UnloadItem(pNewItem
);
264 CoTaskMemFree(pidlChild
);
272 UINT
CSendToMenu::InsertSendToItems(HMENU hMenu
, UINT idCmdFirst
, UINT Pos
)
274 if (m_pItems
== NULL
)
276 if (!LoadAllItems(NULL
))
278 ERR("LoadAllItems\n");
283 m_idCmdFirst
= idCmdFirst
;
285 UINT idCmd
= idCmdFirst
;
287 for (SENDTO_ITEM
*pCurItem
= m_pItems
; pCurItem
; pCurItem
= pCurItem
->pNext
)
289 const UINT uFlags
= MF_BYPOSITION
| MF_STRING
| MF_ENABLED
;
290 if (InsertMenuW(hMenu
, Pos
, uFlags
, idCmd
, pCurItem
->pszText
))
293 mii
.cbSize
= sizeof(mii
);
294 mii
.fMask
= MIIM_DATA
| MIIM_BITMAP
;
295 mii
.dwItemData
= (ULONG_PTR
)pCurItem
;
296 mii
.hbmpItem
= HBMMENU_CALLBACK
;
297 SetMenuItemInfoW(hMenu
, idCmd
, FALSE
, &mii
);
302 if (nCount
>= MAX_ITEM_COUNT
)
309 if (idCmd
== idCmdFirst
)
311 WCHAR szNone
[64] = L
"(None)";
312 LoadStringW(shell32_hInstance
, IDS_NONE
, szNone
, _countof(szNone
));
314 AppendMenuW(hMenu
, MF_GRAYED
| MF_DISABLED
| MF_STRING
, idCmd
, szNone
);
317 return idCmd
- idCmdFirst
;
320 CSendToMenu::SENDTO_ITEM
*CSendToMenu::FindItemFromIdOffset(UINT IdOffset
)
322 UINT idCmd
= m_idCmdFirst
+ IdOffset
;
324 MENUITEMINFOW mii
= { sizeof(mii
) };
325 mii
.fMask
= MIIM_DATA
;
326 if (GetMenuItemInfoW(m_hSubMenu
, idCmd
, FALSE
, &mii
))
327 return (SENDTO_ITEM
*)mii
.dwItemData
;
329 ERR("GetMenuItemInfoW\n");
333 HRESULT
CSendToMenu::DoSendToItem(SENDTO_ITEM
*pItem
, LPCMINVOKECOMMANDINFO lpici
)
337 ERR("!m_pDataObject\n");
342 CComPtr
<IDropTarget
> pDropTarget
;
343 LPITEMIDLIST pidlChild
= pItem
->pidlChild
;
344 hr
= m_pSendTo
->GetUIObjectOf(NULL
, 1, &pidlChild
, IID_IDropTarget
,
345 NULL
, (LPVOID
*)&pDropTarget
);
348 hr
= DoDrop(m_pDataObject
, pDropTarget
);
352 ERR("GetUIObjectOf: %08lX\n", hr
);
358 STDMETHODIMP
CSendToMenu::SetSite(IUnknown
*pUnkSite
)
364 STDMETHODIMP
CSendToMenu::GetSite(REFIID riid
, void **ppvSite
)
369 return m_pSite
->QueryInterface(riid
, ppvSite
);
373 CSendToMenu::QueryContextMenu(HMENU hMenu
,
379 TRACE("%p %p %u %u %u %u\n", this,
380 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
383 if (!LoadStringW(shell32_hInstance
, IDS_SENDTO
,
384 wszSendTo
, _countof(wszSendTo
)))
390 HMENU hSubMenu
= CreateMenu();
397 UINT cItems
= InsertSendToItems(hSubMenu
, idCmdFirst
, 0);
400 ZeroMemory(&mii
, sizeof(mii
));
401 mii
.cbSize
= sizeof(mii
);
402 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
| MIIM_SUBMENU
;
403 mii
.fType
= MFT_STRING
;
405 mii
.dwTypeData
= wszSendTo
;
406 mii
.cch
= wcslen(mii
.dwTypeData
);
407 mii
.fState
= MFS_ENABLED
;
408 mii
.hSubMenu
= hSubMenu
;
409 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
411 ERR("InsertMenuItemW\n");
415 HMENU hOldSubMenu
= m_hSubMenu
;
416 m_hSubMenu
= hSubMenu
;
417 DestroyMenu(hOldSubMenu
);
419 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, cItems
);
423 CSendToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
427 WORD idCmd
= LOWORD(lpici
->lpVerb
);
428 TRACE("idCmd: %d\n", idCmd
);
430 SENDTO_ITEM
*pItem
= FindItemFromIdOffset(idCmd
);
433 hr
= DoSendToItem(pItem
, lpici
);
437 ERR("FindItemFromIdOffset\n");
440 TRACE("CSendToMenu::InvokeCommand %x\n", hr
);
445 CSendToMenu::GetCommandString(UINT_PTR idCmd
,
451 FIXME("%p %lu %u %p %p %u\n", this,
452 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
458 CSendToMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
464 CSendToMenu::HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
,
467 UINT cxSmall
= GetSystemMetrics(SM_CXSMICON
);
468 UINT cySmall
= GetSystemMetrics(SM_CYSMICON
);
474 MEASUREITEMSTRUCT
* lpmis
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
475 if (!lpmis
|| lpmis
->CtlType
!= ODT_MENU
)
478 UINT cxMenuCheck
= GetSystemMetrics(SM_CXMENUCHECK
);
479 if (lpmis
->itemWidth
< cxMenuCheck
)
480 lpmis
->itemWidth
= cxMenuCheck
;
481 if (lpmis
->itemHeight
< cySmall
)
482 lpmis
->itemHeight
= cySmall
;
490 DRAWITEMSTRUCT
* lpdis
= reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
);
491 if (!lpdis
|| lpdis
->CtlType
!= ODT_MENU
)
494 SENDTO_ITEM
*pItem
= (SENDTO_ITEM
*)lpdis
->itemData
;
497 hIcon
= pItem
->hIcon
;
501 RECT rcItem
= lpdis
->rcItem
;
502 DrawIconEx(lpdis
->hDC
, 2,
503 lpdis
->rcItem
.top
+ (rcItem
.bottom
- rcItem
.top
- 16) / 2,
504 hIcon
, cxSmall
, cySmall
, 0, NULL
, DI_NORMAL
);
515 CSendToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder
,
516 IDataObject
*pdtobj
, HKEY hkeyProgID
)
518 m_pDataObject
= pdtobj
;