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.

897 lines
25 KiB

  1. #include "stdafx.h"
  2. #include "sfthost.h"
  3. #include "hostutil.h"
  4. #include "moreprog.h"
  5. #include <desktray.h>
  6. #include "tray.h" // To get access to c_tray
  7. #include "rcids.h" // for IDM_PROGRAMS etc.
  8. //
  9. // Unfortunately, WTL #undef's SelectFont, so we have to define it again.
  10. //
  11. inline HFONT SelectFont(HDC hdc, HFONT hf)
  12. {
  13. return (HFONT)SelectObject(hdc, hf);
  14. }
  15. CMorePrograms::CMorePrograms(HWND hwnd) :
  16. _lRef(1),
  17. _hwnd(hwnd),
  18. _clrText(CLR_INVALID),
  19. _clrBk(CLR_INVALID)
  20. {
  21. }
  22. CMorePrograms::~CMorePrograms()
  23. {
  24. if (_hf)
  25. DeleteObject(_hf);
  26. if (_hfTTBold)
  27. DeleteObject(_hfTTBold);
  28. if (_hfMarlett)
  29. DeleteObject(_hfMarlett);
  30. ATOMICRELEASE(_pdth);
  31. ATOMICRELEASE(_psmPrograms);
  32. // Note that we do not need to clean up our HWNDs.
  33. // USER does that for us automatically.
  34. }
  35. //
  36. // Metrics changed -- update.
  37. //
  38. void CMorePrograms::_InitMetrics()
  39. {
  40. if (_hwndTT)
  41. {
  42. MakeMultilineTT(_hwndTT);
  43. // Disable/enable infotips based on user preference
  44. SendMessage(_hwndTT, TTM_ACTIVATE, ShowInfoTip(), 0);
  45. }
  46. }
  47. LRESULT CMorePrograms::_OnNCCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  48. {
  49. CMorePrograms *self = new CMorePrograms(hwnd);
  50. if (self)
  51. {
  52. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)self);
  53. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  54. }
  55. return FALSE;
  56. }
  57. //
  58. // Create an inner button that is exactly the right size.
  59. //
  60. // Height of inner button = height of text.
  61. // Width of inner button = full width.
  62. //
  63. // This allows us to let USER do most of the work of hit-testing and
  64. // focus rectangling.
  65. //
  66. LRESULT CMorePrograms::_OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  67. {
  68. _hTheme = (PaneDataFromCreateStruct(lParam))->hTheme;
  69. if (!_hTheme)
  70. {
  71. _clrText = GetSysColor(COLOR_MENUTEXT);
  72. _clrBk = GetSysColor(COLOR_MENU);
  73. _hbrBk = GetSysColorBrush(COLOR_MENU);
  74. _colorHighlight = COLOR_HIGHLIGHT;
  75. _colorHighlightText = COLOR_HIGHLIGHTTEXT;
  76. // should match proglist values, in sfthost.cpp
  77. _margins.cxLeftWidth = 2*GetSystemMetrics(SM_CXEDGE);
  78. _margins.cxRightWidth = 2*GetSystemMetrics(SM_CXEDGE);
  79. }
  80. else
  81. {
  82. GetThemeColor(_hTheme, SPP_MOREPROGRAMS, 0, TMT_TEXTCOLOR, &_clrText );
  83. _hbrBk = (HBRUSH) GetStockObject(HOLLOW_BRUSH);
  84. _colorHighlight = COLOR_MENUHILIGHT;
  85. _colorHighlightText = COLOR_HIGHLIGHTTEXT;
  86. // theme designer should make it so these margins match proglist's
  87. GetThemeMargins(_hTheme, NULL, SPP_MOREPROGRAMS, 0, TMT_CONTENTMARGINS, NULL, &_margins);
  88. // get the width of the arrow
  89. SIZE siz = { 0, 0 };
  90. GetThemePartSize(_hTheme, NULL, SPP_MOREPROGRAMSARROW, 0, NULL, TS_TRUE, &siz);
  91. _cxArrow = siz.cx;
  92. }
  93. // If we're restricted, just create the window without doing any work
  94. // We still need to paint our background, so we can't just fail the create
  95. if(SHRestricted(REST_NOSMMOREPROGRAMS))
  96. return TRUE;
  97. if (!LoadString(_Module.GetResourceInstance(),
  98. IDS_STARTPANE_MOREPROGRAMS, _szMessage, ARRAYSIZE(_szMessage)))
  99. {
  100. return FALSE;
  101. }
  102. // Find the accelerator
  103. _chMnem = CharUpperChar(SHFindMnemonic(_szMessage));
  104. _hf = LoadControlFont(_hTheme, SPP_MOREPROGRAMS, FALSE, 0);
  105. // Get some information about the font the user has selected
  106. // and create a Marlett font at a matching size.
  107. TEXTMETRIC tm;
  108. HDC hdc = GetDC(hwnd);
  109. if (hdc)
  110. {
  111. HFONT hfPrev = SelectFont(hdc, _hf);
  112. if (hfPrev)
  113. {
  114. SIZE sizText;
  115. GetTextExtentPoint32(hdc, _szMessage, lstrlen(_szMessage), &sizText);
  116. _cxText = sizText.cx + GetSystemMetrics(SM_CXEDGE); // chevron should be a little right of the text
  117. if (GetTextMetrics(hdc, &tm))
  118. {
  119. _tmAscent = tm.tmAscent;
  120. LOGFONT lf;
  121. ZeroMemory(&lf, sizeof(lf));
  122. lf.lfHeight = _tmAscent;
  123. lf.lfWeight = FW_NORMAL;
  124. lf.lfCharSet = SYMBOL_CHARSET;
  125. lstrcpy(lf.lfFaceName, TEXT("Marlett"));
  126. _hfMarlett = CreateFontIndirect(&lf);
  127. if (_hfMarlett)
  128. {
  129. SelectFont(hdc, _hfMarlett);
  130. if (GetTextMetrics(hdc, &tm))
  131. {
  132. _tmAscentMarlett = tm.tmAscent;
  133. }
  134. if (0 == _cxArrow) // if we're not themed, or the GetThemePartSize failed,
  135. {
  136. // set the width of the Marlett arrow into _cxArrow
  137. GetTextExtentPoint32(hdc, GetLayout(hdc) & LAYOUT_RTL ? TEXT("w") : TEXT("8"), 1, &sizText);
  138. _cxArrow = sizText.cx;
  139. }
  140. }
  141. }
  142. SelectFont(hdc, hfPrev);
  143. }
  144. ReleaseDC(hwnd, hdc);
  145. }
  146. if (!_tmAscentMarlett)
  147. {
  148. return FALSE;
  149. }
  150. // This is the same large icon setting from proglist
  151. BOOL bLargeIcons = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, REGSTR_VAL_DV2_LARGEICONS, FALSE, TRUE /* default to large*/);
  152. RECT rc;
  153. GetClientRect(_hwnd, &rc);
  154. rc.left += _margins.cxLeftWidth;
  155. rc.right -= _margins.cxRightWidth;
  156. rc.top += _margins.cyTopHeight;
  157. rc.bottom -= _margins.cyBottomHeight;
  158. // Compute the text indent value, so more programs lines up with text on icons in programs list
  159. _cxTextIndent = (3 * GetSystemMetrics(SM_CXEDGE)) + // 2 between icon&text + 1 before icon
  160. GetSystemMetrics(bLargeIcons ? SM_CXICON : SM_CXSMICON);
  161. // truncate the indent, if the text won't fit in the given area
  162. if (_cxTextIndent > RECTWIDTH(rc) - (_cxText + _cxArrow))
  163. {
  164. TraceMsg(TF_WARNING, "StartMenu: '%s' is %dpx, only room for %d- notify localizers!",_szMessage, _cxText, RECTWIDTH(rc)-(_cxArrow+_cxTextIndent));
  165. _cxTextIndent = max(0, RECTWIDTH(rc) - (_cxText + _cxArrow));
  166. }
  167. ASSERT(RECTHEIGHT(rc) > _tmAscent);
  168. _iTextCenterVal = (RECTHEIGHT(rc) - _tmAscent) / 2;
  169. // Do not set WS_TABSTOP or WS_GROUP; CMorePrograms handles that
  170. // BS_NOTIFY ensures that we get BN_SETFOCUS and BN_KILLFOCUS
  171. DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE |
  172. BS_OWNERDRAW;
  173. _hwndButton = CreateWindowEx(0, TEXT("button"), _szMessage, dwStyle,
  174. rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
  175. _hwnd, (HMENU)IntToPtr(IDC_BUTTON),
  176. _Module.GetModuleInstance(), NULL);
  177. if (!_hwndButton)
  178. {
  179. return FALSE;
  180. }
  181. //
  182. // Don't freak out if this fails. It just means that the accessibility
  183. // stuff won't be perfect.
  184. //
  185. SetAccessibleSubclassWindow(_hwndButton);
  186. if (_hf)
  187. SetWindowFont(_hwndButton, _hf, FALSE);
  188. // Unlike the button itself, failure to create the tooltip is nonfatal.
  189. // only create the tooltip if auto-cascade is off
  190. if (!SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, REGSTR_VAL_DV2_AUTOCASCADE, FALSE, TRUE))
  191. _hwndTT = _CreateTooltip();
  192. _InitMetrics();
  193. // We can survive if this fails to be created
  194. CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
  195. IID_PPV_ARG(IDropTargetHelper, &_pdth));
  196. //
  197. // If this fails, no big whoop - you just don't get
  198. // drag/drop, boo hoo.
  199. //
  200. RegisterDragDrop(_hwndButton, this);
  201. return TRUE;
  202. }
  203. HWND CMorePrograms::_CreateTooltip()
  204. {
  205. DWORD dwStyle = WS_BORDER | TTS_NOPREFIX;
  206. HWND hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, dwStyle,
  207. 0, 0, 0, 0,
  208. _hwndButton, NULL,
  209. _Module.GetModuleInstance(), NULL);
  210. if (hwnd)
  211. {
  212. TCHAR szBuf[MAX_PATH];
  213. TOOLINFO ti;
  214. ti.cbSize = sizeof(ti);
  215. ti.hwnd = _hwnd;
  216. ti.uId = reinterpret_cast<UINT_PTR>(_hwndButton);
  217. ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
  218. ti.hinst = _Module.GetResourceInstance();
  219. // We can't use MAKEINTRESOURCE because that allows only up to 80
  220. // characters for text, and our text can be longer than that.
  221. UINT ids = IDS_STARTPANE_MOREPROGRAMS_TIP;
  222. ti.lpszText = szBuf;
  223. if (LoadString(_Module.GetResourceInstance(), ids, szBuf, ARRAYSIZE(szBuf)))
  224. {
  225. SendMessage(hwnd, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&ti));
  226. }
  227. }
  228. return hwnd;
  229. }
  230. LRESULT CMorePrograms::_OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  231. {
  232. RevokeDragDrop(_hwndButton);
  233. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  234. }
  235. LRESULT CMorePrograms::_OnNCDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  236. {
  237. // WARNING! "this" might be invalid (if WM_NCCREATE failed), so
  238. // do not use any member variables!
  239. LRESULT lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
  240. SetWindowPtr0(hwnd, 0);
  241. if (this)
  242. {
  243. this->Release();
  244. }
  245. return lres;
  246. }
  247. LRESULT CMorePrograms::_OnCtlColorBtn(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  248. {
  249. HDC hdc = reinterpret_cast<HDC>(wParam);
  250. if (_clrText != CLR_INVALID)
  251. {
  252. SetTextColor(hdc, _clrText);
  253. }
  254. if (_clrBk != CLR_INVALID)
  255. {
  256. SetBkColor(hdc, _clrBk);
  257. }
  258. return reinterpret_cast<LRESULT>(_hbrBk);
  259. }
  260. LRESULT CMorePrograms::_OnDrawItem(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  261. {
  262. LPDRAWITEMSTRUCT pdis = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
  263. ASSERT(pdis->CtlType == ODT_BUTTON);
  264. ASSERT(pdis->CtlID == IDC_BUTTON);
  265. if (pdis->itemAction & (ODA_DRAWENTIRE | ODA_FOCUS))
  266. {
  267. BOOL fRTLReading = GetLayout(pdis->hDC) & LAYOUT_RTL;
  268. UINT fuOptions = 0;
  269. if (fRTLReading)
  270. {
  271. fuOptions |= ETO_RTLREADING;
  272. }
  273. HFONT hfPrev = SelectFont(pdis->hDC, _hf);
  274. if (hfPrev)
  275. {
  276. BOOL fHot = (pdis->itemState & ODS_FOCUS) || _tmHoverStart;
  277. if (fHot)
  278. {
  279. // hot background
  280. FillRect(pdis->hDC, &pdis->rcItem, GetSysColorBrush(_colorHighlight));
  281. SetTextColor(pdis->hDC, GetSysColor(_colorHighlightText));
  282. }
  283. else if (_hTheme)
  284. {
  285. // Themed non-hot background = custom
  286. RECT rc;
  287. GetClientRect(hwnd, &rc);
  288. MapWindowRect(hwnd, pdis->hwndItem, &rc);
  289. DrawThemeBackground(_hTheme, pdis->hDC, SPP_MOREPROGRAMS, 0, &rc, 0);
  290. }
  291. else
  292. {
  293. // non-themed non-hot background
  294. FillRect(pdis->hDC, &pdis->rcItem, _hbrBk);
  295. }
  296. int iOldMode = SetBkMode(pdis->hDC, TRANSPARENT);
  297. // _cxTextIndent will move it in the current width of an icon (small or large), plus the space we add between an icon and the text
  298. pdis->rcItem.left += _cxTextIndent;
  299. UINT dtFlags = DT_VCENTER | DT_SINGLELINE | DT_EDITCONTROL;
  300. if (fRTLReading)
  301. {
  302. dtFlags |= DT_RTLREADING;
  303. }
  304. if (pdis->itemState & ODS_NOACCEL)
  305. {
  306. dtFlags |= DT_HIDEPREFIX;
  307. }
  308. DrawText(pdis->hDC, _szMessage, -1, &pdis->rcItem, dtFlags);
  309. RECT rc = pdis->rcItem;
  310. rc.left += _cxText;
  311. if (_hTheme)
  312. {
  313. if (_iTextCenterVal < 0) // text is taller than the bitmap
  314. rc.top += (-_iTextCenterVal);
  315. rc.right = rc.left + _cxArrow; // clip rectangle down to the minumum size...
  316. DrawThemeBackground(_hTheme, pdis->hDC, SPP_MOREPROGRAMSARROW,
  317. fHot ? SPS_HOT : 0, &rc, 0);
  318. }
  319. else
  320. {
  321. if (SelectFont(pdis->hDC, _hfMarlett))
  322. {
  323. rc.top = rc.top + _tmAscent - _tmAscentMarlett + (_iTextCenterVal > 0 ? _iTextCenterVal : 0);
  324. TCHAR chOut = fRTLReading ? TEXT('w') : TEXT('8');
  325. if (EVAL(!IsRectEmpty(&rc)))
  326. {
  327. ExtTextOut(pdis->hDC, rc.left, rc.top, fuOptions, &rc, &chOut, 1, NULL);
  328. rc.right = rc.left + _cxArrow;
  329. }
  330. }
  331. }
  332. _rcExclude = rc;
  333. _rcExclude.left -= _cxText; // includes the text in the exclusion rectangle.
  334. MapWindowRect(pdis->hwndItem, NULL, &_rcExclude);
  335. SetBkMode(pdis->hDC, iOldMode);
  336. SelectFont(pdis->hDC, hfPrev);
  337. }
  338. }
  339. //
  340. // Since we are emulating a menu item, we don't need to draw a
  341. // focus rectangle.
  342. //
  343. return TRUE;
  344. }
  345. void CMorePrograms::_TrackShellMenu(DWORD dwFlags)
  346. {
  347. // Pop the balloon tip and tell the Start Menu not to offer it any more
  348. _PopBalloon();
  349. _SendNotify(_hwnd, SMN_SEENNEWITEMS);
  350. SMNTRACKSHELLMENU tsm;
  351. tsm.itemID = 0;
  352. tsm.dwFlags = dwFlags;
  353. if (!_psmPrograms)
  354. {
  355. CoCreateInstance(CLSID_PersonalStartMenu, NULL, CLSCTX_INPROC,
  356. IID_PPV_ARG(IShellMenu, &_psmPrograms));
  357. }
  358. if (_psmPrograms)
  359. {
  360. tsm.psm = _psmPrograms;
  361. tsm.rcExclude = _rcExclude;
  362. HWND hwnd = _hwnd;
  363. _fMenuOpen = TRUE;
  364. _SendNotify(_hwnd, SMN_TRACKSHELLMENU, &tsm.hdr);
  365. }
  366. }
  367. LRESULT CMorePrograms::_OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  368. {
  369. switch (GET_WM_COMMAND_ID(wParam, lParam))
  370. {
  371. case IDC_BUTTON:
  372. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  373. {
  374. case BN_CLICKED:
  375. _TrackShellMenu(0);
  376. break;
  377. }
  378. break;
  379. case IDC_KEYPRESS:
  380. _TrackShellMenu(MPPF_KEYBOARD | MPPF_INITIALSELECT);
  381. break;
  382. }
  383. return 0;
  384. }
  385. LRESULT CMorePrograms::_OnEraseBkgnd(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  386. {
  387. RECT rc;
  388. GetClientRect(hwnd, &rc);
  389. if (_hTheme)
  390. {
  391. DrawThemeBackground(_hTheme, (HDC)wParam, SPP_MOREPROGRAMS, 0, &rc, 0);
  392. }
  393. else
  394. SHFillRectClr((HDC)wParam, &rc, _clrBk);
  395. return 0;
  396. }
  397. LRESULT CMorePrograms::_OnNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  398. {
  399. LPNMHDR pnm = reinterpret_cast<LPNMHDR>(lParam);
  400. switch (pnm->code)
  401. {
  402. case SMN_FINDITEM:
  403. return _OnSMNFindItem(CONTAINING_RECORD(pnm, SMNDIALOGMESSAGE, hdr));
  404. case SMN_SHOWNEWAPPSTIP:
  405. return _OnSMNShowNewAppsTip(CONTAINING_RECORD(pnm, SMNMBOOL, hdr));
  406. case SMN_DISMISS:
  407. return _OnSMNDismiss();
  408. case SMN_APPLYREGION:
  409. return HandleApplyRegion(_hwnd, _hTheme, (SMNMAPPLYREGION *)lParam, SPP_MOREPROGRAMS, 0);
  410. case SMN_SHELLMENUDISMISSED:
  411. _fMenuOpen = FALSE;
  412. return 0;
  413. }
  414. return 0;
  415. }
  416. LRESULT CMorePrograms::_OnSMNFindItem(PSMNDIALOGMESSAGE pdm)
  417. {
  418. if(SHRestricted(REST_NOSMMOREPROGRAMS))
  419. return 0;
  420. switch (pdm->flags & SMNDM_FINDMASK)
  421. {
  422. // Life is simple if you have only one item -- all searches succeed!
  423. case SMNDM_FINDFIRST:
  424. case SMNDM_FINDLAST:
  425. case SMNDM_FINDNEAREST:
  426. case SMNDM_HITTEST:
  427. pdm->itemID = 0;
  428. return TRUE;
  429. case SMNDM_FINDFIRSTMATCH:
  430. {
  431. TCHAR tch = CharUpperChar((TCHAR)pdm->pmsg->wParam);
  432. if (tch == _chMnem)
  433. {
  434. pdm->itemID = 0;
  435. return TRUE;
  436. }
  437. }
  438. break; // not found
  439. case SMNDM_FINDNEXTMATCH:
  440. break; // there is only one item so there can't be a "next"
  441. case SMNDM_FINDNEXTARROW:
  442. if (pdm->flags & SMNDM_TRYCASCADE)
  443. {
  444. FORWARD_WM_COMMAND(_hwnd, IDC_KEYPRESS, NULL, 0, PostMessage);
  445. return TRUE;
  446. }
  447. break; // not found
  448. case SMNDM_INVOKECURRENTITEM:
  449. case SMNDM_OPENCASCADE:
  450. if (pdm->flags & SMNDM_KEYBOARD)
  451. {
  452. FORWARD_WM_COMMAND(_hwnd, IDC_KEYPRESS, NULL, 0, PostMessage);
  453. }
  454. else
  455. {
  456. FORWARD_WM_COMMAND(_hwnd, IDC_BUTTON, NULL, 0, PostMessage);
  457. }
  458. return TRUE;
  459. case SMNDM_FINDITEMID:
  460. return TRUE;
  461. default:
  462. ASSERT(!"Unknown SMNDM command");
  463. break;
  464. }
  465. //
  466. // If not found, then tell caller what our orientation is (vertical)
  467. // and where the currently-selected item is.
  468. //
  469. pdm->flags |= SMNDM_VERTICAL;
  470. pdm->pt.x = 0;
  471. pdm->pt.y = 0;
  472. return FALSE;
  473. }
  474. //
  475. // The boolean parameter in the SMNMBOOL tells us whether to display or
  476. // hide the balloon tip.
  477. //
  478. LRESULT CMorePrograms::_OnSMNShowNewAppsTip(PSMNMBOOL psmb)
  479. {
  480. if(SHRestricted(REST_NOSMMOREPROGRAMS))
  481. return 0;
  482. if (psmb->f)
  483. {
  484. if (_hwndTT)
  485. {
  486. SendMessage(_hwndTT, TTM_ACTIVATE, FALSE, 0);
  487. }
  488. if (!_hwndBalloon)
  489. {
  490. RECT rc;
  491. GetWindowRect(_hwndButton, &rc);
  492. if (!_hfTTBold)
  493. {
  494. NONCLIENTMETRICS ncm;
  495. ncm.cbSize = sizeof(ncm);
  496. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
  497. {
  498. ncm.lfStatusFont.lfWeight = FW_BOLD;
  499. SHAdjustLOGFONT(&ncm.lfStatusFont);
  500. _hfTTBold = CreateFontIndirect(&ncm.lfStatusFont);
  501. }
  502. }
  503. _hwndBalloon = CreateBalloonTip(_hwnd,
  504. rc.left + _cxTextIndent + _cxText,
  505. (rc.top + rc.bottom)/2,
  506. _hfTTBold, 0,
  507. IDS_STARTPANE_MOREPROGRAMS_BALLOONTITLE);
  508. if (_hwndBalloon)
  509. {
  510. SetProp(_hwndBalloon, PROP_DV2_BALLOONTIP, DV2_BALLOONTIP_MOREPROG);
  511. }
  512. }
  513. }
  514. else
  515. {
  516. _PopBalloon();
  517. }
  518. return 0;
  519. }
  520. void CMorePrograms::_PopBalloon()
  521. {
  522. if (_hwndBalloon)
  523. {
  524. DestroyWindow(_hwndBalloon);
  525. _hwndBalloon = NULL;
  526. }
  527. if (_hwndTT)
  528. {
  529. SendMessage(_hwndTT, TTM_ACTIVATE, TRUE, 0);
  530. }
  531. }
  532. LRESULT CMorePrograms::_OnSMNDismiss()
  533. {
  534. _PopBalloon();
  535. return 0;
  536. }
  537. LRESULT CMorePrograms::_OnSysColorChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  538. {
  539. // update colors in classic mode
  540. if (!_hTheme)
  541. {
  542. _clrText = GetSysColor(COLOR_MENUTEXT);
  543. _clrBk = GetSysColor(COLOR_MENU);
  544. _hbrBk = GetSysColorBrush(COLOR_MENU);
  545. }
  546. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL);
  547. return 0;
  548. }
  549. LRESULT CMorePrograms::_OnDisplayChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  550. {
  551. _InitMetrics();
  552. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL);
  553. return 0;
  554. }
  555. LRESULT CMorePrograms::_OnSettingChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  556. {
  557. // _InitMetrics() is so cheap it's not worth getting too upset about
  558. // calling it too many times.
  559. _InitMetrics();
  560. SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL);
  561. return 0;
  562. }
  563. LRESULT CMorePrograms::_OnContextMenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  564. {
  565. if(SHRestricted(REST_NOSMMOREPROGRAMS))
  566. return 0;
  567. if (IS_WM_CONTEXTMENU_KEYBOARD(lParam))
  568. {
  569. RECT rc;
  570. GetWindowRect(_hwnd, &rc);
  571. lParam = MAKELPARAM(rc.left, rc.top);
  572. }
  573. c_tray.StartMenuContextMenu(_hwnd, (DWORD)lParam);
  574. return 0;
  575. }
  576. LRESULT CALLBACK CMorePrograms::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  577. {
  578. CMorePrograms *self = reinterpret_cast<CMorePrograms *>(GetWindowPtr(hwnd, GWLP_USERDATA));
  579. switch (uMsg)
  580. {
  581. case WM_NCCREATE:
  582. return self->_OnNCCreate(hwnd, uMsg, wParam, lParam);
  583. case WM_CREATE:
  584. return self->_OnCreate(hwnd, uMsg, wParam, lParam);
  585. case WM_DESTROY:
  586. return self->_OnDestroy(hwnd, uMsg, wParam, lParam);
  587. case WM_NCDESTROY:
  588. return self->_OnNCDestroy(hwnd, uMsg, wParam, lParam);
  589. case WM_CTLCOLORBTN:
  590. return self->_OnCtlColorBtn(hwnd, uMsg, wParam, lParam);
  591. case WM_DRAWITEM:
  592. return self->_OnDrawItem(hwnd, uMsg, wParam, lParam);
  593. case WM_ERASEBKGND:
  594. return self->_OnEraseBkgnd(hwnd, uMsg, wParam, lParam);
  595. case WM_COMMAND:
  596. return self->_OnCommand(hwnd, uMsg, wParam, lParam);
  597. case WM_SYSCOLORCHANGE:
  598. return self->_OnSysColorChange(hwnd, uMsg, wParam, lParam);
  599. case WM_DISPLAYCHANGE:
  600. return self->_OnDisplayChange(hwnd, uMsg, wParam, lParam);
  601. case WM_SETTINGCHANGE:
  602. return self->_OnSettingChange(hwnd, uMsg, wParam, lParam);
  603. case WM_NOTIFY:
  604. return self->_OnNotify(hwnd, uMsg, wParam, lParam);
  605. case WM_CONTEXTMENU:
  606. return self->_OnContextMenu(hwnd, uMsg, wParam, lParam);
  607. }
  608. return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  609. }
  610. BOOL WINAPI MorePrograms_RegisterClass()
  611. {
  612. WNDCLASSEX wc;
  613. ZeroMemory(&wc, sizeof(wc));
  614. wc.cbSize = sizeof(wc);
  615. wc.style = CS_GLOBALCLASS;
  616. wc.lpfnWndProc = CMorePrograms::s_WndProc;
  617. wc.hInstance = _Module.GetModuleInstance();
  618. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  619. wc.hbrBackground = NULL;
  620. wc.lpszClassName = WC_MOREPROGRAMS;
  621. return RegisterClassEx(&wc);
  622. }
  623. // We implement a minimal drop target so we can auto-open the More Programs
  624. // list when the user hovers over the More Programs button.
  625. // *** IUnknown ***
  626. HRESULT CMorePrograms::QueryInterface(REFIID riid, void * *ppvOut)
  627. {
  628. static const QITAB qit[] = {
  629. QITABENT(CMorePrograms, IDropTarget),
  630. QITABENT(CMorePrograms, IAccessible),
  631. QITABENT(CMorePrograms, IDispatch), // IAccessible derives from IDispatch
  632. { 0 },
  633. };
  634. return QISearch(this, qit, riid, ppvOut);
  635. }
  636. ULONG CMorePrograms::AddRef()
  637. {
  638. return InterlockedIncrement(&_lRef);
  639. }
  640. ULONG CMorePrograms::Release()
  641. {
  642. ULONG cRef = InterlockedDecrement(&_lRef);
  643. if (cRef)
  644. return cRef;
  645. delete this;
  646. return 0;
  647. }
  648. // *** IDropTarget::DragEnter ***
  649. HRESULT CMorePrograms::DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  650. {
  651. POINT pt = { ptl.x, ptl.y };
  652. if (_pdth) {
  653. _pdth->DragEnter(_hwnd, pdto, &pt, *pdwEffect);
  654. }
  655. // Remember when the hover started.
  656. _tmHoverStart = NonzeroGetTickCount();
  657. InvalidateRect(_hwndButton, NULL, TRUE); // draw with drop highlight
  658. return DragOver(grfKeyState, ptl, pdwEffect);
  659. }
  660. // *** IDropTarget::DragOver ***
  661. HRESULT CMorePrograms::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  662. {
  663. POINT pt = { ptl.x, ptl.y };
  664. if (_pdth) {
  665. _pdth->DragOver(&pt, *pdwEffect);
  666. }
  667. // Hover time is 1 second, the same as the hard-coded value for the
  668. // Start Button.
  669. if (_tmHoverStart && GetTickCount() - _tmHoverStart > 1000)
  670. {
  671. _tmHoverStart = 0;
  672. FORWARD_WM_COMMAND(_hwnd, IDC_BUTTON, _hwndButton, BN_CLICKED, PostMessage);
  673. }
  674. *pdwEffect = DROPEFFECT_NONE;
  675. return S_OK;
  676. }
  677. // *** IDropTarget::DragLeave ***
  678. HRESULT CMorePrograms::DragLeave()
  679. {
  680. if (_pdth) {
  681. _pdth->DragLeave();
  682. }
  683. _tmHoverStart = 0;
  684. InvalidateRect(_hwndButton, NULL, TRUE); // draw without drop highlight
  685. return S_OK;
  686. }
  687. // *** IDropTarget::Drop ***
  688. HRESULT CMorePrograms::Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  689. {
  690. POINT pt = { ptl.x, ptl.y };
  691. if (_pdth) {
  692. _pdth->Drop(pdto, &pt, *pdwEffect);
  693. }
  694. _tmHoverStart = 0;
  695. InvalidateRect(_hwndButton, NULL, TRUE); // draw without drop highlight
  696. return S_OK;
  697. }
  698. //****************************************************************************
  699. //
  700. // Accessibility
  701. //
  702. //
  703. // The default accessibility object reports buttons as
  704. // ROLE_SYSTEM_PUSHBUTTON, but we know that we are really a menu.
  705. //
  706. HRESULT CMorePrograms::get_accRole(VARIANT varChild, VARIANT *pvarRole)
  707. {
  708. HRESULT hr = CAccessible::get_accRole(varChild, pvarRole);
  709. if (SUCCEEDED(hr) && V_VT(pvarRole) == VT_I4)
  710. {
  711. switch (V_I4(pvarRole))
  712. {
  713. case ROLE_SYSTEM_PUSHBUTTON:
  714. V_I4(pvarRole) = ROLE_SYSTEM_MENUITEM;
  715. break;
  716. }
  717. }
  718. return hr;
  719. }
  720. HRESULT CMorePrograms::get_accState(VARIANT varChild, VARIANT *pvarState)
  721. {
  722. HRESULT hr = CAccessible::get_accState(varChild, pvarState);
  723. if (SUCCEEDED(hr) && V_VT(pvarState) == VT_I4)
  724. {
  725. V_I4(pvarState) |= STATE_SYSTEM_HASPOPUP;
  726. }
  727. return hr;
  728. }
  729. HRESULT CMorePrograms::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut)
  730. {
  731. return CreateAcceleratorBSTR(_chMnem, pszKeyboardShortcut);
  732. }
  733. HRESULT CMorePrograms::get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction)
  734. {
  735. DWORD dwRole = _fMenuOpen ? ACCSTR_CLOSE : ACCSTR_OPEN;
  736. return GetRoleString(dwRole, pszDefAction);
  737. }
  738. HRESULT CMorePrograms::accDoDefaultAction(VARIANT varChild)
  739. {
  740. if (_fMenuOpen)
  741. {
  742. _SendNotify(_hwnd, SMN_CANCELSHELLMENU);
  743. return S_OK;
  744. }
  745. else
  746. {
  747. return CAccessible::accDoDefaultAction(varChild);
  748. }
  749. }