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.

801 lines
24 KiB

  1. #include "stdafx.h"
  2. #include "sfthost.h"
  3. #include "uxtheme.h"
  4. #include "uxthemep.h"
  5. #include "rcids.h"
  6. // WARNING! Must be in sync with c_rgidmLegacy
  7. #define NUM_TBBUTTON_IMAGES 3
  8. static const TBBUTTON tbButtonsCreate [] =
  9. {
  10. {0, SMNLC_EJECT, TBSTATE_ENABLED, BTNS_SHOWTEXT|BTNS_AUTOSIZE, {0,0}, IDS_LOGOFF_TIP_EJECT, 0},
  11. {1, SMNLC_LOGOFF, TBSTATE_ENABLED, BTNS_SHOWTEXT|BTNS_AUTOSIZE, {0,0}, IDS_LOGOFF_TIP_LOGOFF, 1},
  12. {2, SMNLC_TURNOFF, TBSTATE_ENABLED, BTNS_SHOWTEXT|BTNS_AUTOSIZE, {0,0}, IDS_LOGOFF_TIP_SHUTDOWN, 2},
  13. {2,SMNLC_DISCONNECT,TBSTATE_ENABLED, BTNS_SHOWTEXT|BTNS_AUTOSIZE, {0,0}, IDS_LOGOFF_TIP_DISCONNECT, 3},
  14. };
  15. // WARNING! Must be in sync with tbButtonsCreate
  16. static const UINT c_rgidmLegacy[] =
  17. {
  18. IDM_EJECTPC,
  19. IDM_LOGOFF,
  20. IDM_EXITWIN,
  21. IDM_MU_DISCONNECT,
  22. };
  23. class CLogoffPane
  24. : public CUnknown
  25. , public CAccessible
  26. {
  27. public:
  28. // *** IUnknown ***
  29. STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj);
  30. STDMETHODIMP_(ULONG) AddRef(void) { return CUnknown::AddRef(); }
  31. STDMETHODIMP_(ULONG) Release(void) { return CUnknown::Release(); }
  32. // *** IAccessible overridden methods ***
  33. STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut);
  34. STDMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction);
  35. CLogoffPane::CLogoffPane();
  36. CLogoffPane::~CLogoffPane();
  37. static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  38. LRESULT _OnCreate(LPARAM lParam);
  39. void _OnDestroy();
  40. LRESULT _OnNCCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  41. LRESULT _OnNCDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  42. LRESULT _OnNotify(NMHDR *pnm);
  43. LRESULT _OnCommand(int id);
  44. LRESULT _OnCustomDraw(NMTBCUSTOMDRAW *pnmcd);
  45. LRESULT _OnSMNFindItem(PSMNDIALOGMESSAGE pdm);
  46. LRESULT _OnSMNFindItemWorker(PSMNDIALOGMESSAGE pdm);
  47. LRESULT _OnSysColorChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  48. LRESULT _OnDisplayChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  49. LRESULT _OnSettingChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  50. void _InitMetrics();
  51. LRESULT _OnSize(int x, int y);
  52. private:
  53. HWND _hwnd;
  54. HWND _hwndTB;
  55. HWND _hwndTT; //Tooltip window.
  56. COLORREF _clr;
  57. int _colorHighlight;
  58. int _colorHighlightText;
  59. HTHEME _hTheme;
  60. BOOL _fSettingHotItem;
  61. MARGINS _margins;
  62. // helper functions
  63. int _GetCurButton();
  64. LRESULT _NextVisibleButton(PSMNDIALOGMESSAGE pdm, int i, int direction);
  65. BOOL _IsButtonHidden(int i);
  66. TCHAR _GetButtonAccelerator(int i);
  67. void _RightAlign();
  68. void _ApplyOptions();
  69. BOOL _SetTBButtons(int id, UINT iMsg);
  70. BOOL _ThemedSetTBButtons(int iState, UINT iMsg);
  71. friend BOOL CLogoffPane_RegisterClass();
  72. };
  73. CLogoffPane::CLogoffPane()
  74. {
  75. ASSERT(_hwndTB == NULL);
  76. ASSERT(_hwndTT == NULL);
  77. _clr = CLR_INVALID;
  78. }
  79. CLogoffPane::~CLogoffPane()
  80. {
  81. }
  82. HRESULT CLogoffPane::QueryInterface(REFIID riid, void * *ppvOut)
  83. {
  84. static const QITAB qit[] = {
  85. QITABENT(CLogoffPane, IAccessible),
  86. QITABENT(CLogoffPane, IDispatch), // IAccessible derives from IDispatch
  87. { 0 },
  88. };
  89. return QISearch(this, qit, riid, ppvOut);
  90. }
  91. LRESULT CALLBACK CLogoffPane::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  92. {
  93. CLogoffPane *self = reinterpret_cast<CLogoffPane *>(GetWindowPtr0(hwnd));
  94. switch (uMsg)
  95. {
  96. case WM_NCCREATE:
  97. return self->_OnNCCreate(hwnd, uMsg, wParam, lParam);
  98. case WM_CREATE:
  99. return self->_OnCreate(lParam);
  100. case WM_DESTROY:
  101. self->_OnDestroy();
  102. break;
  103. case WM_NCDESTROY:
  104. return self->_OnNCDestroy(hwnd, uMsg, wParam, lParam);
  105. case WM_ERASEBKGND:
  106. {
  107. RECT rc;
  108. GetClientRect(hwnd, &rc);
  109. if (self->_hTheme)
  110. {
  111. DrawThemeBackground(self->_hTheme, (HDC)wParam, SPP_LOGOFF, 0, &rc, 0);
  112. }
  113. else
  114. {
  115. SHFillRectClr((HDC)wParam, &rc, GetSysColor(COLOR_MENU));
  116. DrawEdge((HDC)wParam, &rc, EDGE_ETCHED, BF_TOP);
  117. }
  118. return 1;
  119. }
  120. case WM_COMMAND:
  121. return self->_OnCommand(GET_WM_COMMAND_ID(wParam, lParam));
  122. case WM_NOTIFY:
  123. return self->_OnNotify((NMHDR*)(lParam));
  124. case WM_SIZE:
  125. return self->_OnSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  126. case WM_SYSCOLORCHANGE:
  127. return self->_OnSysColorChange(hwnd, uMsg, wParam, lParam);
  128. case WM_DISPLAYCHANGE:
  129. return self->_OnDisplayChange(hwnd, uMsg, wParam, lParam);
  130. case WM_SETTINGCHANGE:
  131. return self->_OnSettingChange(hwnd, uMsg, wParam, lParam);
  132. }
  133. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  134. }
  135. LRESULT CLogoffPane::_OnNCCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  136. {
  137. LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
  138. CLogoffPane *self = new CLogoffPane;
  139. if (self)
  140. {
  141. SetWindowPtr0(hwnd, self);
  142. self->_hwnd = hwnd;
  143. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  144. }
  145. return FALSE;
  146. }
  147. void AddBitmapToToolbar(HWND hwndTB, HBITMAP hBitmap, int cxTotal, int cy, UINT iMsg)
  148. {
  149. HIMAGELIST himl = ImageList_Create(cxTotal / NUM_TBBUTTON_IMAGES, cy, ILC_COLOR32, 0, NUM_TBBUTTON_IMAGES);
  150. if (himl)
  151. {
  152. ImageList_Add(himl, hBitmap, NULL);
  153. HIMAGELIST himlPrevious = (HIMAGELIST) SendMessage(hwndTB, iMsg, 0, (LPARAM)himl);
  154. if (himlPrevious)
  155. {
  156. ImageList_Destroy(himlPrevious);
  157. }
  158. }
  159. }
  160. BOOL CLogoffPane::_SetTBButtons(int id, UINT iMsg)
  161. {
  162. HBITMAP hBitmap = LoadBitmap(_Module.GetModuleInstance(), MAKEINTRESOURCE(id));
  163. if (hBitmap)
  164. {
  165. BITMAP bm;
  166. if (GetObject(hBitmap, sizeof(BITMAP), &bm))
  167. {
  168. AddBitmapToToolbar(_hwndTB, hBitmap, bm.bmWidth, bm.bmHeight, iMsg);
  169. }
  170. DeleteObject(hBitmap);
  171. }
  172. return BOOLIFY(hBitmap);
  173. }
  174. BOOL CLogoffPane::_ThemedSetTBButtons(int iState, UINT iMsg)
  175. {
  176. BOOL bRet = FALSE;
  177. HDC hdcScreen = GetDC(NULL);
  178. HDC hdc = CreateCompatibleDC(hdcScreen);
  179. if (hdc)
  180. {
  181. SIZE siz;
  182. if (SUCCEEDED(GetThemePartSize(_hTheme, NULL, SPP_LOGOFFBUTTONS, iState,
  183. NULL, TS_TRUE, &siz)))
  184. {
  185. void *pvDestBits;
  186. BITMAPINFO bi = {0};
  187. bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  188. bi.bmiHeader.biWidth = siz.cx;
  189. bi.bmiHeader.biHeight = siz.cy;
  190. bi.bmiHeader.biPlanes = 1;
  191. bi.bmiHeader.biBitCount = 32;
  192. bi.bmiHeader.biCompression = BI_RGB;
  193. // Create a DIB Section so we can force it to be 32 bits, and preserve the alpha channel.
  194. HBITMAP hbm = CreateDIBSection(hdcScreen, &bi, DIB_RGB_COLORS, &pvDestBits, NULL, 0);
  195. if (hbm)
  196. {
  197. HBITMAP hbmOld = (HBITMAP) SelectObject(hdc, hbm);
  198. RECT rc={0,0,siz.cx,siz.cy};
  199. // draws into the DC, which updates the bitmap
  200. DTBGOPTS dtbg = {sizeof(DTBGOPTS), DTBG_DRAWSOLID, 0,}; // tell drawthemebackground to preserve the alpha channel
  201. bRet = SUCCEEDED(DrawThemeBackgroundEx(_hTheme, hdc, SPP_LOGOFFBUTTONS, iState, &rc, &dtbg));
  202. SelectObject(hdc, hbmOld); // unselect the bitmap, so we can use it
  203. if (bRet)
  204. AddBitmapToToolbar(_hwndTB, hbm, siz.cx, siz.cy, iMsg);
  205. DeleteObject(hbm);
  206. }
  207. }
  208. DeleteDC(hdc);
  209. }
  210. if (hdcScreen)
  211. ReleaseDC(NULL, hdcScreen);
  212. return bRet;
  213. }
  214. void CLogoffPane::_OnDestroy()
  215. {
  216. if (IsWindow(_hwndTB))
  217. {
  218. HIMAGELIST himl = (HIMAGELIST) SendMessage(_hwndTB, TB_GETIMAGELIST, 0, 0);
  219. if (himl)
  220. {
  221. ImageList_Destroy(himl);
  222. }
  223. himl = (HIMAGELIST) SendMessage(_hwndTB, TB_GETHOTIMAGELIST, 0, 0);
  224. if (himl)
  225. {
  226. ImageList_Destroy(himl);
  227. }
  228. }
  229. }
  230. LRESULT CLogoffPane::_OnCreate(LPARAM lParam)
  231. {
  232. // Do not set WS_TABSTOP here; that's CLogoffPane's job
  233. DWORD dwStyle = WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE | CCS_NORESIZE|CCS_NODIVIDER | TBSTYLE_FLAT|TBSTYLE_LIST|TBSTYLE_TOOLTIPS;
  234. RECT rc;
  235. _hTheme = (PaneDataFromCreateStruct(lParam))->hTheme;
  236. if (_hTheme)
  237. {
  238. GetThemeColor(_hTheme, SPP_LOGOFF, 0, TMT_TEXTCOLOR, &_clr);
  239. _colorHighlight = COLOR_MENUHILIGHT;
  240. _colorHighlightText = COLOR_HIGHLIGHTTEXT;
  241. GetThemeMargins(_hTheme, NULL, SPP_LOGOFF, 0, TMT_CONTENTMARGINS, NULL, &_margins);
  242. }
  243. else
  244. {
  245. _clr = GetSysColor(COLOR_MENUTEXT);
  246. _colorHighlight = COLOR_HIGHLIGHT;
  247. _colorHighlightText = COLOR_HIGHLIGHTTEXT;
  248. _margins.cyTopHeight = _margins.cyBottomHeight = 2 * GetSystemMetrics(SM_CYEDGE);
  249. ASSERT(_margins.cxLeftWidth == 0);
  250. ASSERT(_margins.cxRightWidth == 0);
  251. }
  252. GetClientRect(_hwnd, &rc);
  253. rc.left += _margins.cxLeftWidth;
  254. rc.right -= _margins.cxRightWidth;
  255. rc.top += _margins.cyTopHeight;
  256. rc.bottom -= _margins.cyBottomHeight;
  257. _hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, dwStyle,
  258. rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), _hwnd,
  259. NULL, NULL, NULL );
  260. if (_hwndTB)
  261. {
  262. //
  263. // Don't freak out if this fails. It just means that the accessibility
  264. // stuff won't be perfect.
  265. //
  266. SetAccessibleSubclassWindow(_hwndTB);
  267. // we do our own themed drawing...
  268. SetWindowTheme(_hwndTB, L"", L"");
  269. // Scale up on HIDPI
  270. SendMessage(_hwndTB, CCM_DPISCALE, TRUE, 0);
  271. SendMessage(_hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
  272. if (!_hTheme ||
  273. !_ThemedSetTBButtons(SPLS_NORMAL, TB_SETIMAGELIST) ||
  274. !_ThemedSetTBButtons(SPLS_HOT, TB_SETHOTIMAGELIST) )
  275. {
  276. // if we don't have a theme, or failed at setting the images from the theme
  277. // set buttons images from the rc file
  278. _SetTBButtons(IDB_LOGOFF_NORMAL, TB_SETIMAGELIST);
  279. _SetTBButtons(IDB_LOGOFF_HOT, TB_SETHOTIMAGELIST);
  280. }
  281. SendMessage(_hwndTB, TB_ADDBUTTONS, ARRAYSIZE(tbButtonsCreate), (LPARAM) tbButtonsCreate);
  282. int idText = IsOS(OS_FRIENDLYLOGONUI) ? IDS_LOGOFF_TEXT_FRIENDLY : IDS_LOGOFF_TEXT_DOMAIN;
  283. SendMessage(_hwndTB, TB_ADDSTRING, (WPARAM) _Module.GetModuleInstance(), (LPARAM) idText);
  284. _ApplyOptions();
  285. _hwndTT = (HWND)SendMessage(_hwndTB, TB_GETTOOLTIPS, 0, 0); //Get the tooltip window.
  286. _InitMetrics();
  287. return 0;
  288. }
  289. return -1; // no point in sticking around if we couldn't create the toolbar
  290. }
  291. BOOL CLogoffPane::_IsButtonHidden(int i)
  292. {
  293. TBBUTTON but;
  294. SendMessage(_hwndTB, TB_GETBUTTON, i, (LPARAM) &but);
  295. return but.fsState & TBSTATE_HIDDEN;
  296. }
  297. void CLogoffPane::_RightAlign()
  298. {
  299. int iWidthOfButtons=0;
  300. // add up the width of all the non-hidden buttons
  301. for(int i=0;i<ARRAYSIZE(tbButtonsCreate);i++)
  302. {
  303. if (!_IsButtonHidden(i))
  304. {
  305. RECT rc;
  306. SendMessage(_hwndTB, TB_GETITEMRECT, i, (LPARAM) &rc);
  307. iWidthOfButtons += RECTWIDTH(rc);
  308. }
  309. }
  310. if (iWidthOfButtons)
  311. {
  312. RECT rc;
  313. GetClientRect(_hwndTB, &rc);
  314. int iIndent = RECTWIDTH(rc) - iWidthOfButtons - GetSystemMetrics(SM_CXEDGE);
  315. if (iIndent < 0)
  316. iIndent = 0;
  317. SendMessage(_hwndTB, TB_SETINDENT, iIndent, 0);
  318. }
  319. }
  320. LRESULT CLogoffPane::_OnSize(int x, int y)
  321. {
  322. if (_hwndTB)
  323. {
  324. SetWindowPos(_hwndTB, NULL, _margins.cxLeftWidth, _margins.cyTopHeight,
  325. x-(_margins.cxRightWidth+_margins.cxLeftWidth), y-(_margins.cyBottomHeight+_margins.cyTopHeight),
  326. SWP_NOACTIVATE | SWP_NOOWNERZORDER);
  327. _RightAlign();
  328. }
  329. return 0;
  330. }
  331. LRESULT CLogoffPane::_OnNCDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  332. {
  333. // WARNING! "this" might be invalid (if WM_NCCREATE failed), so
  334. // do not use any member variables!
  335. LRESULT lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
  336. SetWindowPtr0(hwnd, 0);
  337. if (this)
  338. {
  339. this->Release();
  340. }
  341. return lres;
  342. }
  343. void CLogoffPane::_ApplyOptions()
  344. {
  345. SMNFILTEROPTIONS nmopt;
  346. nmopt.smnop = SMNOP_LOGOFF | SMNOP_TURNOFF | SMNOP_DISCONNECT | SMNOP_EJECT;
  347. _SendNotify(_hwnd, SMN_FILTEROPTIONS, &nmopt.hdr);
  348. SendMessage(_hwndTB, TB_HIDEBUTTON, SMNLC_EJECT, !(nmopt.smnop & SMNOP_EJECT));
  349. SendMessage(_hwndTB, TB_HIDEBUTTON, SMNLC_LOGOFF, !(nmopt.smnop & SMNOP_LOGOFF));
  350. SendMessage(_hwndTB, TB_HIDEBUTTON, SMNLC_TURNOFF, !(nmopt.smnop & SMNOP_TURNOFF));
  351. SendMessage(_hwndTB, TB_HIDEBUTTON, SMNLC_DISCONNECT, !(nmopt.smnop & SMNOP_DISCONNECT));
  352. _RightAlign();
  353. }
  354. LRESULT CLogoffPane::_OnNotify(NMHDR *pnm)
  355. {
  356. if (pnm->hwndFrom == _hwndTB)
  357. {
  358. switch (pnm->code)
  359. {
  360. case NM_CUSTOMDRAW:
  361. return _OnCustomDraw((NMTBCUSTOMDRAW*)pnm);
  362. case TBN_WRAPACCELERATOR:
  363. return TRUE; // Disable wraparound; we want DeskHost to do navigation
  364. case TBN_GETINFOTIP:
  365. {
  366. NMTBGETINFOTIP *ptbgit = (NMTBGETINFOTIP *)pnm;
  367. ASSERT(ptbgit->lParam >= IDS_LOGOFF_TIP_EJECT && ptbgit->lParam <= IDS_LOGOFF_TIP_DISCONNECT);
  368. LoadString(_Module.GetModuleInstance(), ptbgit->lParam, ptbgit->pszText, ptbgit->cchTextMax);
  369. return TRUE;
  370. }
  371. case TBN_HOTITEMCHANGE:
  372. {
  373. // Disallow setting a hot item if we are not focus
  374. // (unless it was specifically our idea in the first place)
  375. // Otherwise we interfere with keyboard navigation
  376. NMTBHOTITEM *phot = (NMTBHOTITEM*)pnm;
  377. if (!(phot->dwFlags & HICF_LEAVING) &&
  378. GetFocus() != pnm->hwndFrom &&
  379. !_fSettingHotItem)
  380. {
  381. return TRUE; // deny hot item change
  382. }
  383. }
  384. break;
  385. }
  386. }
  387. else // from host
  388. {
  389. switch (pnm->code)
  390. {
  391. case SMN_REFRESHLOGOFF:
  392. _ApplyOptions();
  393. return TRUE;
  394. case SMN_FINDITEM:
  395. return _OnSMNFindItem(CONTAINING_RECORD(pnm, SMNDIALOGMESSAGE, hdr));
  396. case SMN_APPLYREGION:
  397. return HandleApplyRegion(_hwnd, _hTheme, (SMNMAPPLYREGION *)pnm, SPP_LOGOFF, 0);
  398. }
  399. }
  400. return FALSE;
  401. }
  402. LRESULT CLogoffPane::_OnCommand(int id)
  403. {
  404. int i;
  405. for (i = 0; i < ARRAYSIZE(tbButtonsCreate); i++)
  406. {
  407. if (tbButtonsCreate[i].idCommand == id)
  408. {
  409. if (!_IsButtonHidden(i))
  410. {
  411. PostMessage(v_hwndTray, WM_COMMAND, c_rgidmLegacy[i], 0);
  412. SMNMCOMMANDINVOKED ci;
  413. SendMessage(_hwndTB, TB_GETITEMRECT, i, (LPARAM)&ci.rcItem);
  414. MapWindowRect(_hwndTB, NULL, &ci.rcItem);
  415. _SendNotify(_hwnd, SMN_COMMANDINVOKED, &ci.hdr);
  416. }
  417. break;
  418. }
  419. }
  420. return 0;
  421. }
  422. LRESULT CLogoffPane::_OnCustomDraw(NMTBCUSTOMDRAW *pnmtbcd)
  423. {
  424. LRESULT lres;
  425. switch (pnmtbcd->nmcd.dwDrawStage)
  426. {
  427. case CDDS_PREPAINT:
  428. return CDRF_NOTIFYITEMDRAW;
  429. case CDDS_ITEMPREPAINT:
  430. #if 0 //Do we still need this?
  431. pnmtbcd->nHLStringBkMode = TRANSPARENT; // needed to reduce flicker- bug in toolbar?
  432. #endif
  433. pnmtbcd->clrText = _clr;
  434. pnmtbcd->clrTextHighlight = GetSysColor(_colorHighlightText);
  435. pnmtbcd->clrHighlightHotTrack = GetSysColor(_colorHighlight);
  436. lres = TBCDRF_NOEDGES | TBCDRF_HILITEHOTTRACK;
  437. // todo - FIX TOOLBAR to respect clrTextHighlight when item is hot.
  438. if (pnmtbcd->nmcd.uItemState == CDIS_HOT)
  439. {
  440. pnmtbcd->clrText = pnmtbcd->clrTextHighlight;
  441. }
  442. return lres;
  443. }
  444. return CDRF_DODEFAULT;
  445. }
  446. LRESULT CLogoffPane::_NextVisibleButton(PSMNDIALOGMESSAGE pdm, int i, int direction)
  447. {
  448. ASSERT(direction == +1 || direction == -1);
  449. i += direction;
  450. while (i >= 0 && i < ARRAYSIZE(tbButtonsCreate))
  451. {
  452. if (!_IsButtonHidden(i))
  453. {
  454. pdm->itemID = i;
  455. return TRUE;
  456. }
  457. i += direction;
  458. }
  459. return FALSE;
  460. }
  461. int CLogoffPane::_GetCurButton()
  462. {
  463. return (int)SendMessage(_hwndTB, TB_GETHOTITEM, 0, 0);
  464. }
  465. LRESULT CLogoffPane::_OnSMNFindItem(PSMNDIALOGMESSAGE pdm)
  466. {
  467. LRESULT lres = _OnSMNFindItemWorker(pdm);
  468. if (lres)
  469. {
  470. //
  471. // If caller requested that the item also be selected, then do so.
  472. //
  473. if (pdm->flags & SMNDM_SELECT)
  474. {
  475. if (_GetCurButton() != pdm->itemID)
  476. {
  477. // Explicitly pop the tooltip so we don't have the problem
  478. // of a "virtual" tooltip causing the infotip to appear
  479. // the instant the mouse moves into the window.
  480. if (_hwndTT)
  481. {
  482. SendMessage(_hwndTT, TTM_POP, 0, 0);
  483. }
  484. // _fSettingHotItem tells our WM_NOTIFY handler to
  485. // allow this hot item change to go through
  486. _fSettingHotItem = TRUE;
  487. SendMessage(_hwndTB, TB_SETHOTITEM, pdm->itemID, 0);
  488. _fSettingHotItem = FALSE;
  489. // Do the SetFocus after setting the hot item to prevent
  490. // toolbar from autoselecting the first button (which
  491. // is what it does if you SetFocus when there is no hot
  492. // item). SetFocus returns the previous focus window.
  493. if (SetFocus(_hwndTB) != _hwndTB)
  494. {
  495. // Send the notify since we tricked toolbar into not sending it
  496. // (Toolbar doesn't send a subobject focus notification
  497. // on WM_SETFOCUS if the item was already hot when it gained focus)
  498. NotifyWinEvent(EVENT_OBJECT_FOCUS, _hwndTB, OBJID_CLIENT, pdm->itemID + 1);
  499. }
  500. }
  501. }
  502. }
  503. else
  504. {
  505. //
  506. // If not found, then tell caller what our orientation is (horizontal)
  507. // and where the currently-selected item is.
  508. //
  509. pdm->flags |= SMNDM_HORIZONTAL;
  510. int i = _GetCurButton();
  511. RECT rc;
  512. if (i >= 0 && SendMessage(_hwndTB, TB_GETITEMRECT, i, (LPARAM)&rc))
  513. {
  514. pdm->pt.x = (rc.left + rc.right)/2;
  515. pdm->pt.y = (rc.top + rc.bottom)/2;
  516. }
  517. else
  518. {
  519. pdm->pt.x = 0;
  520. pdm->pt.y = 0;
  521. }
  522. }
  523. return lres;
  524. }
  525. TCHAR CLogoffPane::_GetButtonAccelerator(int i)
  526. {
  527. TCHAR szText[MAX_PATH];
  528. if (SendMessage(_hwndTB, TB_GETBUTTONTEXT, tbButtonsCreate[i].idCommand, (LPARAM)szText) > 0)
  529. {
  530. return CharUpperChar(SHFindMnemonic(szText));
  531. }
  532. return 0;
  533. }
  534. //
  535. // Metrics changed -- update.
  536. //
  537. void CLogoffPane::_InitMetrics()
  538. {
  539. if (_hwndTT)
  540. {
  541. // Disable/enable infotips based on user preference
  542. SendMessage(_hwndTT, TTM_ACTIVATE, ShowInfoTip(), 0);
  543. // Toolbar control doesn't set the tooltip font so we have to do it ourselves
  544. SetWindowFont(_hwndTT, GetWindowFont(_hwndTB), FALSE);
  545. }
  546. }
  547. LRESULT CLogoffPane::_OnDisplayChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  548. {
  549. // Propagate first because _InitMetrics needs to talk to the updated toolbar
  550. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL);
  551. _InitMetrics();
  552. return 0;
  553. }
  554. LRESULT CLogoffPane::_OnSettingChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  555. {
  556. // Propagate first because _InitMetrics needs to talk to the updated toolbar
  557. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL);
  558. // _InitMetrics() is so cheap it's not worth getting too upset about
  559. // calling it too many times.
  560. _InitMetrics();
  561. _RightAlign();
  562. return 0;
  563. }
  564. LRESULT CLogoffPane::_OnSysColorChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  565. {
  566. // update colors in classic mode
  567. if (!_hTheme)
  568. {
  569. _clr = GetSysColor(COLOR_MENUTEXT);
  570. }
  571. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL);
  572. return 0;
  573. }
  574. LRESULT CLogoffPane::_OnSMNFindItemWorker(PSMNDIALOGMESSAGE pdm)
  575. {
  576. int i;
  577. switch (pdm->flags & SMNDM_FINDMASK)
  578. {
  579. case SMNDM_FINDFIRST:
  580. return _NextVisibleButton(pdm, -1, +1);
  581. case SMNDM_FINDLAST:
  582. return _NextVisibleButton(pdm, ARRAYSIZE(tbButtonsCreate), -1);
  583. case SMNDM_FINDNEAREST:
  584. // HACK! but we know that we are the only control in our group
  585. // so this doesn't need to be implemented
  586. return FALSE;
  587. case SMNDM_HITTEST:
  588. pdm->itemID = SendMessage(_hwndTB, TB_HITTEST, 0, (LPARAM)&pdm->pt);
  589. return pdm->itemID >= 0;
  590. case SMNDM_FINDFIRSTMATCH:
  591. case SMNDM_FINDNEXTMATCH:
  592. {
  593. if ((pdm->flags & SMNDM_FINDMASK) == SMNDM_FINDFIRSTMATCH)
  594. {
  595. i = 0;
  596. }
  597. else
  598. {
  599. i = _GetCurButton() + 1;
  600. }
  601. TCHAR tch = CharUpperChar((TCHAR)pdm->pmsg->wParam);
  602. for ( ; i < ARRAYSIZE(tbButtonsCreate); i++)
  603. {
  604. if (_IsButtonHidden(i))
  605. continue; // skip hidden buttons
  606. if (_GetButtonAccelerator(i) == tch)
  607. {
  608. pdm->itemID = i;
  609. return TRUE;
  610. }
  611. }
  612. }
  613. break; // not found
  614. case SMNDM_FINDNEXTARROW:
  615. switch (pdm->pmsg->wParam)
  616. {
  617. case VK_LEFT:
  618. case VK_UP:
  619. return _NextVisibleButton(pdm, _GetCurButton(), -1);
  620. case VK_RIGHT:
  621. case VK_DOWN:
  622. return _NextVisibleButton(pdm, _GetCurButton(), +1);
  623. }
  624. return FALSE; // not found
  625. case SMNDM_INVOKECURRENTITEM:
  626. i = _GetCurButton();
  627. if (i >= 0 && i < ARRAYSIZE(tbButtonsCreate))
  628. {
  629. FORWARD_WM_COMMAND(_hwnd, tbButtonsCreate[i].idCommand, _hwndTB, BN_CLICKED, PostMessage);
  630. return TRUE;
  631. }
  632. return FALSE;
  633. case SMNDM_OPENCASCADE:
  634. return FALSE; // none of our items cascade
  635. default:
  636. ASSERT(!"Unknown SMNDM command");
  637. break;
  638. }
  639. return FALSE;
  640. }
  641. HRESULT CLogoffPane::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut)
  642. {
  643. if (varChild.lVal)
  644. {
  645. return CreateAcceleratorBSTR(_GetButtonAccelerator(varChild.lVal - 1), pszKeyboardShortcut);
  646. }
  647. return CAccessible::get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
  648. }
  649. HRESULT CLogoffPane::get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction)
  650. {
  651. if (varChild.lVal)
  652. {
  653. return GetRoleString(ACCSTR_EXECUTE, pszDefAction);
  654. }
  655. return CAccessible::get_accDefaultAction(varChild, pszDefAction);
  656. }
  657. BOOL WINAPI LogoffPane_RegisterClass()
  658. {
  659. WNDCLASSEX wc;
  660. ZeroMemory( &wc, sizeof(wc) );
  661. wc.cbSize = sizeof(wc);
  662. wc.style = CS_GLOBALCLASS;
  663. wc.cbWndExtra = sizeof(LPVOID);
  664. wc.lpfnWndProc = CLogoffPane::WndProc;
  665. wc.hInstance = _Module.GetModuleInstance();
  666. wc.hCursor = LoadCursor( NULL, IDC_ARROW );
  667. wc.hbrBackground = (HBRUSH)(NULL);
  668. wc.lpszClassName = TEXT("DesktopLogoffPane");
  669. return RegisterClassEx( &wc );
  670. }