Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2653 lines
74 KiB

  1. #include "stdafx.h"
  2. #include <startids.h> // for IDM_PROGRAMS et al
  3. #include "regstr.h"
  4. #include "rcids.h"
  5. #include <desktray.h>
  6. #include "tray.h"
  7. #include "startmnu.h"
  8. #include "hostutil.h"
  9. #include "deskhost.h"
  10. #include "shdguid.h"
  11. #define REGSTR_EXPLORER_ADVANCED REGSTR_PATH_EXPLORER TEXT("\\Advanced")
  12. #define TF_DV2HOST 0
  13. // #define TF_DV2HOST TF_CUSTOM1
  14. #define TF_DV2DIALOG 0
  15. // #define TF_DV2DIALOG TF_CUSTOM1
  16. EXTERN_C HINSTANCE hinstCabinet;
  17. HRESULT StartMenuHost_Create(IMenuPopup** ppmp, IMenuBand** ppmb);
  18. void RegisterDesktopControlClasses();
  19. const WCHAR c_wzStartMenuTheme[] = L"StartMenu";
  20. //*****************************************************************
  21. CPopupMenu::~CPopupMenu()
  22. {
  23. IUnknown_SetSite(_pmp, NULL);
  24. ATOMICRELEASE(_pmp);
  25. ATOMICRELEASE(_pmb);
  26. ATOMICRELEASE(_psm);
  27. }
  28. HRESULT CPopupMenu::Popup(RECT *prcExclude, DWORD dwFlags)
  29. {
  30. COMPILETIME_ASSERT(sizeof(RECT) == sizeof(RECTL));
  31. return _pmp->Popup((POINTL*)prcExclude, (RECTL*)prcExclude, dwFlags);
  32. }
  33. HRESULT CPopupMenu::Initialize(IShellMenu *psm, IUnknown *punkSite, HWND hwnd)
  34. {
  35. HRESULT hr;
  36. // We should have been zero-initialized
  37. ASSERT(_pmp == NULL);
  38. ASSERT(_pmb == NULL);
  39. ASSERT(_psm == NULL);
  40. hr = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER,
  41. IID_PPV_ARG(IMenuPopup, &_pmp));
  42. if (SUCCEEDED(hr))
  43. {
  44. IUnknown_SetSite(_pmp, punkSite);
  45. IBandSite *pbs;
  46. hr = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER,
  47. IID_PPV_ARG(IBandSite, &pbs));
  48. if (SUCCEEDED(hr))
  49. {
  50. hr = _pmp->SetClient(pbs);
  51. if (SUCCEEDED(hr))
  52. {
  53. IDeskBand *pdb;
  54. if (SUCCEEDED(psm->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb))))
  55. {
  56. hr = pbs->AddBand(pdb);
  57. if (SUCCEEDED(hr))
  58. {
  59. DWORD dwBandID;
  60. hr = pbs->EnumBands(0, &dwBandID);
  61. if (SUCCEEDED(hr))
  62. {
  63. hr = pbs->GetBandObject(dwBandID, IID_PPV_ARG(IMenuBand, &_pmb));
  64. }
  65. }
  66. pdb->Release();
  67. }
  68. }
  69. pbs->Release();
  70. }
  71. }
  72. if (SUCCEEDED(hr))
  73. {
  74. // Failure to set the theme is nonfatal
  75. IShellMenu2* psm2;
  76. if (SUCCEEDED(psm->QueryInterface(IID_PPV_ARG(IShellMenu2, &psm2))))
  77. {
  78. BOOL fThemed = IsAppThemed();
  79. psm2->SetTheme(fThemed ? c_wzStartMenuTheme : NULL);
  80. psm2->SetNoBorder(fThemed ? TRUE : FALSE);
  81. psm2->Release();
  82. }
  83. // Tell the popup that we are the window to parent UI on
  84. // This will fail on purpose so don't freak out
  85. psm->SetMenu(NULL, hwnd, 0);
  86. }
  87. if (SUCCEEDED(hr))
  88. {
  89. _psm = psm;
  90. psm->AddRef();
  91. hr = S_OK;
  92. }
  93. return hr;
  94. }
  95. HRESULT CPopupMenu_CreateInstance(IShellMenu *psm,
  96. IUnknown *punkSite,
  97. HWND hwnd,
  98. CPopupMenu **ppmOut)
  99. {
  100. HRESULT hr;
  101. *ppmOut = NULL;
  102. CPopupMenu *ppm = new CPopupMenu();
  103. if (ppm)
  104. {
  105. hr = ppm->Initialize(psm, punkSite, hwnd);
  106. if (FAILED(hr))
  107. {
  108. ppm->Release();
  109. }
  110. else
  111. {
  112. *ppmOut = ppm; // transfer ownership to called
  113. }
  114. }
  115. else
  116. {
  117. hr = E_OUTOFMEMORY;
  118. }
  119. return hr;
  120. }
  121. //*****************************************************************
  122. const STARTPANELMETRICS g_spmDefault = {
  123. {380,440},
  124. {
  125. {WC_USERPANE, 0, SPP_USERPANE, {380, 40}, NULL, NULL},
  126. {WC_SFTBARHOST, WS_TABSTOP, SPP_PROGLIST, {190, 330}, NULL, NULL},
  127. {WC_MOREPROGRAMS, 0, SPP_MOREPROGRAMS, {190, 30}, NULL, NULL},
  128. {WC_SFTBARHOST, 0, SPP_PLACESLIST, {190, 360}, NULL, NULL},
  129. {WC_LOGOFF, 0, SPP_LOGOFF, {380, 40}, NULL, NULL},
  130. }
  131. };
  132. HRESULT
  133. CDesktopHost::Initialize()
  134. {
  135. ASSERT(_hwnd == NULL);
  136. //
  137. // Load some settings.
  138. //
  139. _fAutoCascade = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("Start_AutoCascade"), FALSE, TRUE);
  140. return S_OK;
  141. }
  142. HRESULT CDesktopHost::QueryInterface(REFIID riid, void **ppvObj)
  143. {
  144. static const QITAB qit[] =
  145. {
  146. QITABENT(CDesktopHost, IMenuPopup),
  147. QITABENT(CDesktopHost, IDeskBar), // IMenuPopup derives from IDeskBar
  148. QITABENTMULTI(CDesktopHost, IOleWindow, IMenuPopup), // IDeskBar derives from IOleWindow
  149. QITABENT(CDesktopHost, IMenuBand),
  150. QITABENT(CDesktopHost, IServiceProvider),
  151. QITABENT(CDesktopHost, IOleCommandTarget),
  152. QITABENT(CDesktopHost, IObjectWithSite),
  153. QITABENT(CDesktopHost, ITrayPriv), // going away
  154. QITABENT(CDesktopHost, ITrayPriv2), // going away
  155. { 0 },
  156. };
  157. return QISearch(this, qit, riid, ppvObj);
  158. }
  159. HRESULT CDesktopHost::SetSite(IUnknown *punkSite)
  160. {
  161. CObjectWithSite::SetSite(punkSite);
  162. if (!_punkSite)
  163. {
  164. // This is our cue to break the recursive reference loop
  165. // The _ppmpPrograms contains multiple backreferences to
  166. // the CDesktopHost (we are its site, it also references
  167. // us via CDesktopShellMenuCallback...)
  168. ATOMICRELEASE(_ppmPrograms);
  169. }
  170. return S_OK;
  171. }
  172. CDesktopHost::~CDesktopHost()
  173. {
  174. if (_hbmCachedSnapshot)
  175. {
  176. DeleteObject(_hbmCachedSnapshot);
  177. }
  178. ATOMICRELEASE(_ppmPrograms);
  179. ATOMICRELEASE(_ppmTracking);
  180. if (_hwnd)
  181. {
  182. ASSERT(GetWindowThreadProcessId(_hwnd, NULL) == GetCurrentThreadId());
  183. DestroyWindow(_hwnd);
  184. }
  185. ATOMICRELEASE(_ptFader);
  186. }
  187. BOOL CDesktopHost::Register()
  188. {
  189. _wmDragCancel = RegisterWindowMessage(TEXT("CMBDragCancel"));
  190. WNDCLASSEX wndclass;
  191. wndclass.cbSize = sizeof(wndclass);
  192. wndclass.style = CS_DROPSHADOW;
  193. wndclass.lpfnWndProc = WndProc;
  194. wndclass.cbClsExtra = 0;
  195. wndclass.cbWndExtra = 0;
  196. wndclass.hInstance = hinstCabinet;
  197. wndclass.hIcon = NULL;
  198. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  199. wndclass.hbrBackground = GetStockBrush(HOLLOW_BRUSH);
  200. wndclass.lpszMenuName = NULL;
  201. wndclass.lpszClassName = WC_DV2;
  202. wndclass.hIconSm = NULL;
  203. return (0 != RegisterClassEx(&wndclass));
  204. }
  205. inline int _ClipCoord(int x, int xMin, int xMax)
  206. {
  207. if (x < xMin) x = xMin;
  208. if (x > xMax) x = xMax;
  209. return x;
  210. }
  211. //
  212. // Everybody conspires against us.
  213. //
  214. // CTray does not pass us any MPPF_POS_MASK flags to tell us where we
  215. // need to pop up relative to the point, so there's no point looking
  216. // at the dwFlags parameter. Which is for the better, I guess, because
  217. // the MPPF_* flags are not the same as the TPM_* flags. Go figure.
  218. //
  219. // And then the designers decided that the Start Menu should pop up
  220. // in a location different from the location that the standard
  221. // TrackPopupMenuEx function chooses, so we need a custom positioning
  222. // algorithm anyway.
  223. //
  224. // And finally, the AnimateWindow function takes AW_* flags, which are
  225. // not the same as TPM_*ANIMATE flags. Go figure. But since we gave up
  226. // on trying to map IMenuPopup::Popup to TrackPopupMenuEx anyway, we
  227. // don't have to do any translation here anyway.
  228. //
  229. // Returns animation direction.
  230. //
  231. void CDesktopHost::_ChoosePopupPosition(POINT *ppt, LPCRECT prcExclude, LPRECT prcWindow)
  232. {
  233. //
  234. // Calculate the monitor BEFORE we adjust the point. Otherwise, we might
  235. // move the point offscreen. In which case, we will end up pinning the
  236. // popup to the primary display, which is wron_
  237. //
  238. HMONITOR hmon = MonitorFromPoint(*ppt, MONITOR_DEFAULTTONEAREST);
  239. MONITORINFO minfo;
  240. minfo.cbSize = sizeof(minfo);
  241. GetMonitorInfo(hmon, &minfo);
  242. // Clip the exclude rectangle to the monitor
  243. RECT rcExclude;
  244. if (prcExclude)
  245. {
  246. // We can't use IntersectRect because it turns the rectangle
  247. // into (0,0,0,0) if the intersection is empty (which can happen if
  248. // the taskbar is autohide) but we want to glue it to the nearest
  249. // valid edge.
  250. rcExclude.left = _ClipCoord(prcExclude->left, minfo.rcMonitor.left, minfo.rcMonitor.right);
  251. rcExclude.right = _ClipCoord(prcExclude->right, minfo.rcMonitor.left, minfo.rcMonitor.right);
  252. rcExclude.top = _ClipCoord(prcExclude->top, minfo.rcMonitor.top, minfo.rcMonitor.bottom);
  253. rcExclude.bottom = _ClipCoord(prcExclude->bottom, minfo.rcMonitor.top, minfo.rcMonitor.bottom);
  254. }
  255. else
  256. {
  257. rcExclude.left = rcExclude.right = ppt->x;
  258. rcExclude.top = rcExclude.bottom = ppt->y;
  259. }
  260. _ComputeActualSize(&minfo, &rcExclude);
  261. // initialize the height and width from what the layout asked for
  262. int cy=RECTHEIGHT(_rcActual);
  263. int cx=RECTWIDTH(_rcActual);
  264. ASSERT(cx && cy); // we're in trouble if these are zero
  265. int x, y;
  266. //
  267. // First: Determine whether we are going to pop upwards or downwards.
  268. //
  269. BOOL fSide = FALSE;
  270. if (rcExclude.top - cy >= minfo.rcMonitor.top)
  271. {
  272. // There is room above.
  273. y = rcExclude.top - cy;
  274. }
  275. else if (rcExclude.bottom - cy >= minfo.rcMonitor.top)
  276. {
  277. // There is room above if we slide to the side.
  278. y = rcExclude.bottom - cy;
  279. fSide = TRUE;
  280. }
  281. else if (rcExclude.bottom + cy <= minfo.rcMonitor.bottom)
  282. {
  283. // There is room below.
  284. y = rcExclude.bottom;
  285. }
  286. else if (rcExclude.top + cy <= minfo.rcMonitor.bottom)
  287. {
  288. // There is room below if we slide to the side.
  289. y = rcExclude.top;
  290. fSide = TRUE;
  291. }
  292. else
  293. {
  294. // We don't fit anywhere. Pin to the appropriate edge of the screen.
  295. // And we have to go to the side, too.
  296. fSide = TRUE;
  297. if (rcExclude.top - minfo.rcMonitor.top < minfo.rcMonitor.bottom - rcExclude.bottom)
  298. {
  299. // Start button at top of screen; pin to top
  300. y = minfo.rcMonitor.top;
  301. }
  302. else
  303. {
  304. // Start button at bottom of screen; pin to bottom
  305. y = minfo.rcMonitor.bottom - cy;
  306. }
  307. }
  308. //
  309. // Now choose whether we will pop left or right. Try right first.
  310. //
  311. x = fSide ? rcExclude.right : rcExclude.left;
  312. if (x + cx > minfo.rcMonitor.right)
  313. {
  314. // Doesn't fit to the right; pin to the right edge.
  315. // Notice that we do *not* try to pop left. For some reason,
  316. // the start menu never pops left.
  317. x = minfo.rcMonitor.right - cx;
  318. }
  319. SetRect(prcWindow, x, y, x+cx, y+cy);
  320. }
  321. int GetDesiredHeight(HWND hwndHost, SMPANEDATA *psmpd)
  322. {
  323. SMNGETMINSIZE nmgms = {0};
  324. nmgms.hdr.hwndFrom = hwndHost;
  325. nmgms.hdr.code = SMN_GETMINSIZE;
  326. nmgms.siz = psmpd->size;
  327. SendMessage(psmpd->hwnd, WM_NOTIFY, nmgms.hdr.idFrom, (LPARAM)&nmgms);
  328. return nmgms.siz.cy;
  329. }
  330. //
  331. // Query each item to see if it has any size requirements.
  332. // Position all the items at their final locations.
  333. //
  334. void CDesktopHost::_ComputeActualSize(MONITORINFO *pminfo, LPCRECT prcExclude)
  335. {
  336. // Compute the maximum permissible space above/below the Start Menu.
  337. // Designers don't want the Start Menu to slide horizontally; it must
  338. // fit entirely above or below.
  339. int cxMax = RECTWIDTH(pminfo->rcWork);
  340. int cyMax = max(prcExclude->top - pminfo->rcMonitor.top,
  341. pminfo->rcMonitor.bottom - prcExclude->bottom);
  342. // Start at the minimum size and grow as necesary
  343. _rcActual = _rcDesired;
  344. // Ask the windows if they wants any adjustments
  345. int iMFUHeight = GetDesiredHeight(_hwnd, &_spm.panes[SMPANETYPE_MFU]);
  346. int iPlacesHeight = GetDesiredHeight(_hwnd, &_spm.panes[SMPANETYPE_PLACES]);
  347. int iMoreProgHeight = _spm.panes[SMPANETYPE_MOREPROG].size.cy;
  348. // Figure out the maximum size for each pane
  349. int cyPlacesMax = cyMax - (_spm.panes[SMPANETYPE_USER].size.cy + _spm.panes[SMPANETYPE_LOGOFF].size.cy);
  350. int cyMFUMax = cyPlacesMax - _spm.panes[SMPANETYPE_MOREPROG].size.cy;
  351. TraceMsg(TF_DV2HOST, "MFU Desired Height=%d(cur=%d,max=%d), Places Desired Height=%d(cur=%d,max=%d)",
  352. iMFUHeight, _spm.panes[SMPANETYPE_MFU].size.cy, cyMFUMax,
  353. iPlacesHeight, _spm.panes[SMPANETYPE_PLACES].size.cy, cyPlacesMax);
  354. // Clip each pane to its max - the smaller of (The largest possible or The largest we want to be)
  355. _fClipped = FALSE;
  356. if (iMFUHeight > cyMFUMax)
  357. {
  358. iMFUHeight = cyMFUMax;
  359. _fClipped = TRUE;
  360. }
  361. if (iPlacesHeight > cyPlacesMax)
  362. {
  363. iPlacesHeight = cyPlacesMax;
  364. _fClipped = TRUE;
  365. }
  366. // ensure that places == mfu + moreprog by growing the smaller of the two.
  367. if (iPlacesHeight > iMFUHeight + iMoreProgHeight)
  368. iMFUHeight = iPlacesHeight - iMoreProgHeight;
  369. else
  370. iPlacesHeight = iMFUHeight + iMoreProgHeight;
  371. //
  372. // move the actual windows
  373. // See diagram of layout in deskhost.h for the hardcoded assumptions here.
  374. // this could be made more flexible/variable, but we want to lock in this layout
  375. //
  376. // helper variables...
  377. DWORD dwUserBottomEdge = _spm.panes[SMPANETYPE_USER].size.cy;
  378. DWORD dwMFURightEdge = _spm.panes[SMPANETYPE_MFU].size.cx;
  379. DWORD dwMFUBottomEdge = dwUserBottomEdge + iMFUHeight;
  380. DWORD dwMoreProgBottomEdge = dwMFUBottomEdge + iMoreProgHeight;
  381. // set the size of the overall pane
  382. _rcActual.right = _spm.panes[SMPANETYPE_USER].size.cx;
  383. _rcActual.bottom = dwMoreProgBottomEdge + _spm.panes[SMPANETYPE_LOGOFF].size.cy;
  384. HDWP hdwp = BeginDeferWindowPos(5);
  385. const DWORD dwSWPFlags = SWP_NOACTIVATE | SWP_NOZORDER;
  386. DeferWindowPos(hdwp, _spm.panes[SMPANETYPE_USER].hwnd, NULL, 0, 0, _rcActual.right, dwUserBottomEdge, dwSWPFlags);
  387. DeferWindowPos(hdwp, _spm.panes[SMPANETYPE_MFU].hwnd, NULL, 0, dwUserBottomEdge, dwMFURightEdge, iMFUHeight, dwSWPFlags);
  388. DeferWindowPos(hdwp, _spm.panes[SMPANETYPE_MOREPROG].hwnd, NULL,0, dwMFUBottomEdge, dwMFURightEdge, iMoreProgHeight, dwSWPFlags);
  389. DeferWindowPos(hdwp, _spm.panes[SMPANETYPE_PLACES].hwnd, NULL, dwMFURightEdge, dwUserBottomEdge, _rcActual.right-dwMFURightEdge, iPlacesHeight, dwSWPFlags);
  390. DeferWindowPos(hdwp, _spm.panes[SMPANETYPE_LOGOFF].hwnd, NULL, 0, dwMoreProgBottomEdge, _rcActual.right, _spm.panes[SMPANETYPE_LOGOFF].size.cy, dwSWPFlags);
  391. EndDeferWindowPos(hdwp);
  392. }
  393. HWND CDesktopHost::_Create()
  394. {
  395. TCHAR szTitle[MAX_PATH];
  396. LoadString(hinstCabinet, IDS_STARTMENU, szTitle, MAX_PATH);
  397. Register();
  398. // Must load metrics early to determine whether we are themed or not
  399. LoadPanelMetrics();
  400. DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
  401. if (IS_BIDI_LOCALIZED_SYSTEM())
  402. {
  403. dwExStyle |= WS_EX_LAYOUTRTL;
  404. }
  405. DWORD dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // We will make it visible as part of animation
  406. if (!_hTheme)
  407. {
  408. // Normally the theme provides the border effects, but if there is
  409. // no theme then we have to do it ourselves.
  410. dwStyle |= WS_DLGFRAME;
  411. }
  412. _hwnd = CreateWindowEx(
  413. dwExStyle,
  414. WC_DV2,
  415. szTitle,
  416. dwStyle,
  417. 0, 0,
  418. 0, 0,
  419. v_hwndTray,
  420. NULL,
  421. hinstCabinet,
  422. this);
  423. v_hwndStartPane = _hwnd;
  424. return _hwnd;
  425. }
  426. void CDesktopHost::_ReapplyRegion()
  427. {
  428. SMNMAPPLYREGION ar;
  429. // If we fail to create a rectangular region, then remove the region
  430. // entirely so we don't carry the old (bad) region around.
  431. // Yes it means you get ugly black corners, but it's better than
  432. // clipping away huge chunks of the Start Menu!
  433. ar.hrgn = CreateRectRgn(0, 0, _sizWindowPrev.cx, _sizWindowPrev.cy);
  434. if (ar.hrgn)
  435. {
  436. // Let all the clients take a bite out of it
  437. ar.hdr.hwndFrom = _hwnd;
  438. ar.hdr.idFrom = 0;
  439. ar.hdr.code = SMN_APPLYREGION;
  440. SHPropagateMessage(_hwnd, WM_NOTIFY, 0, (LPARAM)&ar, SPM_SEND | SPM_ONELEVEL);
  441. }
  442. if (!SetWindowRgn(_hwnd, ar.hrgn, FALSE))
  443. {
  444. // SetWindowRgn takes ownership on success
  445. // On failure we need to free it ourselves
  446. if (ar.hrgn)
  447. {
  448. DeleteObject(ar.hrgn);
  449. }
  450. }
  451. }
  452. //
  453. // We need to use PrintWindow because WM_PRINT messes up RTL.
  454. // PrintWindow requires that the window be visible.
  455. // Making the window visible causes the shadow to appear.
  456. // We don't want the shadow to appear until we are ready.
  457. // So we have to do a lot of goofy style mangling to suppress the
  458. // shadow until we're ready.
  459. //
  460. BOOL ShowCachedWindow(HWND hwnd, SIZE sizWindow, HBITMAP hbmpSnapshot, BOOL fRepaint)
  461. {
  462. BOOL fSuccess = FALSE;
  463. if (hbmpSnapshot)
  464. {
  465. // Turn off the shadow so it won't get triggered by our SetWindowPos
  466. DWORD dwClassStylePrev = GetClassLong(hwnd, GCL_STYLE);
  467. SetClassLong(hwnd, GCL_STYLE, dwClassStylePrev & ~CS_DROPSHADOW);
  468. // Show the window and tell it not to repaint; we'll do that
  469. SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
  470. SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER |
  471. SWP_NOREDRAW | SWP_SHOWWINDOW);
  472. // Turn the shadow back on
  473. SetClassLong(hwnd, GCL_STYLE, dwClassStylePrev);
  474. // Disable WS_CLIPCHILDREN because we need to draw over the kids for our BLT
  475. DWORD dwStylePrev = SHSetWindowBits(hwnd, GWL_STYLE, WS_CLIPCHILDREN, 0);
  476. HDC hdcWindow = GetDCEx(hwnd, NULL, DCX_WINDOW | DCX_CACHE);
  477. if (hdcWindow)
  478. {
  479. HDC hdcMem = CreateCompatibleDC(hdcWindow);
  480. if (hdcMem)
  481. {
  482. HBITMAP hbmPrev = (HBITMAP)SelectObject(hdcMem, hbmpSnapshot);
  483. // PrintWindow only if fRepaint says it's necessary
  484. if (!fRepaint || PrintWindow(hwnd, hdcMem, 0))
  485. {
  486. // Do this horrible dance because sometimes GDI takes a long
  487. // time to do a BitBlt so you end up seeing the shadow for
  488. // a half second before the bits show up.
  489. //
  490. // So show the bits first, then show the shadow.
  491. if (BitBlt(hdcWindow, 0, 0, sizWindow.cx, sizWindow.cy, hdcMem, 0, 0, SRCCOPY))
  492. {
  493. // Tell USER to attach the shadow
  494. // Do this by hiding the window and then showing it
  495. // again, but do it in this goofy way to avoid flicker.
  496. // (If we used ShowWindow(SW_HIDE), then the window
  497. // underneath us would repaint pointlessly.)
  498. SHSetWindowBits(hwnd, GWL_STYLE, WS_VISIBLE, 0);
  499. SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
  500. SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER |
  501. SWP_NOREDRAW | SWP_SHOWWINDOW);
  502. // Validate the window now that we've drawn it
  503. RedrawWindow(hwnd, NULL, NULL, RDW_NOERASE | RDW_NOFRAME |
  504. RDW_NOINTERNALPAINT | RDW_VALIDATE);
  505. fSuccess = TRUE;
  506. }
  507. }
  508. SelectObject(hdcMem, hbmPrev);
  509. DeleteDC(hdcMem);
  510. }
  511. ReleaseDC(hwnd, hdcWindow);
  512. }
  513. SetWindowLong(hwnd, GWL_STYLE, dwStylePrev);
  514. }
  515. if (!fSuccess)
  516. {
  517. // re-hide the window so USER knows it's all invalid again
  518. ShowWindow(hwnd, SW_HIDE);
  519. }
  520. return fSuccess;
  521. }
  522. BOOL CDesktopHost::_TryShowBuffered()
  523. {
  524. BOOL fSuccess = FALSE;
  525. BOOL fRepaint = FALSE;
  526. if (!_hbmCachedSnapshot)
  527. {
  528. HDC hdcWindow = GetDCEx(_hwnd, NULL, DCX_WINDOW | DCX_CACHE);
  529. if (hdcWindow)
  530. {
  531. _hbmCachedSnapshot = CreateCompatibleBitmap(hdcWindow, _sizWindowPrev.cx, _sizWindowPrev.cy);
  532. fRepaint = TRUE;
  533. ReleaseDC(_hwnd, hdcWindow);
  534. }
  535. }
  536. if (_hbmCachedSnapshot)
  537. {
  538. fSuccess = ShowCachedWindow(_hwnd, _sizWindowPrev, _hbmCachedSnapshot, fRepaint);
  539. if (!fSuccess)
  540. {
  541. DeleteObject(_hbmCachedSnapshot);
  542. _hbmCachedSnapshot = NULL;
  543. }
  544. }
  545. return fSuccess;
  546. }
  547. LRESULT CDesktopHost::OnNeedRepaint()
  548. {
  549. if (_hwnd && _hbmCachedSnapshot)
  550. {
  551. // This will force a repaint the next time the window is shown
  552. DeleteObject(_hbmCachedSnapshot);
  553. _hbmCachedSnapshot = NULL;
  554. }
  555. return 0;
  556. }
  557. HRESULT CDesktopHost::_Popup(POINT *ppt, RECT *prcExclude, DWORD dwFlags)
  558. {
  559. if (_hwnd)
  560. {
  561. RECT rcWindow;
  562. _ChoosePopupPosition(ppt, prcExclude, &rcWindow);
  563. SIZE sizWindow = { RECTWIDTH(rcWindow), RECTHEIGHT(rcWindow) };
  564. MoveWindow(_hwnd, rcWindow.left, rcWindow.top,
  565. sizWindow.cx, sizWindow.cy, TRUE);
  566. if (sizWindow.cx != _sizWindowPrev.cx ||
  567. sizWindow.cy != _sizWindowPrev.cy)
  568. {
  569. _sizWindowPrev = sizWindow;
  570. _ReapplyRegion();
  571. // We need to repaint since our size has changed
  572. OnNeedRepaint();
  573. }
  574. // If the user toggles the tray between topmost and nontopmost
  575. // our own topmostness can get messed up, so re-assert it here.
  576. SetWindowZorder(_hwnd, HWND_TOPMOST);
  577. if (GetSystemMetrics(SM_REMOTESESSION))
  578. {
  579. // If running remotely, then don't cache the Start Menu
  580. // or double-buffer. Show the keyboard cues accurately
  581. // from the start (to avoid flicker).
  582. SendMessage(_hwnd, WM_CHANGEUISTATE, UIS_INITIALIZE, 0);
  583. if (dwFlags & MPPF_KEYBOARD)
  584. {
  585. _EnableKeyboardCues();
  586. }
  587. ShowWindow(_hwnd, SW_SHOW);
  588. }
  589. else
  590. {
  591. // If running locally, then force keyboard cues off so our
  592. // cached bitmap won't have underlines. Then draw the
  593. // Start Menu, then turn on keyboard cues if necessary.
  594. SendMessage(_hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
  595. if (!_TryShowBuffered())
  596. {
  597. ShowWindow(_hwnd, SW_SHOW);
  598. }
  599. NotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, _hwnd, OBJID_CLIENT, CHILDID_SELF);
  600. if (dwFlags & MPPF_KEYBOARD)
  601. {
  602. _EnableKeyboardCues();
  603. }
  604. }
  605. // Tell tray that the start pane is active, so it knows to eat
  606. // mouse clicks on the Start Button.
  607. Tray_SetStartPaneActive(TRUE);
  608. _fOpen = TRUE;
  609. _fMenuBlocked = FALSE;
  610. _fMouseEntered = FALSE;
  611. _fOfferedNewApps = FALSE;
  612. _MaybeOfferNewApps();
  613. _MaybeShowClipBalloon();
  614. // Tell all our child windows it's time to maybe revalidate
  615. NMHDR nm = { _hwnd, 0, SMN_POSTPOPUP };
  616. SHPropagateMessage(_hwnd, WM_NOTIFY, 0, (LPARAM)&nm, SPM_SEND | SPM_ONELEVEL);
  617. ExplorerPlaySound(TEXT("MenuPopup"));
  618. return S_OK;
  619. }
  620. else
  621. {
  622. return E_FAIL;
  623. }
  624. }
  625. HRESULT CDesktopHost::Popup(POINTL *pptl, RECTL *prclExclude, DWORD dwFlags)
  626. {
  627. COMPILETIME_ASSERT(sizeof(POINTL) == sizeof(POINT));
  628. POINT *ppt = reinterpret_cast<POINT*>(pptl);
  629. COMPILETIME_ASSERT(sizeof(RECTL) == sizeof(RECT));
  630. RECT *prcExclude = reinterpret_cast<RECT*>(prclExclude);
  631. if (_hwnd == NULL)
  632. {
  633. _hwnd = _Create();
  634. }
  635. return _Popup(ppt, prcExclude, dwFlags);
  636. }
  637. LRESULT CDesktopHost::OnHaveNewItems(NMHDR *pnm)
  638. {
  639. PSMNMHAVENEWITEMS phni = (PSMNMHAVENEWITEMS)pnm;
  640. _hwndNewHandler = pnm->hwndFrom;
  641. // We have a new "new app" list, so tell the cached Programs menu
  642. // its cache is no longer valid and it should re-query us
  643. // so we can color the new apps appropriately.
  644. if (_ppmPrograms)
  645. {
  646. _ppmPrograms->Invalidate();
  647. }
  648. //
  649. // Were any apps in the list installed since the last time the
  650. // user acknowledged a new app?
  651. //
  652. FILETIME ftBalloon = { 0, 0 }; // assume never
  653. DWORD dwSize = sizeof(ftBalloon);
  654. SHRegGetUSValue(DV2_REGPATH, DV2_NEWAPP_BALLOON_TIME, NULL,
  655. &ftBalloon, &dwSize, FALSE, NULL, 0);
  656. if (CompareFileTime(&ftBalloon, &phni->ftNewestApp) < 0)
  657. {
  658. _iOfferNewApps = NEWAPP_OFFER_COUNT;
  659. _MaybeOfferNewApps();
  660. }
  661. return 1;
  662. }
  663. void CDesktopHost::_MaybeOfferNewApps()
  664. {
  665. // Display the balloon tip only once per pop-open,
  666. // and only if there are new apps to offer
  667. // and only if we're actually visible
  668. if (_fOfferedNewApps || !_iOfferNewApps || !IsWindowVisible(_hwnd) ||
  669. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, REGSTR_VAL_DV2_NOTIFYNEW, FALSE, TRUE))
  670. {
  671. return;
  672. }
  673. _fOfferedNewApps = TRUE;
  674. _iOfferNewApps--;
  675. SMNMBOOL nmb = { { _hwnd, 0, SMN_SHOWNEWAPPSTIP }, TRUE };
  676. SHPropagateMessage(_hwnd, WM_NOTIFY, 0, (LPARAM)&nmb, SPM_SEND | SPM_ONELEVEL);
  677. }
  678. void CDesktopHost::OnSeenNewItems()
  679. {
  680. _iOfferNewApps = 0; // Do not offer More Programs balloon tip again
  681. // Remember the time the user acknowledged the balloon so we only
  682. // offer the balloon if there is an app installed after this point.
  683. FILETIME ftNow;
  684. GetSystemTimeAsFileTime(&ftNow);
  685. SHRegSetUSValue(DV2_REGPATH, DV2_NEWAPP_BALLOON_TIME, REG_BINARY,
  686. &ftNow, sizeof(ftNow), SHREGSET_FORCE_HKCU);
  687. }
  688. void CDesktopHost::_MaybeShowClipBalloon()
  689. {
  690. if (_fClipped && !_fWarnedClipped)
  691. {
  692. _fWarnedClipped = TRUE;
  693. RECT rc;
  694. GetWindowRect(_spm.panes[SMPANETYPE_MFU].hwnd, &rc); // show the clipped ballon pointing to the bottom of the MFU
  695. _hwndClipBalloon = CreateBalloonTip(_hwnd,
  696. (rc.right+rc.left)/2, rc.bottom,
  697. NULL,
  698. IDS_STARTPANE_CLIPPED_TITLE,
  699. IDS_STARTPANE_CLIPPED_TEXT);
  700. if (_hwndClipBalloon)
  701. {
  702. SetProp(_hwndClipBalloon, PROP_DV2_BALLOONTIP, DV2_BALLOONTIP_CLIP);
  703. }
  704. }
  705. }
  706. void CDesktopHost::OnContextMenu(LPARAM lParam)
  707. {
  708. if (!IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOTRAYCONTEXTMENU, TEXT("Advanced"), TEXT("TaskbarContextMenu"), ROUS_KEYALLOWS | ROUS_DEFAULTALLOW))
  709. {
  710. HMENU hmenu = SHLoadMenuPopup(hinstCabinet, MENU_STARTPANECONTEXT);
  711. if (hmenu)
  712. {
  713. POINT pt;
  714. if (IS_WM_CONTEXTMENU_KEYBOARD(lParam))
  715. {
  716. pt.x = pt.y = 0;
  717. MapWindowPoints(_hwnd, HWND_DESKTOP, &pt, 1);
  718. }
  719. else
  720. {
  721. pt.x = GET_X_LPARAM(lParam);
  722. pt.y = GET_Y_LPARAM(lParam);
  723. }
  724. int idCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  725. pt.x, pt.y, _hwnd, NULL);
  726. if (idCmd == IDSYSPOPUP_STARTMENUPROP)
  727. {
  728. DesktopHost_Dismiss(_hwnd);
  729. Tray_DoProperties(TPF_STARTMENUPAGE);
  730. }
  731. DestroyMenu(hmenu);
  732. }
  733. }
  734. }
  735. BOOL CDesktopHost::_ShouldIgnoreFocusChange(HWND hwndFocusRecipient)
  736. {
  737. // Ignore focus changes when a popup menu is up
  738. if (_ppmTracking)
  739. {
  740. return TRUE;
  741. }
  742. // If a focus change from a special balloon, this means that the
  743. // user is clicking a tooltip. So dismiss the ballon and not the Start Menu.
  744. HANDLE hProp = GetProp(hwndFocusRecipient, PROP_DV2_BALLOONTIP);
  745. if (hProp)
  746. {
  747. SendMessage(hwndFocusRecipient, TTM_POP, 0, 0);
  748. if (hProp == DV2_BALLOONTIP_MOREPROG)
  749. {
  750. OnSeenNewItems();
  751. }
  752. return TRUE;
  753. }
  754. // Otherwise, dismiss ourselves
  755. return FALSE;
  756. }
  757. HRESULT CDesktopHost::TranslatePopupMenuMessage(MSG *pmsg, LRESULT *plres)
  758. {
  759. BOOL fDismissOnlyPopup = FALSE;
  760. // If the user drags an item off of a popup menu, the popup menu
  761. // will autodismiss itself. If the user is over our window, then
  762. // we only want it to dismiss up to our level.
  763. // (under low memory conditions, _wmDragCancel might be WM_NULL)
  764. if (pmsg->message == _wmDragCancel && pmsg->message != WM_NULL)
  765. {
  766. RECT rc;
  767. POINT pt;
  768. if (GetWindowRect(_hwnd, &rc) &&
  769. GetCursorPos(&pt) &&
  770. PtInRect(&rc, pt))
  771. {
  772. fDismissOnlyPopup = TRUE;
  773. }
  774. }
  775. if (fDismissOnlyPopup)
  776. _fDismissOnlyPopup++;
  777. HRESULT hr = _ppmTracking->TranslateMenuMessage(pmsg, plres);
  778. if (fDismissOnlyPopup)
  779. _fDismissOnlyPopup--;
  780. return hr;
  781. }
  782. LRESULT CALLBACK CDesktopHost::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  783. {
  784. CDesktopHost *pdh = (CDesktopHost *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  785. LPCREATESTRUCT pcs;
  786. if (pdh && pdh->_ppmTracking)
  787. {
  788. MSG msg = { hwnd, uMsg, wParam, lParam };
  789. LRESULT lres;
  790. if (pdh->TranslatePopupMenuMessage(&msg, &lres) == S_OK)
  791. {
  792. return lres;
  793. }
  794. wParam = msg.wParam;
  795. lParam = msg.lParam;
  796. }
  797. switch(uMsg)
  798. {
  799. case WM_NCCREATE:
  800. pcs = (LPCREATESTRUCT)lParam;
  801. pdh = (CDesktopHost *)pcs->lpCreateParams;
  802. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pdh);
  803. break;
  804. case WM_CREATE:
  805. pdh->OnCreate(hwnd);
  806. break;
  807. case WM_ACTIVATEAPP:
  808. if (!wParam)
  809. {
  810. DesktopHost_Dismiss(hwnd);
  811. }
  812. break;
  813. case WM_ACTIVATE:
  814. if (pdh)
  815. {
  816. if (LOWORD(wParam) == WA_INACTIVE)
  817. {
  818. pdh->_SaveChildFocus();
  819. HWND hwndAncestor = GetAncestor((HWND) lParam, GA_ROOTOWNER);
  820. if (hwnd != hwndAncestor &&
  821. !(hwndAncestor == v_hwndTray && pdh->_ShouldIgnoreFocusChange((HWND)lParam)) &&
  822. !pdh->_ppmTracking)
  823. // Losing focus to somebody unrelated to us = dismiss
  824. {
  825. #ifdef FULL_DEBUG
  826. if (! (GetAsyncKeyState(VK_SHIFT) <0) )
  827. #endif
  828. DesktopHost_Dismiss(hwnd);
  829. }
  830. }
  831. else
  832. {
  833. pdh->_RestoreChildFocus();
  834. }
  835. }
  836. break;
  837. case WM_DESTROY:
  838. pdh->OnDestroy();
  839. break;
  840. case WM_SHOWWINDOW:
  841. /*
  842. * If hiding the window, save the focus for restoration later.
  843. */
  844. if (!wParam)
  845. {
  846. pdh->_SaveChildFocus();
  847. }
  848. break;
  849. case WM_SETFOCUS:
  850. pdh->OnSetFocus((HWND)wParam);
  851. break;
  852. case WM_ERASEBKGND:
  853. pdh->OnPaint((HDC)wParam, TRUE);
  854. return TRUE;
  855. #if 0
  856. // currently, the host doesn't do anything on WM_PAINT
  857. case WM_PAINT:
  858. {
  859. PAINTSTRUCT ps;
  860. HDC hdc;
  861. if(hdc = BeginPaint(hwnd, &ps))
  862. {
  863. pdh->OnPaint(hdc, FALSE);
  864. EndPaint(hwnd, &ps);
  865. }
  866. }
  867. break;
  868. #endif
  869. case WM_NOTIFY:
  870. {
  871. LPNMHDR pnm = (LPNMHDR)lParam;
  872. switch (pnm->code)
  873. {
  874. case SMN_HAVENEWITEMS:
  875. return pdh->OnHaveNewItems(pnm);
  876. case SMN_COMMANDINVOKED:
  877. return pdh->OnCommandInvoked(pnm);
  878. case SMN_FILTEROPTIONS:
  879. return pdh->OnFilterOptions(pnm);
  880. case SMN_NEEDREPAINT:
  881. return pdh->OnNeedRepaint();
  882. case SMN_TRACKSHELLMENU:
  883. pdh->OnTrackShellMenu(pnm);
  884. return 0;
  885. case SMN_BLOCKMENUMODE:
  886. pdh->_fMenuBlocked = ((SMNMBOOL*)pnm)->f;
  887. break;
  888. case SMN_SEENNEWITEMS:
  889. pdh->OnSeenNewItems();
  890. break;
  891. case SMN_CANCELSHELLMENU:
  892. pdh->_DismissTrackShellMenu();
  893. break;
  894. }
  895. }
  896. break;
  897. case WM_CONTEXTMENU:
  898. pdh->OnContextMenu(lParam);
  899. return 0; // do not bubble up
  900. case WM_SETTINGCHANGE:
  901. if ((wParam == SPI_ICONVERTICALSPACING) ||
  902. ((wParam == 0) && (lParam != 0) && (StrCmpIC((LPCTSTR)lParam, TEXT("Policy")) == 0)))
  903. {
  904. // A change in icon vertical spacing is how the themes control
  905. // panel tells us that it changed the Large Icons setting (!)
  906. ::PostMessage(v_hwndTray, SBM_REBUILDMENU, 0, 0);
  907. }
  908. // Toss our cached bitmap because the user may have changed something
  909. // that affects our appearance (e.g., toggled keyboard cues)
  910. pdh->OnNeedRepaint();
  911. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL); // forward to kids
  912. break;
  913. case WM_DISPLAYCHANGE:
  914. case WM_SYSCOLORCHANGE:
  915. // Toss our cached bitmap because these settings may affect our
  916. // appearance (e.g., color changes)
  917. pdh->OnNeedRepaint();
  918. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL); // forward to kids
  919. break;
  920. case WM_TIMER:
  921. switch (wParam)
  922. {
  923. case IDT_MENUCHANGESEL:
  924. pdh->_OnMenuChangeSel();
  925. return 0;
  926. }
  927. break;
  928. case DHM_DISMISS:
  929. pdh->_OnDismiss((BOOL)wParam);
  930. break;
  931. // Alt+F4 dismisses the window, but doesn't destroy it
  932. case WM_CLOSE:
  933. pdh->_OnDismiss(FALSE);
  934. return 0;
  935. case WM_SYSCOMMAND:
  936. switch (wParam & ~0xF) // must ignore bottom 4 bits
  937. {
  938. case SC_SCREENSAVE:
  939. DesktopHost_Dismiss(hwnd);
  940. break;
  941. }
  942. break;
  943. }
  944. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  945. }
  946. //
  947. // If the user executes something or cancels out, we dismiss ourselves.
  948. //
  949. HRESULT CDesktopHost::OnSelect(DWORD dwSelectType)
  950. {
  951. HRESULT hr = E_NOTIMPL;
  952. switch (dwSelectType)
  953. {
  954. case MPOS_EXECUTE:
  955. case MPOS_CANCELLEVEL:
  956. _DismissMenuPopup();
  957. hr = S_OK;
  958. break;
  959. case MPOS_FULLCANCEL:
  960. if (!_fDismissOnlyPopup)
  961. {
  962. _DismissMenuPopup();
  963. }
  964. // Don't _CleanupTrackShellMenu yet; wait for
  965. // _smTracking.IsMenuMessage() to return E_FAIL
  966. // because it might have some modal UI up
  967. hr = S_OK;
  968. break;
  969. case MPOS_SELECTLEFT:
  970. _DismissTrackShellMenu();
  971. hr = S_OK;
  972. break;
  973. }
  974. return hr;
  975. }
  976. void CDesktopHost::_DismissTrackShellMenu()
  977. {
  978. if (_ppmTracking)
  979. {
  980. _fDismissOnlyPopup++;
  981. _ppmTracking->OnSelect(MPOS_FULLCANCEL);
  982. _fDismissOnlyPopup--;
  983. }
  984. }
  985. void CDesktopHost::_CleanupTrackShellMenu()
  986. {
  987. ATOMICRELEASE(_ppmTracking);
  988. _hwndTracking = NULL;
  989. _hwndAltTracking = NULL;
  990. KillTimer(_hwnd, IDT_MENUCHANGESEL);
  991. NMHDR nm = { _hwnd, 0, SMN_SHELLMENUDISMISSED };
  992. SHPropagateMessage(_hwnd, WM_NOTIFY, 0, (LPARAM)&nm, SPM_SEND | SPM_ONELEVEL);
  993. }
  994. void CDesktopHost::_DismissMenuPopup()
  995. {
  996. DesktopHost_Dismiss(_hwnd);
  997. }
  998. //
  999. // The PMs want custom keyboard navigation behavior on the Start Panel,
  1000. // so we have to do it all manually.
  1001. //
  1002. BOOL CDesktopHost::_IsDialogMessage(MSG *pmsg)
  1003. {
  1004. //
  1005. // If this is a keyboard message or a mouse click, then remove the
  1006. // Start Menu lock.
  1007. if ((pmsg->message >= WM_KEYFIRST && pmsg->message <= WM_KEYLAST) ||
  1008. pmsg->message == WM_LBUTTONDOWN ||
  1009. pmsg->message == WM_RBUTTONDOWN)
  1010. {
  1011. Tray_UnlockStartPane();
  1012. }
  1013. //
  1014. // If the menu isn't even open or if menu mode is blocked, then
  1015. // do not mess with the message.
  1016. //
  1017. if (!_fOpen || _fMenuBlocked) {
  1018. return FALSE;
  1019. }
  1020. //
  1021. // Tapping the ALT key dismisses menus.
  1022. //
  1023. if (pmsg->message == WM_SYSKEYDOWN && pmsg->wParam == VK_MENU)
  1024. {
  1025. DesktopHost_Dismiss(_hwnd);
  1026. // For accessibility purposes, dismissing the
  1027. // Start Menu should place focus on the Start Button.
  1028. SetFocus(c_tray._hwndStart);
  1029. return TRUE;
  1030. }
  1031. if (SHIsChildOrSelf(_hwnd, pmsg->hwnd) != S_OK) {
  1032. //
  1033. // If this is an uncaptured mouse move message, then eat it.
  1034. // That's what menus do -- they eat mouse moves.
  1035. // Let clicks go through, however, so the user
  1036. // can click away to dismiss the menu and activate
  1037. // whatever they clicked on.
  1038. if (!GetCapture() && pmsg->message == WM_MOUSEMOVE) {
  1039. return TRUE;
  1040. }
  1041. return FALSE;
  1042. }
  1043. //
  1044. // Destination window must be a grandchild of us. The child is the
  1045. // host control; the grandchild is the real control. Note also that
  1046. // we do not attempt to modify the behavior of great-grandchildren,
  1047. // because that would mess up inplace editing (which creates an
  1048. // edit control as a child of the listview).
  1049. HWND hwndTarget = GetParent(pmsg->hwnd);
  1050. if (hwndTarget != NULL && GetParent(hwndTarget) != _hwnd)
  1051. {
  1052. hwndTarget = NULL;
  1053. }
  1054. //
  1055. // Intercept mouse messages so we can do mouse hot tracking goo.
  1056. // (But not if a client has blocked menu mode because it has gone
  1057. // into some modal state.)
  1058. //
  1059. switch (pmsg->message) {
  1060. case WM_MOUSEMOVE:
  1061. _FilterMouseMove(pmsg, hwndTarget);
  1062. break;
  1063. case WM_MOUSELEAVE:
  1064. _FilterMouseLeave(pmsg, hwndTarget);
  1065. break;
  1066. case WM_MOUSEHOVER:
  1067. _FilterMouseHover(pmsg, hwndTarget);
  1068. break;
  1069. }
  1070. //
  1071. // Keyboard messages require a valid target.
  1072. //
  1073. if (hwndTarget == NULL) {
  1074. return FALSE;
  1075. }
  1076. //
  1077. // Okay, hwndTarget is the host control that understands our
  1078. // wacky notification messages.
  1079. //
  1080. switch (pmsg->message)
  1081. {
  1082. case WM_KEYDOWN:
  1083. _EnableKeyboardCues();
  1084. switch (pmsg->wParam)
  1085. {
  1086. case VK_LEFT:
  1087. case VK_RIGHT:
  1088. case VK_UP:
  1089. case VK_DOWN:
  1090. return _DlgNavigateArrow(hwndTarget, pmsg);
  1091. case VK_ESCAPE:
  1092. case VK_CANCEL:
  1093. DesktopHost_Dismiss(_hwnd);
  1094. // For accessibility purposes, hitting ESC to dismiss the
  1095. // Start Menu should place focus on the Start Button.
  1096. SetFocus(c_tray._hwndStart);
  1097. return TRUE;
  1098. case VK_RETURN:
  1099. _FindChildItem(hwndTarget, NULL, SMNDM_INVOKECURRENTITEM | SMNDM_KEYBOARD);
  1100. return TRUE;
  1101. // Eat space
  1102. case VK_SPACE:
  1103. return TRUE;
  1104. default:
  1105. break;
  1106. }
  1107. return FALSE;
  1108. // Must dispatch there here so Tray's TranslateAccelerator won't see them
  1109. case WM_SYSKEYDOWN:
  1110. case WM_SYSKEYUP:
  1111. case WM_SYSCHAR:
  1112. DispatchMessage(pmsg);
  1113. return TRUE;
  1114. case WM_CHAR:
  1115. return _DlgNavigateChar(hwndTarget, pmsg);
  1116. }
  1117. return FALSE;
  1118. }
  1119. LRESULT CDesktopHost::_FindChildItem(HWND hwnd, SMNDIALOGMESSAGE *pnmdm, UINT smndm)
  1120. {
  1121. SMNDIALOGMESSAGE nmdm;
  1122. if (!pnmdm)
  1123. {
  1124. pnmdm = &nmdm;
  1125. }
  1126. pnmdm->hdr.hwndFrom = _hwnd;
  1127. pnmdm->hdr.idFrom = 0;
  1128. pnmdm->hdr.code = SMN_FINDITEM;
  1129. pnmdm->flags = smndm;
  1130. LRESULT lres = ::SendMessage(hwnd, WM_NOTIFY, 0, (LPARAM)pnmdm);
  1131. if (lres && (smndm & SMNDM_SELECT))
  1132. {
  1133. SetFocus(::GetWindow(hwnd, GW_CHILD));
  1134. }
  1135. return lres;
  1136. }
  1137. void CDesktopHost::_EnableKeyboardCues()
  1138. {
  1139. SendMessage(_hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
  1140. }
  1141. //
  1142. // _DlgFindItem does the grunt work of walking the group/tab order
  1143. // looking for an item.
  1144. //
  1145. // hwndStart = window after which to start searching
  1146. // pnmdm = structure to receive results
  1147. // smndm = flags for _FindChildItem call
  1148. // GetNextDlgItem = GetNextDlgTabItem or GetNextDlgGroupItem
  1149. // fl = flags (DFI_*)
  1150. //
  1151. // DFI_INCLUDESTARTLAST: Include hwndStart at the end of the search.
  1152. // Otherwise do not search in hwndStart.
  1153. //
  1154. // Returns the found window, or NULL.
  1155. //
  1156. #define DFI_FORWARDS 0x0000
  1157. #define DFI_BACKWARDS 0x0001
  1158. #define DFI_INCLUDESTARTLAST 0x0002
  1159. HWND CDesktopHost::_DlgFindItem(
  1160. HWND hwndStart, SMNDIALOGMESSAGE *pnmdm, UINT smndm,
  1161. GETNEXTDLGITEM GetNextDlgItem, UINT fl)
  1162. {
  1163. HWND hwndT = hwndStart;
  1164. int iLoopCount = 0;
  1165. while ((hwndT = GetNextDlgItem(_hwnd, hwndT, fl & DFI_BACKWARDS)) != NULL)
  1166. {
  1167. if (!(fl & DFI_INCLUDESTARTLAST) && hwndT == hwndStart)
  1168. {
  1169. return NULL;
  1170. }
  1171. if (_FindChildItem(hwndT, pnmdm, smndm))
  1172. {
  1173. return hwndT;
  1174. }
  1175. if (hwndT == hwndStart)
  1176. {
  1177. ASSERT(fl & DFI_INCLUDESTARTLAST);
  1178. return NULL;
  1179. }
  1180. if (++iLoopCount > 10)
  1181. {
  1182. // If this assert fires, it means that the controls aren't
  1183. // playing nice with WS_TABSTOP and WS_GROUP and we got stuck.
  1184. ASSERT(iLoopCount < 10);
  1185. return NULL;
  1186. }
  1187. }
  1188. return NULL;
  1189. }
  1190. BOOL CDesktopHost::_DlgNavigateArrow(HWND hwndStart, MSG *pmsg)
  1191. {
  1192. HWND hwndT;
  1193. SMNDIALOGMESSAGE nmdm;
  1194. MSG msg;
  1195. nmdm.pmsg = pmsg; // other fields will be filled in by _FindChildItem
  1196. TraceMsg(TF_DV2DIALOG, "idm.arrow(%04x)", pmsg->wParam);
  1197. // If RTL, then flip the left and right arrows
  1198. UINT vk = (UINT)pmsg->wParam;
  1199. BOOL fRTL = GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL;
  1200. if (fRTL)
  1201. {
  1202. if (vk == VK_LEFT) vk = VK_RIGHT;
  1203. else if (vk == VK_RIGHT) vk = VK_LEFT;
  1204. // Put the flipped arrows into the MSG structure so clients don't
  1205. // have to know anything about RTL.
  1206. msg = *pmsg;
  1207. nmdm.pmsg = &msg;
  1208. msg.wParam = vk;
  1209. }
  1210. BOOL fBackwards = vk == VK_LEFT || vk == VK_UP;
  1211. BOOL fVerticalKey = vk == VK_UP || vk == VK_DOWN;
  1212. //
  1213. // First see if the navigation can be handled by the control natively.
  1214. // We have to let the control get first crack because it might want to
  1215. // override default behavior (e.g., open a menu when VK_RIGHT is pressed
  1216. // instead of moving to the right).
  1217. //
  1218. //
  1219. // Holding the shift key while hitting the Right [RTL:Left] arrow
  1220. // suppresses the attempt to cascade.
  1221. //
  1222. DWORD dwTryCascade = 0;
  1223. if (vk == VK_RIGHT && GetKeyState(VK_SHIFT) >= 0)
  1224. {
  1225. dwTryCascade |= SMNDM_TRYCASCADE;
  1226. }
  1227. if (_FindChildItem(hwndStart, &nmdm, dwTryCascade | SMNDM_FINDNEXTARROW | SMNDM_SELECT | SMNDM_KEYBOARD))
  1228. {
  1229. // That was easy
  1230. return TRUE;
  1231. }
  1232. //
  1233. // If the arrow key is in alignment with the control's orientation,
  1234. // then walk through the other controls in the group until we find
  1235. // one that contains an item, or until we loop back.
  1236. //
  1237. ASSERT(nmdm.flags & (SMNDM_VERTICAL | SMNDM_HORIZONTAL));
  1238. // Save this because subsequent callbacks will wipe it out.
  1239. DWORD dwDirection = nmdm.flags;
  1240. //
  1241. // Up/Down arrow always do prev/next. Left/right arrow will
  1242. // work if we are in a horizontal control.
  1243. //
  1244. if (fVerticalKey || (dwDirection & SMNDM_HORIZONTAL))
  1245. {
  1246. // Search for next/prev control in group.
  1247. UINT smndm = fBackwards ? SMNDM_FINDLAST : SMNDM_FINDFIRST;
  1248. UINT fl = fBackwards ? DFI_BACKWARDS : DFI_FORWARDS;
  1249. hwndT = _DlgFindItem(hwndStart, &nmdm,
  1250. smndm | SMNDM_SELECT | SMNDM_KEYBOARD,
  1251. GetNextDlgGroupItem,
  1252. fl | DFI_INCLUDESTARTLAST);
  1253. // Always return TRUE to eat the message
  1254. return TRUE;
  1255. }
  1256. //
  1257. // Navigate to next column or row. Look for controls that intersect
  1258. // the x (or y) coordinate of the current item and ask them to select
  1259. // the nearest available item.
  1260. //
  1261. // Note that in this loop we do not want to let the starting point
  1262. // try again because it already told us that the navigation key was
  1263. // trying to leave the starting point.
  1264. //
  1265. //
  1266. // Note: For RTL compatibility, we must map rectangles.
  1267. //
  1268. RECT rcSrc = { nmdm.pt.x, nmdm.pt.y, nmdm.pt.x, nmdm.pt.y };
  1269. MapWindowRect(hwndStart, HWND_DESKTOP, &rcSrc);
  1270. hwndT = hwndStart;
  1271. while ((hwndT = GetNextDlgGroupItem(_hwnd, hwndT, fBackwards)) != NULL &&
  1272. hwndT != hwndStart)
  1273. {
  1274. // Does this window intersect in the desired direction?
  1275. RECT rcT;
  1276. BOOL fIntersect;
  1277. GetWindowRect(hwndT, &rcT);
  1278. if (dwDirection & SMNDM_VERTICAL)
  1279. {
  1280. rcSrc.left = rcSrc.right = fRTL ? rcT.right : rcT.left;
  1281. fIntersect = rcSrc.top >= rcT.top && rcSrc.top < rcT.bottom;
  1282. }
  1283. else
  1284. {
  1285. rcSrc.top = rcSrc.bottom = rcT.top;
  1286. fIntersect = rcSrc.left >= rcT.left && rcSrc.left < rcT.right;
  1287. }
  1288. if (fIntersect)
  1289. {
  1290. rcT = rcSrc;
  1291. MapWindowRect(HWND_DESKTOP, hwndT, &rcT);
  1292. nmdm.pt.x = rcT.left;
  1293. nmdm.pt.y = rcT.top;
  1294. if (_FindChildItem(hwndT, &nmdm,
  1295. SMNDM_FINDNEAREST | SMNDM_SELECT | SMNDM_KEYBOARD))
  1296. {
  1297. return TRUE;
  1298. }
  1299. }
  1300. }
  1301. // Always return TRUE to eat the message
  1302. return TRUE;
  1303. }
  1304. //
  1305. // Find the next/prev tabstop and tell it to select its first item.
  1306. // Keep doing this until we run out of controls or we find a control
  1307. // that is nonempty.
  1308. //
  1309. HWND CDesktopHost::_FindNextDlgChar(HWND hwndStart, SMNDIALOGMESSAGE *pnmdm, UINT smndm)
  1310. {
  1311. //
  1312. // See if there is a match in the hwndStart control.
  1313. //
  1314. if (_FindChildItem(hwndStart, pnmdm, SMNDM_FINDNEXTMATCH | SMNDM_KEYBOARD | smndm))
  1315. {
  1316. return hwndStart;
  1317. }
  1318. //
  1319. // Oh well, look for some other control, possibly wrapping back around
  1320. // to the start.
  1321. //
  1322. return _DlgFindItem(hwndStart, pnmdm,
  1323. SMNDM_FINDFIRSTMATCH | SMNDM_KEYBOARD | smndm,
  1324. GetNextDlgGroupItem,
  1325. DFI_FORWARDS | DFI_INCLUDESTARTLAST);
  1326. }
  1327. //
  1328. // Find the next item that begins with the typed letter and
  1329. // invoke it if it is unique.
  1330. //
  1331. BOOL CDesktopHost::_DlgNavigateChar(HWND hwndStart, MSG *pmsg)
  1332. {
  1333. SMNDIALOGMESSAGE nmdm;
  1334. nmdm.pmsg = pmsg; // other fields will be filled in by _FindChildItem
  1335. //
  1336. // See if there is a match in the hwndStart control.
  1337. //
  1338. HWND hwndFound = _FindNextDlgChar(hwndStart, &nmdm, SMNDM_SELECT);
  1339. if (hwndFound)
  1340. {
  1341. LRESULT idFound = nmdm.itemID;
  1342. //
  1343. // See if there is another match for this character.
  1344. // We are only looking, so don't pass SMNDM_SELECT.
  1345. //
  1346. HWND hwndFound2 = _FindNextDlgChar(hwndFound, &nmdm, 0);
  1347. if (hwndFound2 == hwndFound && nmdm.itemID == idFound)
  1348. {
  1349. //
  1350. // There is only one item that begins with this character.
  1351. // Invoke it!
  1352. //
  1353. UpdateWindow(_hwnd);
  1354. _FindChildItem(hwndFound2, &nmdm, SMNDM_INVOKECURRENTITEM | SMNDM_KEYBOARD);
  1355. }
  1356. }
  1357. return TRUE;
  1358. }
  1359. void CDesktopHost::_FilterMouseMove(MSG *pmsg, HWND hwndTarget)
  1360. {
  1361. if (!_fMouseEntered) {
  1362. _fMouseEntered = TRUE;
  1363. TRACKMOUSEEVENT tme;
  1364. tme.cbSize = sizeof(tme);
  1365. tme.dwFlags = TME_LEAVE;
  1366. tme.hwndTrack = pmsg->hwnd;
  1367. TrackMouseEvent(&tme);
  1368. }
  1369. //
  1370. // If the mouse is in the same place as last time, then ignore it.
  1371. // We can get spurious "no-motion" messages when the user is
  1372. // keyboard navigating.
  1373. //
  1374. if (_hwndLastMouse == pmsg->hwnd &&
  1375. _lParamLastMouse == pmsg->lParam)
  1376. {
  1377. return;
  1378. }
  1379. _hwndLastMouse = pmsg->hwnd;
  1380. _lParamLastMouse = pmsg->lParam;
  1381. //
  1382. // See if the target window can hit-test this item successfully.
  1383. //
  1384. LRESULT lres;
  1385. if (hwndTarget)
  1386. {
  1387. SMNDIALOGMESSAGE nmdm;
  1388. nmdm.pt.x = GET_X_LPARAM(pmsg->lParam);
  1389. nmdm.pt.y = GET_Y_LPARAM(pmsg->lParam);
  1390. lres = _FindChildItem(hwndTarget, &nmdm, SMNDM_HITTEST | SMNDM_SELECT);
  1391. }
  1392. else
  1393. {
  1394. lres = 0; // No target, so no hit-test
  1395. }
  1396. if (!lres)
  1397. {
  1398. _RemoveSelection();
  1399. }
  1400. else
  1401. {
  1402. //
  1403. // We selected a guy. Turn on the hover timer so we can
  1404. // do the auto-open thingie.
  1405. //
  1406. if (_fAutoCascade)
  1407. {
  1408. TRACKMOUSEEVENT tme;
  1409. tme.cbSize = sizeof(tme);
  1410. tme.dwFlags = TME_HOVER;
  1411. tme.hwndTrack = pmsg->hwnd;
  1412. if (!SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &tme.dwHoverTime, 0))
  1413. {
  1414. tme.dwHoverTime = HOVER_DEFAULT;
  1415. }
  1416. TrackMouseEvent(&tme);
  1417. }
  1418. }
  1419. }
  1420. void CDesktopHost::_FilterMouseLeave(MSG *pmsg, HWND hwndTarget)
  1421. {
  1422. _fMouseEntered = FALSE;
  1423. _hwndLastMouse = NULL;
  1424. // If we got a WM_MOUSELEAVE due to a menu popping up, don't
  1425. // give up the focus since it really didn't leave yet.
  1426. if (!_ppmTracking)
  1427. {
  1428. _RemoveSelection();
  1429. }
  1430. }
  1431. void CDesktopHost::_FilterMouseHover(MSG *pmsg, HWND hwndTarget)
  1432. {
  1433. _FindChildItem(hwndTarget, NULL, SMNDM_OPENCASCADE);
  1434. }
  1435. //
  1436. // Remove the menu selection and put it in the "dead space" above
  1437. // the first visible item.
  1438. //
  1439. void CDesktopHost::_RemoveSelection()
  1440. {
  1441. // Put focus on first valid child control
  1442. // The real control is the grandchild
  1443. HWND hwndChild = GetNextDlgTabItem(_hwnd, NULL, FALSE);
  1444. if (hwndChild)
  1445. {
  1446. // The inner ::GetWindow will always succeed
  1447. // because all our controls contain inner windows
  1448. // (and if they failed to create their inner window,
  1449. // they would've failed their WM_CREATE message)
  1450. HWND hwndInner = ::GetWindow(hwndChild, GW_CHILD);
  1451. SetFocus(hwndInner);
  1452. //
  1453. // Now lie to the control and make it think it lost
  1454. // focus. This will cause the selection to clear.
  1455. //
  1456. NMHDR hdr;
  1457. hdr.hwndFrom = hwndInner;
  1458. hdr.idFrom = GetDlgCtrlID(hwndInner);
  1459. hdr.code = NM_KILLFOCUS;
  1460. ::SendMessage(hwndChild, WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr);
  1461. }
  1462. }
  1463. HRESULT CDesktopHost::IsMenuMessage(MSG *pmsg)
  1464. {
  1465. if (_hwnd)
  1466. {
  1467. if (_ppmTracking)
  1468. {
  1469. HRESULT hr = _ppmTracking->IsMenuMessage(pmsg);
  1470. if (hr == E_FAIL)
  1471. {
  1472. _CleanupTrackShellMenu();
  1473. hr = S_FALSE;
  1474. }
  1475. if (hr == S_OK)
  1476. {
  1477. return hr;
  1478. }
  1479. }
  1480. if (_IsDialogMessage(pmsg))
  1481. {
  1482. return S_OK; // message handled
  1483. }
  1484. else
  1485. {
  1486. return S_FALSE; // message not handled
  1487. }
  1488. }
  1489. else
  1490. {
  1491. return E_FAIL; // Menu is gone
  1492. }
  1493. }
  1494. HRESULT CDesktopHost::TranslateMenuMessage(MSG *pmsg, LRESULT *plres)
  1495. {
  1496. if (_ppmTracking)
  1497. {
  1498. return _ppmTracking->TranslateMenuMessage(pmsg, plres);
  1499. }
  1500. return E_NOTIMPL;
  1501. }
  1502. // IServiceProvider::QueryService
  1503. STDMETHODIMP CDesktopHost::QueryService(REFGUID guidService, REFIID riid, void ** ppvObject)
  1504. {
  1505. if(IsEqualGUID(guidService,SID_SMenuPopup))
  1506. return QueryInterface(riid,ppvObject);
  1507. return E_FAIL;
  1508. }
  1509. // *** IOleCommandTarget ***
  1510. STDMETHODIMP CDesktopHost::QueryStatus (const GUID * pguidCmdGroup,
  1511. ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  1512. {
  1513. return E_NOTIMPL;
  1514. }
  1515. STDMETHODIMP CDesktopHost::Exec (const GUID * pguidCmdGroup,
  1516. DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  1517. {
  1518. if (IsEqualGUID(CLSID_MenuBand,*pguidCmdGroup))
  1519. {
  1520. switch (nCmdID)
  1521. {
  1522. case MBANDCID_REFRESH:
  1523. {
  1524. // There was a session or WM_DEVICECHANGE, we need to refresh our logoff options
  1525. NMHDR nm = { _hwnd, 0, SMN_REFRESHLOGOFF};
  1526. SHPropagateMessage(_hwnd, WM_NOTIFY, 0, (LPARAM)&nm, SPM_SEND | SPM_ONELEVEL);
  1527. OnNeedRepaint();
  1528. }
  1529. break;
  1530. default:
  1531. break;
  1532. }
  1533. }
  1534. return NOERROR;
  1535. }
  1536. // ITrayPriv2::ModifySMInfo
  1537. HRESULT CDesktopHost::ModifySMInfo(IN LPSMDATA psmd, IN OUT SMINFO *psminfo)
  1538. {
  1539. if (_hwndNewHandler)
  1540. {
  1541. SMNMMODIFYSMINFO nmsmi;
  1542. nmsmi.hdr.hwndFrom = _hwnd;
  1543. nmsmi.hdr.idFrom = 0;
  1544. nmsmi.hdr.code = SMN_MODIFYSMINFO;
  1545. nmsmi.psmd = psmd;
  1546. nmsmi.psminfo = psminfo;
  1547. SendMessage(_hwndNewHandler, WM_NOTIFY, 0, (LPARAM)&nmsmi);
  1548. }
  1549. return S_OK;
  1550. }
  1551. BOOL CDesktopHost::AddWin32Controls()
  1552. {
  1553. RegisterDesktopControlClasses();
  1554. // we create the controls with an arbitrary size, since we won't know how big we are until we pop up...
  1555. // Note that we do NOT set WS_EX_CONTROLPARENT because we want the
  1556. // dialog manager to think that our child controls are the interesting
  1557. // objects, not the inner grandchildren.
  1558. //
  1559. // Setting the control ID equal to the internal index number is just
  1560. // for the benefit of the test automation tools.
  1561. for (int i=0; i<ARRAYSIZE(_spm.panes); i++)
  1562. {
  1563. _spm.panes[i].hwnd = CreateWindowExW(0, _spm.panes[i].pszClassName,
  1564. NULL,
  1565. _spm.panes[i].dwStyle | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
  1566. 0, 0, _spm.panes[i].size.cx, _spm.panes[i].size.cy,
  1567. _hwnd, IntToPtr_(HMENU, i), NULL,
  1568. &_spm.panes[i]);
  1569. }
  1570. return TRUE;
  1571. }
  1572. void CDesktopHost::OnPaint(HDC hdc, BOOL bBackground)
  1573. {
  1574. }
  1575. void CDesktopHost::_ReadPaneSizeFromTheme(SMPANEDATA *psmpd)
  1576. {
  1577. RECT rc;
  1578. if (SUCCEEDED(GetThemeRect(psmpd->hTheme, psmpd->iPartId, 0, TMT_DEFAULTPANESIZE, &rc)))
  1579. {
  1580. // semi-hack to take care of the fact that if one the start panel parts is missing a property,
  1581. // themes will use the next level up (to the panel itself)
  1582. if ((rc.bottom != _spm.sizPanel.cy) || (rc.right != _spm.sizPanel.cx))
  1583. {
  1584. psmpd->size.cx = RECTWIDTH(rc);
  1585. psmpd->size.cy = RECTHEIGHT(rc);
  1586. }
  1587. }
  1588. }
  1589. void RemapSizeForHighDPI(SIZE *psiz)
  1590. {
  1591. static int iLPX, iLPY;
  1592. if (!iLPX || !iLPY)
  1593. {
  1594. HDC hdc = GetDC(NULL);
  1595. iLPX = GetDeviceCaps(hdc, LOGPIXELSX);
  1596. iLPY = GetDeviceCaps(hdc, LOGPIXELSY);
  1597. ReleaseDC(NULL, hdc);
  1598. }
  1599. // 96 DPI is small fonts, so scale based on the multiple of that.
  1600. psiz->cx = (psiz->cx * iLPX)/96;
  1601. psiz->cy = (psiz->cy * iLPY)/96;
  1602. }
  1603. void CDesktopHost::LoadResourceInt(UINT ids, LONG *pl)
  1604. {
  1605. TCHAR sz[64];
  1606. if (LoadString(hinstCabinet, ids, sz, ARRAYSIZE(sz)))
  1607. {
  1608. int i = StrToInt(sz);
  1609. if (i)
  1610. {
  1611. *pl = i;
  1612. }
  1613. }
  1614. }
  1615. void CDesktopHost::LoadPanelMetrics()
  1616. {
  1617. // initialize our copy of the panel metrics from the default...
  1618. _spm = g_spmDefault;
  1619. // Adjust for localization
  1620. LoadResourceInt(IDS_STARTPANE_TOTALHEIGHT, &_spm.sizPanel.cy);
  1621. LoadResourceInt(IDS_STARTPANE_TOTALWIDTH, &_spm.sizPanel.cx);
  1622. LoadResourceInt(IDS_STARTPANE_USERHEIGHT, &_spm.panes[SMPANETYPE_USER].size.cy);
  1623. LoadResourceInt(IDS_STARTPANE_MOREPROGHEIGHT,&_spm.panes[SMPANETYPE_MOREPROG].size.cy);
  1624. LoadResourceInt(IDS_STARTPANE_LOGOFFHEIGHT, &_spm.panes[SMPANETYPE_LOGOFF].size.cy);
  1625. // wacky raymondc logic to scale using the values in g_spmDefault as relative ratio's
  1626. // Now apply those numbers; widths are easy
  1627. int i;
  1628. for (i = 0; i < ARRAYSIZE(_spm.panes); i++)
  1629. {
  1630. _spm.panes[i].size.cx = MulDiv(g_spmDefault.panes[i].size.cx,
  1631. _spm.sizPanel.cx,
  1632. g_spmDefault.sizPanel.cx);
  1633. }
  1634. // Places gets all height not eaten by User and Logoff
  1635. _spm.panes[SMPANETYPE_PLACES].size.cy = _spm.sizPanel.cy
  1636. - _spm.panes[SMPANETYPE_USER].size.cy
  1637. - _spm.panes[SMPANETYPE_LOGOFF].size.cy;
  1638. // MFU gets Places minus More Programs
  1639. _spm.panes[SMPANETYPE_MFU].size.cy = _spm.panes[SMPANETYPE_PLACES].size.cy
  1640. - _spm.panes[SMPANETYPE_MOREPROG].size.cy;
  1641. // End of adjustments for localization
  1642. // load the theme file (which shouldn't be loaded yet)
  1643. ASSERT(!_hTheme);
  1644. // only try to use themes if our color depth is greater than 8bpp.
  1645. if (SHGetCurColorRes() > 8)
  1646. _hTheme = OpenThemeData(_hwnd, STARTPANELTHEME);
  1647. if (_hTheme)
  1648. {
  1649. // if we fail reading the size from the theme, it will fall back to the defaul size....
  1650. RECT rcT;
  1651. if (SUCCEEDED(GetThemeRect(_hTheme, 0, 0, TMT_DEFAULTPANESIZE, &rcT))) // the overall pane
  1652. {
  1653. _spm.sizPanel.cx = RECTWIDTH(rcT);
  1654. _spm.sizPanel.cy = RECTHEIGHT(rcT);
  1655. for (int i=0;i<ARRAYSIZE(_spm.panes);i++)
  1656. {
  1657. _spm.panes[i].hTheme = _hTheme;
  1658. _ReadPaneSizeFromTheme(&_spm.panes[i]);
  1659. }
  1660. }
  1661. }
  1662. // ASSERT that the layout matches up somewhat...
  1663. ASSERT(_spm.sizPanel.cx == _spm.panes[SMPANETYPE_USER].size.cx);
  1664. ASSERT(_spm.sizPanel.cx == _spm.panes[SMPANETYPE_MFU].size.cx + _spm.panes[SMPANETYPE_PLACES].size.cx );
  1665. ASSERT(_spm.sizPanel.cx == _spm.panes[SMPANETYPE_LOGOFF].size.cx);
  1666. ASSERT(_spm.panes[SMPANETYPE_MOREPROG].size.cx == _spm.panes[SMPANETYPE_MFU].size.cx);
  1667. TraceMsg(TF_DV2HOST, "sizPanel.cy = %d, user = %d, MFU =%d, moreprog=%d, logoff=%d",
  1668. _spm.sizPanel.cy, _spm.panes[SMPANETYPE_USER].size.cy, _spm.panes[SMPANETYPE_MFU].size.cy,
  1669. _spm.panes[SMPANETYPE_MOREPROG].size.cy, _spm.panes[SMPANETYPE_LOGOFF].size.cy);
  1670. ASSERT(_spm.sizPanel.cy == _spm.panes[SMPANETYPE_USER].size.cy + _spm.panes[SMPANETYPE_MFU].size.cy + _spm.panes[SMPANETYPE_MOREPROG].size.cy + _spm.panes[SMPANETYPE_LOGOFF].size.cy);
  1671. // one final pass to adjust everything for DPI
  1672. // note that things may not match up exactly after this due to rounding, but _ComputeActualSize can deal
  1673. RemapSizeForHighDPI(&_spm.sizPanel);
  1674. for (int i=0;i<ARRAYSIZE(_spm.panes);i++)
  1675. {
  1676. RemapSizeForHighDPI(&_spm.panes[i].size);
  1677. }
  1678. SetRect(&_rcDesired, 0, 0, _spm.sizPanel.cx, _spm.sizPanel.cy);
  1679. }
  1680. void CDesktopHost::OnCreate(HWND hwnd)
  1681. {
  1682. _hwnd = hwnd;
  1683. TraceMsg(TF_DV2HOST, "Entering CDesktopHost::OnCreate");
  1684. // Add the controls and background images
  1685. AddWin32Controls();
  1686. }
  1687. void CDesktopHost::OnDestroy()
  1688. {
  1689. _hwnd = NULL;
  1690. if (_hTheme)
  1691. {
  1692. CloseThemeData(_hTheme);
  1693. _hTheme = NULL;
  1694. }
  1695. }
  1696. void CDesktopHost::OnSetFocus(HWND hwndLose)
  1697. {
  1698. if (!_RestoreChildFocus())
  1699. {
  1700. _RemoveSelection();
  1701. }
  1702. }
  1703. LRESULT CDesktopHost::OnCommandInvoked(NMHDR *pnm)
  1704. {
  1705. // Invoking a command indicates explicit user activity
  1706. Tray_UnlockStartPane();
  1707. PSMNMCOMMANDINVOKED pci = (PSMNMCOMMANDINVOKED)pnm;
  1708. ExplorerPlaySound(TEXT("MenuCommand"));
  1709. BOOL fFade = FALSE;
  1710. if (SystemParametersInfo(SPI_GETSELECTIONFADE, 0, &fFade, 0) && fFade)
  1711. {
  1712. if (!_ptFader)
  1713. {
  1714. CoCreateInstance(CLSID_FadeTask, NULL, CLSCTX_INPROC, IID_PPV_ARG(IFadeTask, &_ptFader));
  1715. }
  1716. if (_ptFader)
  1717. {
  1718. _ptFader->FadeRect(&pci->rcItem);
  1719. }
  1720. }
  1721. return OnSelect(MPOS_EXECUTE);
  1722. }
  1723. LRESULT CDesktopHost::OnFilterOptions(NMHDR *pnm)
  1724. {
  1725. PSMNFILTEROPTIONS popt = (PSMNFILTEROPTIONS)pnm;
  1726. if ((popt->smnop & SMNOP_LOGOFF) &&
  1727. !_ShowStartMenuLogoff())
  1728. {
  1729. popt->smnop &= ~SMNOP_LOGOFF;
  1730. }
  1731. if ((popt->smnop & SMNOP_TURNOFF) &&
  1732. !_ShowStartMenuShutdown())
  1733. {
  1734. popt->smnop &= ~SMNOP_TURNOFF;
  1735. }
  1736. if ((popt->smnop & SMNOP_DISCONNECT) &&
  1737. !_ShowStartMenuDisconnect())
  1738. {
  1739. popt->smnop &= ~SMNOP_DISCONNECT;
  1740. }
  1741. if ((popt->smnop & SMNOP_EJECT) &&
  1742. !_ShowStartMenuEject())
  1743. {
  1744. popt->smnop &= ~SMNOP_EJECT;
  1745. }
  1746. return 0;
  1747. }
  1748. LRESULT CDesktopHost::OnTrackShellMenu(NMHDR *pnm)
  1749. {
  1750. // Opening a menu indicates explicit user activity
  1751. Tray_UnlockStartPane();
  1752. PSMNTRACKSHELLMENU ptsm = CONTAINING_RECORD(pnm, SMNTRACKSHELLMENU, hdr);
  1753. HRESULT hr;
  1754. _hwndTracking = ptsm->hdr.hwndFrom;
  1755. _itemTracking = ptsm->itemID;
  1756. _hwndAltTracking = NULL;
  1757. _itemAltTracking = 0;
  1758. //
  1759. // Decide which direction we need to pop.
  1760. //
  1761. DWORD dwFlags;
  1762. if (GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
  1763. {
  1764. dwFlags = MPPF_LEFT;
  1765. }
  1766. else
  1767. {
  1768. dwFlags = MPPF_RIGHT;
  1769. }
  1770. // Don't _CleanupTrackShellMenu because that will undo some of the
  1771. // work we've already done and make the client think that the popup
  1772. // they requested got dismissed.
  1773. //
  1774. // ISSUE raymondc: actually this abandons the trackpopupmenu that
  1775. // may already be in progress - its mouse UI gets messed up as a result.
  1776. //
  1777. ATOMICRELEASE(_ppmTracking);
  1778. if (_hwndTracking == _spm.panes[SMPANETYPE_MOREPROG].hwnd)
  1779. {
  1780. if (_ppmPrograms && _ppmPrograms->IsSame(ptsm->psm))
  1781. {
  1782. // It's already in our cache, woo-hoo!
  1783. hr = S_OK;
  1784. }
  1785. else
  1786. {
  1787. ATOMICRELEASE(_ppmPrograms);
  1788. _SubclassTrackShellMenu(ptsm->psm);
  1789. hr = CPopupMenu_CreateInstance(ptsm->psm, GetUnknown(), _hwnd, &_ppmPrograms);
  1790. }
  1791. if (SUCCEEDED(hr))
  1792. {
  1793. _ppmTracking = _ppmPrograms;
  1794. _ppmTracking->AddRef();
  1795. }
  1796. }
  1797. else
  1798. {
  1799. _SubclassTrackShellMenu(ptsm->psm);
  1800. hr = CPopupMenu_CreateInstance(ptsm->psm, GetUnknown(), _hwnd, &_ppmTracking);
  1801. }
  1802. if (SUCCEEDED(hr))
  1803. {
  1804. hr = _ppmTracking->Popup(&ptsm->rcExclude, ptsm->dwFlags | dwFlags);
  1805. }
  1806. if (FAILED(hr))
  1807. {
  1808. // In addition to freeing any partially-allocated objects,
  1809. // this also sends a SMN_SHELLMENUDISMISSED so the client
  1810. // knows to remove the highlight from the item being cascaded
  1811. _CleanupTrackShellMenu();
  1812. }
  1813. return 0;
  1814. }
  1815. HRESULT CDesktopHost::_MenuMouseFilter(LPSMDATA psmd, BOOL fRemove, LPMSG pmsg)
  1816. {
  1817. HRESULT hr = S_FALSE;
  1818. SMNDIALOGMESSAGE nmdm;
  1819. enum {
  1820. WHERE_IGNORE, // ignore this message
  1821. WHERE_OUTSIDE, // outside the Start Menu entirely
  1822. WHERE_DEADSPOT, // a dead spot on the Start Menu
  1823. WHERE_ONSELF, // over the item that initiated the popup
  1824. WHERE_ONOTHER, // over some other item in the Start Menu
  1825. } uiWhere;
  1826. //
  1827. // Figure out where the mouse is.
  1828. //
  1829. // Note: ChildWindowFromPointEx searches only immediate
  1830. // children; it does not search grandchildren. Fortunately, that's
  1831. // exactly what we want...
  1832. //
  1833. HWND hwndTarget = NULL;
  1834. if (fRemove)
  1835. {
  1836. if (psmd->punk)
  1837. {
  1838. // Inside a menuband - mouse has left our window
  1839. uiWhere = WHERE_OUTSIDE;
  1840. }
  1841. else
  1842. {
  1843. POINT pt = { GET_X_LPARAM(pmsg->lParam), GET_Y_LPARAM(pmsg->lParam) };
  1844. ScreenToClient(_hwnd, &pt);
  1845. hwndTarget = ChildWindowFromPointEx(_hwnd, pt, CWP_SKIPINVISIBLE);
  1846. if (hwndTarget == _hwnd)
  1847. {
  1848. uiWhere = WHERE_DEADSPOT;
  1849. }
  1850. else if (hwndTarget)
  1851. {
  1852. LRESULT lres;
  1853. nmdm.pt = pt;
  1854. HWND hwndChild = ::GetWindow(hwndTarget, GW_CHILD);
  1855. MapWindowPoints(_hwnd, hwndChild, &nmdm.pt, 1);
  1856. lres = _FindChildItem(hwndTarget, &nmdm, SMNDM_HITTEST | SMNDM_SELECT);
  1857. if (lres)
  1858. {
  1859. // Mouse is over something; is it over the current item?
  1860. if (nmdm.itemID == _itemTracking &&
  1861. hwndTarget == _hwndTracking)
  1862. {
  1863. uiWhere = WHERE_ONSELF;
  1864. }
  1865. else
  1866. {
  1867. uiWhere = WHERE_ONOTHER;
  1868. }
  1869. }
  1870. else
  1871. {
  1872. uiWhere = WHERE_DEADSPOT;
  1873. }
  1874. }
  1875. else
  1876. {
  1877. // ChildWindowFromPoint failed - user has left the Start Menu
  1878. uiWhere = WHERE_OUTSIDE;
  1879. }
  1880. }
  1881. }
  1882. else
  1883. {
  1884. // Ignore PM_NOREMOVE messages; we'll pay attention to them when
  1885. // they are PM_REMOVE'd.
  1886. uiWhere = WHERE_IGNORE;
  1887. }
  1888. //
  1889. // Now do appropriate stuff depending on where the mouse is.
  1890. //
  1891. switch (uiWhere)
  1892. {
  1893. case WHERE_IGNORE:
  1894. break;
  1895. case WHERE_OUTSIDE:
  1896. //
  1897. // If you've left the menu entirely, then we return the menu to
  1898. // its original state, which is to say, as if you are hovering
  1899. // over the item that caused the popup to open in the first place.
  1900. // as being in a dead zone.
  1901. //
  1902. // FALL THROUGH
  1903. goto L_WHERE_ONSELF_HOVER;
  1904. case WHERE_DEADSPOT:
  1905. // To avoid annoying flicker as the user wanders over dead spots,
  1906. // we ignore mouse motion over them (but dismiss if they click
  1907. // in a dead spot).
  1908. if (pmsg->message == WM_LBUTTONDOWN ||
  1909. pmsg->message == WM_RBUTTONDOWN)
  1910. {
  1911. // Must explicitly dismiss; if we let it fall through to the
  1912. // default handler, then it will dismiss for us, causing the
  1913. // entire Start Menu to go away instead of just the tracking
  1914. // part.
  1915. _DismissTrackShellMenu();
  1916. hr = S_OK;
  1917. }
  1918. break;
  1919. case WHERE_ONSELF:
  1920. if (pmsg->message == WM_LBUTTONDOWN ||
  1921. pmsg->message == WM_RBUTTONDOWN)
  1922. {
  1923. _DismissTrackShellMenu();
  1924. hr = S_OK;
  1925. }
  1926. else
  1927. {
  1928. L_WHERE_ONSELF_HOVER:
  1929. _hwndAltTracking = NULL;
  1930. _itemAltTracking = 0;
  1931. nmdm.itemID = _itemTracking;
  1932. _FindChildItem(_hwndTracking, &nmdm, SMNDM_FINDITEMID | SMNDM_SELECT);
  1933. KillTimer(_hwnd, IDT_MENUCHANGESEL);
  1934. }
  1935. break;
  1936. case WHERE_ONOTHER:
  1937. if (pmsg->message == WM_LBUTTONDOWN ||
  1938. pmsg->message == WM_RBUTTONDOWN)
  1939. {
  1940. _DismissTrackShellMenu();
  1941. hr = S_OK;
  1942. }
  1943. else if (hwndTarget == _hwndAltTracking && nmdm.itemID == _itemAltTracking)
  1944. {
  1945. // Don't restart the timer if the user wiggles the mouse
  1946. // within a single item
  1947. }
  1948. else
  1949. {
  1950. _hwndAltTracking = hwndTarget;
  1951. _itemAltTracking = nmdm.itemID;
  1952. DWORD dwHoverTime;
  1953. if (!SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &dwHoverTime, 0))
  1954. {
  1955. dwHoverTime = 0;
  1956. }
  1957. SetTimer(_hwnd, IDT_MENUCHANGESEL, dwHoverTime, 0);
  1958. }
  1959. break;
  1960. }
  1961. return hr;
  1962. }
  1963. void CDesktopHost::_OnMenuChangeSel()
  1964. {
  1965. KillTimer(_hwnd, IDT_MENUCHANGESEL);
  1966. _DismissTrackShellMenu();
  1967. }
  1968. void CDesktopHost::_SaveChildFocus()
  1969. {
  1970. if (!_hwndChildFocus)
  1971. {
  1972. HWND hwndFocus = GetFocus();
  1973. if (hwndFocus && IsChild(_hwnd, hwndFocus))
  1974. {
  1975. _hwndChildFocus = hwndFocus;
  1976. }
  1977. }
  1978. }
  1979. // Returns non-NULL if focus was successfully restored
  1980. HWND CDesktopHost::_RestoreChildFocus()
  1981. {
  1982. HWND hwndRet = NULL;
  1983. if (IsWindow(_hwndChildFocus))
  1984. {
  1985. HWND hwndT = _hwndChildFocus;
  1986. _hwndChildFocus = NULL;
  1987. hwndRet = SetFocus(hwndT);
  1988. }
  1989. return hwndRet;
  1990. }
  1991. void CDesktopHost::_DestroyClipBalloon()
  1992. {
  1993. if (_hwndClipBalloon)
  1994. {
  1995. DestroyWindow(_hwndClipBalloon);
  1996. _hwndClipBalloon = NULL;
  1997. }
  1998. }
  1999. void CDesktopHost::_OnDismiss(BOOL bDestroy)
  2000. {
  2001. // Break the recursion loop: Call IMenuPopup::OnSelect only if the
  2002. // window was previously visible.
  2003. _fOpen = FALSE;
  2004. if (ShowWindow(_hwnd, SW_HIDE))
  2005. {
  2006. if (_ppmTracking)
  2007. {
  2008. _ppmTracking->OnSelect(MPOS_FULLCANCEL);
  2009. }
  2010. OnSelect(MPOS_FULLCANCEL);
  2011. NMHDR nm = { _hwnd, 0, SMN_DISMISS };
  2012. SHPropagateMessage(_hwnd, WM_NOTIFY, 0, (LPARAM)&nm, SPM_SEND | SPM_ONELEVEL);
  2013. _DestroyClipBalloon();
  2014. // Allow clicking on Start button to pop the menu immediately
  2015. Tray_SetStartPaneActive(FALSE);
  2016. // Don't try to preserve child focus across popups
  2017. _hwndChildFocus = NULL;
  2018. Tray_OnStartMenuDismissed();
  2019. NotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, _hwnd, OBJID_CLIENT, CHILDID_SELF);
  2020. }
  2021. if (bDestroy)
  2022. {
  2023. v_hwndStartPane = NULL;
  2024. ASSERT(GetWindowThreadProcessId(_hwnd, NULL) == GetCurrentThreadId());
  2025. DestroyWindow(_hwnd);
  2026. }
  2027. }
  2028. HRESULT CDesktopHost::Build()
  2029. {
  2030. HRESULT hr = S_OK;
  2031. if (_hwnd == NULL)
  2032. {
  2033. _hwnd = _Create();
  2034. if (_hwnd)
  2035. {
  2036. // Tell all our child windows it's time to reinitialize
  2037. NMHDR nm = { _hwnd, 0, SMN_INITIALUPDATE };
  2038. SHPropagateMessage(_hwnd, WM_NOTIFY, 0, (LPARAM)&nm, SPM_SEND | SPM_ONELEVEL);
  2039. }
  2040. }
  2041. if (_hwnd == NULL)
  2042. {
  2043. hr = E_OUTOFMEMORY;
  2044. }
  2045. return hr;
  2046. }
  2047. //*****************************************************************
  2048. //
  2049. // CDeskHostShellMenuCallback
  2050. //
  2051. // Create a wrapper IShellMenuCallback that picks off mouse
  2052. // messages.
  2053. //
  2054. class CDeskHostShellMenuCallback
  2055. : public CUnknown
  2056. , public IShellMenuCallback
  2057. , public IServiceProvider
  2058. , public CObjectWithSite
  2059. {
  2060. friend class CDesktopHost;
  2061. public:
  2062. // *** IUnknown ***
  2063. STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj);
  2064. STDMETHODIMP_(ULONG) AddRef(void) { return CUnknown::AddRef(); }
  2065. STDMETHODIMP_(ULONG) Release(void) { return CUnknown::Release(); }
  2066. // *** IShellMenuCallback ***
  2067. STDMETHODIMP CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  2068. // *** IObjectWithSite ***
  2069. STDMETHODIMP SetSite(IUnknown *punkSite);
  2070. // *** IServiceProvider ***
  2071. STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void ** ppvObject);
  2072. private:
  2073. CDeskHostShellMenuCallback(CDesktopHost *pdh)
  2074. {
  2075. _pdh = pdh; _pdh->AddRef();
  2076. }
  2077. ~CDeskHostShellMenuCallback()
  2078. {
  2079. ATOMICRELEASE(_pdh);
  2080. IUnknown_SetSite(_psmcPrev, NULL);
  2081. ATOMICRELEASE(_psmcPrev);
  2082. }
  2083. IShellMenuCallback *_psmcPrev;
  2084. CDesktopHost *_pdh;
  2085. };
  2086. HRESULT CDeskHostShellMenuCallback::QueryInterface(REFIID riid, void **ppvObj)
  2087. {
  2088. static const QITAB qit[] =
  2089. {
  2090. QITABENT(CDeskHostShellMenuCallback, IShellMenuCallback),
  2091. QITABENT(CDeskHostShellMenuCallback, IObjectWithSite),
  2092. QITABENT(CDeskHostShellMenuCallback, IServiceProvider),
  2093. { 0 },
  2094. };
  2095. return QISearch(this, qit, riid, ppvObj);
  2096. }
  2097. BOOL FeatureEnabled(LPTSTR pszFeature)
  2098. {
  2099. return SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, pszFeature,
  2100. FALSE, // Don't ignore HKCU
  2101. FALSE); // Disable this cool feature.
  2102. }
  2103. HRESULT CDeskHostShellMenuCallback::CallbackSM(LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2104. {
  2105. switch (uMsg)
  2106. {
  2107. case SMC_MOUSEFILTER:
  2108. if (_pdh)
  2109. return _pdh->_MenuMouseFilter(psmd, (BOOL)wParam, (MSG*)lParam);
  2110. case SMC_GETSFINFOTIP:
  2111. if (!FeatureEnabled(TEXT("ShowInfoTip")))
  2112. return E_FAIL; // E_FAIL means don't show. S_FALSE means show default
  2113. break;
  2114. }
  2115. if (_psmcPrev)
  2116. return _psmcPrev->CallbackSM(psmd, uMsg, wParam, lParam);
  2117. return S_FALSE;
  2118. }
  2119. HRESULT CDeskHostShellMenuCallback::SetSite(IUnknown *punkSite)
  2120. {
  2121. CObjectWithSite::SetSite(punkSite);
  2122. // Each time our site changes, reassert ourselves as the site of
  2123. // the inner object so he can try a new QueryService.
  2124. IUnknown_SetSite(_psmcPrev, this->GetUnknown());
  2125. // If the game is over, break our backreference
  2126. if (!punkSite)
  2127. {
  2128. ATOMICRELEASE(_pdh);
  2129. }
  2130. return S_OK;
  2131. }
  2132. HRESULT CDeskHostShellMenuCallback::QueryService(REFGUID guidService, REFIID riid, void ** ppvObject)
  2133. {
  2134. return IUnknown_QueryService(_punkSite, guidService, riid, ppvObject);
  2135. }
  2136. void CDesktopHost::_SubclassTrackShellMenu(IShellMenu *psm)
  2137. {
  2138. CDeskHostShellMenuCallback *psmc = new CDeskHostShellMenuCallback(this);
  2139. if (psmc)
  2140. {
  2141. UINT uId, uIdAncestor;
  2142. DWORD dwFlags;
  2143. if (SUCCEEDED(psm->GetMenuInfo(&psmc->_psmcPrev, &uId, &uIdAncestor, &dwFlags)))
  2144. {
  2145. psm->Initialize(psmc, uId, uIdAncestor, dwFlags);
  2146. }
  2147. psmc->Release();
  2148. }
  2149. }
  2150. STDAPI DesktopV2_Build(void *pvStartPane)
  2151. {
  2152. HRESULT hr = E_POINTER;
  2153. if (pvStartPane)
  2154. {
  2155. hr = reinterpret_cast<CDesktopHost *>(pvStartPane)->Build();
  2156. }
  2157. return hr;
  2158. }
  2159. STDAPI DesktopV2_Create(
  2160. IMenuPopup **ppmp, IMenuBand **ppmb, void **ppvStartPane)
  2161. {
  2162. *ppmp = NULL;
  2163. *ppmb = NULL;
  2164. HRESULT hr;
  2165. CDesktopHost *pdh = new CDesktopHost;
  2166. if (pdh)
  2167. {
  2168. *ppvStartPane = pdh;
  2169. hr = pdh->Initialize();
  2170. if (SUCCEEDED(hr))
  2171. {
  2172. hr = pdh->QueryInterface(IID_PPV_ARG(IMenuPopup, ppmp));
  2173. if (SUCCEEDED(hr))
  2174. {
  2175. hr = pdh->QueryInterface(IID_PPV_ARG(IMenuBand, ppmb));
  2176. }
  2177. }
  2178. pdh->GetUnknown()->Release();
  2179. }
  2180. else
  2181. {
  2182. hr = E_OUTOFMEMORY;
  2183. }
  2184. if (FAILED(hr))
  2185. {
  2186. ATOMICRELEASE(*ppmp);
  2187. ATOMICRELEASE(*ppmb);
  2188. ppvStartPane = NULL;
  2189. }
  2190. return hr;
  2191. }
  2192. HBITMAP CreateMirroredBitmap( HBITMAP hbmOrig)
  2193. {
  2194. HDC hdc, hdcMem1, hdcMem2;
  2195. HBITMAP hbm = NULL, hOld_bm1, hOld_bm2;
  2196. BITMAP bm;
  2197. int IncOne = 0;
  2198. if (!hbmOrig)
  2199. return NULL;
  2200. if (!GetObject(hbmOrig, sizeof(BITMAP), &bm))
  2201. return NULL;
  2202. // Grab the screen DC
  2203. hdc = GetDC(NULL);
  2204. if (hdc)
  2205. {
  2206. hdcMem1 = CreateCompatibleDC(hdc);
  2207. if (!hdcMem1)
  2208. {
  2209. ReleaseDC(NULL, hdc);
  2210. return NULL;
  2211. }
  2212. hdcMem2 = CreateCompatibleDC(hdc);
  2213. if (!hdcMem2)
  2214. {
  2215. DeleteDC(hdcMem1);
  2216. ReleaseDC(NULL, hdc);
  2217. return NULL;
  2218. }
  2219. hbm = CreateCompatibleBitmap(hdc, bm.bmWidth, bm.bmHeight);
  2220. if (!hbm)
  2221. {
  2222. ReleaseDC(NULL, hdc);
  2223. DeleteDC(hdcMem1);
  2224. DeleteDC(hdcMem2);
  2225. return NULL;
  2226. }
  2227. //
  2228. // Flip the bitmap
  2229. //
  2230. hOld_bm1 = (HBITMAP)SelectObject(hdcMem1, hbmOrig);
  2231. hOld_bm2 = (HBITMAP)SelectObject(hdcMem2 , hbm );
  2232. SET_DC_RTL_MIRRORED(hdcMem2);
  2233. BitBlt(hdcMem2, IncOne, 0, bm.bmWidth, bm.bmHeight, hdcMem1, 0, 0, SRCCOPY);
  2234. SelectObject(hdcMem1, hOld_bm1 );
  2235. SelectObject(hdcMem1, hOld_bm2 );
  2236. DeleteDC(hdcMem1);
  2237. DeleteDC(hdcMem2);
  2238. ReleaseDC(NULL, hdc);
  2239. }
  2240. return hbm;
  2241. }