9889a950629abd1f17d241368dc792d910177ccc
[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 #include "CMergedFolder.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(CStartMenu);
25
26 // TODO: declare these GUIDs and interfaces in the right place (whatever that may be)
27 IID IID_IAugmentedShellFolder = { 0x91EA3F8C, 0xC99B, 0x11D0, { 0x98, 0x15, 0x00, 0xC0, 0x4F, 0xD9, 0x19, 0x72 } };
28 IID IID_IAugmentedShellFolder2 = { 0x8DB3B3F4, 0x6CFE, 0x11D1, { 0x8A, 0xE9, 0x00, 0xC0, 0x4F, 0xD9, 0x18, 0xD0 } };
29 CLSID CLSID_MergedFolder = { 0x26FDC864, 0xBE88, 0x46E7, { 0x92, 0x35, 0x03, 0x2D, 0x8E, 0xA5, 0x16, 0x2E } };
30
31 //#define TEST_TRACKPOPUPMENU_SUBMENUS
32
33
34 /* NOTE: The following constants *MUST NOT* be changed because
35 they're hardcoded and need to be the exact values
36 in order to get the start menu to work! */
37 #define IDM_RUN 401
38 #define IDM_LOGOFF 402
39 #define IDM_UNDOCKCOMPUTER 410
40 #define IDM_TASKBARANDSTARTMENU 413
41 #define IDM_LASTSTARTMENU_SEPARATOR 450
42 #define IDM_DOCUMENTS 501
43 #define IDM_HELPANDSUPPORT 503
44 #define IDM_PROGRAMS 504
45 #define IDM_CONTROLPANEL 505
46 #define IDM_SHUTDOWN 506
47 #define IDM_FAVORITES 507
48 #define IDM_SETTINGS 508
49 #define IDM_PRINTERSANDFAXES 510
50 #define IDM_SEARCH 520
51 #define IDM_SYNCHRONIZE 553
52 #define IDM_NETWORKCONNECTIONS 557
53 #define IDM_DISCONNECT 5000
54 #define IDM_SECURITY 5001
55
56 /*
57 * TODO:
58 * 1. append the start menu contents from all users
59 * 2. implement the context menu for start menu entries (programs, control panel, network connetions, printers)
60 * 3. filter out programs folder from the shell folder part of the start menu
61 * 4. showing the programs start menu is SLOW compared to windows. this needs some investigation
62 */
63
64 class CShellMenuCallback :
65 public CComObjectRootEx<CComMultiThreadModelNoCS>,
66 public IShellMenuCallback
67 {
68 private:
69
70 HWND m_hwndTray;
71 CComPtr<IShellMenu> m_pShellMenu;
72 CComPtr<IBandSite> m_pBandSite;
73 CComPtr<IDeskBar> m_pDeskBar;
74 CComPtr<ITrayPriv> m_pTrayPriv;
75 CComPtr<IShellFolder> m_psfPrograms;
76
77 LPITEMIDLIST m_pidlPrograms;
78
79 HRESULT OnInitMenu()
80 {
81 HMENU hmenu;
82 HRESULT hr;
83
84 if (m_pTrayPriv.p)
85 return S_OK;
86
87 hr = IUnknown_GetSite(m_pDeskBar, IID_PPV_ARG(ITrayPriv, &m_pTrayPriv));
88 hr = IUnknown_GetWindow(m_pTrayPriv, &m_hwndTray);
89 hr = m_pTrayPriv->AppendMenuW(&hmenu);
90 #ifndef TEST_TRACKPOPUPMENU_SUBMENUS
91 hr = m_pShellMenu->SetMenu(hmenu, NULL, SMSET_BOTTOM);
92 #else
93 hr = m_pShellMenu->SetMenu(hmenu, m_hwndTray, SMSET_BOTTOM);
94 #endif
95
96 return hr;
97 }
98
99 HRESULT OnGetInfo(LPSMDATA psmd, SMINFO *psminfo)
100 {
101 int iconIndex = 0;
102
103 switch (psmd->uId)
104 {
105 // Smaller "24x24" icons used for the start menu
106 // The bitmaps are still 32x32, but the image is centered
107 case IDM_FAVORITES: iconIndex = -322; break;
108 case IDM_SEARCH: iconIndex = -323; break;
109 case IDM_HELPANDSUPPORT: iconIndex = -324; break;
110 case IDM_LOGOFF: iconIndex = -325; break;
111 case IDM_PROGRAMS: iconIndex = -326; break;
112 case IDM_DOCUMENTS: iconIndex = -327; break;
113 case IDM_RUN: iconIndex = -328; break;
114 case IDM_SHUTDOWN: iconIndex = -329; break;
115 case IDM_SETTINGS: iconIndex = -330; break;
116
117 case IDM_CONTROLPANEL: iconIndex = -22; break;
118 case IDM_NETWORKCONNECTIONS: iconIndex = -257; break;
119 case IDM_PRINTERSANDFAXES: iconIndex = -138; break;
120 case IDM_TASKBARANDSTARTMENU: iconIndex = -40; break;
121 //case IDM_SECURITY: iconIndex = -21; break;
122 //case IDM_SYNCHRONIZE: iconIndex = -21; break;
123 //case IDM_DISCONNECT: iconIndex = -21; break;
124 //case IDM_UNDOCKCOMPUTER: iconIndex = -21; break;
125 default:
126 return S_FALSE;
127 }
128
129 if (iconIndex)
130 {
131 if ((psminfo->dwMask & SMIM_TYPE) != 0)
132 psminfo->dwType = SMIT_STRING;
133 if ((psminfo->dwMask & SMIM_ICON) != 0)
134 psminfo->iIcon = Shell_GetCachedImageIndex(L"shell32.dll", iconIndex, FALSE);
135 if ((psminfo->dwMask & SMIM_FLAGS) != 0)
136 psminfo->dwFlags |= SMIF_ICON;
137 #ifdef TEST_TRACKPOPUPMENU_SUBMENUS
138 if ((psminfo->dwMask & SMIM_FLAGS) != 0)
139 psminfo->dwFlags |= SMIF_TRACKPOPUP;
140 #endif
141 }
142 else
143 {
144 if ((psminfo->dwMask & SMIM_TYPE) != 0)
145 psminfo->dwType = SMIT_SEPARATOR;
146 }
147 return S_OK;
148 }
149
150 HRESULT OnGetSubMenu(LPSMDATA psmd, REFIID iid, void ** pv)
151 {
152 HRESULT hr;
153 int csidl = 0;
154 IShellMenu *pShellMenu;
155
156 #if USE_SYSTEM_MENUBAND
157 hr = CoCreateInstance(CLSID_MenuBand,
158 NULL,
159 CLSCTX_INPROC_SERVER,
160 IID_PPV_ARG(IShellMenu, &pShellMenu));
161 #else
162 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
163 #endif
164 if (FAILED_UNEXPECTEDLY(hr))
165 return hr;
166
167 hr = pShellMenu->Initialize(this, 0, ANCESTORDEFAULT, SMINIT_VERTICAL);
168
169 switch (psmd->uId)
170 {
171 case IDM_PROGRAMS: csidl = CSIDL_PROGRAMS; break;
172 case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break;
173 case IDM_DOCUMENTS: csidl = CSIDL_RECENT; break;
174 }
175
176 if (csidl)
177 {
178 IShellFolder *psfStartMenu;
179
180 if (csidl == CSIDL_PROGRAMS && m_psfPrograms)
181 {
182 psfStartMenu = m_psfPrograms;
183 }
184 else
185 {
186 LPITEMIDLIST pidlStartMenu;
187 IShellFolder *psfDestop;
188 hr = SHGetFolderLocation(NULL, csidl, 0, 0, &pidlStartMenu);
189 hr = SHGetDesktopFolder(&psfDestop);
190 hr = psfDestop->BindToObject(pidlStartMenu, NULL, IID_PPV_ARG(IShellFolder, &psfStartMenu));
191 }
192
193 hr = pShellMenu->SetShellFolder(psfStartMenu, NULL, NULL, 0);
194 }
195 else
196 {
197 MENUITEMINFO mii;
198 mii.cbSize = sizeof(mii);
199 mii.fMask = MIIM_SUBMENU;
200 if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
201 {
202 hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
203 }
204 }
205 return pShellMenu->QueryInterface(iid, pv);
206 }
207
208 HRESULT OnGetContextMenu(LPSMDATA psmd, REFIID iid, void ** pv)
209 {
210 if (psmd->uId == IDM_PROGRAMS ||
211 psmd->uId == IDM_CONTROLPANEL ||
212 psmd->uId == IDM_NETWORKCONNECTIONS ||
213 psmd->uId == IDM_PRINTERSANDFAXES)
214 {
215 //UNIMPLEMENTED
216 }
217
218 return S_FALSE;
219 }
220
221 HRESULT OnGetObject(LPSMDATA psmd, REFIID iid, void ** pv)
222 {
223 if (IsEqualIID(iid, IID_IShellMenu))
224 return OnGetSubMenu(psmd, iid, pv);
225 else if (IsEqualIID(iid, IID_IContextMenu))
226 return OnGetContextMenu(psmd, iid, pv);
227
228 return S_FALSE;
229 }
230
231 HRESULT OnExec(LPSMDATA psmd)
232 {
233 // HACK: Instead of running explorer.exe with the path, we should be using ShellExecute to "open" the path directly!
234 // Remove once ShellExecute can handle CLSID path components.
235
236 if (psmd->uId == IDM_CONTROLPANEL)
237 ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL, SW_SHOWNORMAL);
238 else if (psmd->uId == IDM_NETWORKCONNECTIONS)
239 ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}", NULL, SW_SHOWNORMAL);
240 else if (psmd->uId == IDM_PRINTERSANDFAXES)
241 ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}", NULL, SW_SHOWNORMAL);
242 else
243 PostMessageW(m_hwndTray, WM_COMMAND, psmd->uId, 0);
244
245 return S_OK;
246 }
247
248 public:
249
250 DECLARE_NOT_AGGREGATABLE(CShellMenuCallback)
251 DECLARE_PROTECT_FINAL_CONSTRUCT()
252 BEGIN_COM_MAP(CShellMenuCallback)
253 COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback, IShellMenuCallback)
254 END_COM_MAP()
255
256 void Initialize(
257 IShellMenu* pShellMenu,
258 IBandSite* pBandSite,
259 IDeskBar* pDeskBar)
260 {
261 m_pShellMenu = pShellMenu;
262 m_pBandSite = pBandSite;
263 m_pDeskBar = pDeskBar;
264 }
265
266 ~CShellMenuCallback()
267 {
268 }
269
270 HRESULT _SetProgramsFolder(IShellFolder * psf, LPITEMIDLIST pidl)
271 {
272 m_psfPrograms = psf;
273 m_pidlPrograms = pidl;
274 return S_OK;
275 }
276
277 HRESULT STDMETHODCALLTYPE CallbackSM(
278 LPSMDATA psmd,
279 UINT uMsg,
280 WPARAM wParam,
281 LPARAM lParam)
282 {
283 switch (uMsg)
284 {
285 case SMC_INITMENU:
286 return OnInitMenu();
287 case SMC_GETINFO:
288 return OnGetInfo(psmd, reinterpret_cast<SMINFO*>(lParam));
289 case SMC_GETOBJECT:
290 return OnGetObject(psmd, *reinterpret_cast<IID *>(wParam), reinterpret_cast<void **>(lParam));
291 case SMC_EXEC:
292 return OnExec(psmd);
293 case SMC_SFEXEC:
294 m_pTrayPriv->Execute(psmd->psf, psmd->pidlItem);
295 break;
296 case 0x10000000: // _FilterPIDL from CMenuSFToolbar
297 if (psmd->psf->CompareIDs(0, psmd->pidlItem, m_pidlPrograms) == 0)
298 return S_FALSE;
299 return S_OK;
300 }
301
302 return S_FALSE;
303 }
304 };
305
306 HRESULT BindToDesktop(LPCITEMIDLIST pidl, IShellFolder ** ppsfResult)
307 {
308 HRESULT hr;
309 CComPtr<IShellFolder> psfDesktop;
310
311 *ppsfResult = NULL;
312
313 hr = SHGetDesktopFolder(&psfDesktop);
314 if (FAILED(hr))
315 return hr;
316
317 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, ppsfResult));
318
319 return hr;
320 }
321
322 static HRESULT GetStartMenuFolder(IShellFolder ** ppsfStartMenu)
323 {
324 HRESULT hr;
325 LPITEMIDLIST pidlUserStartMenu;
326 LPITEMIDLIST pidlCommonStartMenu;
327 CComPtr<IShellFolder> psfUserStartMenu;
328 CComPtr<IShellFolder> psfCommonStartMenu;
329 CComPtr<IAugmentedShellFolder> pasf;
330
331 *ppsfStartMenu = NULL;
332
333 hr = SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &pidlUserStartMenu);
334 if (FAILED(hr))
335 {
336 WARN("Failed to get the USER start menu folder. Trying to run with just the COMMON one.\n");
337
338 hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommonStartMenu);
339 if (FAILED_UNEXPECTEDLY(hr))
340 return hr;
341
342 TRACE("COMMON start menu obtained.\n");
343 hr = BindToDesktop(pidlCommonStartMenu, ppsfStartMenu);
344 ILFree(pidlCommonStartMenu);
345 return hr;
346 }
347
348 hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommonStartMenu);
349 if (FAILED_UNEXPECTEDLY(hr))
350 {
351 WARN("Failed to get the COMMON start menu folder. Will use only the USER contents.\n");
352 hr = BindToDesktop(pidlUserStartMenu, ppsfStartMenu);
353 ILFree(pidlUserStartMenu);
354 return hr;
355 }
356
357 TRACE("Both COMMON and USER statr menu folders obtained, merging them...\n");
358
359 hr = BindToDesktop(pidlUserStartMenu, &psfUserStartMenu);
360 if (FAILED_UNEXPECTEDLY(hr))
361 return hr;
362
363 hr = BindToDesktop(pidlCommonStartMenu, &psfCommonStartMenu);
364 if (FAILED_UNEXPECTEDLY(hr))
365 return hr;
366
367 #if !USE_SYSTEM_MERGED_FOLDERS
368 hr = CMergedFolder_Constructor(IID_PPV_ARG(IAugmentedShellFolder, &pasf));
369 #else
370 hr = CoCreateInstance(CLSID_MergedFolder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IAugmentedShellFolder, &pasf));
371 #endif
372 if (FAILED_UNEXPECTEDLY(hr))
373 {
374 *ppsfStartMenu = psfUserStartMenu.Detach();
375 ILFree(pidlCommonStartMenu);
376 ILFree(pidlUserStartMenu);
377 return hr;
378 }
379
380 hr = pasf->AddNameSpace(NULL, psfUserStartMenu, pidlUserStartMenu, 0xFF00);
381 if (FAILED_UNEXPECTEDLY(hr))
382 return hr;
383
384 hr = pasf->AddNameSpace(NULL, psfCommonStartMenu, pidlCommonStartMenu, 0);
385 if (FAILED_UNEXPECTEDLY(hr))
386 return hr;
387
388 hr = pasf->QueryInterface(IID_PPV_ARG(IShellFolder, ppsfStartMenu));
389 pasf.Release();
390
391 ILFree(pidlCommonStartMenu);
392 ILFree(pidlUserStartMenu);
393
394 return hr;
395 }
396
397 extern "C"
398 HRESULT WINAPI
399 CStartMenu_Constructor(REFIID riid, void **ppv)
400 {
401 CComPtr<IShellMenu> pShellMenu;
402 CComPtr<IBandSite> pBandSite;
403 CComPtr<IDeskBar> pDeskBar;
404
405 HRESULT hr;
406 IShellFolder * psf;
407
408 LPITEMIDLIST pidlProgramsAbsolute;
409 LPITEMIDLIST pidlPrograms;
410 CComPtr<IShellFolder> psfPrograms;
411
412 #if USE_SYSTEM_MENUBAND
413 hr = CoCreateInstance(CLSID_MenuBand,
414 NULL,
415 CLSCTX_INPROC_SERVER,
416 IID_PPV_ARG(IShellMenu, &pShellMenu));
417 #else
418 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
419 #endif
420 if (FAILED_UNEXPECTEDLY(hr))
421 return hr;
422
423 #if USE_SYSTEM_MENUSITE
424 hr = CoCreateInstance(CLSID_MenuBandSite,
425 NULL,
426 CLSCTX_INPROC_SERVER,
427 IID_PPV_ARG(IBandSite, &pBandSite));
428 #else
429 hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
430 #endif
431 if (FAILED_UNEXPECTEDLY(hr))
432 return hr;
433
434 #if USE_SYSTEM_MENUDESKBAR
435 hr = CoCreateInstance(CLSID_MenuDeskBar,
436 NULL,
437 CLSCTX_INPROC_SERVER,
438 IID_PPV_ARG(IDeskBar, &pDeskBar));
439 #else
440 hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
441 #endif
442 if (FAILED_UNEXPECTEDLY(hr))
443 return hr;
444
445 CComObject<CShellMenuCallback> *pCallback;
446 hr = CComObject<CShellMenuCallback>::CreateInstance(&pCallback);
447 if (FAILED_UNEXPECTEDLY(hr))
448 return hr;
449 pCallback->AddRef(); // CreateInstance returns object with 0 ref count */
450 pCallback->Initialize(pShellMenu, pBandSite, pDeskBar);
451
452 pShellMenu->Initialize(pCallback, (UINT) -1, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
453 if (FAILED_UNEXPECTEDLY(hr))
454 return hr;
455
456 hr = GetStartMenuFolder(&psf);
457 if (FAILED_UNEXPECTEDLY(hr))
458 return hr;
459
460 hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlProgramsAbsolute);
461 if (FAILED(hr))
462 {
463 WARN("USER Programs folder not found.");
464 hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlProgramsAbsolute);
465 if (FAILED_UNEXPECTEDLY(hr))
466 return hr;
467 }
468
469 pidlPrograms = ILClone(ILFindLastID(pidlProgramsAbsolute));
470 ILFree(pidlProgramsAbsolute);
471
472 hr = psf->BindToObject(pidlPrograms, NULL, IID_PPV_ARG(IShellFolder, &psfPrograms));
473 if (FAILED_UNEXPECTEDLY(hr))
474 return hr;
475
476 hr = pCallback->_SetProgramsFolder(psfPrograms, pidlPrograms);
477 if (FAILED_UNEXPECTEDLY(hr))
478 return hr;
479
480 hr = pShellMenu->SetShellFolder(psf, NULL, NULL, 0);
481 if (FAILED_UNEXPECTEDLY(hr))
482 return hr;
483
484 hr = pDeskBar->SetClient(pBandSite);
485 if (FAILED_UNEXPECTEDLY(hr))
486 return hr;
487
488 hr = pBandSite->AddBand(pShellMenu);
489 if (FAILED_UNEXPECTEDLY(hr))
490 return hr;
491
492 return pDeskBar->QueryInterface(riid, ppv);
493 }