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