[SHELL32] CDrivesFolder: Implement the eject and disconnect menu items. CORE-13841
[reactos.git] / dll / win32 / shell32 / CDefViewBckgrndMenu.cpp
1 /*
2 * PROJECT: shell32
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/shell32/CDefViewBckgrndMenu.cpp
5 * PURPOSE: background context menu of the CDefView
6 * PROGRAMMERS: Giannis Adamopoulos
7 */
8
9 #include <precomp.h>
10
11 WINE_DEFAULT_DEBUG_CHANNEL(shell);
12
13 class CDefViewBckgrndMenu :
14 public CComObjectRootEx<CComMultiThreadModelNoCS>,
15 public IContextMenu3,
16 public IObjectWithSite
17 {
18 private:
19 CComPtr<IUnknown> m_site;
20 CComPtr<IShellFolder> m_psf;
21 CComPtr<IContextMenu> m_folderCM;
22
23 UINT m_idCmdFirst;
24 UINT m_LastFolderCMId;
25
26 BOOL _bIsDesktopBrowserMenu();
27 BOOL _bCanPaste();
28 public:
29 CDefViewBckgrndMenu();
30 ~CDefViewBckgrndMenu();
31 HRESULT Initialize(IShellFolder* psf);
32
33 // IContextMenu
34 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
35 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
36 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
37
38 // IContextMenu2
39 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
40
41 // IContextMenu3
42 virtual HRESULT WINAPI HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
43
44 // IObjectWithSite
45 virtual HRESULT STDMETHODCALLTYPE SetSite(IUnknown *pUnkSite);
46 virtual HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, void **ppvSite);
47
48 BEGIN_COM_MAP(CDefViewBckgrndMenu)
49 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
50 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
51 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3)
52 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
53 END_COM_MAP()
54 };
55
56 CDefViewBckgrndMenu::CDefViewBckgrndMenu()
57 {
58 m_idCmdFirst = 0;
59 m_LastFolderCMId = 0;
60 }
61
62 CDefViewBckgrndMenu::~CDefViewBckgrndMenu()
63 {
64 }
65
66 BOOL CDefViewBckgrndMenu::_bIsDesktopBrowserMenu()
67 {
68 if (!m_site)
69 return FALSE;
70
71 /* Get a pointer to the shell browser */
72 CComPtr<IShellView> psv;
73 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
74 if (FAILED_UNEXPECTEDLY(hr))
75 return FALSE;
76
77 FOLDERSETTINGS FolderSettings;
78 hr = psv->GetCurrentInfo(&FolderSettings);
79 if (FAILED_UNEXPECTEDLY(hr))
80 return FALSE;
81
82 return ((FolderSettings.fFlags & FWF_DESKTOP) == FWF_DESKTOP);
83 }
84
85 BOOL CDefViewBckgrndMenu::_bCanPaste()
86 {
87 /* If the folder doesn't have a drop target we can't paste */
88 CComPtr<IDropTarget> pdt;
89 HRESULT hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdt));
90 if (FAILED(hr))
91 return FALSE;
92
93 /* We can only paste if CFSTR_SHELLIDLIST is present in the clipboard */
94 CComPtr<IDataObject> pDataObj;
95 hr = OleGetClipboard(&pDataObj);
96 if (FAILED(hr))
97 return FALSE;
98
99 STGMEDIUM medium;
100 FORMATETC formatetc;
101
102 /* Set the FORMATETC structure*/
103 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
104 hr = pDataObj->GetData(&formatetc, &medium);
105 if (FAILED(hr))
106 return FALSE;
107
108 ReleaseStgMedium(&medium);
109 return TRUE;
110 }
111
112 HRESULT
113 CDefViewBckgrndMenu::Initialize(IShellFolder* psf)
114 {
115 m_psf = psf;
116 return S_OK;
117 }
118
119 HRESULT
120 WINAPI
121 CDefViewBckgrndMenu::SetSite(IUnknown *pUnkSite)
122 {
123 m_site = pUnkSite;
124 return S_OK;
125 }
126
127 HRESULT
128 WINAPI
129 CDefViewBckgrndMenu::GetSite(REFIID riid, void **ppvSite)
130 {
131 if (!m_site)
132 return E_FAIL;
133
134 return m_site->QueryInterface(riid, ppvSite);
135 }
136
137 HRESULT
138 WINAPI
139 CDefViewBckgrndMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
140 {
141 HRESULT hr;
142 HMENU hMenuPart;
143 UINT cIds = 0;
144
145 /* This is something the implementations of IContextMenu should never really do.
146 However CDefViewBckgrndMenu is more or less an overengineering result, its code could really be part of the
147 CDefView. Given this, I think that abusing the interface here is not that bad since only CDefView is the ony
148 user of this class. Here we need to do two things to keep things as simple as possible.
149 First we want the menu part added by the shell folder to be the first to add so as to make as few id translations
150 as possible. Second, we want to add the default part of the background menu without shifted ids, so as
151 to let the CDefView fill some parts like filling the arrange modes or checking the view mode. In order
152 for that to work we need to save idCmdFirst because our caller will pass id offsets to InvokeCommand.
153 This makes it impossible to concatenate the CDefViewBckgrndMenu with other menus since it abuses IContextMenu
154 but as stated above, its sole user is CDefView and should really be that way. */
155 m_idCmdFirst = idCmdFirst;
156
157 /* Query the shell folder to add any items it wants to add in the background context menu */
158 hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IContextMenu, &m_folderCM));
159 if (SUCCEEDED(hr))
160 {
161 hr = m_folderCM->QueryContextMenu(hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
162 if (SUCCEEDED(hr))
163 {
164 m_LastFolderCMId = LOWORD(hr);
165 cIds = m_LastFolderCMId;
166 }
167 else
168 {
169 WARN("QueryContextMenu failed!\n");
170 }
171 }
172 else
173 {
174 WARN("GetUIObjectOf didn't give any context menu!\n");
175 }
176
177 /* Load the default part of the background context menu */
178 hMenuPart = LoadMenuW(shell32_hInstance, L"MENU_002");
179 if (hMenuPart)
180 {
181 /* Don't show the view submenu for the desktop */
182 if (_bIsDesktopBrowserMenu())
183 {
184 DeleteMenu(hMenuPart, FCIDM_SHVIEW_VIEW, MF_BYCOMMAND);
185 }
186
187 /* Disable the paste options if it is not possible */
188 if (!_bCanPaste())
189 {
190 EnableMenuItem(hMenuPart, FCIDM_SHVIEW_INSERT, MF_BYCOMMAND | MF_GRAYED);
191 EnableMenuItem(hMenuPart, FCIDM_SHVIEW_INSERTLINK, MF_BYCOMMAND | MF_GRAYED);
192 }
193
194 /* merge general background context menu in */
195 Shell_MergeMenus(hMenu, GetSubMenu(hMenuPart, 0), indexMenu, 0, idCmdLast, MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR);
196 DestroyMenu(hMenuPart);
197 }
198 else
199 {
200 ERR("Failed to load menu from resource!\n");
201 }
202
203 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
204 }
205
206 HRESULT
207 WINAPI
208 CDefViewBckgrndMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
209 {
210 UINT idCmd = LOWORD(lpcmi->lpVerb);
211 if(HIWORD(lpcmi->lpVerb) != 0 || idCmd < m_LastFolderCMId)
212 {
213 return m_folderCM->InvokeCommand(lpcmi);
214 }
215
216 /* The default part of the background menu doesn't have shifted ids so we need to convert the id offset to the real id */
217 idCmd += m_idCmdFirst;
218
219 /* The commands that are handled by the def view are forwarded to it */
220 switch (idCmd)
221 {
222 case FCIDM_SHVIEW_INSERT:
223 case FCIDM_SHVIEW_INSERTLINK:
224 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd);
225 return m_folderCM->InvokeCommand(lpcmi);
226 case FCIDM_SHVIEW_BIGICON:
227 case FCIDM_SHVIEW_SMALLICON:
228 case FCIDM_SHVIEW_LISTVIEW:
229 case FCIDM_SHVIEW_REPORTVIEW:
230 case 0x30: /* FIX IDS in resource files */
231 case 0x31:
232 case 0x32:
233 case 0x33:
234 case FCIDM_SHVIEW_AUTOARRANGE:
235 case FCIDM_SHVIEW_SNAPTOGRID:
236 case FCIDM_SHVIEW_REFRESH:
237 if (!m_site)
238 return E_FAIL;
239
240 /* Get a pointer to the shell browser */
241 CComPtr<IShellView> psv;
242 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
243 if (FAILED_UNEXPECTEDLY(hr))
244 return hr;
245
246 HWND hwndSV = NULL;
247 if (SUCCEEDED(psv->GetWindow(&hwndSV)))
248 SendMessageW(hwndSV, WM_COMMAND, MAKEWPARAM(idCmd, 0), 0);
249 return S_OK;
250 }
251
252 ERR("Got unknown command id %ul\n", LOWORD(lpcmi->lpVerb));
253 return E_FAIL;
254 }
255
256 HRESULT
257 WINAPI
258 CDefViewBckgrndMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
259 {
260 if (m_folderCM)
261 {
262 return m_folderCM->GetCommandString(idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
263 }
264
265 return E_NOTIMPL;
266 }
267
268 HRESULT
269 WINAPI
270 CDefViewBckgrndMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
271 {
272 if(m_folderCM)
273 {
274 CComPtr<IContextMenu2> pfolderCM2;
275 HRESULT hr = m_folderCM->QueryInterface(IID_PPV_ARG(IContextMenu2, &pfolderCM2));
276 if (FAILED_UNEXPECTEDLY(hr))
277 return hr;
278
279 return pfolderCM2->HandleMenuMsg(uMsg, wParam, lParam);
280 }
281
282 return E_NOTIMPL;
283 }
284
285 HRESULT
286 WINAPI
287 CDefViewBckgrndMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
288 {
289 if(m_folderCM)
290 {
291 CComPtr<IContextMenu3> pfolderCM3;
292 HRESULT hr = m_folderCM->QueryInterface(IID_PPV_ARG(IContextMenu3, &pfolderCM3));
293 if (FAILED_UNEXPECTEDLY(hr))
294 return hr;
295
296 return pfolderCM3->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
297 }
298
299 return E_NOTIMPL;
300 }
301
302
303 HRESULT
304 CDefViewBckgrndMenu_CreateInstance(IShellFolder* psf, REFIID riid, void **ppv)
305 {
306 return ShellObjectCreatorInit<CDefViewBckgrndMenu>(psf, riid, ppv);
307 }