[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 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 if (FAILED_UNEXPECTEDLY(hr))
89 return hr;
90
91 hr = IUnknown_GetWindow(m_pTrayPriv, &m_hwndTray);
92 if (FAILED_UNEXPECTEDLY(hr))
93 return hr;
94
95 hr = m_pTrayPriv->AppendMenuW(&hmenu);
96 if (FAILED_UNEXPECTEDLY(hr))
97 return hr;
98
99 hr = m_pShellMenu->SetMenu(hmenu, NULL, SMSET_BOTTOM);
100 if (FAILED_UNEXPECTEDLY(hr))
101 return hr;
102
103 return hr;
104 }
105
106 HRESULT OnGetInfo(LPSMDATA psmd, SMINFO *psminfo)
107 {
108 int iconIndex = 0;
109
110 switch (psmd->uId)
111 {
112 // Smaller "24x24" icons used for the start menu
113 // The bitmaps are still 32x32, but the image is centered
114 case IDM_FAVORITES: iconIndex = -322; break;
115 case IDM_SEARCH: iconIndex = -323; break;
116 case IDM_HELPANDSUPPORT: iconIndex = -324; break;
117 case IDM_LOGOFF: iconIndex = -325; break;
118 case IDM_PROGRAMS: iconIndex = -326; break;
119 case IDM_DOCUMENTS: iconIndex = -327; break;
120 case IDM_RUN: iconIndex = -328; break;
121 case IDM_SHUTDOWN: iconIndex = -329; break;
122 case IDM_SETTINGS: iconIndex = -330; break;
123
124 case IDM_CONTROLPANEL: iconIndex = -22; break;
125 case IDM_NETWORKCONNECTIONS: iconIndex = -257; break;
126 case IDM_PRINTERSANDFAXES: iconIndex = -138; break;
127 case IDM_TASKBARANDSTARTMENU: iconIndex = -40; break;
128 //case IDM_SECURITY: iconIndex = -21; break;
129 //case IDM_SYNCHRONIZE: iconIndex = -21; break;
130 //case IDM_DISCONNECT: iconIndex = -21; break;
131 //case IDM_UNDOCKCOMPUTER: iconIndex = -21; break;
132 default:
133 return S_FALSE;
134 }
135
136 if (iconIndex)
137 {
138 if ((psminfo->dwMask & SMIM_TYPE) != 0)
139 psminfo->dwType = SMIT_STRING;
140 if ((psminfo->dwMask & SMIM_ICON) != 0)
141 psminfo->iIcon = Shell_GetCachedImageIndex(L"shell32.dll", iconIndex, FALSE);
142 if ((psminfo->dwMask & SMIM_FLAGS) != 0)
143 psminfo->dwFlags |= SMIF_ICON;
144 #ifdef TEST_TRACKPOPUPMENU_SUBMENUS
145 if ((psminfo->dwMask & SMIM_FLAGS) != 0)
146 psminfo->dwFlags |= SMIF_TRACKPOPUP;
147 #endif
148 }
149 else
150 {
151 if ((psminfo->dwMask & SMIM_TYPE) != 0)
152 psminfo->dwType = SMIT_SEPARATOR;
153 }
154 return S_OK;
155 }
156
157 HRESULT OnGetSubMenu(LPSMDATA psmd, REFIID iid, void ** pv)
158 {
159 HRESULT hr;
160 int csidl = 0;
161 IShellMenu *pShellMenu;
162
163 #if USE_SYSTEM_MENUBAND
164 hr = CoCreateInstance(CLSID_MenuBand,
165 NULL,
166 CLSCTX_INPROC_SERVER,
167 IID_PPV_ARG(IShellMenu, &pShellMenu));
168 #else
169 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
170 #endif
171 if (FAILED_UNEXPECTEDLY(hr))
172 return hr;
173
174 hr = pShellMenu->Initialize(this, 0, ANCESTORDEFAULT, SMINIT_VERTICAL);
175
176 switch (psmd->uId)
177 {
178 case IDM_PROGRAMS: csidl = CSIDL_PROGRAMS; break;
179 case IDM_FAVORITES: csidl = CSIDL_FAVORITES; break;
180 case IDM_DOCUMENTS: csidl = CSIDL_RECENT; break;
181 }
182
183 if (csidl)
184 {
185 IShellFolder *psfStartMenu;
186
187 if (csidl == CSIDL_PROGRAMS && m_psfPrograms)
188 {
189 psfStartMenu = m_psfPrograms;
190 }
191 else
192 {
193 LPITEMIDLIST pidlStartMenu;
194 IShellFolder *psfDestop;
195 hr = SHGetFolderLocation(NULL, csidl, 0, 0, &pidlStartMenu);
196 hr = SHGetDesktopFolder(&psfDestop);
197 hr = psfDestop->BindToObject(pidlStartMenu, NULL, IID_PPV_ARG(IShellFolder, &psfStartMenu));
198 }
199
200 hr = pShellMenu->SetShellFolder(psfStartMenu, NULL, NULL, 0);
201 }
202 else
203 {
204 MENUITEMINFO mii;
205 mii.cbSize = sizeof(mii);
206 mii.fMask = MIIM_SUBMENU;
207 if (GetMenuItemInfoW(psmd->hmenu, psmd->uId, FALSE, &mii))
208 {
209 hr = pShellMenu->SetMenu(mii.hSubMenu, NULL, SMSET_BOTTOM);
210 }
211 }
212 return pShellMenu->QueryInterface(iid, pv);
213 }
214
215 HRESULT OnGetContextMenu(LPSMDATA psmd, REFIID iid, void ** pv)
216 {
217 if (psmd->uId == IDM_PROGRAMS ||
218 psmd->uId == IDM_CONTROLPANEL ||
219 psmd->uId == IDM_NETWORKCONNECTIONS ||
220 psmd->uId == IDM_PRINTERSANDFAXES)
221 {
222 //UNIMPLEMENTED
223 }
224
225 return S_FALSE;
226 }
227
228 HRESULT OnGetObject(LPSMDATA psmd, REFIID iid, void ** pv)
229 {
230 if (IsEqualIID(iid, IID_IShellMenu))
231 return OnGetSubMenu(psmd, iid, pv);
232 else if (IsEqualIID(iid, IID_IContextMenu))
233 return OnGetContextMenu(psmd, iid, pv);
234
235 return S_FALSE;
236 }
237
238 HRESULT OnExec(LPSMDATA psmd)
239 {
240 // HACK: Instead of running explorer.exe with the path, we should be using ShellExecute to "open" the path directly!
241 // Remove once ShellExecute can handle CLSID path components.
242
243 if (psmd->uId == IDM_CONTROLPANEL)
244 ShellExecuteW(NULL, NULL, L"explorer.exe", L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", NULL, SW_SHOWNORMAL);
245 else if (psmd->uId == IDM_NETWORKCONNECTIONS)
246 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);
247 else if (psmd->uId == IDM_PRINTERSANDFAXES)
248 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);
249 else
250 PostMessageW(m_hwndTray, WM_COMMAND, psmd->uId, 0);
251
252 return S_OK;
253 }
254
255 public:
256
257 DECLARE_NOT_AGGREGATABLE(CShellMenuCallback)
258 DECLARE_PROTECT_FINAL_CONSTRUCT()
259 BEGIN_COM_MAP(CShellMenuCallback)
260 COM_INTERFACE_ENTRY_IID(IID_IShellMenuCallback, IShellMenuCallback)
261 END_COM_MAP()
262
263 void Initialize(
264 IShellMenu* pShellMenu,
265 IBandSite* pBandSite,
266 IDeskBar* pDeskBar)
267 {
268 m_pShellMenu = pShellMenu;
269 m_pBandSite = pBandSite;
270 m_pDeskBar = pDeskBar;
271 }
272
273 ~CShellMenuCallback()
274 {
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 {
343 WARN("Failed to get the USER start menu folder. Trying to run with just the COMMON one.\n");
344
345 hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommonStartMenu);
346 if (FAILED_UNEXPECTEDLY(hr))
347 return hr;
348
349 TRACE("COMMON start menu obtained.\n");
350 hr = BindToDesktop(pidlCommonStartMenu, ppsfStartMenu);
351 ILFree(pidlCommonStartMenu);
352 return hr;
353 }
354
355 hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &pidlCommonStartMenu);
356 if (FAILED_UNEXPECTEDLY(hr))
357 {
358 WARN("Failed to get the COMMON start menu folder. Will use only the USER contents.\n");
359 hr = BindToDesktop(pidlUserStartMenu, ppsfStartMenu);
360 ILFree(pidlUserStartMenu);
361 return hr;
362 }
363
364 TRACE("Both COMMON and USER statr menu folders obtained, merging them...\n");
365
366 hr = BindToDesktop(pidlUserStartMenu, &psfUserStartMenu);
367 if (FAILED_UNEXPECTEDLY(hr))
368 return hr;
369
370 hr = BindToDesktop(pidlCommonStartMenu, &psfCommonStartMenu);
371 if (FAILED_UNEXPECTEDLY(hr))
372 return hr;
373
374 #if !USE_SYSTEM_MERGED_FOLDERS
375 hr = CMergedFolder_Constructor(IID_PPV_ARG(IAugmentedShellFolder, &pasf));
376 #else
377 hr = CoCreateInstance(CLSID_MergedFolder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IAugmentedShellFolder, &pasf));
378 #endif
379 if (FAILED_UNEXPECTEDLY(hr))
380 {
381 *ppsfStartMenu = psfUserStartMenu.Detach();
382 ILFree(pidlCommonStartMenu);
383 ILFree(pidlUserStartMenu);
384 return hr;
385 }
386
387 hr = pasf->AddNameSpace(NULL, psfUserStartMenu, pidlUserStartMenu, 0xFF00);
388 if (FAILED_UNEXPECTEDLY(hr))
389 return hr;
390
391 hr = pasf->AddNameSpace(NULL, psfCommonStartMenu, pidlCommonStartMenu, 0);
392 if (FAILED_UNEXPECTEDLY(hr))
393 return hr;
394
395 hr = pasf->QueryInterface(IID_PPV_ARG(IShellFolder, ppsfStartMenu));
396 pasf.Release();
397
398 ILFree(pidlCommonStartMenu);
399 ILFree(pidlUserStartMenu);
400
401 return hr;
402 }
403
404 extern "C"
405 HRESULT WINAPI
406 CStartMenu_Constructor(REFIID riid, void **ppv)
407 {
408 CComPtr<IShellMenu> pShellMenu;
409 CComPtr<IBandSite> pBandSite;
410 CComPtr<IDeskBar> pDeskBar;
411
412 HRESULT hr;
413 IShellFolder * psf;
414
415 LPITEMIDLIST pidlProgramsAbsolute;
416 LPITEMIDLIST pidlPrograms;
417 CComPtr<IShellFolder> psfPrograms;
418
419 #if USE_SYSTEM_MENUBAND
420 hr = CoCreateInstance(CLSID_MenuBand,
421 NULL,
422 CLSCTX_INPROC_SERVER,
423 IID_PPV_ARG(IShellMenu, &pShellMenu));
424 #else
425 hr = CMenuBand_Constructor(IID_PPV_ARG(IShellMenu, &pShellMenu));
426 #endif
427 if (FAILED_UNEXPECTEDLY(hr))
428 return hr;
429
430 #if USE_SYSTEM_MENUSITE
431 hr = CoCreateInstance(CLSID_MenuBandSite,
432 NULL,
433 CLSCTX_INPROC_SERVER,
434 IID_PPV_ARG(IBandSite, &pBandSite));
435 #else
436 hr = CMenuSite_Constructor(IID_PPV_ARG(IBandSite, &pBandSite));
437 #endif
438 if (FAILED_UNEXPECTEDLY(hr))
439 return hr;
440
441 #if USE_SYSTEM_MENUDESKBAR
442 hr = CoCreateInstance(CLSID_MenuDeskBar,
443 NULL,
444 CLSCTX_INPROC_SERVER,
445 IID_PPV_ARG(IDeskBar, &pDeskBar));
446 #else
447 hr = CMenuDeskBar_Constructor(IID_PPV_ARG(IDeskBar, &pDeskBar));
448 #endif
449 if (FAILED_UNEXPECTEDLY(hr))
450 return hr;
451
452 CComObject<CShellMenuCallback> *pCallback;
453 hr = CComObject<CShellMenuCallback>::CreateInstance(&pCallback);
454 if (FAILED_UNEXPECTEDLY(hr))
455 return hr;
456 pCallback->AddRef(); // CreateInstance returns object with 0 ref count */
457 pCallback->Initialize(pShellMenu, pBandSite, pDeskBar);
458
459 pShellMenu->Initialize(pCallback, (UINT) -1, 0, SMINIT_TOPLEVEL | SMINIT_VERTICAL);
460 if (FAILED_UNEXPECTEDLY(hr))
461 return hr;
462
463 hr = GetStartMenuFolder(&psf);
464 if (FAILED_UNEXPECTEDLY(hr))
465 return hr;
466
467 hr = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlProgramsAbsolute);
468 if (FAILED(hr))
469 {
470 WARN("USER Programs folder not found.");
471 hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_PROGRAMS, &pidlProgramsAbsolute);
472 if (FAILED_UNEXPECTEDLY(hr))
473 return hr;
474 }
475
476 pidlPrograms = ILClone(ILFindLastID(pidlProgramsAbsolute));
477 ILFree(pidlProgramsAbsolute);
478
479 hr = psf->BindToObject(pidlPrograms, NULL, IID_PPV_ARG(IShellFolder, &psfPrograms));
480 if (FAILED_UNEXPECTEDLY(hr))
481 return hr;
482
483 hr = pCallback->_SetProgramsFolder(psfPrograms, pidlPrograms);
484 if (FAILED_UNEXPECTEDLY(hr))
485 return hr;
486
487 hr = pShellMenu->SetShellFolder(psf, NULL, NULL, 0);
488 if (FAILED_UNEXPECTEDLY(hr))
489 return hr;
490
491 hr = pDeskBar->SetClient(pBandSite);
492 if (FAILED_UNEXPECTEDLY(hr))
493 return hr;
494
495 hr = pBandSite->AddBand(pShellMenu);
496 if (FAILED_UNEXPECTEDLY(hr))
497 return hr;
498
499 return pDeskBar->QueryInterface(riid, ppv);
500 }