Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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