Leaked source code of windows server 2003
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.

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