[RSHELL]
[reactos.git] / base / shell / rshell / CStartMenu.cpp
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2014 Giannis Adamopoulos
5 *
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.
10 *
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.
15 *
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include "precomp.h"
21
22 //#define TEST_TRACKPOPUPMENU_SUBMENUS
23
24
25 /* NOTE: The following constants *MUST NOT* be changed because
26 they're hardcoded and need to be the exact values
27 in order to get the start menu to work! */
28 #define IDM_PROGRAMS 504
29 #define IDM_FAVORITES 507
30 #define IDM_DOCUMENTS 501
31 #define IDM_SETTINGS 508
32 #define IDM_CONTROLPANEL 505
33 #define IDM_SECURITY 5001
34 #define IDM_NETWORKCONNECTIONS 557
35 #define IDM_PRINTERSANDFAXES 510
36 #define IDM_TASKBARANDSTARTMENU 413
37 #define IDM_SEARCH 520
38 #define IDM_HELPANDSUPPORT 503
39 #define IDM_RUN 401
40 #define IDM_SYNCHRONIZE 553
41 #define IDM_LOGOFF 402
42 #define IDM_DISCONNECT 5000
43 #define IDM_UNDOCKCOMPUTER 410
44 #define IDM_SHUTDOWN 506
45 #define IDM_LASTSTARTMENU_SEPARATOR 450
46
47 /*
48 * TODO:
49 * 1. append the start menu contents from all users
50 * 2. implement the context menu for start menu entries (programs, control panel, network connetions, printers)
51 * 3. filter out programs folder from the shell folder part of the start menu
52 * 4. showing the programs start menu is SLOW compared to windows. this needs some investigation
53 */
54
55 class CShellMenuCallback :
56 public CComObjectRootEx<CComMultiThreadModelNoCS>,
57 public IShellMenuCallback
58 {
59 private:
60
61 HWND m_hwndTray;
62 CComPtr<IShellMenu> m_pShellMenu;
63 CComPtr<IBandSite> m_pBandSite;
64 CComPtr<IDeskBar> m_pDeskBar;
65 CComPtr<ITrayPriv> m_pTrayPriv;
66
67 HRESULT OnInitMenu()
68 {
69 HMENU hmenu;
70 HRESULT hr;
71
72 if (m_pTrayPriv.p)
73 return S_OK;
74
75 hr = IUnknown_GetSite(m_pDeskBar, IID_PPV_ARG(ITrayPriv, &m_pTrayPriv));
76 hr = IUnknown_GetWindow(m_pTrayPriv, &m_hwndTray);
77 hr = m_pTrayPriv->AppendMenuW(&hmenu);
78 #ifndef TEST_TRACKPOPUPMENU_SUBMENUS
79 hr = m_pShellMenu->SetMenu(hmenu, NULL, SMSET_BOTTOM);
80 #else
81 hr = m_pShellMenu->SetMenu(hmenu, m_hwndTray, SMSET_BOTTOM);
82 #endif
83
84 return hr;
85 }
86
87 HRESULT OnGetInfo(LPSMDATA psmd, SMINFO *psminfo)
88 {
89 int iconIndex = 0;
90
91 switch (psmd->uId)
92 {
93 case IDM_PROGRAMS: iconIndex = -20; break;
94 case IDM_FAVORITES: iconIndex = -173; break;
95 case IDM_DOCUMENTS: iconIndex = -21; break;
96 case IDM_SETTINGS: iconIndex = -22; break;
97 case IDM_CONTROLPANEL: iconIndex = -22; break;
98 //case IDM_SECURITY: iconIndex = -21; break;
99 case IDM_NETWORKCONNECTIONS: iconIndex = -257; break;
100 case IDM_PRINTERSANDFAXES: iconIndex = -138; break;
101 case IDM_TASKBARANDSTARTMENU: iconIndex = -40; break;
102 case IDM_SEARCH: iconIndex = -23; break;
103 case IDM_HELPANDSUPPORT: iconIndex = -24; break;
104 case IDM_RUN: iconIndex = -25; break;
105 //case IDM_SYNCHRONIZE: iconIndex = -21; break;
106 case IDM_LOGOFF: iconIndex = -45; break;
107 //case IDM_DISCONNECT: iconIndex = -21; break;
108 //case IDM_UNDOCKCOMPUTER: iconIndex = -21; break;
109 case IDM_SHUTDOWN: iconIndex = -28; break;
110 default:
111 return S_FALSE;
112 }
113
114 if (iconIndex)
115 {
116 if ((psminfo->dwMask & SMIM_ICON) != 0)
117 psminfo->iIcon = Shell_GetCachedImageIndex(L"shell32.dll", iconIndex, FALSE);
118 #ifdef TEST_TRACKPOPUPMENU_SUBMENUS
119 if ((psminfo->dwMask & SMIM_FLAGS) != 0)
120 psminfo->dwFlags |= SMIF_TRACKPOPUP;
121 #endif
122 }
123 return S_OK;
124 }
125
126 HRESULT OnGetSubMenu(LPSMDATA psmd, REFIID iid, void ** pv)
127 {
128 HRESULT hr;
129 int csidl = 0;
130 IShellMenu *pShellMenu;
131
132 switch (psmd->uId)
133 {
134 case IDM_PROGRAMS: csidl = CSIDL_PROGRAMS; break;
135 case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break;
136 case IDM_DOCUMENTS: csidl = CSIDL_RECENT; break;
137 }
138
139 #if USE_SYSTEM_MENUBAND
140 hr = CoCreateInstance(CLSID_MenuBand,
141 NULL,
142 CLSCTX_INPROC_SERVER,
143 IID_PPV_ARG(IShellMenu, &pShellMenu));
144 #else
145 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
146 #endif
147 if (FAILED(hr))
148 return hr;
149 #if WRAP_MENUBAND
150 hr = CMenuBand_Wrapper(pShellMenu, IID_PPV_ARG(IShellMenu, &pShellMenu));
151 if (FAILED(hr))
152 return hr;
153 #endif
154
155 hr = pShellMenu->Initialize(this, 0, ANCESTORDEFAULT, SMINIT_VERTICAL);
156
157 if (csidl)
158 {
159 LPITEMIDLIST pidlStartMenu;
160 IShellFolder *psfDestop, *psfStartMenu;
161
162 hr = SHGetFolderLocation(NULL, csidl, 0, 0, &pidlStartMenu);
163 hr = SHGetDesktopFolder(&psfDestop);
164 hr = psfDestop->BindToObject(pidlStartMenu, NULL, IID_PPV_ARG(IShellFolder, &psfStartMenu));
165
166 hr = pShellMenu->SetShellFolder(psfStartMenu, NULL, NULL, 0);
167 }
168 else
169 {
170 MENUITEMINFO mii;
171 mii.cbSize = sizeof(mii);
172 mii.fMask = MIIM_SUBMENU;
173 if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
174 {
175 hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
176 }
177 }
178 return pShellMenu->QueryInterface(iid, pv);
179 }
180
181 HRESULT OnGetContextMenu(LPSMDATA psmd, REFIID iid, void ** pv)
182 {
183 if (psmd->uId == IDM_PROGRAMS ||
184 psmd->uId == IDM_CONTROLPANEL ||
185 psmd->uId == IDM_NETWORKCONNECTIONS ||
186 psmd->uId == IDM_PRINTERSANDFAXES)
187 {
188 //UNIMPLEMENTED
189 }
190
191 return S_FALSE;
192 }
193
194 HRESULT OnGetObject(LPSMDATA psmd, REFIID iid, void ** pv)
195 {
196 if (IsEqualIID(iid, IID_IShellMenu))
197 return OnGetSubMenu(psmd, iid, pv);
198 else if (IsEqualIID(iid, IID_IContextMenu))
199 return OnGetContextMenu(psmd, iid, pv);
200
201 return S_FALSE;
202 }
203
204 HRESULT OnExec(LPSMDATA psmd)
205 {
206 if (psmd->uId == IDM_CONTROLPANEL)
207 ShellExecuteW(NULL, L"open", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL, NULL, 1);
208 else if (psmd->uId == IDM_NETWORKCONNECTIONS)
209 ShellExecuteW(NULL, L"open", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}", NULL, NULL, 1);
210 else if (psmd->uId == IDM_PRINTERSANDFAXES)
211 ShellExecuteW(NULL, L"open", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}", NULL, NULL, 1);
212 else
213 PostMessageW(m_hwndTray, WM_COMMAND, psmd->uId, 0);
214
215 return S_OK;
216 }
217
218 public:
219
220 DECLARE_NOT_AGGREGATABLE(CShellMenuCallback)
221 DECLARE_PROTECT_FINAL_CONSTRUCT()
222 BEGIN_COM_MAP(CShellMenuCallback)
223 COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback, IShellMenuCallback)
224 END_COM_MAP()
225
226 void Initialize(
227 IShellMenu* pShellMenu,
228 IBandSite* pBandSite,
229 IDeskBar* pDeskBar)
230 {
231 m_pShellMenu.Attach(pShellMenu);
232 m_pBandSite.Attach(pBandSite);
233 m_pDeskBar.Attach(pDeskBar);
234 }
235
236 ~CShellMenuCallback()
237 {
238 m_pShellMenu.Release();
239 m_pBandSite.Release();
240 m_pDeskBar.Release();
241 }
242
243 HRESULT STDMETHODCALLTYPE CallbackSM(
244 LPSMDATA psmd,
245 UINT uMsg,
246 WPARAM wParam,
247 LPARAM lParam)
248 {
249 switch (uMsg)
250 {
251 case SMC_INITMENU:
252 return OnInitMenu();
253 case SMC_GETINFO:
254 return OnGetInfo(psmd, reinterpret_cast<SMINFO*>(lParam));
255 case SMC_GETOBJECT:
256 return OnGetObject(psmd, *reinterpret_cast<IID *>(wParam), reinterpret_cast<void **>(lParam));
257 case SMC_EXEC:
258 return OnExec(psmd);
259 case SMC_SFEXEC:
260 m_pTrayPriv->Execute(psmd->psf, psmd->pidlItem);
261 break;
262 }
263
264 return S_FALSE;
265 }
266 };
267
268 extern "C"
269 HRESULT
270 CStartMenu_Constructor(REFIID riid, void **ppv)
271 {
272 IShellMenu* pShellMenu;
273 IBandSite* pBandSite;
274 IDeskBar* pDeskBar;
275 LPITEMIDLIST pidlStartMenu;
276
277 HRESULT hr;
278 IShellFolder *shellFolder;
279 IShellFolder *psfStartMenu;
280
281 #if USE_SYSTEM_MENUBAND
282 hr = CoCreateInstance(CLSID_MenuBand,
283 NULL,
284 CLSCTX_INPROC_SERVER,
285 IID_PPV_ARG(IShellMenu, &pShellMenu));
286 #else
287 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
288 #endif
289 if (FAILED(hr))
290 return hr;
291 #if WRAP_MENUBAND
292 hr = CMenuBand_Wrapper(pShellMenu, IID_PPV_ARG(IShellMenu, &pShellMenu));
293 if (FAILED(hr))
294 return hr;
295 #endif
296
297 #if USE_SYSTEM_MENUSITE
298 hr = CoCreateInstance(CLSID_MenuBandSite,
299 NULL,
300 CLSCTX_INPROC_SERVER,
301 IID_PPV_ARG(IBandSite, &pBandSite));
302 #else
303 hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
304 #endif
305 if (FAILED(hr))
306 return hr;
307 #if WRAP_MENUSITE
308 hr = CMenuSite_Wrapper(pBandSite, IID_PPV_ARG(IBandSite, &pBandSite));
309 if (FAILED(hr))
310 return hr;
311 #endif
312
313 #if USE_SYSTEM_MENUDESKBAR
314 hr = CoCreateInstance(CLSID_MenuDeskBar,
315 NULL,
316 CLSCTX_INPROC_SERVER,
317 IID_PPV_ARG(IDeskBar, &pDeskBar));
318 #else
319 hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
320 #endif
321 if (FAILED(hr))
322 return hr;
323 #if WRAP_MENUDESKBAR
324 hr = CMenuDeskBar_Wrapper(pDeskBar, IID_PPV_ARG(IDeskBar, &pDeskBar));
325 if (FAILED(hr))
326 return hr;
327 #endif
328
329 CComObject<CShellMenuCallback> *pCallback;
330 hr = CComObject<CShellMenuCallback>::CreateInstance(&pCallback);
331 if (FAILED(hr))
332 return hr;
333 pCallback->AddRef(); // CreateInstance returns object with 0 ref count */
334 pCallback->Initialize(pShellMenu, pBandSite, pDeskBar);
335
336 pShellMenu->Initialize(pCallback, (UINT)-1, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
337 if (FAILED(hr))
338 return hr;
339
340 /* FIXME: Use CLSID_MergedFolder class and IID_IAugmentedShellFolder2 interface here */
341 /* CLSID_MergedFolder 26fdc864-be88-46e7-9235-032d8ea5162e */
342 /* IID_IAugmentedShellFolder2 8db3b3f4-6cfe-11d1-8ae9-00c04fd918d0 */
343 hr = SHGetFolderLocation(NULL, CSIDL_STARTMENU, 0, 0, &pidlStartMenu);
344 hr = SHGetDesktopFolder(&shellFolder);
345 hr = shellFolder->BindToObject(pidlStartMenu, NULL, IID_IShellFolder, (void**) &psfStartMenu);
346
347 hr = pShellMenu->SetShellFolder(psfStartMenu, NULL, NULL, 0);
348
349 hr = pDeskBar->SetClient(pBandSite);
350 if (FAILED(hr))
351 return hr;
352
353 hr = pBandSite->AddBand(pShellMenu);
354 if (FAILED(hr))
355 return hr;
356
357 return pDeskBar->QueryInterface(riid, ppv);
358 }