Sync with trunk r62529.
[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_RUN 401
29 #define IDM_LOGOFF 402
30 #define IDM_UNDOCKCOMPUTER 410
31 #define IDM_TASKBARANDSTARTMENU 413
32 #define IDM_LASTSTARTMENU_SEPARATOR 450
33 #define IDM_DOCUMENTS 501
34 #define IDM_HELPANDSUPPORT 503
35 #define IDM_PROGRAMS 504
36 #define IDM_CONTROLPANEL 505
37 #define IDM_SHUTDOWN 506
38 #define IDM_FAVORITES 507
39 #define IDM_SETTINGS 508
40 #define IDM_PRINTERSANDFAXES 510
41 #define IDM_SEARCH 520
42 #define IDM_SYNCHRONIZE 553
43 #define IDM_NETWORKCONNECTIONS 557
44 #define IDM_DISCONNECT 5000
45 #define IDM_SECURITY 5001
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 // Smaller "24x24" icons used for the start menu
94 // The bitmaps are still 32x32, but the image is centered
95 case IDM_FAVORITES: iconIndex = -322; break;
96 case IDM_SEARCH: iconIndex = -323; break;
97 case IDM_HELPANDSUPPORT: iconIndex = -324; break;
98 case IDM_LOGOFF: iconIndex = -325; break;
99 case IDM_PROGRAMS: iconIndex = -326; break;
100 case IDM_DOCUMENTS: iconIndex = -327; break;
101 case IDM_RUN: iconIndex = -328; break;
102 case IDM_SHUTDOWN: iconIndex = -329; break;
103 case IDM_SETTINGS: iconIndex = -330; break;
104
105 case IDM_CONTROLPANEL: iconIndex = -22; break;
106 case IDM_NETWORKCONNECTIONS: iconIndex = -257; break;
107 case IDM_PRINTERSANDFAXES: iconIndex = -138; break;
108 case IDM_TASKBARANDSTARTMENU: iconIndex = -40; break;
109 //case IDM_SECURITY: iconIndex = -21; break;
110 //case IDM_SYNCHRONIZE: iconIndex = -21; break;
111 //case IDM_DISCONNECT: iconIndex = -21; break;
112 //case IDM_UNDOCKCOMPUTER: iconIndex = -21; break;
113 default:
114 return S_FALSE;
115 }
116
117 if (iconIndex)
118 {
119 if ((psminfo->dwMask & SMIM_TYPE) != 0)
120 psminfo->dwType = SMIT_STRING;
121 if ((psminfo->dwMask & SMIM_ICON) != 0)
122 psminfo->iIcon = Shell_GetCachedImageIndex(L"shell32.dll", iconIndex, FALSE);
123 if ((psminfo->dwMask & SMIM_FLAGS) != 0)
124 psminfo->dwFlags |= SMIF_ICON;
125 #ifdef TEST_TRACKPOPUPMENU_SUBMENUS
126 if ((psminfo->dwMask & SMIM_FLAGS) != 0)
127 psminfo->dwFlags |= SMIF_TRACKPOPUP;
128 #endif
129 }
130 else
131 {
132 if ((psminfo->dwMask & SMIM_TYPE) != 0)
133 psminfo->dwType = SMIT_SEPARATOR;
134 }
135 return S_OK;
136 }
137
138 HRESULT OnGetSubMenu(LPSMDATA psmd, REFIID iid, void ** pv)
139 {
140 HRESULT hr;
141 int csidl = 0;
142 IShellMenu *pShellMenu;
143
144 switch (psmd->uId)
145 {
146 case IDM_PROGRAMS: csidl = CSIDL_PROGRAMS; break;
147 case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break;
148 case IDM_DOCUMENTS: csidl = CSIDL_RECENT; break;
149 }
150
151 #if USE_SYSTEM_MENUBAND
152 hr = CoCreateInstance(CLSID_MenuBand,
153 NULL,
154 CLSCTX_INPROC_SERVER,
155 IID_PPV_ARG(IShellMenu, &pShellMenu));
156 #else
157 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
158 #endif
159 if (FAILED_UNEXPECTEDLY(hr))
160 return hr;
161 #if WRAP_MENUBAND
162 hr = CMenuBand_Wrapper(pShellMenu, IID_PPV_ARG(IShellMenu, &pShellMenu));
163 if (FAILED_UNEXPECTEDLY(hr))
164 return hr;
165 #endif
166
167 hr = pShellMenu->Initialize(this, 0, ANCESTORDEFAULT, SMINIT_VERTICAL);
168
169 if (csidl)
170 {
171 LPITEMIDLIST pidlStartMenu;
172 IShellFolder *psfDestop, *psfStartMenu;
173
174 hr = SHGetFolderLocation(NULL, csidl, 0, 0, &pidlStartMenu);
175 hr = SHGetDesktopFolder(&psfDestop);
176 hr = psfDestop->BindToObject(pidlStartMenu, NULL, IID_PPV_ARG(IShellFolder, &psfStartMenu));
177
178 hr = pShellMenu->SetShellFolder(psfStartMenu, NULL, NULL, 0);
179 }
180 else
181 {
182 MENUITEMINFO mii;
183 mii.cbSize = sizeof(mii);
184 mii.fMask = MIIM_SUBMENU;
185 if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
186 {
187 hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
188 }
189 }
190 return pShellMenu->QueryInterface(iid, pv);
191 }
192
193 HRESULT OnGetContextMenu(LPSMDATA psmd, REFIID iid, void ** pv)
194 {
195 if (psmd->uId == IDM_PROGRAMS ||
196 psmd->uId == IDM_CONTROLPANEL ||
197 psmd->uId == IDM_NETWORKCONNECTIONS ||
198 psmd->uId == IDM_PRINTERSANDFAXES)
199 {
200 //UNIMPLEMENTED
201 }
202
203 return S_FALSE;
204 }
205
206 HRESULT OnGetObject(LPSMDATA psmd, REFIID iid, void ** pv)
207 {
208 if (IsEqualIID(iid, IID_IShellMenu))
209 return OnGetSubMenu(psmd, iid, pv);
210 else if (IsEqualIID(iid, IID_IContextMenu))
211 return OnGetContextMenu(psmd, iid, pv);
212
213 return S_FALSE;
214 }
215
216 HRESULT OnExec(LPSMDATA psmd)
217 {
218 if (psmd->uId == IDM_CONTROLPANEL)
219 ShellExecuteW(NULL, L"open", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL, NULL, 1);
220 else if (psmd->uId == IDM_NETWORKCONNECTIONS)
221 ShellExecuteW(NULL, L"open", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}", NULL, NULL, 1);
222 else if (psmd->uId == IDM_PRINTERSANDFAXES)
223 ShellExecuteW(NULL, L"open", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}", NULL, NULL, 1);
224 else
225 PostMessageW(m_hwndTray, WM_COMMAND, psmd->uId, 0);
226
227 return S_OK;
228 }
229
230 public:
231
232 DECLARE_NOT_AGGREGATABLE(CShellMenuCallback)
233 DECLARE_PROTECT_FINAL_CONSTRUCT()
234 BEGIN_COM_MAP(CShellMenuCallback)
235 COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback, IShellMenuCallback)
236 END_COM_MAP()
237
238 void Initialize(
239 IShellMenu* pShellMenu,
240 IBandSite* pBandSite,
241 IDeskBar* pDeskBar)
242 {
243 m_pShellMenu.Attach(pShellMenu);
244 m_pBandSite.Attach(pBandSite);
245 m_pDeskBar.Attach(pDeskBar);
246 }
247
248 ~CShellMenuCallback()
249 {
250 m_pShellMenu.Release();
251 m_pBandSite.Release();
252 m_pDeskBar.Release();
253 }
254
255 HRESULT STDMETHODCALLTYPE CallbackSM(
256 LPSMDATA psmd,
257 UINT uMsg,
258 WPARAM wParam,
259 LPARAM lParam)
260 {
261 switch (uMsg)
262 {
263 case SMC_INITMENU:
264 return OnInitMenu();
265 case SMC_GETINFO:
266 return OnGetInfo(psmd, reinterpret_cast<SMINFO*>(lParam));
267 case SMC_GETOBJECT:
268 return OnGetObject(psmd, *reinterpret_cast<IID *>(wParam), reinterpret_cast<void **>(lParam));
269 case SMC_EXEC:
270 return OnExec(psmd);
271 case SMC_SFEXEC:
272 m_pTrayPriv->Execute(psmd->psf, psmd->pidlItem);
273 break;
274 }
275
276 return S_FALSE;
277 }
278 };
279
280 extern "C"
281 HRESULT WINAPI
282 CStartMenu_Constructor(REFIID riid, void **ppv)
283 {
284 IShellMenu* pShellMenu;
285 IBandSite* pBandSite;
286 IDeskBar* pDeskBar;
287
288 HRESULT hr;
289 IShellFolder *shellFolder;
290
291 LPITEMIDLIST pidlStartMenuUser;
292 IShellFolder *psfStartMenuUser;
293
294 #if MERGE_FOLDERS
295 LPITEMIDLIST pidlStartMenuAll;
296 IShellFolder *psfStartMenuAll;
297 #endif
298
299 #if USE_SYSTEM_MENUBAND
300 hr = CoCreateInstance(CLSID_MenuBand,
301 NULL,
302 CLSCTX_INPROC_SERVER,
303 IID_PPV_ARG(IShellMenu, &pShellMenu));
304 #else
305 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
306 #endif
307 if (FAILED_UNEXPECTEDLY(hr))
308 return hr;
309 #if WRAP_MENUBAND
310 hr = CMenuBand_Wrapper(pShellMenu, IID_PPV_ARG(IShellMenu, &pShellMenu));
311 if (FAILED_UNEXPECTEDLY(hr))
312 return hr;
313 #endif
314
315 #if USE_SYSTEM_MENUSITE
316 hr = CoCreateInstance(CLSID_MenuBandSite,
317 NULL,
318 CLSCTX_INPROC_SERVER,
319 IID_PPV_ARG(IBandSite, &pBandSite));
320 #else
321 hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
322 #endif
323 if (FAILED_UNEXPECTEDLY(hr))
324 return hr;
325 #if WRAP_MENUSITE
326 hr = CMenuSite_Wrapper(pBandSite, IID_PPV_ARG(IBandSite, &pBandSite));
327 if (FAILED_UNEXPECTEDLY(hr))
328 return hr;
329 #endif
330
331 #if USE_SYSTEM_MENUDESKBAR
332 hr = CoCreateInstance(CLSID_MenuDeskBar,
333 NULL,
334 CLSCTX_INPROC_SERVER,
335 IID_PPV_ARG(IDeskBar, &pDeskBar));
336 #else
337 hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
338 #endif
339 if (FAILED_UNEXPECTEDLY(hr))
340 return hr;
341 #if WRAP_MENUDESKBAR
342 hr = CMenuDeskBar_Wrapper(pDeskBar, IID_PPV_ARG(IDeskBar, &pDeskBar));
343 if (FAILED_UNEXPECTEDLY(hr))
344 return hr;
345 #endif
346
347 CComObject<CShellMenuCallback> *pCallback;
348 hr = CComObject<CShellMenuCallback>::CreateInstance(&pCallback);
349 if (FAILED_UNEXPECTEDLY(hr))
350 return hr;
351 pCallback->AddRef(); // CreateInstance returns object with 0 ref count */
352 pCallback->Initialize(pShellMenu, pBandSite, pDeskBar);
353
354 pShellMenu->Initialize(pCallback, (UINT)-1, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
355 if (FAILED_UNEXPECTEDLY(hr))
356 return hr;
357
358 hr = SHGetDesktopFolder(&shellFolder);
359
360 /* FIXME: Use CLSID_MergedFolder class and IID_IAugmentedShellFolder2 interface here */
361 /* CLSID_MergedFolder 26fdc864-be88-46e7-9235-032d8ea5162e */
362 /* IID_IAugmentedShellFolder2 8db3b3f4-6cfe-11d1-8ae9-00c04fd918d0 */
363 hr = SHGetFolderLocation(NULL, CSIDL_STARTMENU, 0, 0, &pidlStartMenuUser);
364 hr = shellFolder->BindToObject(pidlStartMenuUser, NULL, IID_IShellFolder, (void**) &psfStartMenuUser);
365
366 #if MERGE_FOLDERS
367 hr = SHGetFolderLocation(NULL, CSIDL_COMMON_STARTMENU, 0, 0, &pidlStartMenuAll);
368 hr = shellFolder->BindToObject(pidlStartMenuAll, NULL, IID_IShellFolder, (void**) &psfStartMenuAll);
369
370 IShellFolder * psfMerged;
371 hr = CMergedFolder_Constructor(psfStartMenuUser, psfStartMenuAll, IID_PPV_ARG(IShellFolder, &psfMerged));
372 if (FAILED_UNEXPECTEDLY(hr))
373 return hr;
374
375 hr = pShellMenu->SetShellFolder(psfMerged, NULL, NULL, 0);
376 if (FAILED_UNEXPECTEDLY(hr))
377 return hr;
378 #else
379 hr = pShellMenu->SetShellFolder(psfStartMenuUser, NULL, NULL, 0);
380 if (FAILED_UNEXPECTEDLY(hr))
381 return hr;
382 #endif
383
384 hr = pDeskBar->SetClient(pBandSite);
385 if (FAILED_UNEXPECTEDLY(hr))
386 return hr;
387
388 hr = pBandSite->AddBand(pShellMenu);
389 if (FAILED_UNEXPECTEDLY(hr))
390 return hr;
391
392 return pDeskBar->QueryInterface(riid, ppv);
393 }