XTTrayIcon.cpp
上传用户:szled88
上传日期:2015-04-09
资源大小:43957k
文件大小:22k
源码类别:

对话框与窗口

开发平台:

Visual C++

  1. // XTTrayIcon.cpp : implementation of the CXTTrayIcon class.
  2. //
  3. // This file is a part of the XTREME CONTROLS MFC class library.
  4. // (c)1998-2008 Codejock Software, All Rights Reserved.
  5. //
  6. // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
  7. // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
  8. // CONSENT OF CODEJOCK SOFTWARE.
  9. //
  10. // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
  11. // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
  12. // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
  13. // SINGLE COMPUTER.
  14. //
  15. // CONTACT INFORMATION:
  16. // support@codejock.com
  17. // http://www.codejock.com
  18. //
  19. /////////////////////////////////////////////////////////////////////////////
  20. #include "stdafx.h"
  21. #include "Common/XTPVC80Helpers.h"
  22. #include "Common/XTPSystemHelpers.h"
  23. #include "XTDefines.h"
  24. #include "XTUtil.h"
  25. #include "XTTrayIcon.h"
  26. #ifdef _DEBUG
  27. #undef THIS_FILE
  28. static char THIS_FILE[] = __FILE__;
  29. #define new DEBUG_NEW
  30. #endif
  31. const UINT XT_WM_TASKBARCREATED = ::RegisterWindowMessage(_T("TaskbarCreated"));
  32. #ifndef IDANI_CAPTION
  33. #define IDANI_CAPTION               3
  34. #endif//IDANI_CAPTION
  35. /////////////////////////////////////////////////////////////////////////////
  36. // CXTTrayIcon
  37. CXTTrayIcon::CXTTrayIcon()
  38. {
  39. SetDefaultValues();
  40. }
  41. CXTTrayIcon::~CXTTrayIcon()
  42. {
  43. RemoveIcon();
  44. DestroyWindow();
  45. RemoveAnimationIcons();
  46. }
  47. BEGIN_MESSAGE_MAP(CXTTrayIcon, CWnd)
  48. //{{AFX_MSG_MAP(CXTTrayIcon)
  49. ON_WM_TIMER()
  50. ON_WM_SETTINGCHANGE()
  51. //}}AFX_MSG_MAP
  52. ON_REGISTERED_MESSAGE(XT_WM_TASKBARCREATED, OnTaskbarCreated)
  53. END_MESSAGE_MAP()
  54. /////////////////////////////////////////////////////////////////////////////
  55. // CXTTrayIcon initialization
  56. void CXTTrayIcon::SetDefaultValues()
  57. {
  58. ::ZeroMemory(&m_niData, sizeof(NOTIFYICONDATAEX));
  59. // Default initialization for the NOTIFYICONDATA base struct.
  60. m_niData.cbSize = sizeof(NOTIFYICONDATAEX);
  61. m_niData.hWnd = NULL;
  62. m_niData.uID = 0;
  63. m_niData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  64. m_niData.uCallbackMessage = TIN_XT_TRAYICON;
  65. m_niData.hIcon = NULL;
  66. m_niData.szTip[0] = 0;
  67. // Initialize the remaining member data.
  68. m_iMaxTipSize = 64;
  69. m_nIconID = 0;
  70. m_strToolTip = _T("");
  71. m_nIDEvent = 1001;
  72. m_nCounter = 0;
  73. m_uDefMenuItemID = 0;
  74. m_bDefMenuItemByPos = true;
  75. m_bHidden = true;
  76. m_bRemoved = true;
  77. m_bShowPending = false;
  78. m_hWndNotify = NULL;
  79. m_hIcon = NULL;
  80. }
  81. BOOL CXTTrayIcon::IsShellVersion5() const
  82. {
  83. static BOOL bVersion5 = -1;
  84. if (bVersion5 != -1)
  85. return bVersion5;
  86. CXTPModuleHandle hShell(_T("shell32.dll"));
  87. bVersion5 = HIWORD(hShell.GetVersion()) >= 5;
  88. return bVersion5;
  89. }
  90. BOOL CXTTrayIcon::ShellNotify(DWORD dwMessage)
  91. {
  92. return ::Shell_NotifyIcon(dwMessage, (PNOTIFYICONDATA)&m_niData);
  93. }
  94. /////////////////////////////////////////////////////////////////////////////
  95. // CXTTrayIcon notification window initialization
  96. bool CXTTrayIcon::SetNotificationWnd(CWnd* pWndNotify)
  97. {
  98. ASSERT_VALID(pWndNotify);
  99. // Make sure Notification window is valid
  100. if (!pWndNotify || !::IsWindow(pWndNotify->GetSafeHwnd()))
  101. {
  102. return false;
  103. }
  104. // assign values
  105. m_niData.hWnd = pWndNotify->GetSafeHwnd();
  106. m_niData.uFlags = 0;
  107. if (!ShellNotify(NIM_MODIFY))
  108. {
  109. return false;
  110. }
  111. return true;
  112. }
  113. CWnd* CXTTrayIcon::GetNotificationWnd()
  114. {
  115. return CWnd::FromHandle(m_niData.hWnd);
  116. }
  117. /////////////////////////////////////////////////////////////////////////////
  118. // CXTTrayIcon notification message handling
  119. bool CXTTrayIcon::SetCallbackMessage(UINT uNewCallbackMessage)
  120. {
  121. // Make sure we avoid conflict with other messages
  122. ASSERT(m_niData.uCallbackMessage >= WM_APP);
  123. m_niData.uCallbackMessage = uNewCallbackMessage;
  124. m_niData.uFlags = NIF_MESSAGE;
  125. if (!ShellNotify(NIM_MODIFY))
  126. {
  127. return false;
  128. }
  129. return true;
  130. }
  131. UINT CXTTrayIcon::GetCallbackMessage()
  132. {
  133. return m_niData.uCallbackMessage;
  134. }
  135. /////////////////////////////////////////////////////////////////////////////
  136. // CXTTrayIcon icon initialization
  137. bool CXTTrayIcon::SetIcon(HICON hIcon)
  138. {
  139. // check to see if this icon was already set.
  140. if (m_niData.hIcon == hIcon)
  141. {
  142. return true;
  143. }
  144. m_niData.uFlags = NIF_ICON;
  145. m_niData.hIcon = hIcon;
  146. if (m_bHidden)
  147. {
  148. return true;
  149. }
  150. if (!ShellNotify(NIM_MODIFY))
  151. {
  152. return false;
  153. }
  154. return true;
  155. }
  156. bool CXTTrayIcon::SetIcon(LPCTSTR lpszIconName)
  157. {
  158. int cx = ::GetSystemMetrics(SM_CXSMICON);
  159. int cy = ::GetSystemMetrics(SM_CYSMICON);
  160. m_hIcon = (HICON)::LoadImage(AfxGetResourceHandle(),
  161. lpszIconName, IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
  162. return SetIcon(m_hIcon);
  163. }
  164. bool CXTTrayIcon::SetIcon(UINT nIDResource)
  165. {
  166. return SetIcon(MAKEINTRESOURCE(nIDResource));
  167. }
  168. void CXTTrayIcon::RemoveAnimationIcons()
  169. {
  170. while (!m_arTrayIcons.IsEmpty())
  171. {
  172. TRAYICONDATA ti = m_arTrayIcons.RemoveHead();
  173. ::DestroyIcon(ti.hIcon);
  174. }
  175. }
  176. bool CXTTrayIcon::SetAnimationIcons(const UINT* lpIDArray, int nIDCount, const CString* lpStrTipArray/*= NULL*/)
  177. {
  178. RemoveAnimationIcons();
  179. int cx = ::GetSystemMetrics(SM_CXSMICON);
  180. int cy = ::GetSystemMetrics(SM_CYSMICON);
  181. int iIcon;
  182. for (iIcon = 0; iIcon < nIDCount; ++iIcon)
  183. {
  184. TRAYICONDATA ti;
  185. if (lpStrTipArray != NULL)
  186. ti.strToolTip = lpStrTipArray[ iIcon ];
  187. ti.hIcon = (HICON)::LoadImage(AfxGetResourceHandle(),
  188. MAKEINTRESOURCE(lpIDArray[ iIcon ]), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
  189. if (ti.hIcon == NULL)
  190. {
  191. return false;
  192. }
  193. m_arTrayIcons.AddTail(ti);
  194. }
  195. return true;
  196. }
  197. HICON CXTTrayIcon::GetIcon() const
  198. {
  199. return m_niData.hIcon;
  200. }
  201. /////////////////////////////////////////////////////////////////////////////
  202. // CXTTrayIcon tooltip initialization
  203. bool CXTTrayIcon::SetTooltipText(LPCTSTR lpszTipText)
  204. {
  205. m_strToolTip = lpszTipText;
  206. if (m_strToolTip.GetLength() >= (int)m_iMaxTipSize)
  207. m_strToolTip = m_strToolTip.Left((int)m_iMaxTipSize-1);
  208. return SetShellTooltip(m_strToolTip);
  209. }
  210. bool CXTTrayIcon::SetShellTooltip(LPCTSTR lpszTipText)
  211. {
  212. ASSERT(AfxIsValidString(lpszTipText));
  213. ASSERT(_tcslen(lpszTipText) < m_iMaxTipSize);
  214. m_niData.uFlags = NIF_TIP;
  215. STRNCPY_S(m_niData.szTip, _countof(m_niData.szTip), lpszTipText, m_iMaxTipSize-1);
  216. if (m_bHidden)
  217. {
  218. return true;
  219. }
  220. if (!ShellNotify(NIM_MODIFY))
  221. {
  222. return false;
  223. }
  224. return true;
  225. }
  226. bool CXTTrayIcon::SetTooltipText(UINT nTipText)
  227. {
  228. CString strTipText;
  229. if (!strTipText.LoadString(nTipText))
  230. {
  231. return false;
  232. }
  233. return SetTooltipText(strTipText);
  234. }
  235. CString CXTTrayIcon::GetTooltipText() const
  236. {
  237. return m_strToolTip;
  238. }
  239. /////////////////////////////////////////////////////////////////////////////
  240. // CXTTrayIcon manipulation
  241. bool CXTTrayIcon::AddIcon()
  242. {
  243. if (!m_bRemoved)
  244. {
  245. RemoveIcon();
  246. }
  247. m_niData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  248. if (!ShellNotify(NIM_ADD))
  249. {
  250. m_bShowPending = true;
  251. return false;
  252. }
  253. m_bRemoved = m_bHidden = false;
  254. return true;
  255. }
  256. bool CXTTrayIcon::RemoveIcon()
  257. {
  258. m_bShowPending = false;
  259. if (m_bRemoved)
  260. {
  261. return true;
  262. }
  263. m_niData.uFlags = 0;
  264. if (!ShellNotify(NIM_DELETE))
  265. {
  266. return false;
  267. }
  268. m_bRemoved = m_bHidden = true;
  269. return true;
  270. }
  271. bool CXTTrayIcon::HideIcon()
  272. {
  273. if (m_bRemoved || m_bHidden)
  274. {
  275. return true;
  276. }
  277. if (IsShellVersion5())
  278. {
  279. m_niData.uFlags = NIF_STATE;
  280. m_niData.dwState = NIS_HIDDEN;
  281. m_niData.dwStateMask = NIS_HIDDEN;
  282. if (!ShellNotify(NIM_MODIFY))
  283. {
  284. m_bHidden = false;
  285. }
  286. else
  287. {
  288. m_bHidden = true;
  289. }
  290. return m_bHidden;
  291. }
  292. return RemoveIcon();
  293. }
  294. bool CXTTrayIcon::ShowIcon()
  295. {
  296. if (m_bRemoved)
  297. return AddIcon();
  298. if (!m_bHidden)
  299. return TRUE;
  300. if (IsShellVersion5())
  301. {
  302. m_niData.uFlags = NIF_STATE;
  303. m_niData.dwState = 0;
  304. m_niData.dwStateMask = NIS_HIDDEN;
  305. if (!ShellNotify(NIM_MODIFY))
  306. return false;
  307. m_bRemoved = m_bHidden = false;
  308. return true;
  309. }
  310. return AddIcon();
  311. }
  312. /////////////////////////////////////////////////////////////////////////////
  313. // CXTTrayIcon menu initialization
  314. bool CXTTrayIcon::SetDefaultMenuItem(UINT uItem, bool bByPos)
  315. {
  316. // if the values already exist, return true.
  317. if ((m_uDefMenuItemID == uItem) && (m_bDefMenuItemByPos == bByPos))
  318. {
  319. return true;
  320. }
  321. m_uDefMenuItemID = uItem;
  322. m_bDefMenuItemByPos = bByPos;
  323. // verify that we can load the menu defined by uID.
  324. CMenu menu;
  325. if (!menu.LoadMenu(m_niData.uID))
  326. {
  327. return false;
  328. }
  329. // see if we can access the submenu
  330. CMenu* pSubMenu = menu.GetSubMenu(0);
  331. if (!pSubMenu)
  332. {
  333. return false;
  334. }
  335. // check to see if we can set the submenu for the popup.  This is just a check to ensure
  336. // that everything has been correctly set.
  337. if (!::SetMenuDefaultItem(pSubMenu->m_hMenu, m_uDefMenuItemID, m_bDefMenuItemByPos))
  338. {
  339. return false;
  340. }
  341. return true;
  342. }
  343. void CXTTrayIcon::GetDefaultMenuItem(UINT& uItem, bool& bByPos)
  344. {
  345. uItem = m_uDefMenuItemID;
  346. bByPos = m_bDefMenuItemByPos;
  347. }
  348. /////////////////////////////////////////////////////////////////////////////
  349. // CXTTrayIcon message handlers
  350. void CXTTrayIcon::OnTimer(UINT_PTR nIDEvent)
  351. {
  352. // Update the tray icon and tooltip text.
  353. if (nIDEvent == m_nIDEvent)
  354. {
  355. POSITION pos = m_arTrayIcons.FindIndex(m_nCounter);
  356. if (pos != NULL)
  357. {
  358. TRAYICONDATA& ti = m_arTrayIcons.GetAt(pos);
  359. if (!ti.strToolTip.IsEmpty())
  360. {
  361. SetShellTooltip(ti.strToolTip);
  362. }
  363. else
  364. {
  365. SetShellTooltip(m_strToolTip);
  366. }
  367. if (ti.hIcon != NULL)
  368. {
  369. SetIcon(ti.hIcon);
  370. }
  371. }
  372. m_nCounter = (m_nCounter + 1) % (int)m_arTrayIcons.GetCount();
  373. }
  374. }
  375. bool CXTTrayIcon::Create(
  376. LPCTSTR lpszCaption,
  377. CWnd* pParentWnd,
  378. UINT nIconID,
  379. UINT uMenuID/*= 0*/,
  380. UINT uDefMenuItemID/*= 0*/,
  381. bool bDefMenuItemByPos/*= false*/)
  382. {
  383. ASSERT(pParentWnd); // must be valid.
  384. if (!pParentWnd)
  385. return false;
  386. m_nIconID = nIconID;
  387. m_strToolTip = lpszCaption;
  388. m_hWndNotify = pParentWnd->GetSafeHwnd();
  389. m_iMaxTipSize = _countof(m_niData.szTip) - 1;
  390. // Set the tray icon and tooltip text
  391. SetIcon(m_nIconID);
  392. SetTooltipText(m_strToolTip);
  393. // Create an invisible window
  394. CWnd::CreateEx(0, AfxRegisterWndClass(0), _T(""), WS_POPUP, 0, 0, 0, 0, NULL, 0);
  395. m_niData.hWnd = m_hWnd;
  396. m_niData.uID = uMenuID;
  397. m_uDefMenuItemID = uDefMenuItemID;
  398. m_bDefMenuItemByPos = bDefMenuItemByPos;
  399. m_uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  400. return AddIcon();
  401. }
  402. bool CXTTrayIcon::ShowBalloonTip(LPCTSTR lpszInfo, LPCTSTR lpszInfoTitle/*= NULL*/, DWORD dwInfoFlags/*= NIIF_NONE*/, UINT uTimeout/*= 10*/)
  403. {
  404. bool bResult = false;
  405. if (IsShellVersion5() && lpszInfo)
  406. {
  407. // The balloon tooltip text can be up to 255 chars long.
  408. ASSERT(AfxIsValidString(lpszInfo));
  409. ASSERT(lstrlen(lpszInfo) < 256);
  410. // The balloon title text can be up to 63 chars long.
  411. if (lpszInfoTitle)
  412. {
  413. ASSERT(AfxIsValidString(lpszInfoTitle));
  414. ASSERT(lstrlen(lpszInfoTitle) < 64);
  415. }
  416. // dwInfoFlags must be valid.
  417. ASSERT(NIIF_NONE == dwInfoFlags ||
  418. NIIF_INFO == dwInfoFlags ||
  419. NIIF_WARNING == dwInfoFlags ||
  420. NIIF_ERROR == dwInfoFlags);
  421. // The timeout must be between 10 and 30 seconds.
  422. ASSERT(uTimeout >= 10 && uTimeout <= 30);
  423. m_niData.uFlags |= NIF_INFO;
  424. STRNCPY_S(m_niData.szInfo, _countof(m_niData.szInfo), lpszInfo, 255);
  425. if (lpszInfoTitle)
  426. {
  427. STRNCPY_S(m_niData.szInfoTitle, _countof(m_niData.szInfoTitle), lpszInfoTitle, 63);
  428. }
  429. else
  430. {
  431. m_niData.szInfoTitle[0] = _T('');
  432. }
  433. m_niData.uTimeout = (uTimeout * 1000); // convert time to millisecs
  434. m_niData.dwInfoFlags = dwInfoFlags;
  435. if (ShellNotify(NIM_MODIFY))
  436. {
  437. bResult = true;
  438. }
  439. // Zero out the balloon text string so that later operations won't redisplay
  440. // the balloon.
  441. m_niData.szInfo[0] = _T('');
  442. }
  443. return bResult;
  444. }
  445. void CXTTrayIcon::StartAnimation(UINT uElapse/*= 500*/)
  446. {
  447. CWnd::SetTimer(m_nIDEvent, uElapse, NULL);
  448. }
  449. void CXTTrayIcon::StopAnimation()
  450. {
  451. CWnd::KillTimer(m_nIDEvent);
  452. SetShellTooltip(m_strToolTip);
  453. SetIcon(m_nIconID);
  454. }
  455. LRESULT CXTTrayIcon::OnTrayNotification(WPARAM wParam, LPARAM lParam)
  456. {
  457. // Return quickly if its not for this tray icon
  458. if (wParam != m_niData.uID)
  459. {
  460. return 0L;
  461. }
  462. if (m_hWndNotify == NULL)
  463. {
  464. return 0L;
  465. }
  466. // Check to see if our notification window has already handled this
  467. // message, if so then return success.
  468. if (::SendMessage(m_hWndNotify, TIN_XT_TRAYICON, wParam, lParam))
  469. {
  470. return 1;
  471. }
  472. switch (LOWORD(lParam))
  473. {
  474. // Sent when the balloon is shown (balloons are queued).
  475. case NIN_BALLOONSHOW:
  476. break;
  477. // Sent when the balloon disappears-for example, when the
  478. // icon is deleted. This message is not sent if the balloon
  479. // is dismissed because of a timeout or a mouse click.
  480. case NIN_BALLOONHIDE:
  481. break;
  482. // Sent when the balloon is dismissed because of a timeout.
  483. case NIN_BALLOONTIMEOUT:
  484. break;
  485. // Sent when the balloon is dismissed because of a mouse click.
  486. case NIN_BALLOONUSERCLICK:
  487. break;
  488. // The version 5.0 Shell sends the associated application a NIN_SELECT message
  489. // if a user selects a notify icon with the mouse and activates it with the ENTER key
  490. case NIN_SELECT:
  491. // intentional fall thru...
  492. // The version 5.0 Shell sends the associated application a NIN_KEYSELECT message
  493. // if a user selects a notify icon with the keyboard and activates it with the space bar or ENTER key
  494. case NIN_KEYSELECT:
  495. // intentional fall thru...
  496. // The version 5.0 Shell sends the associated application a WM_CONTEXTMENU
  497. // If a user requests a notify icon's shortcut menu with the keyboard
  498. case WM_CONTEXTMENU:
  499. // intentional fall thru...
  500. case WM_RBUTTONUP:
  501. {
  502. CMenu menu;
  503. if (!menu.LoadMenu(m_niData.uID))
  504. {
  505. return 0;
  506. }
  507. CMenu* pSubMenu = menu.GetSubMenu(0);
  508. if (pSubMenu == NULL)
  509. {
  510. return 0;
  511. }
  512. // Make chosen menu item the default (bold font)
  513. ::SetMenuDefaultItem(pSubMenu->m_hMenu,
  514. m_uDefMenuItemID, m_bDefMenuItemByPos);
  515. // Display the menu at the current mouse location. There's a "bug"
  516. // (Microsoft calls it a feature) in Windows 95 that requires calling
  517. // SetForegroundWindow. To find out more, search for Q135788 in MSDN.
  518. //
  519. CPoint pos;
  520. GetCursorPos(&pos);
  521. ::SetForegroundWindow(m_hWndNotify);
  522. ::TrackPopupMenu(pSubMenu->m_hMenu, 0, pos.x, pos.y,
  523. 0, m_hWndNotify, NULL);
  524. ::PostMessage(m_hWndNotify, WM_NULL, 0, 0);
  525. menu.DestroyMenu();
  526. ShellNotify(NIM_SETFOCUS);
  527. }
  528. break;
  529. case WM_LBUTTONDBLCLK:
  530. {
  531. // double click received, the default action is to execute default menu item
  532. ::SetForegroundWindow(m_hWndNotify);
  533. UINT uItem;
  534. if (m_bDefMenuItemByPos)
  535. {
  536. CMenu menu;
  537. if (!menu.LoadMenu(m_niData.uID))
  538. {
  539. return 0;
  540. }
  541. CMenu* pSubMenu = menu.GetSubMenu(0);
  542. if (pSubMenu == NULL)
  543. {
  544. return 0;
  545. }
  546. uItem = pSubMenu->GetMenuItemID(m_uDefMenuItemID);
  547. menu.DestroyMenu();
  548. }
  549. else
  550. {
  551. uItem = m_uDefMenuItemID;
  552. }
  553. ::SendMessage(m_hWndNotify, WM_COMMAND, uItem, 0);
  554. }
  555. }
  556. return 1;
  557. }
  558. LRESULT CXTTrayIcon::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  559. {
  560. if (message == m_niData.uCallbackMessage)
  561. {
  562. return OnTrayNotification(wParam, lParam);
  563. }
  564. return CWnd::WindowProc(message, wParam, lParam);
  565. }
  566. // This is called whenever the taskbar is created (eg after explorer crashes
  567. // and restarts. Please note that the WM_TASKBARCREATED message is only passed
  568. // to TOP LEVEL windows (like WM_QUERYNEWPALETTE)
  569. LRESULT CXTTrayIcon::OnTaskbarCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
  570. {
  571. InstallIconPending();
  572. return 0L;
  573. }
  574. void CXTTrayIcon::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
  575. {
  576. CWnd::OnSettingChange(uFlags, lpszSection);
  577. if (uFlags == SPI_SETWORKAREA && m_bShowPending)
  578. {
  579. InstallIconPending();
  580. }
  581. }
  582. void CXTTrayIcon::InstallIconPending()
  583. {
  584. // Is the icon display pending, and it's not been set as "hidden"?
  585. if (m_bRemoved || m_bHidden)
  586. {
  587. return;
  588. }
  589. // Reset the flags to what was used at creation
  590. m_niData.uFlags = m_uFlags;
  591. // Try and recreate the icon
  592. m_bShowPending = (ShellNotify(NIM_ADD) == FALSE);
  593. }
  594. /////////////////////////////////////////////////////////////////////////////
  595. // CXTTrayIcon minimize and restore helper functions
  596. BOOL CALLBACK CXTTrayIcon::FindTrayWnd(HWND hWnd, LPARAM lParam)
  597. {
  598. TCHAR szClassName[256];
  599. ::GetClassName(hWnd, szClassName, 255);
  600. // Did we find the main system tray ? If so, then get its size and keep going
  601. if (_tcscmp(szClassName, _T("TrayNotifyWnd")) == 0)
  602. {
  603. CRect *pRect = (CRect*) lParam;
  604. ::GetWindowRect(hWnd, pRect);
  605. return TRUE;
  606. }
  607. // Did we find the System Clock ? If so, then adjust the size of the rectangle
  608. // we have and quit (clock will be found after the system tray)
  609. if (_tcscmp(szClassName, _T("TrayClockWClass")) == 0)
  610. {
  611. CRect *pRect = (CRect*)lParam;
  612. CRect rectClock;
  613. ::GetWindowRect(hWnd, rectClock);
  614. // if clock is above system tray adjust accordingly
  615. if (rectClock.bottom < pRect->bottom-5)    // 10 = random fudge factor.
  616. {
  617. pRect->top = rectClock.bottom;
  618. }
  619. else
  620. {
  621. pRect->right = rectClock.left;
  622. }
  623. return FALSE;
  624. }
  625. return TRUE;
  626. }
  627. bool CXTTrayIcon::GetTrayWindowRect(CRect& rect)
  628. {
  629. int cx = 150;
  630. int cy = 30;
  631. HWND hWndTray = ::FindWindow(_T("Shell_TrayWnd"), NULL);
  632. if (hWndTray)
  633. {
  634. ::GetWindowRect(hWndTray, &rect);
  635. ::EnumChildWindows(hWndTray, FindTrayWnd, (LPARAM)&rect);
  636. return true;
  637. }
  638. // OK, we failed to get the rect from the quick hack. Either explorer isn't
  639. // running or it's a new version of the shell with the window class names
  640. // changed (how dare Microsoft change these undocumented class names!) So, we
  641. // try to find out what side of the screen the taskbar is connected to. We
  642. // know that the system tray is either on the right or the bottom of the
  643. // taskbar, so we can make a good guess at where to minimize to
  644. APPBARDATA ad;
  645. ::ZeroMemory(&ad, sizeof(APPBARDATA));
  646. ad.cbSize = sizeof(ad);
  647. if (::SHAppBarMessage(ABM_GETTASKBARPOS, &ad))
  648. {
  649. // We know the edge the taskbar is connected to, so guess the rect of the
  650. // system tray. Use various fudge factor to make it look good
  651. switch (ad.uEdge)
  652. {
  653. case ABE_LEFT:
  654. case ABE_RIGHT:
  655. // We want to minimize to the bottom of the taskbar
  656. rect.top = ad.rc.bottom-100;
  657. rect.bottom = ad.rc.bottom-16;
  658. rect.left = ad.rc.left;
  659. rect.right = ad.rc.right;
  660. break;
  661. case ABE_TOP:
  662. case ABE_BOTTOM:
  663. // We want to minimize to the right of the taskbar
  664. rect.top = ad.rc.top;
  665. rect.bottom = ad.rc.bottom;
  666. rect.left = ad.rc.right-100;
  667. rect.right = ad.rc.right-16;
  668. break;
  669. }
  670. return true;
  671. }
  672. // Blimey, we really aren't in luck. It's possible that a third party shell
  673. // is running instead of explorer. This shell might provide support for the
  674. // system tray, by providing a Shell_TrayWnd window (which receives the
  675. // messages for the icons) So, look for a Shell_TrayWnd window and work out
  676. // the rect from that. Remember that explorer's taskbar is the Shell_TrayWnd,
  677. // and stretches either the width or the height of the screen. We can't rely
  678. // on the 3rd party shell's Shell_TrayWnd doing the same, in fact, we can't
  679. // rely on it being any size. The best we can do is just blindly use the
  680. // window rect, perhaps limiting the width and height to, say 150 square.
  681. // Note that if the 3rd party shell supports the same configuration as
  682. // explorer (the icons hosted in NotifyTrayWnd, which is a child window of
  683. // Shell_TrayWnd), we would already have caught it above
  684. if (hWndTray)
  685. {
  686. ::GetWindowRect(hWndTray, &rect);
  687. if (rect.right - rect.left > cx)
  688. {
  689. rect.left = rect.right - cx;
  690. }
  691. if (rect.bottom - rect.top > cy)
  692. {
  693. rect.top = rect.bottom - cy;
  694. }
  695. return true;
  696. }
  697. // OK. Haven't found a thing. Provide a default rect based on the current work
  698. // area
  699. if (::SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
  700. {
  701. rect.left = rect.right - cx;
  702. rect.top = rect.bottom - cy;
  703. return true;
  704. }
  705. return false;
  706. }
  707. bool CXTTrayIcon::CanAnimate() const
  708. {
  709. ANIMATIONINFO ai;
  710. ::ZeroMemory(&ai, sizeof(ANIMATIONINFO));
  711. ai.cbSize = sizeof(ANIMATIONINFO);
  712. ::SystemParametersInfo(SPI_GETANIMATION,
  713. sizeof(ANIMATIONINFO), &ai, 0);
  714. return ai.iMinAnimate ? true : false;
  715. }
  716. /////////////////////////////////////////////////////////////////////////////
  717. // CXTTrayIcon minimize and restore functions
  718. bool CXTTrayIcon::CreateMinimizeWnd(CWnd* pWndApp)
  719. {
  720. // Create the minimize window
  721. if (!::IsWindow(m_wndMinimize.m_hWnd))
  722. {
  723. if (!m_wndMinimize.CreateEx(0, AfxRegisterWndClass(0), _T(""), WS_POPUP,
  724. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, 0))
  725. {
  726. return false;
  727. }
  728. }
  729. pWndApp->SetParent(&m_wndMinimize);
  730. return true;
  731. }
  732. void CXTTrayIcon::MinimizeToTray(CWnd* pWndApp)
  733. {
  734. if (CanAnimate())
  735. {
  736. CRect rectFrom, rectTo;
  737. pWndApp->GetWindowRect(rectFrom);
  738. GetTrayWindowRect(rectTo);
  739. ::DrawAnimatedRects(pWndApp->m_hWnd,
  740. IDANI_CAPTION, rectFrom, rectTo);
  741. }
  742. CreateMinimizeWnd(pWndApp);
  743. pWndApp->ShowWindow(SW_HIDE);
  744. }
  745. void CXTTrayIcon::MaximizeFromTray(CWnd* pWndApp)
  746. {
  747. if (CanAnimate())
  748. {
  749. CRect rectTo;
  750. pWndApp->GetWindowRect(rectTo);
  751. CRect rectFrom;
  752. GetTrayWindowRect(rectFrom);
  753. pWndApp->SetParent(NULL);
  754. ::DrawAnimatedRects(pWndApp->m_hWnd,
  755. IDANI_CAPTION, rectFrom, rectTo);
  756. }
  757. else
  758. {
  759. pWndApp->SetParent(NULL);
  760. }
  761. pWndApp->SetWindowPos(0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
  762. // Move focus away and back again to ensure taskbar icon is recreated
  763. if (::IsWindow(m_wndMinimize.m_hWnd))
  764. {
  765. m_wndMinimize.SetActiveWindow();
  766. }
  767. pWndApp->SetActiveWindow();
  768. pWndApp->SetForegroundWindow();
  769. }