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.

4089 lines
122 KiB

  1. #include "cabinet.h"
  2. #include "trayclok.h"
  3. #include <atlstuff.h>
  4. #include "traynot.h"
  5. #include "rcids.h"
  6. #include "tray.h"
  7. #include "util.h"
  8. #include "shellapi.h"
  9. //
  10. // Tray Notify Icon area implementation notes / details:
  11. //
  12. // - The icons are held in a toolbar with CTrayItem * on each button's lParam
  13. //
  14. //
  15. // #defines for TrayNotify
  16. //
  17. // Internal Tray Notify Timer IDs
  18. #define TID_DEMOTEDMENU 2
  19. #define TID_BALLOONPOP 3
  20. #define TID_BALLOONPOPWAIT 4
  21. #define TID_BALLOONSHOW 5
  22. #define TID_RUDEAPPHIDE 6 // When a fullscreen (rude) app has gone away
  23. #define KEYBOARD_VERSION 3
  24. #define TT_CHEVRON_INFOTIP_INTERVAL 30000 // 30 seconds
  25. #define TT_BALLOONPOP_INTERVAL 50
  26. #define TT_BALLOONPOP_INTERVAL_INCREMENT 30
  27. #define TT_BALLOONSHOW_INTERVAL 3000 // 3 seconds
  28. #define TT_RUDEAPPHIDE_INTERVAL 10000 // 10 seconds
  29. #define TT_DEMOTEDMENU_INTERVAL (2 * g_uDoubleClick)
  30. #define MAX_TIP_WIDTH 300
  31. #define MIN_INFO_TIME 10000 // 10 secs is minimum time a balloon can be up
  32. #define MAX_INFO_TIME 60000 // 1 min is the max time it can be up
  33. // Atleast 2 items are necessary to "demote" them under the chevron...
  34. #define MIN_DEMOTED_ITEMS_THRESHOLD 2
  35. #define PGMP_RECALCSIZE 200
  36. #define BALLOON_INTERVAL_MAX 10000
  37. #define BALLOON_INTERVAL_MEDIUM 3000
  38. #define BALLOON_INTERVAL_MIN 1000
  39. const TCHAR CTrayNotify::c_szTrayNotify[] = TEXT("TrayNotifyWnd");
  40. const WCHAR CTrayNotify::c_wzTrayNotifyTheme[] = L"TrayNotify";
  41. const WCHAR CTrayNotify::c_wzTrayNotifyHorizTheme[] = L"TrayNotifyHoriz";
  42. const WCHAR CTrayNotify::c_wzTrayNotifyVertTheme[] = L"TrayNotifyVert";
  43. const WCHAR CTrayNotify::c_wzTrayNotifyHorizOpenTheme[] = L"TrayNotifyHorizOpen";
  44. const WCHAR CTrayNotify::c_wzTrayNotifyVertOpenTheme[] = L"TrayNotifyVertOpen";
  45. //
  46. // Global functions...
  47. //
  48. int CALLBACK DeleteDPAPtrCB(TNINFOITEM *pItem, void *pData);
  49. int CALLBACK DeleteDPAPtrCB(TNINFOITEM *pItem, void *pData)
  50. {
  51. LocalFree(pItem);
  52. return TRUE;
  53. }
  54. //
  55. // Stub for CTrayNotify, so as to not break the COM rules of refcounting a static object
  56. //
  57. class ATL_NO_VTABLE CTrayNotifyStub :
  58. public CComObjectRootEx<CComSingleThreadModel>,
  59. public CComCoClass<CTrayNotifyStub, &CLSID_TrayNotify>,
  60. public ITrayNotify
  61. {
  62. public:
  63. CTrayNotifyStub() {};
  64. virtual ~CTrayNotifyStub() {};
  65. DECLARE_NOT_AGGREGATABLE(CTrayNotifyStub)
  66. BEGIN_COM_MAP(CTrayNotifyStub)
  67. COM_INTERFACE_ENTRY(ITrayNotify)
  68. END_COM_MAP()
  69. // *** ITrayNotify methoid ***
  70. STDMETHODIMP SetPreference(LPNOTIFYITEM pNotifyItem);
  71. STDMETHODIMP RegisterCallback(INotificationCB* pNotifyCB);
  72. STDMETHODIMP EnableAutoTray(BOOL bTraySetting);
  73. };
  74. //
  75. // CTrayNotifyStub functions...
  76. //
  77. HRESULT CTrayNotifyStub::SetPreference(LPNOTIFYITEM pNotifyItem)
  78. {
  79. return c_tray._trayNotify.SetPreference(pNotifyItem);
  80. }
  81. HRESULT CTrayNotifyStub::RegisterCallback(INotificationCB* pNotifyCB)
  82. {
  83. return c_tray._trayNotify.RegisterCallback(pNotifyCB);
  84. }
  85. HRESULT CTrayNotifyStub::EnableAutoTray(BOOL bTraySetting)
  86. {
  87. return c_tray._trayNotify.EnableAutoTray(bTraySetting);
  88. }
  89. HRESULT CTrayNotifyStub_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk)
  90. {
  91. if (pUnkOuter != NULL)
  92. return CLASS_E_NOAGGREGATION;
  93. CComObject<CTrayNotifyStub> *pStub = new CComObject<CTrayNotifyStub>;
  94. if (pStub)
  95. return pStub->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  96. else
  97. return E_OUTOFMEMORY;
  98. }
  99. //
  100. // CTrayNotify Methods..
  101. //
  102. // IUnknown methods
  103. STDMETHODIMP_(ULONG) CTrayNotify::AddRef()
  104. {
  105. return InterlockedIncrement(&m_cRef);
  106. }
  107. STDMETHODIMP_(ULONG) CTrayNotify::Release()
  108. {
  109. if (InterlockedDecrement(&m_cRef))
  110. return m_cRef;
  111. return 0;
  112. }
  113. #ifdef FULL_DEBUG
  114. void CTrayNotify::_TestNotify()
  115. {
  116. // Loop thru the toolbar
  117. INT_PTR iCount = m_TrayItemManager.GetItemCount();
  118. for (int i = 0; i < iCount; i++)
  119. {
  120. TBBUTTONINFO tbbi;
  121. TCHAR szButtonText[MAX_PATH];
  122. tbbi.cbSize = sizeof(TBBUTTONINFO);
  123. tbbi.dwMask = TBIF_BYINDEX | TBIF_IMAGE | TBIF_TEXT | TBIF_COMMAND;
  124. tbbi.pszText = szButtonText;
  125. tbbi.cchText = ARRAYSIZE(szButtonText);
  126. INT_PTR j = SendMessage(_hwndToolbar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
  127. if (j != -1)
  128. {
  129. TCHAR tempBuf[MAX_PATH];
  130. wsprintf(tempBuf, TEXT("Toolbar pos i = %d ==> idCommand = %d, iImage = %d, pszText = %s"), i, tbbi.idCommand, tbbi.iImage, tbbi.pszText);
  131. MessageBox(NULL, tempBuf, TEXT("My Test Message"), MB_OK);
  132. }
  133. }
  134. }
  135. #endif // DEBUG
  136. void CTrayNotify::_TickleForTooltip(CNotificationItem *pni)
  137. {
  138. if (pni->pszIconText == NULL || *pni->pszIconText == 0)
  139. {
  140. //
  141. // item hasn't set tooltip yet, tickle it by sending
  142. // mouse-moved notification
  143. //
  144. CTrayItem *pti = m_TrayItemManager.GetItemDataByIndex(
  145. m_TrayItemManager.FindItemAssociatedWithHwndUid(pni->hWnd, pni->uID));
  146. if (pti)
  147. {
  148. _SendNotify(pti, WM_MOUSEMOVE);
  149. }
  150. }
  151. }
  152. HRESULT CTrayNotify::RegisterCallback(INotificationCB* pNotifyCB)
  153. {
  154. if (!_fNoTrayItemsDisplayPolicyEnabled)
  155. {
  156. ATOMICRELEASE(_pNotifyCB);
  157. if (pNotifyCB)
  158. {
  159. pNotifyCB->AddRef();
  160. // Add Current Items
  161. int i = 0;
  162. BOOL bStat = FALSE;
  163. do
  164. {
  165. CNotificationItem ni;
  166. if (m_TrayItemManager.GetTrayItem(i++, &ni, &bStat))
  167. {
  168. if (bStat)
  169. {
  170. pNotifyCB->Notify(NIM_ADD, &ni);
  171. _TickleForTooltip(&ni);
  172. }
  173. }
  174. else
  175. break;
  176. } while (TRUE);
  177. // Add Past Items
  178. i = 0;
  179. bStat = FALSE;
  180. do
  181. {
  182. CNotificationItem ni;
  183. if (m_TrayItemRegistry.GetTrayItem(i++, &ni, &bStat))
  184. {
  185. if (bStat)
  186. pNotifyCB->Notify(NIM_ADD, &ni);
  187. }
  188. else
  189. break;
  190. } while (TRUE);
  191. }
  192. _pNotifyCB = pNotifyCB;
  193. }
  194. else
  195. {
  196. _pNotifyCB = NULL;
  197. }
  198. return S_OK;
  199. }
  200. HRESULT CTrayNotify::SetPreference(LPNOTIFYITEM pNotifyItem)
  201. {
  202. // This function should NEVER be called if the NoTrayItemsDisplayPolicy is enabled...
  203. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  204. ASSERT(!GetIsNoAutoTrayPolicyEnabled());
  205. ASSERT( pNotifyItem->dwUserPref == TNUP_AUTOMATIC ||
  206. pNotifyItem->dwUserPref == TNUP_DEMOTED ||
  207. pNotifyItem->dwUserPref == TNUP_PROMOTED );
  208. INT_PTR iItem = -1;
  209. if (pNotifyItem->hWnd)
  210. {
  211. iItem = m_TrayItemManager.FindItemAssociatedWithHwndUid(pNotifyItem->hWnd, pNotifyItem->uID);
  212. if (iItem != -1)
  213. {
  214. CTrayItem * pti = m_TrayItemManager.GetItemDataByIndex(iItem);
  215. if (pti && pti->dwUserPref != pNotifyItem->dwUserPref)
  216. {
  217. pti->dwUserPref = pNotifyItem->dwUserPref;
  218. // If the preference changes, the accumulated time must start again...
  219. if (pti->IsStartupIcon())
  220. pti->uNumSeconds = 0;
  221. _PlaceItem(iItem, pti, TRAYEVENT_ONAPPLYUSERPREF);
  222. _Size();
  223. return S_OK;
  224. }
  225. }
  226. }
  227. else
  228. {
  229. if (m_TrayItemRegistry.SetPastItemPreference(pNotifyItem))
  230. return S_OK;
  231. }
  232. return E_INVALIDARG;
  233. }
  234. UINT CTrayNotify::_GetAccumulatedTime(CTrayItem * pti)
  235. {
  236. // The global user event timer...
  237. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  238. IUserEventTimer * pUserEventTimer = _CreateTimer(TF_ICONDEMOTE_TIMER);
  239. UINT uTimerElapsed = 0;
  240. if (pUserEventTimer)
  241. {
  242. if (SUCCEEDED(pUserEventTimer->GetUserEventTimerElapsed(pti->hWnd, pti->uIconDemoteTimerID, &uTimerElapsed)))
  243. {
  244. uTimerElapsed /= 1000;
  245. }
  246. }
  247. return uTimerElapsed;
  248. }
  249. void CTrayNotify::_RemoveImage(UINT uIMLIndex)
  250. {
  251. INT_PTR nCount;
  252. INT_PTR i;
  253. if (uIMLIndex != (UINT)-1)
  254. {
  255. ImageList_Remove(_himlIcons, uIMLIndex);
  256. nCount = m_TrayItemManager.GetItemCount();
  257. for (i = nCount - 1; i >= 0; i--)
  258. {
  259. int iImage = m_TrayItemManager.GetTBBtnImage(i);
  260. if (iImage > (int)uIMLIndex)
  261. m_TrayItemManager.SetTBBtnImage(i, iImage - 1);
  262. }
  263. }
  264. }
  265. //---------------------------------------------------------------------------
  266. // Returns TRUE if either the images are OK as they are or they needed
  267. // resizing and the resize process worked. FALSE otherwise.
  268. BOOL CTrayNotify::_CheckAndResizeImages()
  269. {
  270. HIMAGELIST himlOld, himlNew;
  271. int cxSmIconNew, cySmIconNew, cxSmIconOld, cySmIconOld;
  272. int i, cItems;
  273. HICON hicon;
  274. BOOL fOK = TRUE;
  275. // if (!ptnd)
  276. // return 0;
  277. if (_fNoTrayItemsDisplayPolicyEnabled)
  278. return fOK;
  279. himlOld = _himlIcons;
  280. // Do dimensions match current icons?
  281. cxSmIconNew = GetSystemMetrics(SM_CXSMICON);
  282. cySmIconNew = GetSystemMetrics(SM_CYSMICON);
  283. ImageList_GetIconSize(himlOld, &cxSmIconOld, &cySmIconOld);
  284. if (cxSmIconNew != cxSmIconOld || cySmIconNew != cySmIconOld)
  285. {
  286. // Nope, we're gonna need a new imagelist.
  287. himlNew = ImageList_Create(cxSmIconNew, cySmIconNew, SHGetImageListFlags(_hwndToolbar), 0, 1);
  288. if (himlNew)
  289. {
  290. // Copy the images over to the new image list.
  291. cItems = ImageList_GetImageCount(himlOld);
  292. for (i = 0; i < cItems; i++)
  293. {
  294. // REVIEW - there's no way to copy images to an empty
  295. // imagelist, resizing it on the way.
  296. hicon = ImageList_GetIcon(himlOld, i, ILD_NORMAL);
  297. if (hicon)
  298. {
  299. if (ImageList_AddIcon(himlNew, hicon) == -1)
  300. {
  301. // Couldn't copy image so bail.
  302. fOK = FALSE;
  303. }
  304. DestroyIcon(hicon);
  305. }
  306. else
  307. {
  308. fOK = FALSE;
  309. }
  310. // FU - bail.
  311. if (!fOK)
  312. break;
  313. }
  314. // Did everything copy over OK?
  315. if (fOK)
  316. {
  317. // Yep, Set things up to use the new one.
  318. _himlIcons = himlNew;
  319. m_TrayItemManager.SetIconList(_himlIcons);
  320. // Destroy the old icon cache.
  321. ImageList_Destroy(himlOld);
  322. SendMessage(_hwndToolbar, TB_SETIMAGELIST, 0, (LPARAM) _himlIcons);
  323. SendMessage(_hwndToolbar, TB_AUTOSIZE, 0, 0);
  324. }
  325. else
  326. {
  327. // Nope, stick with what we have.
  328. ImageList_Destroy(himlNew);
  329. }
  330. }
  331. }
  332. return fOK;
  333. }
  334. void CTrayNotify::_ActivateTips(BOOL bActivate)
  335. {
  336. if (_fNoTrayItemsDisplayPolicyEnabled)
  337. return;
  338. if (bActivate && !_CanActivateTips())
  339. return;
  340. if (_hwndToolbarInfoTip)
  341. SendMessage(_hwndToolbarInfoTip, TTM_ACTIVATE, (WPARAM)bActivate, 0);
  342. }
  343. // x,y in client coords
  344. void CTrayNotify::_InfoTipMouseClick(int x, int y, BOOL bRightMouseButtonClick)
  345. {
  346. if (!_fNoTrayItemsDisplayPolicyEnabled && _pinfo)
  347. {
  348. RECT rect;
  349. GetWindowRect(_hwndInfoTip, &rect);
  350. // x & y are mapped to our window so map the rect to our window as well
  351. MapWindowRect(HWND_DESKTOP, _hwndNotify, &rect); // screen -> client
  352. POINT pt = {x, y};
  353. if (PtInRect(&rect, pt))
  354. {
  355. SHAllowSetForegroundWindow(_pinfo->hWnd);
  356. _beLastBalloonEvent = (bRightMouseButtonClick ? BALLOONEVENT_USERRIGHTCLICK : BALLOONEVENT_USERLEFTCLICK);
  357. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE,
  358. FALSE, (bRightMouseButtonClick ? NIN_BALLOONTIMEOUT : NIN_BALLOONUSERCLICK));
  359. }
  360. }
  361. }
  362. void CTrayNotify::_PositionInfoTip()
  363. {
  364. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  365. if (_pinfo)
  366. {
  367. int x = 0;
  368. int y = 0;
  369. // if (_pinfo->hWnd == _hwndNotify && _pinfo->uID == UID_CHEVRONBUTTON)
  370. if (_IsChevronInfoTip(_pinfo->hWnd, _pinfo->uID))
  371. {
  372. RECT rc;
  373. GetWindowRect(_hwndChevron, &rc);
  374. x = rc.left;
  375. y = rc.top;
  376. }
  377. else
  378. {
  379. INT_PTR iIndex = m_TrayItemManager.FindItemAssociatedWithHwndUid(_pinfo->hWnd, _pinfo->uID);
  380. if (iIndex != -1)
  381. {
  382. RECT rc;
  383. if (SendMessage(_hwndToolbar, TB_GETITEMRECT, iIndex, (LPARAM)&rc))
  384. {
  385. MapWindowRect(_hwndToolbar, HWND_DESKTOP, &rc);
  386. x = (rc.left + rc.right)/2;
  387. y = (rc.top + rc.bottom)/2;
  388. }
  389. }
  390. }
  391. SendMessage(_hwndInfoTip, TTM_TRACKPOSITION, 0, MAKELONG(x, y));
  392. }
  393. }
  394. BOOL CTrayNotify::_IsScreenSaverRunning()
  395. {
  396. BOOL fRunning;
  397. if (SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &fRunning, 0))
  398. {
  399. return fRunning;
  400. }
  401. return FALSE;
  402. }
  403. UINT CTrayNotify::_GetQueueCount()
  404. {
  405. return _dpaInfo ? _dpaInfo.GetPtrCount() : 0;
  406. }
  407. // NOTE: sligtly different versions of this exist in...
  408. // SHPlaySound() -> shell32
  409. // IEPlaySound() -> shdocvw/browseui
  410. STDAPI_(void) ExplorerPlaySound(LPCTSTR pszSound)
  411. {
  412. // note, we have access only to global system sounds here as we use "Apps\.Default"
  413. TCHAR szKey[256];
  414. wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("AppEvents\\Schemes\\Apps\\.Default\\%s\\.current"), pszSound);
  415. TCHAR szFileName[MAX_PATH];
  416. szFileName[0] = 0;
  417. LONG cbSize = sizeof(szFileName);
  418. // test for an empty string, PlaySound will play the Default Sound if we
  419. // give it a sound it cannot find...
  420. if ((RegQueryValue(HKEY_CURRENT_USER, szKey, szFileName, &cbSize) == ERROR_SUCCESS)
  421. && szFileName[0])
  422. {
  423. // flags are relevant, we try to not stomp currently playing sounds
  424. PlaySound(szFileName, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP);
  425. }
  426. }
  427. DWORD CTrayNotify::_ShowBalloonTip(LPTSTR szTitle, DWORD dwInfoFlags, UINT uTimeout, DWORD dwLastSoundTime)
  428. {
  429. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  430. DWORD dwCurrentSoundTime = dwLastSoundTime;
  431. SendMessage(_hwndInfoTip, TTM_SETTITLE, dwInfoFlags & NIIF_ICON_MASK, (LPARAM)szTitle);
  432. if (!(dwInfoFlags & NIIF_NOSOUND))
  433. {
  434. // make sure at least 5 seconds pass between sounds, avoid annoying balloons
  435. if ((GetTickCount() - dwLastSoundTime) >= 5000)
  436. {
  437. dwCurrentSoundTime = GetTickCount();
  438. ExplorerPlaySound(TEXT("SystemNotification"));
  439. }
  440. }
  441. _PositionInfoTip();
  442. // if tray is in auto hide mode unhide it
  443. c_tray.Unhide();
  444. c_tray._fBalloonUp = TRUE;
  445. TOOLINFO ti = {0};
  446. ti.cbSize = sizeof(ti);
  447. ti.hwnd = _hwndNotify;
  448. ti.uId = (INT_PTR)_hwndNotify;
  449. ti.lpszText = _pinfo->szInfo;
  450. SendMessage(_hwndInfoTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  451. // disable regular tooltips
  452. _fInfoTipShowing = TRUE;
  453. _ActivateTips(FALSE);
  454. // show the balloon
  455. SendMessage(_hwndInfoTip, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
  456. _SetTimer(TF_INFOTIP_TIMER, TNM_INFOTIPTIMER, uTimeout, &_uInfoTipTimer);
  457. return dwCurrentSoundTime;
  458. }
  459. void CTrayNotify::_HideBalloonTip()
  460. {
  461. _litsLastInfoTip = LITS_BALLOONDESTROYED;
  462. SendMessage(_hwndInfoTip, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)0);
  463. TOOLINFO ti = {0};
  464. ti.cbSize = sizeof(ti);
  465. ti.hwnd = _hwndNotify;
  466. ti.uId = (INT_PTR)_hwndNotify;
  467. ti.lpszText = NULL;
  468. SendMessage(_hwndInfoTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  469. }
  470. void CTrayNotify::_DisableCurrentInfoTip(CTrayItem * ptiTemp, UINT uReason, BOOL bBalloonShowing)
  471. {
  472. _KillTimer(TF_INFOTIP_TIMER, _uInfoTipTimer);
  473. _uInfoTipTimer = 0;
  474. if (ptiTemp)
  475. {
  476. if (!uReason)
  477. {
  478. uReason = NIN_BALLOONTIMEOUT;
  479. }
  480. _SendNotify(ptiTemp, uReason);
  481. }
  482. delete _pinfo;
  483. _pinfo = NULL;
  484. if (bBalloonShowing)
  485. _HideBalloonTip();
  486. }
  487. void CTrayNotify::_EmptyInfoTipQueue()
  488. {
  489. delete _pinfo;
  490. _pinfo = NULL;
  491. _dpaInfo.EnumCallback(DeleteDPAPtrCB, NULL);
  492. _dpaInfo.DeleteAllPtrs();
  493. }
  494. BOOL CTrayNotify::_CanShowBalloon()
  495. {
  496. if (!_bStartMenuAllowsTrayBalloon || _bWaitingBetweenBalloons || _bWorkStationLocked
  497. || _bRudeAppLaunched || IsDirectXAppRunningFullScreen() || _bWaitAfterRudeAppHide)
  498. return FALSE;
  499. return TRUE;
  500. }
  501. void CTrayNotify::_ShowInfoTip(HWND hwnd, UINT uID, BOOL bShow, BOOL bAsync, UINT uReason)
  502. {
  503. if (_fNoTrayItemsDisplayPolicyEnabled)
  504. return;
  505. // make sure we only show/hide what we intended to show/hide
  506. if (_pinfo && _pinfo->hWnd == hwnd && _pinfo->uID == uID)
  507. {
  508. CTrayItem * pti = NULL;
  509. INT_PTR nIcon = ( _IsChevronInfoTip(hwnd, uID) ?
  510. -1 :
  511. m_TrayItemManager.FindItemAssociatedWithHwndUid(hwnd, uID) );
  512. if (nIcon != -1)
  513. pti = m_TrayItemManager.GetItemDataByIndex(nIcon);
  514. BOOL bNotify = TRUE;
  515. if (bShow && pti)
  516. {
  517. if (!_fEnableUserTrackedInfoTips || pti->dwUserPref == TNUP_DEMOTED)
  518. {
  519. _SendNotify(pti, NIN_BALLOONSHOW);
  520. _SendNotify(pti, NIN_BALLOONTIMEOUT);
  521. }
  522. }
  523. // if ( (!(hwnd == _hwndNotify && uID == UID_CHEVRONBUTTON)) &&
  524. if ( !_IsChevronInfoTip(hwnd, uID) &&
  525. ( !pti || pti->IsHidden()
  526. || pti->dwUserPref == TNUP_DEMOTED
  527. || !_fEnableUserTrackedInfoTips
  528. )
  529. )
  530. {
  531. // icon is hidden, cannot show its balloon
  532. bNotify = !bShow;
  533. bShow = FALSE; //show the next balloon instead
  534. }
  535. if (bShow)
  536. {
  537. if (bAsync)
  538. {
  539. PostMessage(_hwndNotify, TNM_ASYNCINFOTIP, (WPARAM)hwnd, (LPARAM)uID);
  540. }
  541. else if (_CanShowBalloon())
  542. {
  543. DWORD dwLastSoundTime = 0;
  544. if ((nIcon != -1) && pti)
  545. {
  546. _PlaceItem(nIcon, pti, TRAYEVENT_ONINFOTIP);
  547. dwLastSoundTime = pti->dwLastSoundTime;
  548. }
  549. dwLastSoundTime = _ShowBalloonTip(_pinfo->szTitle, _pinfo->dwInfoFlags, _pinfo->uTimeout, dwLastSoundTime);
  550. if ((nIcon != -1) && pti)
  551. {
  552. pti->dwLastSoundTime = dwLastSoundTime;
  553. _SendNotify(pti, NIN_BALLOONSHOW);
  554. }
  555. }
  556. }
  557. else
  558. {
  559. if (_IsChevronInfoTip(hwnd, uID))
  560. {
  561. // If the user clicked on the chevron info tip, we dont want to show the
  562. // chevron any more, otherwise we want to show it once more the next session
  563. // for a maximum of 5 sessions...
  564. m_TrayItemRegistry.IncChevronInfoTipShownInRegistry(uReason == NIN_BALLOONUSERCLICK);
  565. }
  566. _DisableCurrentInfoTip(pti, uReason, TRUE);
  567. _bWaitingBetweenBalloons = TRUE;
  568. c_tray._fBalloonUp = FALSE;
  569. _fInfoTipShowing = FALSE;
  570. _ActivateTips(TRUE);
  571. // we are hiding the current balloon. are there any waiting? yes, then show the first one
  572. if (_GetQueueCount())
  573. {
  574. _pinfo = _dpaInfo.DeletePtr(0);
  575. SetTimer(_hwndNotify, TID_BALLOONPOPWAIT, _GetBalloonWaitInterval(_beLastBalloonEvent), NULL);
  576. }
  577. else
  578. {
  579. _bWaitingBetweenBalloons = FALSE;
  580. UpdateWindow(_hwndToolbar);
  581. }
  582. }
  583. }
  584. else if (_pinfo && !bShow)
  585. {
  586. // we wanted to hide something that wasn't showing up
  587. // maybe it's in the queue
  588. // Remove only the first info tip from this (hwnd, uID)
  589. _RemoveInfoTipFromQueue(hwnd, uID, TRUE);
  590. }
  591. }
  592. void CTrayNotify::_SetInfoTip(HWND hWnd, UINT uID, LPTSTR pszInfo, LPTSTR pszInfoTitle,
  593. DWORD dwInfoFlags, UINT uTimeout, BOOL bAsync)
  594. {
  595. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  596. // show the new one...
  597. if (pszInfo[0])
  598. {
  599. TNINFOITEM *pii = new TNINFOITEM;
  600. if (pii)
  601. {
  602. pii->hWnd = hWnd;
  603. pii->uID = uID;
  604. lstrcpyn(pii->szInfo, pszInfo, ARRAYSIZE(pii->szInfo));
  605. lstrcpyn(pii->szTitle, pszInfoTitle, ARRAYSIZE(pii->szTitle));
  606. pii->uTimeout = uTimeout;
  607. if (pii->uTimeout < MIN_INFO_TIME)
  608. pii->uTimeout = MIN_INFO_TIME;
  609. else if (pii->uTimeout > MAX_INFO_TIME)
  610. pii->uTimeout = MAX_INFO_TIME;
  611. pii->dwInfoFlags = dwInfoFlags;
  612. // if _pinfo is non NULL then we have a balloon showing right now
  613. if (_pinfo || _GetQueueCount())
  614. {
  615. // if this is a different icon making the change request
  616. // we might have to queue this up
  617. if (hWnd != _pinfo->hWnd || uID != _pinfo->uID)
  618. {
  619. // if the current balloon has not been up for the minimum
  620. // show delay or there are other items in the queue
  621. // add this to the queue
  622. if (!_dpaInfo || _dpaInfo.AppendPtr(pii) == -1)
  623. {
  624. delete pii;
  625. }
  626. return;
  627. }
  628. CTrayItem * ptiTemp = NULL;
  629. INT_PTR nIcon = m_TrayItemManager.FindItemAssociatedWithHwndUid(_pinfo->hWnd, _pinfo->uID);
  630. if (nIcon != -1)
  631. ptiTemp = m_TrayItemManager.GetItemDataByIndex(nIcon);
  632. _DisableCurrentInfoTip(ptiTemp, NIN_BALLOONTIMEOUT, FALSE);
  633. }
  634. _pinfo = pii; // in with the new
  635. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, TRUE, bAsync, 0);
  636. }
  637. }
  638. else
  639. {
  640. // empty text means get rid of the balloon
  641. _beLastBalloonEvent = BALLOONEVENT_BALLOONHIDE;
  642. _ShowInfoTip(hWnd, uID, FALSE, FALSE, NIN_BALLOONHIDE);
  643. }
  644. }
  645. DWORD CTrayNotify::_GetBalloonWaitInterval(BALLOONEVENT be)
  646. {
  647. switch (be)
  648. {
  649. case BALLOONEVENT_USERLEFTCLICK:
  650. return BALLOON_INTERVAL_MAX;
  651. case BALLOONEVENT_TIMEOUT:
  652. case BALLOONEVENT_NONE:
  653. case BALLOONEVENT_APPDEMOTE:
  654. case BALLOONEVENT_BALLOONHIDE:
  655. return BALLOON_INTERVAL_MEDIUM;
  656. case BALLOONEVENT_USERRIGHTCLICK:
  657. case BALLOONEVENT_USERXCLICK:
  658. default:
  659. return BALLOON_INTERVAL_MIN;
  660. }
  661. }
  662. BOOL CTrayNotify::_ModifyNotify(PNOTIFYICONDATA32 pnid, INT_PTR nIcon, BOOL *pbRefresh, BOOL bFirstTime)
  663. {
  664. BOOL fResize = FALSE;
  665. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  666. CTrayItem * pti = m_TrayItemManager.GetItemDataByIndex(nIcon);
  667. if (!pti)
  668. {
  669. return FALSE;
  670. }
  671. _CheckAndResizeImages();
  672. if (pnid->uFlags & NIF_STATE)
  673. {
  674. #define NIS_VALIDMASK (NIS_HIDDEN | NIS_SHAREDICON)
  675. DWORD dwOldState = pti->dwState;
  676. // validate mask
  677. if (pnid->dwStateMask & ~NIS_VALIDMASK)
  678. {
  679. return FALSE;
  680. }
  681. pti->dwState = (pnid->dwState & pnid->dwStateMask) | (pti->dwState & ~pnid->dwStateMask);
  682. if (pnid->dwStateMask & NIS_HIDDEN)
  683. {
  684. if (pti->IsHidden())
  685. {
  686. m_TrayItemManager.SetTBBtnStateHelper(nIcon, TBSTATE_ENABLED, FALSE);
  687. _PlaceItem(nIcon, pti, TRAYEVENT_ONICONHIDE);
  688. }
  689. else
  690. {
  691. // When the icon is inserted the first time, this function is called..
  692. // If the icon ended the previous session in the secondary tray, then it would
  693. // start this session in the secondary tray, in which case, the icon should not
  694. // be enabled...
  695. if (!bFirstTime)
  696. {
  697. m_TrayItemManager.SetTBBtnStateHelper(nIcon, TBSTATE_ENABLED, TRUE);
  698. _PlaceItem(nIcon, pti, TRAYEVENT_ONICONUNHIDE);
  699. }
  700. }
  701. }
  702. if ((pnid->dwState ^ dwOldState) & NIS_SHAREDICON)
  703. {
  704. if (dwOldState & NIS_SHAREDICON)
  705. {
  706. // if we're going from shared to not shared,
  707. // clear the icon
  708. m_TrayItemManager.SetTBBtnImage(nIcon, -1);
  709. pti->hIcon = NULL;
  710. }
  711. }
  712. fResize |= ((pnid->dwState ^ dwOldState) & NIS_HIDDEN);
  713. }
  714. if (pnid->uFlags & NIF_GUID)
  715. {
  716. memcpy(&(pti->guidItem), &(pnid->guidItem), sizeof(pnid->guidItem));
  717. }
  718. // The icon is the only thing that can fail, so I will do it first
  719. if (pnid->uFlags & NIF_ICON)
  720. {
  721. int iImageNew, iImageOld;
  722. HICON hIcon1 = NULL, hIcon2 = NULL;
  723. BOOL bIsEqualIcon = FALSE;
  724. iImageOld = m_TrayItemManager.GetTBBtnImage(nIcon);
  725. if (!bFirstTime)
  726. {
  727. if (iImageOld != -1)
  728. {
  729. if (_himlIcons)
  730. {
  731. hIcon1 = ImageList_GetIcon(_himlIcons, iImageOld, ILD_NORMAL);
  732. }
  733. if (hIcon1)
  734. {
  735. hIcon2 = GetHIcon(pnid);
  736. }
  737. }
  738. if (iImageOld != -1 && hIcon1 && hIcon2)
  739. {
  740. bIsEqualIcon = SHAreIconsEqual(hIcon1, hIcon2);
  741. }
  742. if (hIcon1)
  743. DestroyIcon(hIcon1);
  744. }
  745. if (pti->IsIconShared())
  746. {
  747. iImageNew = m_TrayItemManager.FindImageIndex(GetHIcon(pnid), TRUE);
  748. if (iImageNew == -1)
  749. {
  750. return FALSE;
  751. }
  752. }
  753. else
  754. {
  755. if (GetHIcon(pnid))
  756. {
  757. // Replace icon knows how to handle -1 for add
  758. iImageNew = ImageList_ReplaceIcon(_himlIcons, iImageOld, GetHIcon(pnid));
  759. if (iImageNew < 0)
  760. {
  761. return FALSE;
  762. }
  763. }
  764. else
  765. {
  766. _RemoveImage(iImageOld);
  767. iImageNew = -1;
  768. }
  769. if (pti->IsSharedIconSource())
  770. {
  771. INT_PTR iCount = m_TrayItemManager.GetItemCount();
  772. // if we're the source of shared icons, we need to go update all the other icons that
  773. // are using our icon
  774. for (INT_PTR i = 0; i < iCount; i++)
  775. {
  776. if (m_TrayItemManager.GetTBBtnImage(i) == iImageOld)
  777. {
  778. CTrayItem * ptiTemp = m_TrayItemManager.GetItemDataByIndex(i);
  779. ptiTemp->hIcon = GetHIcon(pnid);
  780. m_TrayItemManager.SetTBBtnImage(i, iImageNew);
  781. }
  782. }
  783. }
  784. if (iImageOld == -1 || iImageNew == -1)
  785. fResize = TRUE;
  786. }
  787. pti->hIcon = GetHIcon(pnid);
  788. m_TrayItemManager.SetTBBtnImage(nIcon, iImageNew);
  789. // Dont count HICON_MODIFies the first time...
  790. if (!pti->IsHidden() && !bFirstTime)
  791. {
  792. pti->SetItemSameIconModify(bIsEqualIcon);
  793. _PlaceItem(nIcon, pti, TRAYEVENT_ONICONMODIFY);
  794. }
  795. }
  796. if (pnid->uFlags & NIF_MESSAGE)
  797. {
  798. pti->uCallbackMessage = pnid->uCallbackMessage;
  799. }
  800. if (pnid->uFlags & NIF_TIP)
  801. {
  802. m_TrayItemManager.SetTBBtnText(nIcon, pnid->szTip);
  803. StrCpyN(pti->szIconText, pnid->szTip, ARRAYSIZE(pnid->szTip));
  804. }
  805. if (fResize)
  806. _OnSizeChanged(FALSE);
  807. // need to have info stuff done after resize because we need to
  808. // position the infotip relative to the hwndToolbar
  809. if (pnid->uFlags & NIF_INFO)
  810. {
  811. // if button is hidden we don't show infotip
  812. if (!pti->IsHidden())
  813. {
  814. _SetInfoTip(pti->hWnd, pti->uID, pnid->szInfo, pnid->szInfoTitle, pnid->dwInfoFlags,
  815. pnid->uTimeout, (bFirstTime || fResize));
  816. }
  817. }
  818. if (!bFirstTime)
  819. _NotifyCallback(NIM_MODIFY, nIcon, -1);
  820. return TRUE;
  821. }
  822. BOOL CTrayNotify::_SetVersionNotify(PNOTIFYICONDATA32 pnid, INT_PTR nIcon)
  823. {
  824. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  825. CTrayItem * pti = m_TrayItemManager.GetItemDataByIndex(nIcon);
  826. if (!pti)
  827. return FALSE;
  828. if (pnid->uVersion < NOTIFYICON_VERSION)
  829. {
  830. pti->uVersion = 0;
  831. return TRUE;
  832. }
  833. else if (pnid->uVersion == NOTIFYICON_VERSION)
  834. {
  835. pti->uVersion = NOTIFYICON_VERSION;
  836. return TRUE;
  837. }
  838. else
  839. {
  840. return FALSE;
  841. }
  842. }
  843. void CTrayNotify::_NotifyCallback(DWORD dwMessage, INT_PTR nCurrentItem, INT_PTR nPastItem)
  844. {
  845. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  846. if (_pNotifyCB)
  847. {
  848. CNotificationItem * pni = new CNotificationItem;
  849. if (pni)
  850. {
  851. BOOL bStat = FALSE;
  852. if (nCurrentItem != -1 && m_TrayItemManager.GetTrayItem(nCurrentItem, pni, &bStat) && bStat)
  853. {
  854. PostMessage(_hwndNotify, TNM_NOTIFY, dwMessage, (LPARAM)pni);
  855. }
  856. else if (nCurrentItem == -1 && nPastItem != -1)
  857. {
  858. bStat = FALSE;
  859. m_TrayItemRegistry.GetTrayItem(nPastItem, pni, &bStat);
  860. if (bStat)
  861. PostMessage(_hwndNotify, TNM_NOTIFY, dwMessage, (LPARAM)pni);
  862. else
  863. delete pni;
  864. }
  865. else
  866. delete pni;
  867. }
  868. }
  869. }
  870. void CTrayNotify::_RemoveInfoTipFromQueue(HWND hWnd, UINT uID, BOOL bRemoveFirstOnly /* Default = FALSE */)
  871. {
  872. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  873. int cItems = _GetQueueCount();
  874. for (int i = 0; _dpaInfo && (i < cItems); i++)
  875. {
  876. TNINFOITEM * pii = _dpaInfo.GetPtr(i);
  877. if (pii->hWnd == hWnd && pii->uID == uID)
  878. {
  879. _dpaInfo.DeletePtr(i); // this removes the element from the dpa
  880. delete pii;
  881. if (bRemoveFirstOnly)
  882. {
  883. return;
  884. }
  885. else
  886. {
  887. i = i-1;
  888. cItems = _GetQueueCount();
  889. }
  890. }
  891. }
  892. }
  893. BOOL CTrayNotify::_DeleteNotify(INT_PTR nIcon, BOOL bShutdown, BOOL bShouldSaveIcon)
  894. {
  895. BOOL bRet = FALSE;
  896. if (_fNoTrayItemsDisplayPolicyEnabled)
  897. return bRet;
  898. _NotifyCallback(NIM_DELETE, nIcon, -1);
  899. CTrayItem *pti = m_TrayItemManager.GetItemDataByIndex(nIcon);
  900. if (pti)
  901. {
  902. _RemoveInfoTipFromQueue(pti->hWnd, pti->uID);
  903. // delete info tip if showing
  904. if (_pinfo && _pinfo->hWnd == pti->hWnd && _pinfo->uID == pti->uID)
  905. {
  906. // frees pinfo and shows the next balloon if any
  907. _beLastBalloonEvent = BALLOONEVENT_BALLOONHIDE;
  908. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, TRUE, NIN_BALLOONHIDE);
  909. }
  910. // Save the icon info only if needed...
  911. if (bShouldSaveIcon && pti->ShouldSaveIcon())
  912. {
  913. if (pti->IsStartupIcon() && !pti->IsDemoted() && pti->dwUserPref == TNUP_AUTOMATIC)
  914. pti->uNumSeconds = _GetAccumulatedTime(pti);
  915. // On Delete, add the icon to the past icons list, and the item to the past items list
  916. HICON hIcon = NULL;
  917. if (_himlIcons)
  918. {
  919. hIcon = ImageList_GetIcon(_himlIcons, m_TrayItemManager.GetTBBtnImage(nIcon), ILD_NORMAL);
  920. }
  921. int nPastSessionIndex = m_TrayItemRegistry.DoesIconExistFromPreviousSession(pti, pti->szIconText, hIcon);
  922. if (nPastSessionIndex != -1)
  923. {
  924. _NotifyCallback(NIM_DELETE, -1, nPastSessionIndex);
  925. m_TrayItemRegistry.DeletePastItem(nPastSessionIndex);
  926. }
  927. if (m_TrayItemRegistry.AddToPastItems(pti, hIcon))
  928. _NotifyCallback(NIM_ADD, -1, 0);
  929. if (hIcon)
  930. DestroyIcon(hIcon);
  931. }
  932. _KillItemTimer(pti);
  933. bRet = (BOOL) SendMessage(_hwndToolbar, TB_DELETEBUTTON, nIcon, 0);
  934. if (!bShutdown)
  935. {
  936. _UpdateChevronState(_fBangMenuOpen, FALSE, TRUE);
  937. _OnSizeChanged(FALSE);
  938. }
  939. }
  940. else
  941. {
  942. TraceMsg(TF_ERROR, "Removing nIcon %x - pti is NULL", nIcon);
  943. }
  944. return bRet;
  945. }
  946. BOOL CTrayNotify::_InsertNotify(PNOTIFYICONDATA32 pnid)
  947. {
  948. TBBUTTON tbb;
  949. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  950. // First insert a totally "default" icon
  951. CTrayItem * pti = new CTrayItem;
  952. if (!pti)
  953. {
  954. return FALSE;
  955. }
  956. pti->hWnd = GetHWnd(pnid);
  957. pti->uID = pnid->uID;
  958. if (_bStartupIcon)
  959. pti->SetStartupIcon(TRUE);
  960. if (!SUCCEEDED(SHExeNameFromHWND(pti->hWnd, pti->szExeName, ARRAYSIZE(pti->szExeName))))
  961. {
  962. pti->szExeName[0] = '\0';
  963. }
  964. INT_PTR nPastSessionIndex = m_TrayItemRegistry.CheckAndRestorePersistentIconSettings( pti,
  965. ((pnid->uFlags & NIF_TIP) ? pnid->szTip : NULL),
  966. ((pnid->uFlags & NIF_ICON) ? GetHIcon(pnid) : NULL) );
  967. tbb.dwData = (DWORD_PTR)pti;
  968. tbb.iBitmap = -1;
  969. tbb.idCommand = Toolbar_GetUniqueID(_hwndToolbar);
  970. tbb.fsStyle = BTNS_BUTTON;
  971. tbb.fsState = TBSTATE_ENABLED;
  972. // The "Show Always" flag should be special-cased...
  973. // If the item didnt exist before, and is added for the first time
  974. if (nPastSessionIndex == -1)
  975. {
  976. if (pnid->dwStateMask & NIS_SHOWALWAYS)
  977. {
  978. // Make sure that only the explorer process is setting this mask...
  979. if ( pti->szExeName && _szExplorerExeName &&
  980. lstrcmpi(pti->szExeName, _szExplorerExeName) )
  981. {
  982. pti->dwUserPref = TNUP_PROMOTED;
  983. }
  984. }
  985. }
  986. pnid->dwStateMask &= ~NIS_SHOWALWAYS;
  987. // If one of the icons had been placed in the secondary tray in the previous session...
  988. if (pti->IsDemoted() && m_TrayItemRegistry.IsAutoTrayEnabled())
  989. {
  990. tbb.fsState |= TBSTATE_HIDDEN;
  991. }
  992. tbb.iString = -1;
  993. BOOL fRet = TRUE;
  994. BOOL fRedraw = _SetRedraw(FALSE);
  995. // Insert at the zeroth position (from the beginning)
  996. INT_PTR iInsertPos = 0;
  997. if (SendMessage(_hwndToolbar, TB_INSERTBUTTON, iInsertPos, (LPARAM)&tbb))
  998. {
  999. // Then modify this icon with the specified info
  1000. if (!_ModifyNotify(pnid, iInsertPos, NULL, TRUE))
  1001. {
  1002. _DeleteNotify(iInsertPos, FALSE, FALSE);
  1003. fRet = FALSE;
  1004. }
  1005. // BUG : 404477, Re-entrancy case where traynotify gets a TBN_DELETINGBUTTON
  1006. // when processing a TB_INSERTBUTTON. In this re-entrant scenario, pti is
  1007. // invalid after the TB_INSERTBUTTON above, even though it was created fine
  1008. // before the TB_INSERTBUTTON.
  1009. // Hence check for pti...
  1010. else if (!pti)
  1011. {
  1012. fRet = FALSE;
  1013. }
  1014. else
  1015. {
  1016. // The item has been successfully added to the tray, and the user's
  1017. // settings have been honored. So it can be deleted from the Past Items
  1018. // list and the Past Items bucket...
  1019. if (nPastSessionIndex != -1)
  1020. {
  1021. _NotifyCallback(NIM_DELETE, -1, nPastSessionIndex);
  1022. m_TrayItemRegistry.DeletePastItem(nPastSessionIndex);
  1023. }
  1024. if (!_PlaceItem(0, pti, TRAYEVENT_ONNEWITEMINSERT))
  1025. {
  1026. _UpdateChevronState(_fBangMenuOpen, FALSE, TRUE);
  1027. _OnSizeChanged(FALSE);
  1028. }
  1029. // _hwndToolbar might not be large enough to hold new icon
  1030. _Size();
  1031. }
  1032. }
  1033. _SetRedraw(fRedraw);
  1034. if (fRet)
  1035. _NotifyCallback(NIM_ADD, iInsertPos, -1);
  1036. return fRet;
  1037. }
  1038. // set the mouse cursor to the center of the button.
  1039. // do this becaus our tray notifies don't have enough data slots to
  1040. // pass through info about the button's position.
  1041. void CTrayNotify::_SetCursorPos(INT_PTR i)
  1042. {
  1043. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1044. RECT rc;
  1045. if (SendMessage(_hwndToolbar, TB_GETITEMRECT, i, (LPARAM)&rc))
  1046. {
  1047. MapWindowPoints(_hwndToolbar, HWND_DESKTOP, (LPPOINT)&rc, 2);
  1048. SetCursorPos((rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2);
  1049. }
  1050. }
  1051. LRESULT CTrayNotify::_SendNotify(CTrayItem * pti, UINT uMsg)
  1052. {
  1053. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1054. if (pti->uCallbackMessage && pti->hWnd)
  1055. return SendNotifyMessage(pti->hWnd, pti->uCallbackMessage, pti->uID, uMsg);
  1056. return 0;
  1057. }
  1058. void CTrayNotify::_SetToolbarHotItem(HWND hWndToolbar, UINT nToolbarIcon)
  1059. {
  1060. if (!_fNoTrayItemsDisplayPolicyEnabled && hWndToolbar && nToolbarIcon != -1)
  1061. {
  1062. SetFocus(hWndToolbar);
  1063. InvalidateRect(hWndToolbar, NULL, TRUE);
  1064. SendMessage(hWndToolbar, TB_SETHOTITEM, nToolbarIcon, 0);
  1065. }
  1066. }
  1067. LRESULT CALLBACK CTrayNotify::ChevronSubClassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  1068. LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  1069. {
  1070. CTrayNotify * pTrayNotify = reinterpret_cast<CTrayNotify*>(dwRefData);
  1071. AssertMsg((pTrayNotify != NULL), TEXT("pTrayNotify SHOULD NOT be NULL, as passed to ChevronSubClassWndProc."));
  1072. if (pTrayNotify->_fNoTrayItemsDisplayPolicyEnabled)
  1073. {
  1074. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1075. }
  1076. static BOOL bBangMenuOpenLastTime = pTrayNotify->_fBangMenuOpen;
  1077. switch(uMsg)
  1078. {
  1079. case WM_KEYDOWN:
  1080. {
  1081. BOOL fLastHot = FALSE;
  1082. switch(wParam)
  1083. {
  1084. case VK_UP:
  1085. case VK_LEFT:
  1086. fLastHot = TRUE;
  1087. // Fall through...
  1088. case VK_DOWN:
  1089. case VK_RIGHT:
  1090. {
  1091. INT_PTR nToolbarIconSelected = pTrayNotify->_GetToolbarFirstVisibleItem(
  1092. pTrayNotify->_hwndToolbar, fLastHot);
  1093. pTrayNotify->_fChevronSelected = FALSE;
  1094. if (!fLastHot)
  1095. {
  1096. if (nToolbarIconSelected != -1)
  1097. // The toolbar has been selected
  1098. {
  1099. pTrayNotify->_SetToolbarHotItem(pTrayNotify->_hwndToolbar, nToolbarIconSelected);
  1100. }
  1101. else if (pTrayNotify->_hwndClock)
  1102. {
  1103. SetFocus(pTrayNotify->_hwndClock);
  1104. }
  1105. else
  1106. // No visible items on the tray, no clock, so nothing happens
  1107. {
  1108. pTrayNotify->_fChevronSelected = TRUE;
  1109. }
  1110. }
  1111. else
  1112. {
  1113. if (pTrayNotify->_hwndClock)
  1114. {
  1115. SetFocus(pTrayNotify->_hwndClock);
  1116. }
  1117. else if (nToolbarIconSelected != -1)
  1118. {
  1119. pTrayNotify->_SetToolbarHotItem(pTrayNotify->_hwndToolbar, nToolbarIconSelected);
  1120. }
  1121. else
  1122. {
  1123. pTrayNotify->_fChevronSelected = TRUE;
  1124. }
  1125. }
  1126. return 0;
  1127. }
  1128. break;
  1129. case VK_RETURN:
  1130. case VK_SPACE:
  1131. pTrayNotify->_ToggleDemotedMenu();
  1132. return 0;
  1133. }
  1134. }
  1135. break;
  1136. case WM_SETFOCUS:
  1137. case WM_KILLFOCUS:
  1138. {
  1139. if (pTrayNotify->_hTheme)
  1140. {
  1141. pTrayNotify->_fHasFocus = (uMsg == WM_SETFOCUS);
  1142. }
  1143. }
  1144. break;
  1145. default:
  1146. if (InRange(uMsg, WM_MOUSEFIRST, WM_MOUSELAST))
  1147. {
  1148. if (bBangMenuOpenLastTime != pTrayNotify->_fBangMenuOpen)
  1149. {
  1150. TOOLINFO ti = {0};
  1151. ti.cbSize = sizeof(ti);
  1152. ti.hwnd = pTrayNotify->_hwndNotify;
  1153. ti.uId = (UINT_PTR)pTrayNotify->_hwndChevron;
  1154. ti.lpszText = (LPTSTR)MAKEINTRESOURCE(!pTrayNotify->_fBangMenuOpen ? IDS_SHOWDEMOTEDTIP : IDS_HIDEDEMOTEDTIP);
  1155. ti.hinst = hinstCabinet;
  1156. SendMessage(pTrayNotify->_hwndChevronToolTip, TTM_UPDATETIPTEXT, 0, (LPARAM)(LPTOOLINFO)&ti);
  1157. bBangMenuOpenLastTime = pTrayNotify->_fBangMenuOpen;
  1158. }
  1159. MSG msg = {0};
  1160. msg.lParam = lParam;
  1161. msg.wParam = wParam;
  1162. msg.message = uMsg;
  1163. msg.hwnd = hwnd;
  1164. SendMessage(pTrayNotify->_hwndChevronToolTip, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&msg);
  1165. }
  1166. break;
  1167. }
  1168. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1169. }
  1170. LRESULT CALLBACK CTrayNotify::s_ToolbarWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  1171. LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  1172. {
  1173. BOOL fClickDown = FALSE;
  1174. CTrayNotify * pTrayNotify = reinterpret_cast<CTrayNotify*>(dwRefData);
  1175. AssertMsg((pTrayNotify != NULL), TEXT("pTrayNotify SHOULD NOT be NULL, as passed to s_ToolbarWndProc."));
  1176. if (pTrayNotify->_fNoTrayItemsDisplayPolicyEnabled)
  1177. {
  1178. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1179. }
  1180. switch (uMsg)
  1181. {
  1182. case WM_KEYDOWN:
  1183. pTrayNotify->_fReturn = (wParam == VK_RETURN);
  1184. break;
  1185. case WM_CONTEXTMENU:
  1186. {
  1187. INT_PTR i = SendMessage(pTrayNotify->_hwndToolbar, TB_GETHOTITEM, 0, 0);
  1188. if (i != -1)
  1189. {
  1190. CTrayItem * pti = pTrayNotify->m_TrayItemManager.GetItemDataByIndex(i);
  1191. if (lParam == (LPARAM)-1)
  1192. pTrayNotify->_fKey = TRUE;
  1193. if (pTrayNotify->_fKey)
  1194. {
  1195. pTrayNotify->_SetCursorPos(i);
  1196. }
  1197. if (pTrayNotify->_hwndToolbarInfoTip)
  1198. SendMessage(pTrayNotify->_hwndToolbarInfoTip, TTM_POP, 0, 0);
  1199. if (pti)
  1200. {
  1201. SHAllowSetForegroundWindow(pti->hWnd);
  1202. if (pti->uVersion >= KEYBOARD_VERSION)
  1203. {
  1204. pTrayNotify->_SendNotify(pti, WM_CONTEXTMENU);
  1205. }
  1206. else
  1207. {
  1208. if (pTrayNotify->_fKey)
  1209. {
  1210. pTrayNotify->_SendNotify(pti, WM_RBUTTONDOWN);
  1211. pTrayNotify->_SendNotify(pti, WM_RBUTTONUP);
  1212. }
  1213. }
  1214. }
  1215. return 0;
  1216. }
  1217. }
  1218. break;
  1219. default:
  1220. if (InRange(uMsg, WM_MOUSEFIRST, WM_MOUSELAST))
  1221. {
  1222. pTrayNotify->_OnMouseEvent(uMsg, wParam, lParam);
  1223. }
  1224. break;
  1225. }
  1226. return DefSubclassProc(hwnd, uMsg, wParam, lParam);
  1227. }
  1228. void CTrayNotify::_ToggleTrayItems(BOOL bEnable)
  1229. {
  1230. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1231. for (INT_PTR i = m_TrayItemManager.GetItemCount()-1; i >= 0; i--)
  1232. {
  1233. CTrayItem *pti = m_TrayItemManager.GetItemDataByIndex(i);
  1234. if (pti)
  1235. {
  1236. if (bEnable)
  1237. {
  1238. _PlaceItem(i, pti, TRAYEVENT_ONAPPLYUSERPREF);
  1239. }
  1240. else // if (disable)
  1241. {
  1242. _PlaceItem(i, pti, TRAYEVENT_ONDISABLEAUTOTRAY);
  1243. if (_pinfo && _IsChevronInfoTip(_pinfo->hWnd, _pinfo->uID))
  1244. {
  1245. //hide the balloon
  1246. _beLastBalloonEvent = BALLOONEVENT_NONE;
  1247. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONHIDE);
  1248. }
  1249. }
  1250. }
  1251. }
  1252. }
  1253. HRESULT CTrayNotify::EnableAutoTray(BOOL bTraySetting)
  1254. {
  1255. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1256. // This function will NEVER be called if the auto tray is disabled by policy,
  1257. // or if the system tray is made invisible by policy...
  1258. ASSERT(m_TrayItemRegistry.IsNoAutoTrayPolicyEnabled() == FALSE);
  1259. if (bTraySetting != m_TrayItemRegistry.IsAutoTrayEnabledByUser())
  1260. {
  1261. // NOTENOTE : Always assign this value BEFORE calling _ToggleTrayItems, since the timers
  1262. // are started ONLY if auto tray is enabled..
  1263. m_TrayItemRegistry.SetIsAutoTrayEnabledInRegistry(bTraySetting);
  1264. // Update the duration that the icon was present in the tray
  1265. _SetUsedTime();
  1266. _ToggleTrayItems(bTraySetting);
  1267. }
  1268. return S_OK;
  1269. }
  1270. void CTrayNotify::_ShowChevronInfoTip()
  1271. {
  1272. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1273. if (m_TrayItemRegistry.ShouldChevronInfoTipBeShown() && !SHRestricted(REST_NOSMBALLOONTIP))
  1274. {
  1275. TCHAR szInfoTitle[64];
  1276. LoadString(hinstCabinet, IDS_BANGICONINFOTITLE, szInfoTitle, ARRAYSIZE(szInfoTitle));
  1277. TCHAR szInfoTip[256];
  1278. LoadString(hinstCabinet, IDS_BANGICONINFOTIP1, szInfoTip, ARRAYSIZE(szInfoTip));
  1279. _SetInfoTip(_hwndNotify, UID_CHEVRONBUTTON, szInfoTip, szInfoTitle,
  1280. TT_CHEVRON_INFOTIP_INTERVAL, NIIF_INFO, FALSE);
  1281. }
  1282. }
  1283. void CTrayNotify::_SetUsedTime()
  1284. {
  1285. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1286. for (INT_PTR i = m_TrayItemManager.GetItemCount()-1; i >= 0; i--)
  1287. {
  1288. CTrayItem * pti = m_TrayItemManager.GetItemDataByIndex(i);
  1289. ASSERT(pti);
  1290. if (pti->IsStartupIcon())
  1291. {
  1292. pti->uNumSeconds = (pti->IsIconTimerCurrent() ? _GetAccumulatedTime(pti)
  1293. : pti->uNumSeconds);
  1294. }
  1295. }
  1296. }
  1297. BOOL CTrayNotify::GetTrayItemCB(INT_PTR nIndex, void *pCallbackData, TRAYCBARG trayCallbackArg,
  1298. TRAYCBRET * pOutData)
  1299. {
  1300. ASSERT(pOutData);
  1301. if (pCallbackData)
  1302. {
  1303. CTrayNotify * pTrayNotify = (CTrayNotify *) pCallbackData;
  1304. ASSERT(!pTrayNotify->_fNoTrayItemsDisplayPolicyEnabled);
  1305. if ( (nIndex < 0) || (nIndex >= pTrayNotify->m_TrayItemManager.GetItemCount()) )
  1306. return FALSE;
  1307. switch(trayCallbackArg)
  1308. {
  1309. case TRAYCBARG_ALL:
  1310. case TRAYCBARG_PTI:
  1311. {
  1312. CTrayItem * pti = pTrayNotify->m_TrayItemManager.GetItemDataByIndex(nIndex);
  1313. ASSERT(pti);
  1314. if (pti->IsStartupIcon())
  1315. pti->uNumSeconds = (pti->IsIconTimerCurrent() ? pTrayNotify->_GetAccumulatedTime(pti) : pti->uNumSeconds);
  1316. pOutData->pti = pti;
  1317. }
  1318. if (trayCallbackArg == TRAYCBARG_PTI)
  1319. {
  1320. return TRUE;
  1321. }
  1322. //
  1323. // else fall through..
  1324. //
  1325. case TRAYCBARG_HICON:
  1326. {
  1327. int nImageIndex = pTrayNotify->m_TrayItemManager.GetTBBtnImage(nIndex);
  1328. pOutData->hIcon = NULL;
  1329. if (pTrayNotify->_himlIcons)
  1330. {
  1331. pOutData->hIcon = ImageList_GetIcon(pTrayNotify->_himlIcons, nImageIndex, ILD_NORMAL);
  1332. }
  1333. }
  1334. return TRUE;
  1335. }
  1336. }
  1337. return FALSE;
  1338. }
  1339. LRESULT CTrayNotify::_Create(HWND hWnd)
  1340. {
  1341. LRESULT lres = -1;
  1342. _nMaxHorz = 0x7fff;
  1343. _nMaxVert = 0x7fff;
  1344. _fAnimateMenuOpen = ShouldTaskbarAnimate();
  1345. _fRedraw = TRUE;
  1346. _bStartupIcon = TRUE;
  1347. _fInfoTipShowing = FALSE;
  1348. _fItemClicked = FALSE;
  1349. _fChevronSelected = FALSE;
  1350. _fEnableUserTrackedInfoTips = TRUE;
  1351. _fBangMenuOpen = FALSE;
  1352. _bWorkStationLocked = FALSE;
  1353. _bRudeAppLaunched = FALSE;
  1354. _bWaitAfterRudeAppHide = FALSE;
  1355. _bWaitingBetweenBalloons = FALSE;
  1356. // Assume that the start menu has been auto-popped
  1357. _bStartMenuAllowsTrayBalloon = FALSE;
  1358. _beLastBalloonEvent = BALLOONEVENT_NONE;
  1359. _litsLastInfoTip = LITS_BALLOONNONE;
  1360. _fNoTrayItemsDisplayPolicyEnabled = (SHRestricted(REST_NOTRAYITEMSDISPLAY) != 0);
  1361. _idMouseActiveIcon = -1;
  1362. _hwndNotify = hWnd;
  1363. _hwndClock = ClockCtl_Create(_hwndNotify, IDC_CLOCK, hinstCabinet);
  1364. _hwndPager = CreateWindowEx(0, WC_PAGESCROLLER, NULL,
  1365. WS_CHILD | WS_TABSTOP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | PGS_HORZ,
  1366. 0, 0, 0, 0, _hwndNotify, (HMENU) 0, 0, NULL);
  1367. _hwndToolbar = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL,
  1368. WS_VISIBLE | WS_CHILD | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | WS_CLIPCHILDREN | TBSTYLE_TRANSPARENT |
  1369. WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_WRAPABLE,
  1370. 0, 0, 0, 0, _hwndPager, 0, hinstCabinet, NULL);
  1371. _hwndChevron = CreateWindowEx( 0, WC_BUTTON, NULL,
  1372. WS_VISIBLE | WS_CHILD,
  1373. 0, 0, 0, 0, _hwndNotify, (HMENU)IDC_TRAYNOTIFY_CHEVRON, hinstCabinet, NULL);
  1374. if (_hwndNotify)
  1375. {
  1376. DWORD dwExStyle = 0;
  1377. if (IS_WINDOW_RTL_MIRRORED(_hwndNotify))
  1378. dwExStyle |= WS_EX_LAYOUTRTL;
  1379. _hwndChevronToolTip = CreateWindowEx( dwExStyle, TOOLTIPS_CLASS, NULL,
  1380. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  1381. 0, 0, 0, 0, _hwndNotify, NULL, hinstCabinet, NULL);
  1382. _hwndInfoTip = CreateWindowEx( dwExStyle, TOOLTIPS_CLASS, NULL,
  1383. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON | TTS_CLOSE,
  1384. 0, 0, 0, 0, _hwndNotify, NULL, hinstCabinet, NULL);
  1385. _himlIcons = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
  1386. SHGetImageListFlags(_hwndToolbar), 0, 1);
  1387. }
  1388. // Check to see if any windows failed to create, if so bail
  1389. if (_himlIcons && _hwndNotify && _hwndClock && _hwndToolbar && _hwndPager && _hwndChevron && _hwndChevronToolTip && _hwndInfoTip)
  1390. {
  1391. // Get the explorer exe name, the complete launch path..
  1392. if (!SUCCEEDED(SHExeNameFromHWND(_hwndNotify, _szExplorerExeName, ARRAYSIZE(_szExplorerExeName))))
  1393. {
  1394. _szExplorerExeName[0] = TEXT('\0');
  1395. }
  1396. SetWindowTheme(_hwndClock, c_wzTrayNotifyTheme, NULL);
  1397. SendMessage(_hwndInfoTip, TTM_SETWINDOWTHEME, 0, (LPARAM)c_wzTrayNotifyTheme);
  1398. SendMessage(_hwndToolbar, TB_SETWINDOWTHEME, 0, (LPARAM)c_wzTrayNotifyTheme);
  1399. SetWindowPos(_hwndInfoTip, HWND_TOPMOST,
  1400. 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  1401. TOOLINFO ti = {0};
  1402. RECT rc = {0,-2,0,0};
  1403. ti.cbSize = sizeof(ti);
  1404. ti.hwnd = _hwndNotify;
  1405. ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT;
  1406. ti.uId = (UINT_PTR)_hwndNotify;
  1407. // set the version so we can have non buggy mouse event forwarding
  1408. SendMessage(_hwndInfoTip, CCM_SETVERSION, COMCTL32_VERSION, 0);
  1409. SendMessage(_hwndInfoTip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1410. SendMessage(_hwndInfoTip, TTM_SETMAXTIPWIDTH, 0, (LPARAM)MAX_TIP_WIDTH);
  1411. SendMessage(_hwndInfoTip, TTM_SETMARGIN, 0, (LPARAM)&rc);
  1412. ASSERT(_dpaInfo == NULL);
  1413. _dpaInfo = DPA_Create(10);
  1414. // Tray toolbar is a child of the pager control
  1415. SendMessage(_hwndPager, PGM_SETCHILD, 0, (LPARAM)_hwndToolbar);
  1416. // Set the window title to help out accessibility apps
  1417. TCHAR szTitle[64];
  1418. LoadString(hinstCabinet, IDS_TRAYNOTIFYTITLE, szTitle, ARRAYSIZE(szTitle));
  1419. SetWindowText(_hwndToolbar, szTitle);
  1420. // Toolbar settings - customize the tray toolbar...
  1421. SendMessage(_hwndToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
  1422. SendMessage(_hwndToolbar, TB_SETPADDING, 0, MAKELONG(2, 2));
  1423. SendMessage(_hwndToolbar, TB_SETMAXTEXTROWS, 0, 0);
  1424. SendMessage(_hwndToolbar, CCM_SETVERSION, COMCTL32_VERSION, 0);
  1425. SendMessage(_hwndToolbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_INVERTIBLEIMAGELIST | TBSTYLE_EX_DOUBLEBUFFER | TBSTYLE_EX_TOOLTIPSEXCLUDETOOLBAR);
  1426. SendMessage(_hwndToolbar, TB_SETIMAGELIST, 0, (LPARAM)_himlIcons);
  1427. _hwndToolbarInfoTip = (HWND)SendMessage(_hwndToolbar, TB_GETTOOLTIPS, 0, 0);
  1428. if (_hwndToolbarInfoTip)
  1429. {
  1430. SHSetWindowBits(_hwndToolbarInfoTip, GWL_STYLE, TTS_ALWAYSTIP, TTS_ALWAYSTIP);
  1431. SetWindowZorder(_hwndToolbarInfoTip, HWND_TOPMOST);
  1432. }
  1433. // if this fails, not that big a deal... we'll still show, but won't handle clicks
  1434. SetWindowSubclass(_hwndToolbar, s_ToolbarWndProc, 0, reinterpret_cast<DWORD_PTR>(this));
  1435. ti.cbSize = sizeof(ti);
  1436. ti.hwnd = _hwndNotify;
  1437. ti.uFlags = TTF_IDISHWND | TTF_EXCLUDETOOLAREA;
  1438. ti.uId = (UINT_PTR)_hwndChevron;
  1439. ti.lpszText = (LPTSTR)MAKEINTRESOURCE(IDS_SHOWDEMOTEDTIP);
  1440. ti.hinst = hinstCabinet;
  1441. SetWindowZorder(_hwndChevronToolTip, HWND_TOPMOST);
  1442. // Set the Chevron as the tool for the tooltip
  1443. SendMessage(_hwndChevronToolTip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1444. // Subclass the Chevron button, so we can forward mouse messages to the tooltip
  1445. SetWindowSubclass(_hwndChevron, ChevronSubClassWndProc, 0, reinterpret_cast<DWORD_PTR>(this));
  1446. _OpenTheme();
  1447. m_TrayItemRegistry.InitRegistryValues(SHGetImageListFlags(_hwndToolbar));
  1448. m_TrayItemManager.SetTrayToolbar(_hwndToolbar);
  1449. m_TrayItemManager.SetIconList(_himlIcons);
  1450. lres = 0; // Yeah we succeeded
  1451. }
  1452. return lres;
  1453. }
  1454. LRESULT CTrayNotify::_Destroy()
  1455. {
  1456. if (!_fNoTrayItemsDisplayPolicyEnabled)
  1457. {
  1458. for (INT_PTR i = m_TrayItemManager.GetItemCount() - 1; i >= 0; i--)
  1459. {
  1460. _DeleteNotify(i, TRUE, TRUE);
  1461. }
  1462. if (_pinfo)
  1463. {
  1464. delete _pinfo;
  1465. _pinfo = NULL;
  1466. }
  1467. }
  1468. else
  1469. {
  1470. AssertMsg((_pinfo == NULL), TEXT("_pinfo is being leaked"));
  1471. AssertMsg((!_himlIcons || (ImageList_GetImageCount(_himlIcons) == 0)), TEXT("image list info being leaked"));
  1472. }
  1473. if (_dpaInfo)
  1474. {
  1475. _dpaInfo.DestroyCallback(DeleteDPAPtrCB, NULL);
  1476. }
  1477. if (_himlIcons)
  1478. {
  1479. ImageList_Destroy(_himlIcons);
  1480. _himlIcons = NULL;
  1481. }
  1482. if (_hwndClock)
  1483. {
  1484. DestroyWindow(_hwndClock);
  1485. _hwndClock = NULL;
  1486. }
  1487. if (_hwndToolbar)
  1488. {
  1489. RemoveWindowSubclass(_hwndToolbar, s_ToolbarWndProc, 0);
  1490. DestroyWindow(_hwndToolbar);
  1491. _hwndToolbar = NULL;
  1492. }
  1493. if (_hwndChevron)
  1494. {
  1495. RemoveWindowSubclass(_hwndChevron, ChevronSubClassWndProc, 0);
  1496. DestroyWindow(_hwndChevron);
  1497. _hwndChevron = NULL;
  1498. }
  1499. if (_hwndInfoTip)
  1500. {
  1501. DestroyWindow(_hwndInfoTip);
  1502. _hwndInfoTip = NULL;
  1503. }
  1504. if (_hwndPager)
  1505. {
  1506. DestroyWindow(_hwndPager);
  1507. _hwndPager = NULL;
  1508. }
  1509. if (_hTheme)
  1510. {
  1511. CloseThemeData(_hTheme);
  1512. _hTheme = NULL;
  1513. }
  1514. if (_hwndChevronToolTip)
  1515. {
  1516. DestroyWindow(_hwndChevronToolTip);
  1517. _hwndChevronToolTip = NULL;
  1518. }
  1519. if (_pszCurrentThreadDesktopName)
  1520. {
  1521. LocalFree(_pszCurrentThreadDesktopName);
  1522. }
  1523. // Takes care of clearing up registry-related data
  1524. m_TrayItemRegistry.Delete();
  1525. return 0;
  1526. }
  1527. LRESULT CTrayNotify::_Paint(HDC hdcIn)
  1528. {
  1529. PAINTSTRUCT ps;
  1530. HDC hPaintDC = NULL;
  1531. HDC hMemDC = NULL;
  1532. HBITMAP hMemBm = NULL, hOldBm = NULL;
  1533. if (hdcIn)
  1534. {
  1535. hPaintDC = hdcIn;
  1536. GetClipBox(hPaintDC, &ps.rcPaint);
  1537. }
  1538. else
  1539. {
  1540. BeginPaint(_hwndNotify, &ps);
  1541. if (_fRedraw)
  1542. {
  1543. // Create memory surface and map rendering context if double buffering
  1544. // Only make large enough for clipping region
  1545. hMemDC = CreateCompatibleDC(ps.hdc);
  1546. if (hMemDC)
  1547. {
  1548. hMemBm = CreateCompatibleBitmap(ps.hdc, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint));
  1549. if (hMemBm)
  1550. {
  1551. hOldBm = (HBITMAP) SelectObject(hMemDC, hMemBm);
  1552. // Offset painting to paint in region
  1553. OffsetWindowOrgEx(hMemDC, ps.rcPaint.left, ps.rcPaint.top, NULL);
  1554. hPaintDC = hMemDC;
  1555. }
  1556. else
  1557. {
  1558. DeleteDC(hMemDC);
  1559. hPaintDC = NULL;
  1560. }
  1561. }
  1562. }
  1563. else
  1564. {
  1565. _fRepaint = TRUE;
  1566. hPaintDC = NULL;
  1567. }
  1568. }
  1569. if (hPaintDC)
  1570. {
  1571. RECT rc;
  1572. GetClientRect(_hwndNotify, &rc);
  1573. if (_hTheme)
  1574. {
  1575. SHSendPrintRect(GetParent(_hwnd), _hwnd, hPaintDC, &ps.rcPaint);
  1576. if (_fAnimating)
  1577. {
  1578. if (_fVertical)
  1579. {
  1580. rc.top = rc.bottom - _rcAnimateCurrent.bottom;
  1581. }
  1582. else
  1583. {
  1584. rc.left = rc.right - _rcAnimateCurrent.right;
  1585. }
  1586. }
  1587. DrawThemeBackground(_hTheme, hPaintDC, TNP_BACKGROUND, 0, &rc, 0);
  1588. if (_fHasFocus)
  1589. {
  1590. LRESULT lRes = SendMessage(_hwndChevron, WM_QUERYUISTATE, 0, 0);
  1591. if (!(LOWORD(lRes) & UISF_HIDEFOCUS))
  1592. {
  1593. RECT rcFocus = {0};
  1594. GetClientRect(_hwndChevron, &rcFocus);
  1595. MapWindowRect(_hwndChevron, _hwndNotify, &rcFocus);
  1596. // InflateRect(&rcFocus, 2, 2);
  1597. DrawFocusRect(hPaintDC, &rcFocus);
  1598. }
  1599. }
  1600. }
  1601. else
  1602. {
  1603. FillRect(hPaintDC, &rc, (HBRUSH)(COLOR_3DFACE + 1));
  1604. }
  1605. }
  1606. if (!hdcIn)
  1607. {
  1608. if (hMemDC)
  1609. {
  1610. BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint), hMemDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
  1611. SelectObject(hMemDC, hOldBm);
  1612. DeleteObject(hMemBm);
  1613. DeleteDC(hMemDC);
  1614. }
  1615. EndPaint(_hwndNotify, &ps);
  1616. }
  1617. return 0;
  1618. }
  1619. LRESULT CTrayNotify::_HandleCustomDraw(LPNMCUSTOMDRAW pcd)
  1620. {
  1621. LRESULT lres = CDRF_DODEFAULT;
  1622. // If this policy is enabled, the chevron should NEVER be shown, and if it is not
  1623. // shown, no question about its WM_NOTIFY message handler...
  1624. // ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1625. if (_fNoTrayItemsDisplayPolicyEnabled)
  1626. {
  1627. return lres;
  1628. }
  1629. else if (!_hTheme)
  1630. {
  1631. switch (pcd->dwDrawStage)
  1632. {
  1633. case CDDS_PREERASE:
  1634. {
  1635. DWORD dwFlags = 0;
  1636. if (pcd->uItemState & CDIS_HOT)
  1637. {
  1638. // The chevron is under the pointer, hence the item is hot
  1639. _fChevronSelected = TRUE;
  1640. dwFlags |= DCHF_HOT;
  1641. }
  1642. if (!_fVertical)
  1643. {
  1644. dwFlags |= DCHF_HORIZONTAL;
  1645. }
  1646. if ((!_fBangMenuOpen && _fVertical) || (_fBangMenuOpen && !_fVertical))
  1647. {
  1648. dwFlags |= DCHF_FLIPPED;
  1649. }
  1650. if (pcd->uItemState & CDIS_FOCUS)
  1651. {
  1652. if (_fChevronSelected)
  1653. dwFlags |= DCHF_HOT;
  1654. }
  1655. if (!(pcd->uItemState & CDIS_FOCUS || pcd->uItemState & CDIS_HOT))
  1656. {
  1657. _fChevronSelected = FALSE;
  1658. }
  1659. DrawChevron(pcd->hdc, &(pcd->rc), dwFlags);
  1660. lres = CDRF_SKIPDEFAULT;
  1661. }
  1662. break;
  1663. }
  1664. }
  1665. return lres;
  1666. }
  1667. void CTrayNotify::_SizeWindows(int nMaxHorz, int nMaxVert, LPRECT prcTotal, BOOL fSizeWindows)
  1668. {
  1669. RECT rcClock, rcPager, rcChevron;
  1670. SIZE szNotify;
  1671. RECT rcBound = { 0, 0, nMaxHorz, nMaxVert };
  1672. RECT rcBorder = rcBound;
  1673. rcChevron.left = rcChevron.top = 0;
  1674. rcChevron.right = !_fNoTrayItemsDisplayPolicyEnabled && _fHaveDemoted ? _szChevron.cx : 0;
  1675. rcChevron.bottom = !_fNoTrayItemsDisplayPolicyEnabled && _fHaveDemoted ? _szChevron.cy : 0;
  1676. if (_hTheme)
  1677. {
  1678. GetThemeBackgroundContentRect(_hTheme, NULL, TNP_BACKGROUND, 0, &rcBound, &rcBorder);
  1679. }
  1680. else
  1681. {
  1682. rcBorder.top += g_cyBorder;
  1683. rcBorder.left += g_cxBorder;
  1684. rcBorder.bottom -= g_cyBorder + 2;
  1685. rcBorder.right -= g_cxBorder + 2;
  1686. }
  1687. static LRESULT s_lRes = 0;
  1688. static int s_nMaxVert = -1;
  1689. if (s_nMaxVert != nMaxVert)
  1690. {
  1691. s_lRes = SendMessage(_hwndClock, WM_CALCMINSIZE, nMaxHorz, nMaxVert);
  1692. }
  1693. rcClock.left = rcClock.top = 0;
  1694. rcClock.right = LOWORD(s_lRes);
  1695. rcClock.bottom = HIWORD(s_lRes);
  1696. szNotify.cx = RECTWIDTH(rcBorder);
  1697. szNotify.cy = RECTHEIGHT(rcBorder);
  1698. SendMessage(_hwndToolbar, TB_GETIDEALSIZE, _fVertical, (LPARAM)&szNotify);
  1699. if (_fVertical)
  1700. {
  1701. int cxButtonSize = LOWORD(SendMessage(_hwndToolbar, TB_GETBUTTONSIZE, 0, 0));
  1702. szNotify.cx -= szNotify.cx % (cxButtonSize ? cxButtonSize : 16);
  1703. // Vertical Taskbar, place the clock on the bottom and icons on the top
  1704. rcChevron.left = rcClock.left = rcBorder.left;
  1705. rcChevron.right = rcClock.right = rcBorder.right;
  1706. rcPager.left = (RECTWIDTH(rcBorder) - szNotify.cx) / 2 + rcBorder.left;
  1707. rcPager.right = rcPager.left + szNotify.cx;
  1708. if (_hTheme)
  1709. {
  1710. rcChevron.left = (nMaxHorz - _szChevron.cx) / 2;
  1711. rcChevron.right = rcChevron.left + _szChevron.cx;
  1712. }
  1713. prcTotal->left = 0;
  1714. prcTotal->right = nMaxHorz;
  1715. // If Notification Icons take up more space than available then just set them to the maximum available size
  1716. int cyTemp = max(rcChevron.bottom, rcBorder.top);
  1717. int cyTotal = cyTemp + rcClock.bottom + (nMaxVert - rcBorder.bottom);
  1718. rcPager.top = 0;
  1719. rcPager.bottom = min(szNotify.cy, nMaxVert - cyTemp);
  1720. OffsetRect(&rcPager, 0, cyTemp);
  1721. OffsetRect(&rcClock, 0, rcPager.bottom);
  1722. prcTotal->top = 0;
  1723. prcTotal->bottom = rcClock.bottom + (nMaxVert - rcBorder.bottom);
  1724. }
  1725. else
  1726. {
  1727. int cyButtonSize = HIWORD(SendMessage(_hwndToolbar, TB_GETBUTTONSIZE, 0, 0));
  1728. szNotify.cy -= szNotify.cy % (cyButtonSize ? cyButtonSize : 16);
  1729. // Horizontal Taskbar, place the clock on the right and icons on the left
  1730. rcChevron.top = rcClock.top = rcBorder.top;
  1731. rcChevron.bottom = rcClock.bottom = rcBorder.bottom;
  1732. rcPager.top = ((RECTHEIGHT(rcBorder) - szNotify.cy) / 2) + rcBorder.top;
  1733. rcPager.bottom = rcPager.top + szNotify.cy;
  1734. if (_hTheme)
  1735. {
  1736. rcChevron.top = ((RECTHEIGHT(rcBorder) - _szChevron.cy) / 2) + rcBorder.top;
  1737. rcChevron.bottom = rcChevron.top + _szChevron.cy;
  1738. }
  1739. prcTotal->top = 0;
  1740. prcTotal->bottom = nMaxVert;
  1741. // If Notification Icons take up more space than available then just set them to the maximum available size
  1742. int cxTemp = max(rcChevron.right, rcBorder.left);
  1743. int cxTotal = cxTemp + rcClock.right + (nMaxHorz - rcBorder.right);
  1744. rcPager.left = 0;
  1745. rcPager.right = min(szNotify.cx, nMaxHorz - cxTemp);
  1746. OffsetRect(&rcPager, cxTemp, 0);
  1747. OffsetRect(&rcClock, rcPager.right, 0);
  1748. prcTotal->left = 0;
  1749. prcTotal->right = rcClock.right + (nMaxHorz - rcBorder.right);
  1750. }
  1751. if (fSizeWindows)
  1752. {
  1753. if (_fAnimating)
  1754. {
  1755. RECT rcWin;
  1756. GetWindowRect(_hwndNotify, &rcWin);
  1757. int offsetX = _fVertical ? 0: RECTWIDTH(rcWin) - RECTWIDTH(*prcTotal);
  1758. int offsetY = _fVertical ? RECTHEIGHT(rcWin) - RECTHEIGHT(*prcTotal) : 0;
  1759. OffsetRect(&rcClock, offsetX, offsetY);
  1760. OffsetRect(&rcPager, offsetX, offsetY);
  1761. OffsetRect(&rcChevron, offsetX, offsetY);
  1762. }
  1763. SetWindowPos(_hwndClock, NULL, rcClock.left, rcClock.top, RECTWIDTH(rcClock), RECTHEIGHT(rcClock), SWP_NOZORDER);
  1764. SetWindowPos(_hwndToolbar, NULL, 0, 0, szNotify.cx, szNotify.cy, SWP_NOZORDER | SWP_NOCOPYBITS);
  1765. SetWindowPos(_hwndPager, NULL, rcPager.left, rcPager.top, RECTWIDTH(rcPager), RECTHEIGHT(rcPager), SWP_NOZORDER | SWP_NOCOPYBITS);
  1766. SetWindowPos(_hwndChevron, NULL, rcChevron.left, rcChevron.top, RECTWIDTH(rcChevron), RECTHEIGHT(rcChevron), SWP_NOZORDER | SWP_NOCOPYBITS);
  1767. SendMessage(_hwndPager, PGMP_RECALCSIZE, (WPARAM) 0, (LPARAM) 0);
  1768. }
  1769. if (_fAnimating)
  1770. {
  1771. _rcAnimateCurrent = *prcTotal;
  1772. *prcTotal = _rcAnimateTotal;
  1773. }
  1774. if (fSizeWindows)
  1775. {
  1776. RECT rcInvalid = *prcTotal;
  1777. if (_fVertical)
  1778. {
  1779. rcInvalid.bottom = rcPager.bottom;
  1780. }
  1781. else
  1782. {
  1783. rcInvalid.right = rcPager.right;
  1784. }
  1785. InvalidateRect(_hwndNotify, &rcInvalid, FALSE);
  1786. UpdateWindow(_hwndNotify);
  1787. }
  1788. }
  1789. LRESULT CTrayNotify::_CalcMinSize(int nMaxHorz, int nMaxVert)
  1790. {
  1791. RECT rcTotal;
  1792. _nMaxHorz = nMaxHorz;
  1793. _nMaxVert = nMaxVert;
  1794. if (!(GetWindowLong(_hwndClock, GWL_STYLE) & WS_VISIBLE) && !m_TrayItemManager.GetItemCount())
  1795. {
  1796. // If we are visible, but have nothing to show, then hide ourselves
  1797. ShowWindow(_hwndNotify, SW_HIDE);
  1798. return 0L;
  1799. }
  1800. else if (!IsWindowVisible(_hwndNotify))
  1801. {
  1802. ShowWindow(_hwndNotify, SW_SHOW);
  1803. }
  1804. _SizeWindows(nMaxHorz, nMaxVert, &rcTotal, FALSE);
  1805. // Add on room for borders
  1806. return(MAKELRESULT(rcTotal.right, rcTotal.bottom));
  1807. }
  1808. LRESULT CTrayNotify::_Size()
  1809. {
  1810. RECT rcTotal;
  1811. // use GetWindowRect because _SizeWindows includes the borders
  1812. GetWindowRect(_hwndNotify, &rcTotal);
  1813. // Account for borders on the left and right
  1814. _SizeWindows(RECTWIDTH(rcTotal), RECTHEIGHT(rcTotal), &rcTotal, TRUE);
  1815. return(0);
  1816. }
  1817. void CTrayNotify::_OnInfoTipTimer()
  1818. {
  1819. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1820. _KillTimer(TF_INFOTIP_TIMER, _uInfoTipTimer);
  1821. _uInfoTipTimer = 0;
  1822. if (_pinfo)
  1823. {
  1824. _beLastBalloonEvent = BALLOONEVENT_TIMEOUT;
  1825. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONTIMEOUT); // hide this balloon and show new one
  1826. }
  1827. }
  1828. LRESULT CTrayNotify::_OnTimer(UINT_PTR uTimerID)
  1829. {
  1830. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1831. if (uTimerID == TID_DEMOTEDMENU)
  1832. {
  1833. if (_fBangMenuOpen)
  1834. _ToggleDemotedMenu();
  1835. }
  1836. else if (uTimerID == TID_BALLOONPOP)
  1837. {
  1838. // When the user clicks the 'X' to close a balloon tip, this timer is set.
  1839. // Ensure that the currently showing balloon tip (the one on which the user
  1840. // clicked the 'X') is completely hidden, before showing the next balloon in
  1841. // the queue.
  1842. //
  1843. // Tooltips are layered windows, and comctl32 implements a fadeout effect on
  1844. // them. So there is a time period during which a tooltip is still visible
  1845. // after it has been asked to be deleted/hidden.
  1846. if (IsWindowVisible(_hwndInfoTip))
  1847. {
  1848. SetTimer(_hwndNotify, TID_BALLOONPOP, TT_BALLOONPOP_INTERVAL_INCREMENT, NULL);
  1849. }
  1850. else
  1851. {
  1852. KillTimer(_hwndNotify, TID_BALLOONPOP);
  1853. if (_pinfo)
  1854. {
  1855. _beLastBalloonEvent = BALLOONEVENT_USERXCLICK;
  1856. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONTIMEOUT);
  1857. }
  1858. // This is called only when the user has clicked the 'X'.
  1859. _litsLastInfoTip = LITS_BALLOONXCLICKED;
  1860. }
  1861. }
  1862. else if (uTimerID == TID_BALLOONPOPWAIT)
  1863. {
  1864. KillTimer(_hwndNotify, TID_BALLOONPOPWAIT);
  1865. _bWaitingBetweenBalloons = FALSE;
  1866. if (_pinfo)
  1867. {
  1868. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, TRUE, TRUE, 0);
  1869. }
  1870. }
  1871. else if (uTimerID == TID_BALLOONSHOW)
  1872. {
  1873. KillTimer(_hwndNotify, TID_BALLOONSHOW);
  1874. _bStartMenuAllowsTrayBalloon = TRUE;
  1875. if (_pinfo)
  1876. {
  1877. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, TRUE, TRUE, NIN_BALLOONSHOW);
  1878. }
  1879. }
  1880. else if (uTimerID == TID_RUDEAPPHIDE)
  1881. {
  1882. KillTimer(_hwndNotify, TID_RUDEAPPHIDE);
  1883. if (_pinfo && _bWaitAfterRudeAppHide)
  1884. {
  1885. _bWaitAfterRudeAppHide = FALSE;
  1886. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, TRUE, TRUE, NIN_BALLOONSHOW);
  1887. }
  1888. _bWaitAfterRudeAppHide = FALSE;
  1889. }
  1890. else
  1891. {
  1892. AssertMsg(FALSE, TEXT("CTrayNotify::_OnTimer() not possible"));
  1893. }
  1894. return 0;
  1895. }
  1896. BOOL _IsClickDown(UINT uMsg)
  1897. {
  1898. switch (uMsg)
  1899. {
  1900. case WM_LBUTTONDOWN:
  1901. case WM_MBUTTONDOWN:
  1902. case WM_RBUTTONDOWN:
  1903. case WM_LBUTTONDBLCLK:
  1904. case WM_MBUTTONDBLCLK:
  1905. case WM_RBUTTONDBLCLK:
  1906. return TRUE;
  1907. }
  1908. return FALSE;
  1909. }
  1910. BOOL _UseCachedIcon(UINT uMsg)
  1911. {
  1912. switch (uMsg)
  1913. {
  1914. case WM_LBUTTONDOWN:
  1915. case WM_MBUTTONDOWN:
  1916. case WM_RBUTTONDOWN:
  1917. case WM_MOUSEMOVE:
  1918. case WM_MOUSEWHEEL:
  1919. return FALSE;
  1920. }
  1921. return TRUE;
  1922. }
  1923. LRESULT CTrayNotify::_OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1924. {
  1925. // Icons can jump around between the time we get a down-click message and
  1926. // the time we get a double-click or up-click message. E.g. clicking on
  1927. // the bang icon expands the hidden stuff, or an icon might delete itself
  1928. // in response to the down-click.
  1929. //
  1930. // It's undesirable for a different icon to get the corresponding double-
  1931. // or up-click in this case (very annoying to the user).
  1932. //
  1933. // To deal with this, cache the icon down-clicked and use that cached value
  1934. // (instead of the button the mouse is currently over) on double- or up-click.
  1935. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  1936. // The mouse cursor has moved over the toolbar, so if the chevron was selected
  1937. // earlier, it should not be anymore.
  1938. _fChevronSelected = FALSE;
  1939. BOOL fClickDown = _IsClickDown(uMsg);
  1940. BOOL fUseCachedIcon = _UseCachedIcon(uMsg);
  1941. INT_PTR i = -1;
  1942. if (fUseCachedIcon)
  1943. {
  1944. i = ToolBar_CommandToIndex(_hwndToolbar, _idMouseActiveIcon);
  1945. }
  1946. if (i == -1)
  1947. {
  1948. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  1949. i = SendMessage(_hwndToolbar, TB_HITTEST, 0, (LPARAM)&pt);
  1950. if (fClickDown)
  1951. _idMouseActiveIcon = ToolBar_IndexToCommand(_hwndToolbar, i);
  1952. }
  1953. CTrayItem *pti = m_TrayItemManager.GetItemDataByIndex(i);
  1954. if (pti)
  1955. {
  1956. if (IsWindow(pti->hWnd))
  1957. {
  1958. if (fClickDown)
  1959. {
  1960. SHAllowSetForegroundWindow(pti->hWnd);
  1961. if (_pinfo && _pinfo->hWnd == pti->hWnd && _pinfo->uID == pti->uID)
  1962. {
  1963. if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK)
  1964. _beLastBalloonEvent = BALLOONEVENT_USERRIGHTCLICK;
  1965. else
  1966. _beLastBalloonEvent = BALLOONEVENT_USERLEFTCLICK;
  1967. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONUSERCLICK);
  1968. }
  1969. if (fClickDown)
  1970. {
  1971. // down clicks count as activation
  1972. _PlaceItem(i, pti, TRAYEVENT_ONITEMCLICK);
  1973. }
  1974. _fItemClicked = TRUE;
  1975. _ActivateTips(FALSE);
  1976. }
  1977. _SendNotify(pti, uMsg);
  1978. }
  1979. else
  1980. {
  1981. _DeleteNotify(i, FALSE, TRUE);
  1982. }
  1983. return 1;
  1984. }
  1985. return 0;
  1986. }
  1987. LRESULT CTrayNotify::_OnCDNotify(LPNMTBCUSTOMDRAW pnm)
  1988. {
  1989. // if (_fNoTrayItemsDisplayPolicyEnabled)
  1990. // return CDRF_DODEFAULT;
  1991. switch (pnm->nmcd.dwDrawStage)
  1992. {
  1993. case CDDS_PREPAINT:
  1994. return CDRF_NOTIFYITEMDRAW;
  1995. case CDDS_ITEMPREPAINT:
  1996. {
  1997. LRESULT lRet = TBCDRF_NOOFFSET;
  1998. // notify us for the hot tracked item please
  1999. if (pnm->nmcd.uItemState & CDIS_HOT)
  2000. lRet |= CDRF_NOTIFYPOSTPAINT;
  2001. // we want the buttons to look totally flat all the time
  2002. pnm->nmcd.uItemState = 0;
  2003. return lRet;
  2004. }
  2005. case CDDS_ITEMPOSTPAINT:
  2006. {
  2007. // draw the hot tracked item as a focus rect, since
  2008. // the tray notify area doesn't behave like a button:
  2009. // you can SINGLE click or DOUBLE click or RCLICK
  2010. // (kybd equiv: SPACE, ENTER, SHIFT F10)
  2011. //
  2012. LRESULT lRes = SendMessage(_hwndNotify, WM_QUERYUISTATE, 0, 0);
  2013. if (!(LOWORD(lRes) & UISF_HIDEFOCUS) && _hwndToolbar == GetFocus())
  2014. {
  2015. DrawFocusRect(pnm->nmcd.hdc, &pnm->nmcd.rc);
  2016. }
  2017. break;
  2018. }
  2019. }
  2020. return CDRF_DODEFAULT;
  2021. }
  2022. LRESULT CTrayNotify::_Notify(LPNMHDR pNmhdr)
  2023. {
  2024. LRESULT lRes = 0;
  2025. switch (pNmhdr->code)
  2026. {
  2027. case TTN_POP: // a balloontip/tooltip is about to be hidden...
  2028. if (pNmhdr->hwndFrom == _hwndInfoTip)
  2029. {
  2030. // If this infotip was hidden by means other than the user click on the
  2031. // 'X', the code path sets _litsLastInfoTip to LITS_BALLOONDESTROYED
  2032. // before the infotip is to be hidden...
  2033. //
  2034. // If _litsLastInfoTip is not set to LITS_BALLOONDESTROYED, the infotip
  2035. // was deleted by the user click on the 'X' (that being the only other
  2036. // way to close the infotip). comctl32 sends us a TTN_POP *before* it
  2037. // hides the infotip. Don't set the next infotip to show immediately.
  2038. // (The hiding code would then hide the infotip for the next tool, since the
  2039. // hwnds are the same). Set a timer in this case, and show the
  2040. // next infotip, after ensuring that the current one is truly hidden...
  2041. if ( (_litsLastInfoTip == LITS_BALLOONXCLICKED) ||
  2042. (_litsLastInfoTip == LITS_BALLOONNONE) )
  2043. {
  2044. _KillTimer(TF_INFOTIP_TIMER, _uInfoTipTimer);
  2045. SetTimer(_hwndNotify, TID_BALLOONPOP, TT_BALLOONPOP_INTERVAL, NULL);
  2046. }
  2047. _litsLastInfoTip = LITS_BALLOONXCLICKED;
  2048. }
  2049. break;
  2050. case NM_KEYDOWN:
  2051. _fKey = TRUE;
  2052. break;
  2053. case TBN_ENDDRAG:
  2054. _fKey = FALSE;
  2055. break;
  2056. case TBN_DELETINGBUTTON:
  2057. {
  2058. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2059. TBNOTIFY* ptbn = (TBNOTIFY*)pNmhdr;
  2060. CTrayItem *pti = (CTrayItem *)(void *)ptbn->tbButton.dwData;
  2061. // can be null if its a blank button used for the animation
  2062. if (pti)
  2063. {
  2064. //if it wasn't sharing an icon with another guy, go ahead and delete it
  2065. if (!pti->IsIconShared())
  2066. _RemoveImage(ptbn->tbButton.iBitmap);
  2067. delete pti;
  2068. }
  2069. }
  2070. break;
  2071. case BCN_HOTITEMCHANGE:
  2072. case TBN_HOTITEMCHANGE:
  2073. {
  2074. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2075. DWORD dwFlags = (pNmhdr->code == BCN_HOTITEMCHANGE) ? ((LPNMBCHOTITEM)pNmhdr)->dwFlags : ((LPNMTBHOTITEM)pNmhdr)->dwFlags;
  2076. if (dwFlags & HICF_LEAVING)
  2077. {
  2078. _fItemClicked = FALSE;
  2079. _ActivateTips(TRUE);
  2080. }
  2081. if (_fBangMenuOpen)
  2082. {
  2083. if (dwFlags & HICF_LEAVING)
  2084. {
  2085. //
  2086. // When hottracking moves between button and toolbar,
  2087. // we get the HICF_ENTERING for one before we get the
  2088. // HICF_LEAVING for the other. So before setting the
  2089. // timer to hide the bang menu, we check to see if the
  2090. // other control has a hot item.
  2091. //
  2092. BOOL fOtherHot;
  2093. if (pNmhdr->code == BCN_HOTITEMCHANGE)
  2094. {
  2095. fOtherHot = (SendMessage(_hwndToolbar, TB_GETHOTITEM, 0, 0) != -1);
  2096. }
  2097. else
  2098. {
  2099. fOtherHot = BOOLIFY(SendMessage(_hwndChevron, BM_GETSTATE, 0, 0) & BST_HOT);
  2100. }
  2101. if (!fOtherHot)
  2102. {
  2103. SetTimer(_hwndNotify, TID_DEMOTEDMENU, TT_DEMOTEDMENU_INTERVAL, NULL);
  2104. }
  2105. }
  2106. else if (dwFlags & HICF_ENTERING)
  2107. {
  2108. KillTimer(_hwndNotify, TID_DEMOTEDMENU);
  2109. }
  2110. }
  2111. }
  2112. break;
  2113. case TBN_WRAPHOTITEM:
  2114. {
  2115. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2116. NMTBWRAPHOTITEM * pnmWrapHotItem = (NMTBWRAPHOTITEM *) pNmhdr;
  2117. // If the user hit a key on the tray toolbar icon and it was the first
  2118. // visible item in the tray toolbar, then maybe we want to go to the
  2119. // chevron button...
  2120. switch (pnmWrapHotItem->iDir)
  2121. {
  2122. // Left/Up
  2123. case -1:
  2124. if (_fHaveDemoted)
  2125. {
  2126. SetFocus(_hwndChevron);
  2127. _fChevronSelected = TRUE;
  2128. }
  2129. else if (_hwndClock)
  2130. {
  2131. SetFocus(_hwndClock);
  2132. _fChevronSelected = FALSE;
  2133. }
  2134. else
  2135. // do nothing
  2136. {
  2137. _fChevronSelected = FALSE;
  2138. }
  2139. break;
  2140. // Right/Down
  2141. case 1:
  2142. if (_hwndClock)
  2143. {
  2144. SetFocus(_hwndClock);
  2145. _fChevronSelected = FALSE;
  2146. }
  2147. else if (_fHaveDemoted)
  2148. {
  2149. SetFocus(_hwndChevron);
  2150. _fChevronSelected = TRUE;
  2151. }
  2152. else
  2153. {
  2154. _fChevronSelected = FALSE;
  2155. }
  2156. break;
  2157. }
  2158. break;
  2159. }
  2160. break;
  2161. // NOTENOTE: This notification DOESNT need to be checked. Pager forwards its notifications
  2162. // to our child toolbar control, and TBN_HOTITEMCHANGE above handles this case..
  2163. case PGN_HOTITEMCHANGE:
  2164. {
  2165. LPNMTBHOTITEM pnmhot = (LPNMTBHOTITEM)pNmhdr;
  2166. if (pnmhot->dwFlags & HICF_LEAVING)
  2167. {
  2168. _fItemClicked = FALSE;
  2169. _ActivateTips(TRUE);
  2170. }
  2171. if (_fBangMenuOpen)
  2172. {
  2173. if (pnmhot->dwFlags & HICF_LEAVING)
  2174. {
  2175. SetTimer(_hwndNotify, TID_DEMOTEDMENU, TT_DEMOTEDMENU_INTERVAL, NULL);
  2176. }
  2177. else if (pnmhot->dwFlags & HICF_ENTERING)
  2178. {
  2179. KillTimer(_hwndNotify, TID_DEMOTEDMENU);
  2180. }
  2181. }
  2182. }
  2183. break;
  2184. case PGN_CALCSIZE:
  2185. {
  2186. LPNMPGCALCSIZE pCalcSize = (LPNMPGCALCSIZE)pNmhdr;
  2187. switch(pCalcSize->dwFlag)
  2188. {
  2189. case PGF_CALCWIDTH:
  2190. {
  2191. //Get the optimum WIDTH of the toolbar.
  2192. RECT rcToolBar;
  2193. GetWindowRect(_hwndToolbar, &rcToolBar);
  2194. pCalcSize->iWidth = RECTWIDTH(rcToolBar);
  2195. }
  2196. break;
  2197. case PGF_CALCHEIGHT:
  2198. {
  2199. //Get the optimum HEIGHT of the toolbar.
  2200. RECT rcToolBar;
  2201. GetWindowRect(_hwndToolbar, &rcToolBar);
  2202. pCalcSize->iHeight = RECTHEIGHT(rcToolBar);
  2203. }
  2204. break;
  2205. }
  2206. }
  2207. case NM_CUSTOMDRAW:
  2208. if (pNmhdr->hwndFrom == _hwndChevron)
  2209. {
  2210. return _HandleCustomDraw((LPNMCUSTOMDRAW)pNmhdr);
  2211. }
  2212. else
  2213. {
  2214. return _OnCDNotify((LPNMTBCUSTOMDRAW)pNmhdr);
  2215. }
  2216. break;
  2217. }
  2218. return lRes;
  2219. }
  2220. void CTrayNotify::_OnSysChange(UINT uMsg, WPARAM wParam, LPARAM lParam)
  2221. {
  2222. if (uMsg == WM_WININICHANGE)
  2223. {
  2224. _CheckAndResizeImages();
  2225. if (lParam == SPI_SETMENUANIMATION || lParam == SPI_SETUIEFFECTS || (!wParam &&
  2226. (!lParam || (lstrcmpi((LPTSTR)lParam, TEXT("Windows")) == 0))))
  2227. {
  2228. _fAnimateMenuOpen = ShouldTaskbarAnimate();
  2229. }
  2230. }
  2231. if (_hwndClock)
  2232. SendMessage(_hwndClock, uMsg, wParam, lParam);
  2233. }
  2234. void CTrayNotify::_OnCommand(UINT id, UINT uCmd)
  2235. {
  2236. if (id == IDC_TRAYNOTIFY_CHEVRON)
  2237. {
  2238. AssertMsg(!_fNoTrayItemsDisplayPolicyEnabled, TEXT("Impossible-the chevron shouldnt be shown"));
  2239. switch(uCmd)
  2240. {
  2241. case BN_SETFOCUS:
  2242. break;
  2243. default:
  2244. _ToggleDemotedMenu();
  2245. break;
  2246. }
  2247. }
  2248. else
  2249. {
  2250. switch (uCmd)
  2251. {
  2252. case BN_CLICKED:
  2253. {
  2254. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2255. CTrayItem *pti = m_TrayItemManager.GetItemData(id, FALSE, _hwndToolbar);
  2256. if (pti)
  2257. {
  2258. if (_fKey)
  2259. _SetCursorPos(SendMessage(_hwndToolbar, TB_COMMANDTOINDEX, id, 0));
  2260. SHAllowSetForegroundWindow(pti->hWnd);
  2261. if (pti->uVersion >= KEYBOARD_VERSION)
  2262. {
  2263. // if they are a new version that understands the keyboard messages,
  2264. // send the real message to them.
  2265. _SendNotify(pti, _fKey ? NIN_KEYSELECT : NIN_SELECT);
  2266. // Hitting RETURN is like double-clicking (which in the new
  2267. // style means keyselecting twice)
  2268. if (_fKey && _fReturn)
  2269. _SendNotify(pti, NIN_KEYSELECT);
  2270. }
  2271. else
  2272. {
  2273. // otherwise mock up a mouse event if it was a keyboard select
  2274. // (if it wasn't a keyboard select, we assume they handled it already on
  2275. // the WM_MOUSE message
  2276. if (_fKey)
  2277. {
  2278. _SendNotify(pti, WM_LBUTTONDOWN);
  2279. _SendNotify(pti, WM_LBUTTONUP);
  2280. if (_fReturn)
  2281. {
  2282. _SendNotify(pti, WM_LBUTTONDBLCLK);
  2283. _SendNotify(pti, WM_LBUTTONUP);
  2284. }
  2285. }
  2286. }
  2287. }
  2288. break;
  2289. }
  2290. }
  2291. }
  2292. }
  2293. void CTrayNotify::_OnSizeChanged(BOOL fForceRepaint)
  2294. {
  2295. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2296. if (_pinfo)
  2297. {
  2298. // if balloon is up we have to move it, but we cannot straight up
  2299. // position it because traynotify will be moved around by tray
  2300. // so we do it async
  2301. PostMessage(_hwndNotify, TNM_ASYNCINFOTIPPOS, 0, 0);
  2302. }
  2303. c_tray.VerifySize(TRUE);
  2304. if (fForceRepaint)
  2305. UpdateWindow(_hwndToolbar);
  2306. }
  2307. #define TT_ANIMATIONLENGTH 20 // sum of all the animation steps
  2308. #define TT_ANIMATIONPAUSE 30 // extra pause for last step
  2309. DWORD CTrayNotify::_GetStepTime(int iStep, int cSteps)
  2310. {
  2311. // our requirements here are:
  2312. //
  2313. // - animation velocity should decrease linearly with time
  2314. //
  2315. // - total animation time should be a constant, TT_ANIMATIONLENGTH
  2316. // (it should not vary with number of icons)
  2317. //
  2318. // - figure this out without using floating point math
  2319. //
  2320. // hence the following formula
  2321. //
  2322. if (cSteps == 0)
  2323. {
  2324. return 0;
  2325. }
  2326. else if (iStep == cSteps && cSteps > 2)
  2327. {
  2328. return TT_ANIMATIONPAUSE;
  2329. }
  2330. else
  2331. {
  2332. int iNumerator = (TT_ANIMATIONLENGTH - cSteps) * iStep;
  2333. int iDenominator = (cSteps + 1) * cSteps;
  2334. return (iNumerator / iDenominator);
  2335. }
  2336. }
  2337. void CTrayNotify::_ToggleDemotedMenu()
  2338. {
  2339. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2340. _ActivateTips(FALSE);
  2341. int iAnimStep = 1; // animation steps are 1-based
  2342. int cNumberDemoted = (int) m_TrayItemManager.GetDemotedItemCount();
  2343. if (_fAnimateMenuOpen)
  2344. {
  2345. if (!_fBangMenuOpen)
  2346. {
  2347. _BlankButtons(0, cNumberDemoted, TRUE);
  2348. }
  2349. GetWindowRect(_hwndNotify, &_rcAnimateTotal);
  2350. _SizeWindows(_fVertical ? RECTWIDTH(_rcAnimateTotal) : _nMaxHorz, _fVertical ? _nMaxVert : RECTHEIGHT(_rcAnimateTotal), &_rcAnimateTotal, FALSE);
  2351. if (!_fBangMenuOpen)
  2352. {
  2353. _BlankButtons(0, cNumberDemoted, FALSE);
  2354. }
  2355. _fAnimating = TRUE; // Begin Animation loop
  2356. if (!_fBangMenuOpen)
  2357. {
  2358. _OnSizeChanged(TRUE);
  2359. }
  2360. }
  2361. for (INT_PTR i = m_TrayItemManager.GetItemCount() - 1; i >= 0; i--)
  2362. {
  2363. CTrayItem * pti = m_TrayItemManager.GetItemDataByIndex(i);
  2364. if (!pti->IsHidden() && pti->IsDemoted())
  2365. {
  2366. DWORD dwSleep = _GetStepTime(iAnimStep, cNumberDemoted);
  2367. iAnimStep++;
  2368. if (_fBangMenuOpen)
  2369. {
  2370. m_TrayItemManager.SetTBBtnStateHelper(i, TBSTATE_HIDDEN, TRUE);
  2371. }
  2372. if (_fAnimateMenuOpen)
  2373. {
  2374. _AnimateButtons((int) i, dwSleep, cNumberDemoted, !_fBangMenuOpen);
  2375. }
  2376. if (!_fBangMenuOpen)
  2377. {
  2378. m_TrayItemManager.SetTBBtnStateHelper(i, TBSTATE_HIDDEN, FALSE);
  2379. }
  2380. if (_fAnimateMenuOpen)
  2381. {
  2382. _SizeWindows(_fVertical ? RECTWIDTH(_rcAnimateTotal) : _nMaxHorz, _fVertical ? _nMaxVert : RECTHEIGHT(_rcAnimateTotal), &_rcAnimateTotal, TRUE);
  2383. }
  2384. }
  2385. }
  2386. _fAnimating = FALSE; // End Animation loop
  2387. if (_fBangMenuOpen)
  2388. {
  2389. KillTimer(_hwndNotify, TID_DEMOTEDMENU);
  2390. }
  2391. _ActivateTips(TRUE);
  2392. _UpdateChevronState(!_fBangMenuOpen, FALSE, _fBangMenuOpen);
  2393. _OnSizeChanged(TRUE);
  2394. }
  2395. void CTrayNotify::_BlankButtons(int iPos, int iNumberOfButtons, BOOL fAddButtons)
  2396. {
  2397. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2398. BOOL fRedraw = _SetRedraw(FALSE);
  2399. TBBUTTON tbb;
  2400. tbb.dwData = NULL;
  2401. tbb.iBitmap = -1;
  2402. tbb.fsStyle = BTNS_BUTTON;
  2403. tbb.iString = -1;
  2404. tbb.fsState = TBSTATE_INDETERMINATE;
  2405. for (int i = 0; i < iNumberOfButtons; i++)
  2406. {
  2407. if (fAddButtons)
  2408. {
  2409. tbb.idCommand = Toolbar_GetUniqueID(_hwndToolbar);
  2410. }
  2411. //insert all blank buttons at the front of the toolbar
  2412. SendMessage(_hwndToolbar, fAddButtons ? TB_INSERTBUTTON : TB_DELETEBUTTON, iPos, fAddButtons ? (LPARAM)&tbb : 0);
  2413. }
  2414. _SetRedraw(fRedraw);
  2415. }
  2416. #define TT_ANIMATIONSTEP 3
  2417. #define TT_ANIMATIONSTEPBASE 100
  2418. #define TT_ANIMATIONWRAPPAUSE 25 // pause for no animation for row wraps
  2419. void CTrayNotify::_AnimateButtons(int iIndex, DWORD dwSleep, int iNumberItems, BOOL fGrow)
  2420. {
  2421. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2422. BOOL fInSameRow = TRUE;
  2423. _BlankButtons((int) iIndex, 1, TRUE);
  2424. if ((iIndex + 2 < m_TrayItemManager.GetItemCount()) && (iIndex > 0))
  2425. {
  2426. RECT rcItem1, rcItem2;
  2427. SendMessage(_hwndToolbar, TB_GETITEMRECT, fGrow ? iIndex + 2 : iIndex - 1, (LPARAM)&rcItem1);
  2428. SendMessage(_hwndToolbar, TB_GETITEMRECT, iIndex, (LPARAM)&rcItem2);
  2429. fInSameRow = (rcItem1.top == rcItem2.top);
  2430. }
  2431. if (fInSameRow)
  2432. {
  2433. // target width of button
  2434. WORD wWidth = LOWORD(SendMessage(_hwndToolbar, TB_GETBUTTONSIZE, 0, 0));
  2435. int iAnimationStep = (iNumberItems * iNumberItems) / TT_ANIMATIONSTEPBASE;
  2436. iAnimationStep = max(iAnimationStep, TT_ANIMATIONSTEP);
  2437. TBBUTTONINFO tbbi;
  2438. tbbi.cbSize = sizeof(TBBUTTONINFO);
  2439. tbbi.dwMask = TBIF_SIZE | TBIF_BYINDEX;
  2440. // Set the size of the buttons
  2441. for (WORD cx = 1; cx < wWidth; cx += (WORD) iAnimationStep)
  2442. {
  2443. tbbi.cx = fGrow ? cx : wWidth - cx;
  2444. SendMessage(_hwndToolbar, TB_SETBUTTONINFO, iIndex, (LPARAM) &tbbi);
  2445. RECT rcBogus;
  2446. _SizeWindows(_fVertical ? RECTWIDTH(_rcAnimateTotal) : _nMaxHorz, _fVertical ? _nMaxVert : RECTHEIGHT(_rcAnimateTotal), &rcBogus, TRUE);
  2447. Sleep(dwSleep);
  2448. }
  2449. if (fGrow)
  2450. {
  2451. // set the grow button back to normal size
  2452. tbbi.cx = 0;
  2453. SendMessage(_hwndToolbar, TB_SETBUTTONINFO, iIndex, (LPARAM) &tbbi);
  2454. }
  2455. }
  2456. _BlankButtons((int) iIndex, 1, FALSE);
  2457. }
  2458. BOOL CTrayNotify::_SetRedraw(BOOL fRedraw)
  2459. {
  2460. BOOL fOldRedraw = _fRedraw;
  2461. _fRedraw = fRedraw;
  2462. SendMessage(_hwndToolbar, WM_SETREDRAW, fRedraw, 0);
  2463. if (_fRedraw)
  2464. {
  2465. if (_fRepaint)
  2466. {
  2467. InvalidateRect(_hwndNotify, NULL, FALSE);
  2468. UpdateWindow(_hwndNotify);
  2469. }
  2470. }
  2471. else
  2472. {
  2473. _fRepaint = FALSE;
  2474. }
  2475. return fOldRedraw;
  2476. }
  2477. void CTrayNotify::_OnIconDemoteTimer(WPARAM wParam, LPARAM lParam)
  2478. {
  2479. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2480. INT_PTR nIcon = m_TrayItemManager.FindItemAssociatedWithTimer(lParam);
  2481. if (nIcon >= 0)
  2482. {
  2483. CTrayItem *pti = m_TrayItemManager.GetItemDataByIndex(nIcon);
  2484. ASSERT(pti);
  2485. _PlaceItem(nIcon, pti, TRAYEVENT_ONICONDEMOTETIMER);
  2486. }
  2487. else
  2488. {
  2489. // It looks like a timer for a now-defunct icon. Go ahead and kill it.
  2490. // Though we do handle this case, it's odd for it to happen, so spew a
  2491. // warning.
  2492. TraceMsg(TF_WARNING, "CTrayNotify::_OnIconDemoteTimer -- killing zombie timer %x", lParam);
  2493. _KillTimer(TF_ICONDEMOTE_TIMER, (UINT) lParam);
  2494. }
  2495. }
  2496. BOOL CTrayNotify::_UpdateTrayItems(BOOL bUpdateDemotedItems)
  2497. {
  2498. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2499. BOOL bDemoteItemsOverThreshold = ( m_TrayItemRegistry.IsAutoTrayEnabled() ?
  2500. m_TrayItemManager.DemotedItemsPresent(MIN_DEMOTED_ITEMS_THRESHOLD) :
  2501. FALSE );
  2502. if (bUpdateDemotedItems || !m_TrayItemRegistry.IsAutoTrayEnabled())
  2503. {
  2504. _HideAllDemotedItems(bDemoteItemsOverThreshold);
  2505. }
  2506. return bDemoteItemsOverThreshold;
  2507. }
  2508. void CTrayNotify::_HideAllDemotedItems(BOOL bHide)
  2509. {
  2510. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2511. for (INT_PTR i = m_TrayItemManager.GetItemCount()-1; i >= 0; i--)
  2512. {
  2513. CTrayItem * pti = m_TrayItemManager.GetItemDataByIndex(i);
  2514. ASSERT(pti);
  2515. if (!pti->IsHidden() && pti->IsDemoted() && (pti->dwUserPref == TNUP_AUTOMATIC))
  2516. {
  2517. m_TrayItemManager.SetTBBtnStateHelper(i, TBSTATE_HIDDEN, bHide);
  2518. }
  2519. }
  2520. }
  2521. BOOL CTrayNotify::_PlaceItem(INT_PTR nIcon, CTrayItem * pti, TRAYEVENT tTrayEvent)
  2522. {
  2523. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2524. BOOL bDemoteStatusChange = FALSE;
  2525. if (!pti)
  2526. return bDemoteStatusChange;
  2527. TRAYITEMPOS tiPos = _TrayItemPos(pti, tTrayEvent, &bDemoteStatusChange);
  2528. if (bDemoteStatusChange || tiPos == TIPOS_HIDDEN)
  2529. {
  2530. if (pti->IsStartupIcon() && (pti->IsDemoted() || tiPos == TIPOS_HIDDEN))
  2531. pti->uNumSeconds = 0;
  2532. if (!_fBangMenuOpen || pti->IsHidden())
  2533. {
  2534. if ( (pti->IsDemoted() || tiPos == TIPOS_HIDDEN) &&
  2535. _pinfo && (_pinfo->hWnd == pti->hWnd) && (_pinfo->uID == pti->uID) )
  2536. {
  2537. //hide the balloon
  2538. _beLastBalloonEvent = BALLOONEVENT_APPDEMOTE;
  2539. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONHIDE);
  2540. }
  2541. // hide/show
  2542. m_TrayItemManager.SetTBBtnStateHelper( nIcon,
  2543. TBSTATE_HIDDEN,
  2544. (pti->IsHidden() || (m_TrayItemRegistry.IsAutoTrayEnabled() && pti->IsDemoted())) );
  2545. if (bDemoteStatusChange)
  2546. {
  2547. _UpdateChevronState(_fBangMenuOpen, FALSE, TRUE);
  2548. _OnSizeChanged(FALSE);
  2549. }
  2550. }
  2551. }
  2552. _SetOrKillIconDemoteTimer(pti, tiPos);
  2553. return bDemoteStatusChange;
  2554. }
  2555. TRAYITEMPOS CTrayNotify::_TrayItemPos(CTrayItem * pti, TRAYEVENT tTrayEvent, BOOL *bDemoteStatusChange)
  2556. {
  2557. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2558. TRAYITEMPOS tiPos = TIPOS_STATUSQUO;
  2559. *bDemoteStatusChange = FALSE;
  2560. if (!pti)
  2561. return tiPos;
  2562. switch(tTrayEvent)
  2563. {
  2564. case TRAYEVENT_ONDISABLEAUTOTRAY:
  2565. if (!pti->IsHidden())
  2566. {
  2567. tiPos = TIPOS_ALWAYS_PROMOTED;
  2568. if (pti->IsStartupIcon() && !pti->IsDemoted() && pti->dwUserPref == TNUP_AUTOMATIC)
  2569. pti->uNumSeconds = _GetAccumulatedTime(pti);
  2570. *bDemoteStatusChange = TRUE;
  2571. pti->SetOnceVisible(TRUE);
  2572. pti->SetItemClicked(FALSE);
  2573. }
  2574. break;
  2575. case TRAYEVENT_ONITEMCLICK:
  2576. case TRAYEVENT_ONICONMODIFY:
  2577. case TRAYEVENT_ONINFOTIP:
  2578. if (!pti->IsHidden())
  2579. pti->SetOnceVisible(TRUE);
  2580. if (m_TrayItemRegistry.IsAutoTrayEnabled() && !pti->IsHidden())
  2581. {
  2582. if ( (tTrayEvent == TRAYEVENT_ONICONMODIFY) &&
  2583. (pti->IsItemSameIconModify()) )
  2584. {
  2585. break;
  2586. }
  2587. else if (pti->dwUserPref == TNUP_AUTOMATIC)
  2588. {
  2589. // If the item has been clicked on, note it...
  2590. if (tTrayEvent == TRAYEVENT_ONITEMCLICK)
  2591. {
  2592. pti->SetItemClicked(TRUE);
  2593. }
  2594. tiPos = TIPOS_PROMOTED;
  2595. if (pti->IsDemoted())
  2596. {
  2597. pti->SetDemoted(FALSE);
  2598. *bDemoteStatusChange = TRUE;
  2599. }
  2600. }
  2601. }
  2602. break;
  2603. case TRAYEVENT_ONAPPLYUSERPREF:
  2604. case TRAYEVENT_ONNEWITEMINSERT:
  2605. if (!pti->IsHidden())
  2606. pti->SetOnceVisible(TRUE);
  2607. if (m_TrayItemRegistry.IsAutoTrayEnabled() && !pti->IsHidden())
  2608. {
  2609. if (pti->dwUserPref == TNUP_AUTOMATIC)
  2610. {
  2611. tiPos = (pti->IsDemoted() ? TIPOS_DEMOTED : TIPOS_PROMOTED);
  2612. if (pti->IsDemoted())
  2613. {
  2614. // (1) New Item Insert : The new item is inserted. If it was demoted in the
  2615. // previous session, the setting has carried over, and is copied over
  2616. // before this function is called (in InsertNotify->_PlaceItem). Use this
  2617. // setting to determine if the item is to be demoted.
  2618. // (2) Apply User Pref : This is called in two cases :
  2619. // (a) SetPreference : When the user clicks OK on the Notifications Prop
  2620. // dialog. Since dwUserPref is TNUP_AUTOMATIC, use the current demoted
  2621. // setting of the item. Do not change the demoted setting of the item
  2622. // (b) EnableAutoTray : The AutoTray feature has been enabled. Demote the
  2623. // icon only if it was already demoted before. When the icon was
  2624. // inserted, its previous demote setting was copied. So if its previous
  2625. // demote setting was TRUE, then the item should be demoted, otherwise
  2626. // it shouldnt be.
  2627. // So, in effect, in all these cases, if dwUserPref was TNUP_AUTOMATIC, there
  2628. // is no necessity to change the demote setting, but some cases, it is necessary
  2629. // to hide the icon.
  2630. *bDemoteStatusChange = TRUE;
  2631. }
  2632. }
  2633. else
  2634. {
  2635. pti->SetDemoted(pti->dwUserPref == TNUP_DEMOTED);
  2636. tiPos = ((pti->dwUserPref == TNUP_DEMOTED) ? TIPOS_ALWAYS_DEMOTED : TIPOS_ALWAYS_PROMOTED);
  2637. *bDemoteStatusChange = TRUE;
  2638. pti->SetItemClicked(FALSE);
  2639. }
  2640. }
  2641. break;
  2642. case TRAYEVENT_ONICONDEMOTETIMER:
  2643. // Hidden items cannot have timers, and we will never get this event if
  2644. // the item was hidden...
  2645. ASSERT(!pti->IsHidden());
  2646. ASSERT(m_TrayItemRegistry.IsAutoTrayEnabled());
  2647. tiPos = TIPOS_DEMOTED;
  2648. if (!pti->IsDemoted())
  2649. {
  2650. pti->SetDemoted(TRUE);
  2651. *bDemoteStatusChange = TRUE;
  2652. }
  2653. pti->SetItemClicked(FALSE);
  2654. break;
  2655. case TRAYEVENT_ONICONHIDE:
  2656. tiPos = TIPOS_HIDDEN;
  2657. if (pti->IsDemoted() || pti->dwUserPref == TNUP_DEMOTED)
  2658. {
  2659. pti->SetDemoted(FALSE);
  2660. *bDemoteStatusChange = TRUE;
  2661. }
  2662. pti->SetItemClicked(FALSE);
  2663. break;
  2664. case TRAYEVENT_ONICONUNHIDE:
  2665. pti->SetOnceVisible(TRUE);
  2666. *bDemoteStatusChange = TRUE;
  2667. if (m_TrayItemRegistry.IsAutoTrayEnabled())
  2668. {
  2669. if ((pti->dwUserPref == TNUP_AUTOMATIC) || (pti->dwUserPref == TNUP_PROMOTED))
  2670. {
  2671. tiPos = ((pti->dwUserPref == TNUP_AUTOMATIC) ? TIPOS_PROMOTED : TIPOS_ALWAYS_PROMOTED);
  2672. if (pti->IsDemoted())
  2673. {
  2674. pti->SetDemoted(FALSE);
  2675. }
  2676. }
  2677. else
  2678. {
  2679. ASSERT(pti->dwUserPref == TNUP_DEMOTED);
  2680. tiPos = TIPOS_ALWAYS_DEMOTED;
  2681. if (!pti->IsDemoted())
  2682. {
  2683. pti->SetDemoted(TRUE);
  2684. }
  2685. }
  2686. }
  2687. else
  2688. // NO-AUTO-TRAY mode...
  2689. {
  2690. tiPos = TIPOS_ALWAYS_PROMOTED;
  2691. }
  2692. pti->SetItemClicked(FALSE);
  2693. break;
  2694. }
  2695. return tiPos;
  2696. }
  2697. void CTrayNotify::_SetOrKillIconDemoteTimer(CTrayItem * pti, TRAYITEMPOS tiPos)
  2698. {
  2699. switch(tiPos)
  2700. {
  2701. case TIPOS_PROMOTED:
  2702. _SetItemTimer(pti);
  2703. break;
  2704. case TIPOS_DEMOTED:
  2705. case TIPOS_HIDDEN:
  2706. case TIPOS_ALWAYS_DEMOTED:
  2707. case TIPOS_ALWAYS_PROMOTED:
  2708. _KillItemTimer(pti);
  2709. break;
  2710. case TIPOS_STATUSQUO:
  2711. break;
  2712. }
  2713. }
  2714. LRESULT CTrayNotify::_OnKeyDown(WPARAM wChar, LPARAM lFlags)
  2715. {
  2716. if (_hwndClock && _hwndClock == GetFocus())
  2717. {
  2718. BOOL fLastHot = FALSE;
  2719. //
  2720. // handle keyboard messages forwarded by clock
  2721. //
  2722. switch (wChar)
  2723. {
  2724. case VK_UP:
  2725. case VK_LEFT:
  2726. fLastHot = TRUE;
  2727. //
  2728. // fall through
  2729. //
  2730. case VK_DOWN:
  2731. case VK_RIGHT:
  2732. {
  2733. if (_fNoTrayItemsDisplayPolicyEnabled)
  2734. {
  2735. SetFocus(_hwndClock);
  2736. // this is moot, since the chevron will not be shown
  2737. _fChevronSelected = FALSE;
  2738. return 0;
  2739. }
  2740. else
  2741. {
  2742. INT_PTR nToolbarIconSelected = -1;
  2743. if (fLastHot || !_fHaveDemoted)
  2744. {
  2745. nToolbarIconSelected = _GetToolbarFirstVisibleItem(_hwndToolbar, fLastHot);
  2746. }
  2747. if (nToolbarIconSelected != -1)
  2748. {
  2749. //
  2750. // make it the hot item
  2751. //
  2752. _SetToolbarHotItem(_hwndToolbar, nToolbarIconSelected);
  2753. _fChevronSelected = FALSE;
  2754. }
  2755. else if (_fHaveDemoted)
  2756. {
  2757. SetFocus(_hwndChevron);
  2758. _fChevronSelected = TRUE;
  2759. }
  2760. return 0;
  2761. }
  2762. }
  2763. case VK_RETURN:
  2764. case VK_SPACE:
  2765. //
  2766. // run the default applet in timedate.cpl
  2767. //
  2768. SHRunControlPanel(TEXT("timedate.cpl"), _hwnd);
  2769. return 0;
  2770. }
  2771. }
  2772. return 1;
  2773. }
  2774. void CTrayNotify::_OnWorkStationLocked(BOOL bLocked)
  2775. {
  2776. _bWorkStationLocked = bLocked;
  2777. if (!_bWorkStationLocked && !_fNoTrayItemsDisplayPolicyEnabled &&
  2778. _fEnableUserTrackedInfoTips && _pinfo)
  2779. {
  2780. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, TRUE, TRUE, NIN_BALLOONSHOW);
  2781. }
  2782. }
  2783. void CTrayNotify::_OnRudeApp(BOOL bRudeApp)
  2784. {
  2785. if (_bRudeAppLaunched != bRudeApp)
  2786. {
  2787. _bWaitAfterRudeAppHide = FALSE;
  2788. _bRudeAppLaunched = bRudeApp;
  2789. if (!bRudeApp)
  2790. {
  2791. if (_pinfo)
  2792. {
  2793. SetTimer(_hwndNotify, TID_RUDEAPPHIDE, TT_RUDEAPPHIDE_INTERVAL, 0);
  2794. _bWaitAfterRudeAppHide = TRUE;
  2795. // _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, TRUE, TRUE, NIN_BALLOONSHOW);
  2796. }
  2797. }
  2798. else
  2799. {
  2800. _KillTimer(TF_INFOTIP_TIMER, _uInfoTipTimer);
  2801. _uInfoTipTimer = 0;
  2802. // NOTENOTE : *DO NOT* delete _pinfo, we will show the balloon tip after the fullscreen app has
  2803. // gone away.
  2804. _HideBalloonTip();
  2805. }
  2806. }
  2807. }
  2808. // WndProc as defined in CImpWndProc. s_WndProc function in base class calls
  2809. // virtual v_WndProc, which handles all the messages in the derived class.
  2810. LRESULT CTrayNotify::v_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2811. {
  2812. //
  2813. // protect against re-entrancy after we've been partially destroyed
  2814. //
  2815. if (_hwndToolbar == NULL)
  2816. {
  2817. if (uMsg != WM_CREATE &&
  2818. uMsg != WM_DESTROY)
  2819. {
  2820. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  2821. }
  2822. }
  2823. switch (uMsg)
  2824. {
  2825. case WM_CREATE:
  2826. return _Create(hWnd);
  2827. case WM_DESTROY:
  2828. return _Destroy();
  2829. case WM_COMMAND:
  2830. if (!_fNoTrayItemsDisplayPolicyEnabled)
  2831. _OnCommand(GET_WM_COMMAND_ID(wParam, lParam), GET_WM_COMMAND_CMD(wParam, lParam));
  2832. break;
  2833. case WM_SETFOCUS:
  2834. {
  2835. if (_fNoTrayItemsDisplayPolicyEnabled)
  2836. {
  2837. SetFocus(_hwndClock);
  2838. _fChevronSelected = FALSE;
  2839. }
  2840. else
  2841. {
  2842. BOOL bFocusSet = FALSE;
  2843. //
  2844. // if there's a balloon tip up, start with focus on that icon
  2845. //
  2846. if (_pinfo)
  2847. {
  2848. INT_PTR nIcon = m_TrayItemManager.FindItemAssociatedWithHwndUid(_pinfo->hWnd, _pinfo->uID);
  2849. if (nIcon != -1 && ToolBar_IsVisible(_hwndToolbar, nIcon))
  2850. {
  2851. _SetToolbarHotItem(_hwndToolbar, nIcon);
  2852. _fChevronSelected = FALSE;
  2853. bFocusSet = TRUE;
  2854. }
  2855. }
  2856. if (!bFocusSet && _fHaveDemoted)
  2857. {
  2858. SetFocus(_hwndChevron);
  2859. _fChevronSelected = TRUE;
  2860. bFocusSet = TRUE;
  2861. }
  2862. if (!bFocusSet)
  2863. {
  2864. INT_PTR nToolbarIcon = _GetToolbarFirstVisibleItem(_hwndToolbar, FALSE);
  2865. if (nToolbarIcon != -1)
  2866. {
  2867. _SetToolbarHotItem(_hwndToolbar, nToolbarIcon);
  2868. _fChevronSelected = FALSE;
  2869. }
  2870. else
  2871. {
  2872. SetFocus(_hwndClock);
  2873. _fChevronSelected = FALSE;
  2874. }
  2875. }
  2876. }
  2877. }
  2878. break;
  2879. case WM_SETREDRAW:
  2880. return _SetRedraw((BOOL) wParam);
  2881. case WM_ERASEBKGND:
  2882. if (_hTheme)
  2883. {
  2884. return 1;
  2885. }
  2886. else
  2887. {
  2888. _Paint((HDC)wParam);
  2889. }
  2890. break;
  2891. case WM_PAINT:
  2892. case WM_PRINTCLIENT:
  2893. return _Paint((HDC)wParam);
  2894. case WM_CALCMINSIZE:
  2895. return _CalcMinSize((int)wParam, (int)lParam);
  2896. case WM_KEYDOWN:
  2897. return _OnKeyDown(wParam, lParam);
  2898. case WM_NCHITTEST:
  2899. return(IsPosInHwnd(lParam, _hwndClock) ? HTTRANSPARENT : HTCLIENT);
  2900. case WM_NOTIFY:
  2901. return(_Notify((LPNMHDR)lParam));
  2902. case TNM_GETCLOCK:
  2903. return (LRESULT)_hwndClock;
  2904. case TNM_TRAYHIDE:
  2905. if (lParam && IsWindowVisible(_hwndClock))
  2906. SendMessage(_hwndClock, TCM_RESET, 0, 0);
  2907. break;
  2908. case TNM_HIDECLOCK:
  2909. ShowWindow(_hwndClock, lParam ? SW_HIDE : SW_SHOW);
  2910. break;
  2911. case TNM_TRAYPOSCHANGED:
  2912. if (_pinfo && !_fNoTrayItemsDisplayPolicyEnabled)
  2913. PostMessage(_hwndNotify, TNM_ASYNCINFOTIPPOS, 0, 0);
  2914. break;
  2915. case TNM_ASYNCINFOTIPPOS:
  2916. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2917. _PositionInfoTip();
  2918. break;
  2919. case TNM_ASYNCINFOTIP:
  2920. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2921. _ShowInfoTip((HWND)wParam, (UINT)lParam, TRUE, FALSE, 0);
  2922. break;
  2923. case TNM_NOTIFY:
  2924. {
  2925. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2926. CNotificationItem* pni = (CNotificationItem*)lParam;
  2927. if (pni)
  2928. {
  2929. if (_pNotifyCB)
  2930. {
  2931. _pNotifyCB->Notify((UINT)wParam, pni);
  2932. if (wParam == NIM_ADD)
  2933. {
  2934. _TickleForTooltip(pni);
  2935. }
  2936. }
  2937. delete pni;
  2938. }
  2939. }
  2940. break;
  2941. case WM_SIZE:
  2942. _Size();
  2943. break;
  2944. case WM_TIMER:
  2945. _OnTimer(wParam);
  2946. break;
  2947. case TNM_UPDATEVERTICAL:
  2948. {
  2949. _UpdateVertical((BOOL)lParam);
  2950. }
  2951. break;
  2952. // only button down, mouse move msgs are forwarded down to us from info tip
  2953. //case WM_LBUTTONUP:
  2954. //case WM_MBUTTONUP:
  2955. //case WM_RBUTTONUP:
  2956. case WM_LBUTTONDOWN:
  2957. case WM_MBUTTONDOWN:
  2958. case WM_RBUTTONDOWN:
  2959. _InfoTipMouseClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (uMsg == WM_RBUTTONDOWN));
  2960. break;
  2961. case TNM_ICONDEMOTETIMER:
  2962. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2963. _OnIconDemoteTimer(wParam, lParam);
  2964. break;
  2965. case TNM_INFOTIPTIMER:
  2966. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  2967. _OnInfoTipTimer();
  2968. break;
  2969. case TNM_SAVESTATE:
  2970. if (!_fNoTrayItemsDisplayPolicyEnabled)
  2971. {
  2972. _SetUsedTime();
  2973. m_TrayItemRegistry.InitTrayItemStream(STGM_WRITE, this->GetTrayItemCB, this);
  2974. }
  2975. break;
  2976. case TNM_STARTUPAPPSLAUNCHED:
  2977. _bStartupIcon = FALSE;
  2978. break;
  2979. // This message is sent by a shimmed Winstone app, to prevent the launching of
  2980. // user-tracked balloons. These "new" balloons last till the user has been at
  2981. // the machine for a minimum of 10 seconds. But automated Winstone tests cause
  2982. // this balloon to stay up forever, and screws up the tests. So we shim Winstone
  2983. // to pass us this message, and allow normal balloon tips for such a machine.
  2984. case TNM_ENABLEUSERTRACKINGINFOTIPS:
  2985. if ((BOOL) wParam == FALSE)
  2986. {
  2987. if (!_fNoTrayItemsDisplayPolicyEnabled && _fEnableUserTrackedInfoTips && _pinfo)
  2988. {
  2989. _fEnableUserTrackedInfoTips = (BOOL) wParam;
  2990. _beLastBalloonEvent = BALLOONEVENT_NONE;
  2991. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONHIDE);
  2992. }
  2993. }
  2994. _fEnableUserTrackedInfoTips = (BOOL) wParam;
  2995. break;
  2996. case TNM_WORKSTATIONLOCKED:
  2997. _OnWorkStationLocked((BOOL)wParam);
  2998. break;
  2999. case TNM_RUDEAPP:
  3000. _OnRudeApp((BOOL)wParam);
  3001. break;
  3002. case TNM_SHOWTRAYBALLOON:
  3003. // If we enable display of tray balloons...
  3004. if (wParam)
  3005. {
  3006. // If we had disabled display of tray balloons earlier...
  3007. if (!_bStartMenuAllowsTrayBalloon)
  3008. {
  3009. SetTimer(_hwndNotify, TID_BALLOONSHOW, TT_BALLOONSHOW_INTERVAL, 0);
  3010. }
  3011. }
  3012. else
  3013. {
  3014. KillTimer(_hwndNotify, TID_BALLOONSHOW);
  3015. _bStartMenuAllowsTrayBalloon = FALSE;
  3016. // TO DO : Should we hide the balloon ?
  3017. }
  3018. break;
  3019. case WM_THEMECHANGED:
  3020. _OpenTheme();
  3021. break;
  3022. case WM_TIMECHANGE:
  3023. case WM_WININICHANGE:
  3024. case WM_POWERBROADCAST:
  3025. case WM_POWER:
  3026. _OnSysChange(uMsg, wParam, lParam);
  3027. // Fall through...
  3028. default:
  3029. return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  3030. }
  3031. return 0;
  3032. }
  3033. INT_PTR CTrayNotify::_GetToolbarFirstVisibleItem(HWND hWndToolbar, BOOL bFromLast)
  3034. {
  3035. INT_PTR nToolbarIconSelected = -1;
  3036. if (_fNoTrayItemsDisplayPolicyEnabled)
  3037. return -1;
  3038. INT_PTR nTrayItemCount = m_TrayItemManager.GetItemCount()-1;
  3039. INT_PTR i = ((nTrayItemCount > 0) ? ((bFromLast) ? nTrayItemCount : 0) : -1);
  3040. if (i == -1)
  3041. return i;
  3042. do
  3043. {
  3044. if (ToolBar_IsVisible(hWndToolbar, i))
  3045. {
  3046. nToolbarIconSelected = i;
  3047. break;
  3048. }
  3049. i = (bFromLast ? ((i > 0) ? i-1 : -1) : ((i < nTrayItemCount) ? i+1 : -1));
  3050. }
  3051. while (i != -1);
  3052. return nToolbarIconSelected;
  3053. }
  3054. BOOL CTrayNotify::_TrayNotifyIcon(PTRAYNOTIFYDATA pnid, BOOL *pbRefresh)
  3055. {
  3056. // we want to refrain from re-painting if possible...
  3057. if (pbRefresh)
  3058. *pbRefresh = FALSE;
  3059. PNOTIFYICONDATA32 pNID = &pnid->nid;
  3060. if (pNID->cbSize < sizeof(NOTIFYICONDATA32))
  3061. {
  3062. return FALSE;
  3063. }
  3064. if (_fNoTrayItemsDisplayPolicyEnabled)
  3065. {
  3066. if (pnid->dwMessage == NIM_SETFOCUS)
  3067. {
  3068. if (_hwndClock)
  3069. {
  3070. SetFocus(_hwndClock);
  3071. _fChevronSelected = FALSE;
  3072. }
  3073. }
  3074. return TRUE;
  3075. }
  3076. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3077. INT_PTR nIcon = m_TrayItemManager.FindItemAssociatedWithHwndUid(GetHWnd(pNID), pNID->uID);
  3078. BOOL bRet = FALSE;
  3079. switch (pnid->dwMessage)
  3080. {
  3081. case NIM_SETFOCUS:
  3082. // the notify client is trying to return focus to us
  3083. if (nIcon >= 0)
  3084. {
  3085. if (!(_bRudeAppLaunched || IsDirectXAppRunningFullScreen()))
  3086. {
  3087. SetForegroundWindow(v_hwndTray);
  3088. if (ToolBar_IsVisible(_hwndToolbar, nIcon))
  3089. {
  3090. _SetToolbarHotItem(_hwndToolbar, nIcon);
  3091. _fChevronSelected = FALSE;
  3092. }
  3093. else if (_fHaveDemoted)
  3094. {
  3095. SetFocus(_hwndChevron);
  3096. _fChevronSelected = TRUE;
  3097. }
  3098. else
  3099. {
  3100. INT_PTR nToolbarIcon = _GetToolbarFirstVisibleItem(_hwndToolbar, FALSE);
  3101. if (nToolbarIcon != -1)
  3102. {
  3103. _SetToolbarHotItem(_hwndToolbar, nToolbarIcon);
  3104. _fChevronSelected = FALSE;
  3105. }
  3106. else
  3107. {
  3108. SetFocus(_hwndClock);
  3109. _fChevronSelected = FALSE;
  3110. }
  3111. }
  3112. }
  3113. else
  3114. {
  3115. SHAllowSetForegroundWindow(v_hwndTray);
  3116. }
  3117. if (pbRefresh)
  3118. *pbRefresh = TRUE;
  3119. }
  3120. else
  3121. {
  3122. if (_hwndClock)
  3123. {
  3124. SetFocus(_hwndClock);
  3125. _fChevronSelected = FALSE;
  3126. }
  3127. }
  3128. bRet = TRUE;
  3129. break;
  3130. case NIM_ADD:
  3131. // The icon doesnt already exist, and we dont insert again...
  3132. if (nIcon < 0)
  3133. {
  3134. bRet = _InsertNotify(pNID);
  3135. if (bRet && pbRefresh)
  3136. *pbRefresh = TRUE;
  3137. }
  3138. break;
  3139. case NIM_MODIFY:
  3140. if (nIcon >= 0)
  3141. {
  3142. BOOL bRefresh;
  3143. int nCountBefore = -1, nCountAfter = -1;
  3144. if (pbRefresh)
  3145. {
  3146. nCountBefore = m_TrayItemManager.GetPromotedItemCount();
  3147. if (m_TrayItemManager.GetDemotedItemCount() > 0)
  3148. nCountBefore ++;
  3149. }
  3150. bRet = _ModifyNotify(pNID, nIcon, &bRefresh, FALSE);
  3151. if (bRet && pbRefresh)
  3152. {
  3153. nCountAfter = m_TrayItemManager.GetPromotedItemCount();
  3154. if (m_TrayItemManager.GetDemotedItemCount() > 0)
  3155. nCountAfter ++;
  3156. *pbRefresh = (nCountBefore != nCountAfter);
  3157. }
  3158. }
  3159. break;
  3160. case NIM_DELETE:
  3161. if (nIcon >= 0)
  3162. {
  3163. bRet = _DeleteNotify(nIcon, FALSE, TRUE);
  3164. if (bRet)
  3165. {
  3166. if (pbRefresh)
  3167. {
  3168. *pbRefresh = TRUE;
  3169. }
  3170. }
  3171. }
  3172. break;
  3173. case NIM_SETVERSION:
  3174. if (nIcon >= 0)
  3175. {
  3176. // There is no point in handling NIM_SETVERSION if the "No-Tray-Items-Display"
  3177. // policy is in effect. The version enables proper keyboard and mouse notification
  3178. // messages to be sent to the apps, depending on the version of the shell
  3179. // specified.
  3180. // Since the policy prevents the display of any icons, there is no point in
  3181. // setting the correct version..
  3182. bRet = _SetVersionNotify(pNID, nIcon);
  3183. // No activity occurs in SetVersionNotify, so no need to refresh
  3184. // screen - pbRefresh is not set to TRUE...
  3185. }
  3186. break;
  3187. default:
  3188. break;
  3189. }
  3190. return bRet;
  3191. }
  3192. // Public
  3193. LRESULT CTrayNotify::TrayNotify(HWND hwndNotify, HWND hwndFrom, PCOPYDATASTRUCT pcds, BOOL *pbRefresh)
  3194. {
  3195. PTRAYNOTIFYDATA pnid;
  3196. if (!hwndNotify || !pcds)
  3197. {
  3198. return FALSE;
  3199. }
  3200. if (pcds->cbData < sizeof(TRAYNOTIFYDATA))
  3201. {
  3202. return FALSE;
  3203. }
  3204. // We'll add a signature just in case
  3205. pnid = (PTRAYNOTIFYDATA)pcds->lpData;
  3206. if (pnid->dwSignature != NI_SIGNATURE)
  3207. {
  3208. return FALSE;
  3209. }
  3210. return _TrayNotifyIcon(pnid, pbRefresh);
  3211. }
  3212. // Public
  3213. HWND CTrayNotify::TrayNotifyCreate(HWND hwndParent, UINT uID, HINSTANCE hInst)
  3214. {
  3215. WNDCLASSEX wc;
  3216. ZeroMemory(&wc, sizeof(wc));
  3217. wc.cbSize = sizeof(WNDCLASSEX);
  3218. if (!GetClassInfoEx(hInst, c_szTrayNotify, &wc))
  3219. {
  3220. wc.lpszClassName = c_szTrayNotify;
  3221. wc.style = CS_DBLCLKS;
  3222. wc.lpfnWndProc = s_WndProc;
  3223. wc.hInstance = hInst;
  3224. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  3225. wc.hbrBackground = NULL;
  3226. wc.cbWndExtra = sizeof(CTrayNotify *);
  3227. if (!RegisterClassEx(&wc))
  3228. {
  3229. return(NULL);
  3230. }
  3231. if (!ClockCtl_Class(hInst))
  3232. {
  3233. return(NULL);
  3234. }
  3235. }
  3236. return (CreateWindowEx(0, c_szTrayNotify,
  3237. NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | WS_CLIPCHILDREN, 0, 0, 0, 0,
  3238. hwndParent, IntToPtr_(HMENU,uID), hInst, (void *)this));
  3239. }
  3240. void CTrayNotify::_UpdateChevronSize()
  3241. {
  3242. if (_hTheme)
  3243. {
  3244. HTHEME hTheme = OpenThemeData(_hwndChevron, L"Button");
  3245. if (hTheme)
  3246. {
  3247. HDC hdc = GetDC(_hwndChevron);
  3248. GetThemePartSize(hTheme, hdc, BP_PUSHBUTTON, PBS_DEFAULTED, NULL, TS_TRUE, &_szChevron);
  3249. ReleaseDC(_hwndChevron, hdc);
  3250. CloseThemeData(hTheme);
  3251. }
  3252. }
  3253. else
  3254. {
  3255. _szChevron.cx = GetSystemMetrics(SM_CXSMICON);
  3256. _szChevron.cy = GetSystemMetrics(SM_CYSMICON);
  3257. }
  3258. }
  3259. void CTrayNotify::_UpdateChevronState( BOOL fBangMenuOpen,
  3260. BOOL fTrayOrientationChanged, BOOL fUpdateDemotedItems)
  3261. {
  3262. BOOL fChange = FALSE;
  3263. if (_fNoTrayItemsDisplayPolicyEnabled)
  3264. return;
  3265. BOOL fHaveDemoted = _UpdateTrayItems(fUpdateDemotedItems);
  3266. if (_fHaveDemoted != fHaveDemoted)
  3267. {
  3268. _fHaveDemoted = fHaveDemoted;
  3269. ShowWindow(_hwndChevron, _fHaveDemoted ? SW_SHOW : SW_HIDE);
  3270. if (!_fHaveDemoted)
  3271. {
  3272. if (_fBangMenuOpen)
  3273. {
  3274. fBangMenuOpen = FALSE;
  3275. }
  3276. }
  3277. fChange = TRUE;
  3278. if (_fHaveDemoted && !_fBangMenuOpen)
  3279. {
  3280. _ShowChevronInfoTip();
  3281. }
  3282. else if ( (!_fHaveDemoted || (_fBangMenuOpen != fBangMenuOpen)) &&
  3283. _pinfo && _IsChevronInfoTip(_pinfo->hWnd, _pinfo->uID) )
  3284. {
  3285. _beLastBalloonEvent = BALLOONEVENT_NONE;
  3286. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONHIDE);
  3287. }
  3288. }
  3289. if ( fChange || fTrayOrientationChanged ||
  3290. ( _fHaveDemoted && (_fBangMenuOpen != fBangMenuOpen) )
  3291. )
  3292. {
  3293. if ((_fBangMenuOpen != fBangMenuOpen) && _pinfo && _IsChevronInfoTip(_pinfo->hWnd, _pinfo->uID))
  3294. {
  3295. _beLastBalloonEvent = BALLOONEVENT_NONE;
  3296. _ShowInfoTip(_pinfo->hWnd, _pinfo->uID, FALSE, FALSE, NIN_BALLOONHIDE);
  3297. }
  3298. _fBangMenuOpen = fBangMenuOpen;
  3299. LPCWSTR pwzTheme;
  3300. if (_fBangMenuOpen)
  3301. {
  3302. pwzTheme = _fVertical ? c_wzTrayNotifyVertOpenTheme : c_wzTrayNotifyHorizOpenTheme;
  3303. }
  3304. else
  3305. {
  3306. pwzTheme = _fVertical ? c_wzTrayNotifyVertTheme : c_wzTrayNotifyHorizTheme;
  3307. }
  3308. SetWindowTheme(_hwndChevron, pwzTheme, NULL);
  3309. _UpdateChevronSize();
  3310. }
  3311. }
  3312. void CTrayNotify::_UpdateVertical(BOOL fVertical)
  3313. {
  3314. _fVertical = fVertical;
  3315. LPCWSTR pwzTheme = _fVertical ? c_wzTrayNotifyVertTheme : c_wzTrayNotifyHorizTheme;
  3316. SetWindowTheme(_hwndNotify, pwzTheme, NULL);
  3317. _UpdateChevronState(_fBangMenuOpen, TRUE, TRUE);
  3318. }
  3319. void CTrayNotify::_OpenTheme()
  3320. {
  3321. if (_hTheme)
  3322. {
  3323. CloseThemeData(_hTheme);
  3324. _hTheme = NULL;
  3325. }
  3326. _hTheme = OpenThemeData(_hwndNotify, L"TrayNotify");
  3327. _UpdateChevronSize();
  3328. SetWindowStyleEx(_hwndNotify, WS_EX_STATICEDGE, !_hTheme);
  3329. InvalidateRect(_hwndNotify, NULL, FALSE);
  3330. }
  3331. // *** Helper functions for UserEventTimer..
  3332. HRESULT CTrayNotify::_SetItemTimer(CTrayItem * pti)
  3333. {
  3334. HRESULT hr = E_INVALIDARG;
  3335. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3336. if(pti)
  3337. {
  3338. ASSERT(pti->dwUserPref == TNUP_AUTOMATIC);
  3339. UINT uTimerInterval = m_TrayItemRegistry._uPrimaryCountdown;
  3340. if (pti->IsItemClicked())
  3341. {
  3342. // If the item has been clicked on, add 8 hours to its staying time
  3343. // in the tray...
  3344. uTimerInterval += TT_ICON_COUNTDOWN_INCREMENT;
  3345. }
  3346. if (pti->IsStartupIcon())
  3347. {
  3348. uTimerInterval -= (pti->uNumSeconds)*1000;
  3349. }
  3350. hr = _SetTimer(TF_ICONDEMOTE_TIMER, TNM_ICONDEMOTETIMER, uTimerInterval, &(pti->uIconDemoteTimerID));
  3351. }
  3352. return hr;
  3353. }
  3354. HRESULT CTrayNotify::_SetTimer(int nTimerFlag, UINT uCallbackMessage, UINT uTimerInterval, ULONG * puTimerID)
  3355. {
  3356. HRESULT hr = E_INVALIDARG;
  3357. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3358. if (puTimerID)
  3359. {
  3360. IUserEventTimer * pUserEventTimer = _CreateTimer(nTimerFlag);
  3361. if (pUserEventTimer)
  3362. {
  3363. if (FAILED(hr = pUserEventTimer->SetUserEventTimer( _hwndNotify,
  3364. uCallbackMessage, uTimerInterval, NULL, puTimerID)))
  3365. {
  3366. *puTimerID = 0;
  3367. }
  3368. }
  3369. else
  3370. {
  3371. *puTimerID = 0;
  3372. }
  3373. }
  3374. return hr;
  3375. }
  3376. HRESULT CTrayNotify::_KillItemTimer(CTrayItem *pti)
  3377. {
  3378. HRESULT hr = E_INVALIDARG;
  3379. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3380. if (pti)
  3381. {
  3382. hr = _KillTimer(TF_ICONDEMOTE_TIMER, pti->uIconDemoteTimerID);
  3383. // Irrespective of whether the timer ID was valid or not...
  3384. pti->uIconDemoteTimerID = 0;
  3385. }
  3386. return hr;
  3387. }
  3388. HRESULT CTrayNotify::_KillTimer(int nTimerFlag, ULONG uTimerID)
  3389. {
  3390. HRESULT hr = E_INVALIDARG;
  3391. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3392. if (uTimerID)
  3393. {
  3394. IUserEventTimer * pUserEventTimer = _CreateTimer(nTimerFlag);
  3395. if (pUserEventTimer)
  3396. {
  3397. hr = pUserEventTimer->KillUserEventTimer(_hwndNotify, uTimerID);
  3398. // If we are finished with the user tracking timer, we should release it
  3399. if (_ShouldDestroyTimer(nTimerFlag))
  3400. {
  3401. pUserEventTimer->Release();
  3402. _NullifyTimer(nTimerFlag);
  3403. }
  3404. }
  3405. }
  3406. return hr;
  3407. }
  3408. void CTrayNotify::_NullifyTimer(int nTimerFlag)
  3409. {
  3410. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3411. switch(nTimerFlag)
  3412. {
  3413. case TF_ICONDEMOTE_TIMER:
  3414. m_pIconDemoteTimer = NULL;
  3415. break;
  3416. case TF_INFOTIP_TIMER:
  3417. m_pInfoTipTimer = NULL;
  3418. break;
  3419. }
  3420. }
  3421. BOOL CTrayNotify::_ShouldDestroyTimer(int nTimerFlag)
  3422. {
  3423. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3424. switch(nTimerFlag)
  3425. {
  3426. case TF_ICONDEMOTE_TIMER:
  3427. return (!m_TrayItemRegistry.IsAutoTrayEnabled() || m_TrayItemManager.GetPromotedItemCount() == 0);
  3428. case TF_INFOTIP_TIMER:
  3429. return (!_GetQueueCount());
  3430. default:
  3431. AssertMsg(TF_ERROR, TEXT("No other timer ids are possible"));
  3432. return FALSE;
  3433. }
  3434. }
  3435. IUserEventTimer * CTrayNotify::_CreateTimer(int nTimerFlag)
  3436. {
  3437. IUserEventTimer ** ppUserEventTimer = NULL;
  3438. UINT uTimerTickInterval = 0;
  3439. ASSERT(!_fNoTrayItemsDisplayPolicyEnabled);
  3440. switch(nTimerFlag)
  3441. {
  3442. case TF_ICONDEMOTE_TIMER:
  3443. ppUserEventTimer = &m_pIconDemoteTimer;
  3444. break;
  3445. case TF_INFOTIP_TIMER:
  3446. ppUserEventTimer = &m_pInfoTipTimer;
  3447. break;
  3448. default:
  3449. AssertMsg(TF_ERROR, TEXT("No other Timers are possible"));
  3450. return NULL;
  3451. }
  3452. if (ppUserEventTimer && !*ppUserEventTimer)
  3453. {
  3454. if ( !SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_UserEventTimer, NULL,
  3455. IID_PPV_ARG(IUserEventTimer, ppUserEventTimer))) )
  3456. {
  3457. *ppUserEventTimer = NULL;
  3458. }
  3459. else
  3460. {
  3461. uTimerTickInterval = m_TrayItemRegistry.GetTimerTickInterval(nTimerFlag);
  3462. (*ppUserEventTimer)->InitTimerTickInterval(uTimerTickInterval);
  3463. }
  3464. }
  3465. return *ppUserEventTimer;
  3466. }