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