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
23 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
25 CSendToMenu::CSendToMenu()
30 HRESULT hr
= SHGetDesktopFolder(&m_pDesktop
);
33 ERR("SHGetDesktopFolder: %08lX\n", hr
);
36 GetSpecialFolder(NULL
, &m_pSendTo
, CSIDL_SENDTO
);
39 CSendToMenu::~CSendToMenu()
45 DestroyMenu(m_hSubMenu
);
50 HRESULT
CSendToMenu::DoDrop(IDataObject
*pDataObject
, IDropTarget
*pDropTarget
)
52 DWORD dwEffect
= DROPEFFECT_MOVE
| DROPEFFECT_COPY
| DROPEFFECT_LINK
;
54 BOOL bShift
= (GetAsyncKeyState(VK_SHIFT
) < 0);
55 BOOL bCtrl
= (GetAsyncKeyState(VK_CONTROL
) < 0);
57 // THIS CODE IS NOT HUMAN-FRIENDLY. SORRY.
58 // (We have to translate a SendTo action to a Drop action)
59 DWORD dwKeyState
= MK_LBUTTON
;
61 dwKeyState
|= MK_SHIFT
| MK_CONTROL
;
63 dwKeyState
|= MK_CONTROL
;
65 dwKeyState
|= MK_SHIFT
;
67 POINTL ptl
= { 0, 0 };
68 HRESULT hr
= pDropTarget
->DragEnter(pDataObject
, dwKeyState
, ptl
, &dwEffect
);
69 if (FAILED_UNEXPECTEDLY(hr
))
71 pDropTarget
->DragLeave();
75 if (dwEffect
== DROPEFFECT_NONE
)
77 ERR("DROPEFFECT_NONE\n");
78 pDropTarget
->DragLeave();
82 // THIS CODE IS NOT HUMAN-FRIENDLY. SORRY.
83 // (We have to translate a SendTo action to a Drop action)
85 dwEffect
= DROPEFFECT_LINK
;
87 dwEffect
= DROPEFFECT_MOVE
;
89 dwEffect
= DROPEFFECT_COPY
;
91 hr
= pDropTarget
->Drop(pDataObject
, dwKeyState
, ptl
, &dwEffect
);
92 if (FAILED_UNEXPECTEDLY(hr
))
98 // get an IShellFolder from CSIDL
100 CSendToMenu::GetSpecialFolder(HWND hwnd
, IShellFolder
**ppFolder
,
101 int csidl
, PIDLIST_ABSOLUTE
*ppidl
)
110 CComHeapPtr
<ITEMIDLIST_ABSOLUTE
> pidl
;
111 HRESULT hr
= SHGetSpecialFolderLocation(hwnd
, csidl
, &pidl
);
112 if (FAILED_UNEXPECTEDLY(hr
))
115 IShellFolder
*pFolder
= NULL
;
116 hr
= m_pDesktop
->BindToObject(pidl
, NULL
, IID_PPV_ARG(IShellFolder
, &pFolder
));
119 *ppidl
= pidl
.Detach();
121 if (FAILED_UNEXPECTEDLY(hr
))
128 // get a UI object from PIDL
129 HRESULT
CSendToMenu::GetUIObjectFromPidl(HWND hwnd
, PIDLIST_ABSOLUTE pidl
,
130 REFIID riid
, LPVOID
*ppvOut
)
134 PCITEMID_CHILD pidlLast
;
135 CComPtr
<IShellFolder
> pFolder
;
136 HRESULT hr
= SHBindToParent(pidl
, IID_PPV_ARG(IShellFolder
, &pFolder
), &pidlLast
);
137 if (FAILED_UNEXPECTEDLY(hr
))
140 hr
= pFolder
->GetUIObjectOf(hwnd
, 1, &pidlLast
, riid
, NULL
, ppvOut
);
141 if (FAILED_UNEXPECTEDLY(hr
))
147 void CSendToMenu::UnloadAllItems()
149 SENDTO_ITEM
*pItems
= m_pItems
;
153 SENDTO_ITEM
*pCurItem
= pItems
;
154 pItems
= pItems
->pNext
;
159 HRESULT
CSendToMenu::LoadAllItems(HWND hwnd
)
163 CComHeapPtr
<ITEMIDLIST_ABSOLUTE
> pidlSendTo
;
166 HRESULT hr
= GetSpecialFolder(hwnd
, &m_pSendTo
, CSIDL_SENDTO
, &pidlSendTo
);
167 if (FAILED_UNEXPECTEDLY(hr
))
170 CComPtr
<IEnumIDList
> pEnumIDList
;
171 hr
= m_pSendTo
->EnumObjects(hwnd
,
172 SHCONTF_FOLDERS
| SHCONTF_NONFOLDERS
,
174 if (FAILED_UNEXPECTEDLY(hr
))
179 while (pEnumIDList
->Next(1, &child
, NULL
) == S_OK
)
181 CComHeapPtr
<ITEMID_CHILD
> pidlChild(child
);
184 hr
= m_pSendTo
->GetDisplayNameOf(pidlChild
, SHGDN_NORMAL
, &strret
);
185 if (FAILED_UNEXPECTEDLY(hr
))
188 CComHeapPtr
<WCHAR
> pszText
;
189 hr
= StrRetToStrW(&strret
, pidlChild
, &pszText
);
190 if (FAILED_UNEXPECTEDLY(hr
))
193 CComHeapPtr
<ITEMIDLIST_ABSOLUTE
> pidlAbsolute
;
194 pidlAbsolute
.Attach(ILCombine(pidlSendTo
, pidlChild
));
196 SHFILEINFOW fi
= { NULL
};
197 const UINT uFlags
= SHGFI_PIDL
| SHGFI_TYPENAME
|
198 SHGFI_ICON
| SHGFI_SMALLICON
;
199 SHGetFileInfoW(reinterpret_cast<LPWSTR
>(static_cast<PIDLIST_ABSOLUTE
>(pidlAbsolute
)), 0,
200 &fi
, sizeof(fi
), uFlags
);
202 SENDTO_ITEM
*pNewItem
=
203 new SENDTO_ITEM(pidlChild
.Detach(), pszText
.Detach(), fi
.hIcon
);
206 pNewItem
->pNext
= m_pItems
;
214 UINT
CSendToMenu::InsertSendToItems(HMENU hMenu
, UINT idCmdFirst
, UINT Pos
)
216 if (m_pItems
== NULL
)
218 HRESULT hr
= LoadAllItems(NULL
);
219 if (FAILED_UNEXPECTEDLY(hr
))
223 m_idCmdFirst
= idCmdFirst
;
225 UINT idCmd
= idCmdFirst
;
226 for (SENDTO_ITEM
*pCurItem
= m_pItems
; pCurItem
; pCurItem
= pCurItem
->pNext
)
228 const UINT uFlags
= MF_BYPOSITION
| MF_STRING
| MF_ENABLED
;
229 if (InsertMenuW(hMenu
, Pos
, uFlags
, idCmd
, pCurItem
->pszText
))
232 mii
.cbSize
= sizeof(mii
);
233 mii
.fMask
= MIIM_DATA
| MIIM_BITMAP
;
234 mii
.dwItemData
= reinterpret_cast<ULONG_PTR
>(pCurItem
);
235 mii
.hbmpItem
= HBMMENU_CALLBACK
;
236 SetMenuItemInfoW(hMenu
, idCmd
, FALSE
, &mii
);
243 if (idCmd
== idCmdFirst
)
245 CStringW
strNone(MAKEINTRESOURCEW(IDS_NONE
));
246 AppendMenuW(hMenu
, MF_GRAYED
| MF_DISABLED
| MF_STRING
, idCmd
, strNone
);
249 return idCmd
- idCmdFirst
;
252 CSendToMenu::SENDTO_ITEM
*CSendToMenu::FindItemFromIdOffset(UINT IdOffset
)
254 UINT idCmd
= m_idCmdFirst
+ IdOffset
;
256 MENUITEMINFOW mii
= { sizeof(mii
) };
257 mii
.fMask
= MIIM_DATA
;
258 if (GetMenuItemInfoW(m_hSubMenu
, idCmd
, FALSE
, &mii
))
259 return reinterpret_cast<SENDTO_ITEM
*>(mii
.dwItemData
);
261 ERR("GetMenuItemInfoW: %ld\n", GetLastError());
265 HRESULT
CSendToMenu::DoSendToItem(SENDTO_ITEM
*pItem
, LPCMINVOKECOMMANDINFO lpici
)
269 ERR("!m_pDataObject\n");
274 CComPtr
<IDropTarget
> pDropTarget
;
275 hr
= m_pSendTo
->GetUIObjectOf(NULL
, 1, &pItem
->pidlChild
, IID_IDropTarget
,
276 NULL
, (LPVOID
*)&pDropTarget
);
277 if (FAILED_UNEXPECTEDLY(hr
))
280 hr
= DoDrop(m_pDataObject
, pDropTarget
);
281 if (FAILED_UNEXPECTEDLY(hr
))
288 CSendToMenu::QueryContextMenu(HMENU hMenu
,
294 TRACE("%p %p %u %u %u %u\n", this,
295 hMenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
297 HMENU hSubMenu
= CreateMenu();
300 ERR("CreateMenu: %ld\n", GetLastError());
304 UINT cItems
= InsertSendToItems(hSubMenu
, idCmdFirst
, 0);
306 CStringW
strSendTo(MAKEINTRESOURCEW(IDS_SENDTO_MENU
));
308 MENUITEMINFOW mii
= { sizeof(mii
) };
309 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
| MIIM_SUBMENU
;
310 mii
.fType
= MFT_STRING
;
312 mii
.dwTypeData
= strSendTo
.GetBuffer();
313 mii
.cch
= wcslen(mii
.dwTypeData
);
314 mii
.fState
= MFS_ENABLED
;
315 mii
.hSubMenu
= hSubMenu
;
316 if (!InsertMenuItemW(hMenu
, indexMenu
, TRUE
, &mii
))
318 ERR("InsertMenuItemW: %ld\n", GetLastError());
322 HMENU hOldSubMenu
= m_hSubMenu
;
323 m_hSubMenu
= hSubMenu
;
324 DestroyMenu(hOldSubMenu
);
326 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, cItems
);
330 CSendToMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici
)
334 WORD idCmd
= LOWORD(lpici
->lpVerb
);
335 TRACE("idCmd: %d\n", idCmd
);
337 SENDTO_ITEM
*pItem
= FindItemFromIdOffset(idCmd
);
340 hr
= DoSendToItem(pItem
, lpici
);
343 TRACE("CSendToMenu::InvokeCommand %x\n", hr
);
348 CSendToMenu::GetCommandString(UINT_PTR idCmd
,
354 FIXME("%p %lu %u %p %p %u\n", this,
355 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
361 CSendToMenu::HandleMenuMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
367 CSendToMenu::HandleMenuMsg2(UINT uMsg
, WPARAM wParam
, LPARAM lParam
,
370 UINT cxSmall
= GetSystemMetrics(SM_CXSMICON
);
371 UINT cySmall
= GetSystemMetrics(SM_CYSMICON
);
377 MEASUREITEMSTRUCT
* lpmis
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
378 if (!lpmis
|| lpmis
->CtlType
!= ODT_MENU
)
381 UINT cxMenuCheck
= GetSystemMetrics(SM_CXMENUCHECK
);
382 if (lpmis
->itemWidth
< cxMenuCheck
)
383 lpmis
->itemWidth
= cxMenuCheck
;
384 if (lpmis
->itemHeight
< cySmall
)
385 lpmis
->itemHeight
= cySmall
;
393 DRAWITEMSTRUCT
* lpdis
= reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
);
394 if (!lpdis
|| lpdis
->CtlType
!= ODT_MENU
)
397 SENDTO_ITEM
*pItem
= reinterpret_cast<SENDTO_ITEM
*>(lpdis
->itemData
);
400 hIcon
= pItem
->hIcon
;
404 const RECT
& rcItem
= lpdis
->rcItem
;
406 INT y
= lpdis
->rcItem
.top
;
407 y
+= (rcItem
.bottom
- rcItem
.top
- cySmall
) / 2;
408 DrawIconEx(lpdis
->hDC
, x
, y
, hIcon
, cxSmall
, cySmall
,
420 CSendToMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder
,
421 IDataObject
*pdtobj
, HKEY hkeyProgID
)
423 m_pDataObject
= pdtobj
;