SystemTray.cpp
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:34k
源码类别:

P2P编程

开发平台:

Visual C++

  1. /*
  2.  *  Openmysee
  3.  *
  4.  *  This program is free software; you can redistribute it and/or modify
  5.  *  it under the terms of the GNU General Public License as published by
  6.  *  the Free Software Foundation; either version 2 of the License, or
  7.  *  (at your option) any later version.
  8.  *
  9.  *  This program is distributed in the hope that it will be useful,
  10.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  *  GNU General Public License for more details.
  13.  *
  14.  *  You should have received a copy of the GNU General Public License
  15.  *  along with this program; if not, write to the Free Software
  16.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17.  *
  18.  */
  19. /////////////////////////////////////////////////////////////////////////////
  20. // SystemTray.cpp : implementation file
  21. //
  22. // MFC VERSION
  23. //
  24. // This is a conglomeration of ideas from the MSJ "Webster" application,
  25. // sniffing round the online docs, and from other implementations such
  26. // as PJ Naughter's "CTrayNotifyIcon" (http://indigo.ie/~pjn/ntray.html)
  27. // especially the "CSystemTray::OnTrayNotification" member function.
  28. // Joerg Koenig suggested the icon animation stuff
  29. //
  30. // This class is a light wrapper around the windows system tray stuff. It
  31. // adds an icon to the system tray with the specified ToolTip text and 
  32. // callback notification value, which is sent back to the Parent window.
  33. //
  34. // The tray icon can be instantiated using either the constructor or by
  35. // declaring the object and creating (and displaying) it later on in the
  36. // program. eg.
  37. //
  38. //        CSystemTray m_SystemTray;    // Member variable of some class
  39. //        
  40. //        ... 
  41. //        // in some member function maybe...
  42. //        m_SystemTray.Create(pParentWnd, WM_MY_NOTIFY, "Click here", 
  43. //                          hIcon, nSystemTrayID);
  44. //
  45. // Written by Chris Maunder (cmaunder@mail.com)
  46. // Copyright (c) 1998-2003.
  47. //
  48. // Updated: 25 Jul 1998 - Added icon animation, and derived class
  49. //                        from CWnd in order to handle messages. (CJM)
  50. //                        (icon animation suggested by Joerg Koenig.
  51. //                        Added API to set default menu item. Code provided
  52. //                        by Enrico Lelina.
  53. //
  54. // Updated: 6 June 1999 - SetIcon can now load non-standard sized icons (Chip Calvert)
  55. //                        Added "bHidden" parameter when creating icon
  56. //                        (Thanks to Michael Gombar for these suggestions)
  57. //                        Restricted tooltip text to 64 characters.
  58. //
  59. // Updated: 9 Nov 1999  - Now works in WindowsCE.
  60. //                        Fix for use in NT services (Thomas Mooney, TeleProc, Inc)
  61. //                        Added W2K stuff by Michael Dunn
  62. //
  63. // Updated: 1 Jan 2000  - Added tray minimisation stuff.
  64. // 
  65. // Updated: 21 Sep 2000 - Added GetDoWndAnimation - animation only occurs if the system
  66. //                        settings allow it (Matthew Ellis). Updated the GetTrayWndRect
  67. //                        function to include more fallback logic (Matthew Ellis)
  68. //                        NOTE: Signature of GetTrayWndRect has changed!
  69. // 
  70. // Updated: 4 Aug 2003 - Fixed bug that was stopping icon from being recreated when
  71. //                       Explorer crashed
  72. //                       Fixed resource leak in SetIcon
  73. //  Animate() now checks for empty icon list - Anton Treskunov
  74. //  Added the virutal CustomizeMenu() method - Anton Treskunov
  75. //                       
  76. //
  77. // This code may be used in compiled form in any way you desire. This
  78. // file may be redistributed unmodified by any means PROVIDING it is 
  79. // not sold for profit without the authors written consent, and 
  80. // providing that this notice and the authors name is included. If 
  81. // the source code in  this file is used in any commercial application 
  82. // then acknowledgement must be made to the author of this file 
  83. // (in whatever form you wish).
  84. //
  85. // This file is provided "as is" with no expressed or implied warranty.
  86. // The author accepts no liability for any damage caused through use.
  87. //
  88. // Expect bugs.
  89. // 
  90. // Please use and enjoy. Please let me know of any bugs/mods/improvements 
  91. // that you have found/implemented and I will fix/incorporate them into this
  92. // file. 
  93. //
  94. /////////////////////////////////////////////////////////////////////////////
  95.     
  96. #include "stdafx.h"
  97. #include "SystemTray.h"
  98. #ifdef _DEBUG
  99. #define new DEBUG_NEW
  100. #undef THIS_FILE
  101. static char THIS_FILE[] = __FILE__;
  102. #endif
  103. #ifndef _WIN32_WCE  // Use C++ exception handling instead of structured.
  104. #undef TRY
  105. #undef CATCH
  106. #undef END_CATCH
  107. #define TRY try
  108. #define CATCH(ex_class, ex_object) catch(ex_class* ex_object)
  109. #define END_CATCH
  110. #endif  // _WIN32_WCE
  111. #ifndef _countof
  112. #define _countof(x) ( sizeof(x) / sizeof(x[0]) )
  113. #endif
  114. IMPLEMENT_DYNAMIC(CSystemTray, CWnd)
  115. const UINT CSystemTray::m_nTimerID    = 4567;
  116. UINT CSystemTray::m_nMaxTooltipLength  = 64;     // This may change...
  117. const UINT CSystemTray::m_nTaskbarCreatedMsg = ::RegisterWindowMessage(_T("TaskbarCreated"));
  118. CWnd  CSystemTray::m_wndInvisible;
  119. /////////////////////////////////////////////////////////////////////////////
  120. // CSystemTray construction/creation/destruction
  121. CSystemTray::CSystemTray()
  122. {
  123.     Initialise();
  124. }
  125. CSystemTray::CSystemTray(CWnd* pParent,             // The window that will recieve tray notifications
  126.                          UINT uCallbackMessage,     // the callback message to send to parent
  127.                          LPCTSTR szToolTip,         // tray icon tooltip
  128.                          HICON icon,                // Handle to icon
  129.                          UINT uID,                  // Identifier of tray icon
  130.                          BOOL bHidden /*=FALSE*/,   // Hidden on creation?                  
  131.                          LPCTSTR szBalloonTip /*=NULL*/,    // Ballon tip (w2k only)
  132.                          LPCTSTR szBalloonTitle /*=NULL*/,  // Balloon tip title (w2k)
  133.                          DWORD dwBalloonIcon /*=NIIF_NONE*/,// Ballon tip icon (w2k)
  134.                          UINT uBalloonTimeout /*=10*/)      // Balloon timeout (w2k)
  135. {
  136.     Initialise();
  137.     Create(pParent, uCallbackMessage, szToolTip, icon, uID, bHidden,
  138.            szBalloonTip, szBalloonTitle, dwBalloonIcon, uBalloonTimeout);
  139. }
  140. void CSystemTray::Initialise()
  141. {
  142.     memset(&m_tnd, 0, sizeof(m_tnd));
  143.     m_bEnabled = FALSE;
  144.     m_bHidden  = TRUE;
  145.     m_bRemoved = TRUE;
  146.     m_DefaultMenuItemID    = 0;
  147.     m_DefaultMenuItemByPos = TRUE;
  148.     m_bShowIconPending = FALSE;
  149.     m_uIDTimer   = 0;
  150.     m_hSavedIcon = NULL;
  151. m_pTargetWnd = NULL;
  152. m_uCreationFlags = 0;
  153. #ifdef SYSTEMTRAY_USEW2K
  154.     OSVERSIONINFO os = { sizeof(os) };
  155.     GetVersionEx(&os);
  156.     m_bWin2K = ( VER_PLATFORM_WIN32_NT == os.dwPlatformId && os.dwMajorVersion >= 5 );
  157. #else
  158.     m_bWin2K = FALSE;
  159. #endif
  160. }
  161. // update by Michael Dunn, November 1999
  162. //
  163. //  New version of Create() that handles new features in Win 2K.
  164. //
  165. // Changes:
  166. //  szTip: Same as old, but can be 128 characters instead of 64.
  167. //  szBalloonTip: Text for a balloon tooltip that is shown when the icon
  168. //                is first added to the tray.  Pass "" if you don't want
  169. //                a balloon.
  170. //  szBalloonTitle: Title text for the balloon tooltip.  This text is shown
  171. //                  in bold above the szBalloonTip text.  Pass "" if you
  172. //                  don't want a title.
  173. //  dwBalloonIcon: Specifies which icon will appear in the balloon.  Legal
  174. //                 values are:
  175. //                     NIIF_NONE: No icon
  176. //                     NIIF_INFO: Information
  177. //                     NIIF_WARNING: Exclamation
  178. //                     NIIF_ERROR: Critical error (red circle with X)
  179. //  uBalloonTimeout: Number of seconds for the balloon to remain visible.
  180. //                   Must be between 10 and 30 inclusive.
  181. BOOL CSystemTray::Create(CWnd* pParent, UINT uCallbackMessage, LPCTSTR szToolTip, 
  182.                          HICON icon, UINT uID, BOOL bHidden /*=FALSE*/,
  183.                          LPCTSTR szBalloonTip /*=NULL*/, 
  184.                          LPCTSTR szBalloonTitle /*=NULL*/,  
  185.                          DWORD dwBalloonIcon /*=NIIF_NONE*/,
  186.                          UINT uBalloonTimeout /*=10*/)
  187. {
  188. #ifdef _WIN32_WCE
  189.     m_bEnabled = TRUE;
  190. #else
  191.     // this is only for Windows 95 (or higher)
  192.     m_bEnabled = (GetVersion() & 0xff) >= 4;
  193.     if (!m_bEnabled) 
  194.     {
  195.         ASSERT(FALSE);
  196.         return FALSE;
  197.     }
  198. #endif
  199.     m_nMaxTooltipLength = _countof(m_tnd.szTip);
  200.     
  201.     // Make sure we avoid conflict with other messages
  202.     ASSERT(uCallbackMessage >= WM_APP);
  203.     // Tray only supports tooltip text up to m_nMaxTooltipLength) characters
  204.     ASSERT(AfxIsValidString(szToolTip));
  205.     ASSERT(_tcslen(szToolTip) <= m_nMaxTooltipLength);
  206.     // Create an invisible window
  207.     CWnd::CreateEx(0, AfxRegisterWndClass(0), _T(""), WS_POPUP, 0,0,0,0, NULL, 0);
  208.     // load up the NOTIFYICONDATA structure
  209.     m_tnd.cbSize = sizeof(NOTIFYICONDATA);
  210.     m_tnd.hWnd   = pParent->GetSafeHwnd()? pParent->GetSafeHwnd() : m_hWnd;
  211.     m_tnd.uID    = uID;
  212.     m_tnd.hIcon  = icon;
  213.     m_tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  214.     m_tnd.uCallbackMessage = uCallbackMessage;
  215.     _tcsncpy(m_tnd.szTip, szToolTip, m_nMaxTooltipLength-1);
  216. #ifdef SYSTEMTRAY_USEW2K
  217.     if (m_bWin2K && szBalloonTip)
  218.     {
  219.         // The balloon tooltip text can be up to 255 chars long.
  220.         ASSERT(AfxIsValidString(szBalloonTip));
  221.         ASSERT(lstrlen(szBalloonTip) < 256);
  222.         // The balloon title text can be up to 63 chars long.
  223.         if (szBalloonTitle)
  224.         {
  225.             ASSERT(AfxIsValidString(szBalloonTitle));
  226.             ASSERT(lstrlen(szBalloonTitle) < 64);
  227.         }
  228.         // dwBalloonIcon must be valid.
  229.         ASSERT(NIIF_NONE == dwBalloonIcon    || NIIF_INFO == dwBalloonIcon ||
  230.                NIIF_WARNING == dwBalloonIcon || NIIF_ERROR == dwBalloonIcon);
  231.         // The timeout must be between 10 and 30 seconds.
  232.         ASSERT(uBalloonTimeout >= 10 && uBalloonTimeout <= 30);
  233.         m_tnd.uFlags |= NIF_INFO;
  234.         _tcsncpy(m_tnd.szInfo, szBalloonTip, 255);
  235.         if (szBalloonTitle)
  236.             _tcsncpy(m_tnd.szInfoTitle, szBalloonTitle, 63);
  237.         else
  238.             m_tnd.szInfoTitle[0] = _T('');
  239.         m_tnd.uTimeout    = uBalloonTimeout * 1000; // convert time to ms
  240.         m_tnd.dwInfoFlags = dwBalloonIcon;
  241.     }
  242. #endif
  243.     m_bHidden = bHidden;
  244. #ifdef SYSTEMTRAY_USEW2K    
  245.     if (m_bWin2K && m_bHidden)
  246.     {
  247.         m_tnd.uFlags = NIF_STATE;
  248.         m_tnd.dwState = NIS_HIDDEN;
  249.         m_tnd.dwStateMask = NIS_HIDDEN;
  250.     }
  251. #endif
  252. m_uCreationFlags = m_tnd.uFlags; // Store in case we need to recreate in OnTaskBarCreate
  253.     BOOL bResult = TRUE;
  254.     if (!m_bHidden || m_bWin2K)
  255.     {
  256.         bResult = Shell_NotifyIcon(NIM_ADD, &m_tnd);
  257.         m_bShowIconPending = m_bHidden = m_bRemoved = !bResult;
  258.     }
  259.     
  260. #ifdef SYSTEMTRAY_USEW2K    
  261.     if (m_bWin2K && szBalloonTip)
  262.     {
  263.         // Zero out the balloon text string so that later operations won't redisplay
  264.         // the balloon.
  265.         m_tnd.szInfo[0] = _T('');
  266.     }
  267. #endif
  268.     return bResult;
  269. }
  270. CSystemTray::~CSystemTray()
  271. {
  272.     RemoveIcon();
  273.     m_IconList.RemoveAll();
  274.     DestroyWindow();
  275. }
  276. /////////////////////////////////////////////////////////////////////////////
  277. // CSystemTray icon manipulation
  278. //////////////////////////////////////////////////////////////////////////
  279. //
  280. // Function:    SetFocus()
  281. //
  282. // Description:
  283. //  Sets the focus to the tray icon.  Microsoft's Win 2K UI guidelines
  284. //  say you should do this after the user dismisses the icon's context
  285. //  menu.
  286. //
  287. // Input:
  288. //  Nothing.
  289. //
  290. // Returns:
  291. //  Nothing.
  292. //
  293. //////////////////////////////////////////////////////////////////////////
  294. // Added by Michael Dunn, November, 1999
  295. //////////////////////////////////////////////////////////////////////////
  296. void CSystemTray::SetFocus()
  297. {
  298. #ifdef SYSTEMTRAY_USEW2K
  299.     Shell_NotifyIcon ( NIM_SETFOCUS, &m_tnd );
  300. #endif
  301. }
  302. BOOL CSystemTray::MoveToRight()
  303. {
  304.     RemoveIcon();
  305.     return AddIcon();
  306. }
  307. BOOL CSystemTray::AddIcon()
  308. {
  309.     if (!m_bRemoved)
  310.         RemoveIcon();
  311.     if (m_bEnabled)
  312.     {
  313.         m_tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
  314.         if (!Shell_NotifyIcon(NIM_ADD, &m_tnd))
  315.             m_bShowIconPending = TRUE;
  316.         else
  317.             m_bRemoved = m_bHidden = FALSE;
  318.     }
  319.     return (m_bRemoved == FALSE);
  320. }
  321. BOOL CSystemTray::RemoveIcon()
  322. {
  323.     m_bShowIconPending = FALSE;
  324.     if (!m_bEnabled || m_bRemoved)
  325.         return TRUE;
  326.     m_tnd.uFlags = 0;
  327.     if (Shell_NotifyIcon(NIM_DELETE, &m_tnd))
  328.         m_bRemoved = m_bHidden = TRUE;
  329.     return (m_bRemoved == TRUE);
  330. }
  331. BOOL CSystemTray::HideIcon()
  332. {
  333.     if (!m_bEnabled || m_bRemoved || m_bHidden)
  334.         return TRUE;
  335. #ifdef SYSTEMTRAY_USEW2K
  336.     if (m_bWin2K)
  337.     {
  338.         m_tnd.uFlags = NIF_STATE;
  339.         m_tnd.dwState = NIS_HIDDEN;
  340.         m_tnd.dwStateMask = NIS_HIDDEN;
  341.         m_bHidden = Shell_NotifyIcon( NIM_MODIFY, &m_tnd);
  342.     }
  343.     else
  344. #endif
  345.         RemoveIcon();
  346.     return (m_bHidden == TRUE);
  347. }
  348. BOOL CSystemTray::ShowIcon()
  349. {
  350.     if (m_bRemoved)
  351.         return AddIcon();
  352.     if (!m_bHidden)
  353.         return TRUE;
  354. #ifdef SYSTEMTRAY_USEW2K
  355.     if (m_bWin2K)
  356.     {
  357.         m_tnd.uFlags = NIF_STATE;
  358.         m_tnd.dwState = 0;
  359.         m_tnd.dwStateMask = NIS_HIDDEN;
  360.         Shell_NotifyIcon ( NIM_MODIFY, &m_tnd );
  361.         m_bHidden = FALSE;
  362.     }
  363.     else
  364. #endif
  365.         AddIcon();
  366.     return (m_bHidden == FALSE);
  367. }
  368. BOOL CSystemTray::SetIcon(HICON hIcon)
  369. {
  370.     if (!m_bEnabled)
  371.         return FALSE;
  372.     m_tnd.uFlags = NIF_ICON;
  373.     m_tnd.hIcon = hIcon;
  374.     if (m_bHidden)
  375.         return TRUE;
  376.     else
  377.         return Shell_NotifyIcon(NIM_MODIFY, &m_tnd);
  378. }
  379. BOOL CSystemTray::SetIcon(LPCTSTR lpszIconName)
  380. {
  381.     HICON hIcon = (HICON) ::LoadImage(AfxGetResourceHandle(), 
  382.                                       lpszIconName,
  383.                                       IMAGE_ICON, 
  384.                                       0, 0,
  385.                                       LR_DEFAULTCOLOR);
  386. if (!hIcon)
  387. return FALSE;
  388. BOOL bSuccess = SetIcon(hIcon);
  389. ::DestroyIcon(hIcon);
  390.     return bSuccess;
  391. }
  392. BOOL CSystemTray::SetIcon(UINT nIDResource)
  393. {
  394.     return SetIcon(MAKEINTRESOURCE(nIDResource));
  395. }
  396. BOOL CSystemTray::SetStandardIcon(LPCTSTR lpIconName)
  397. {
  398.     HICON hIcon = LoadIcon(NULL, lpIconName);
  399.     return SetIcon(hIcon);
  400. }
  401. BOOL CSystemTray::SetStandardIcon(UINT nIDResource)
  402. {
  403. return SetStandardIcon(MAKEINTRESOURCE(nIDResource));
  404. }
  405.  
  406. HICON CSystemTray::GetIcon() const
  407. {
  408.     return (m_bEnabled)? m_tnd.hIcon : NULL;
  409. }
  410. BOOL CSystemTray::SetIconList(UINT uFirstIconID, UINT uLastIconID) 
  411. {
  412. if (uFirstIconID > uLastIconID)
  413.         return FALSE;
  414. const CWinApp* pApp = AfxGetApp();
  415.     if (!pApp)
  416.     {
  417.         ASSERT(FALSE);
  418.         return FALSE;
  419.     }
  420.     m_IconList.RemoveAll();
  421.     TRY {
  422.     for (UINT i = uFirstIconID; i <= uLastIconID; i++)
  423.     m_IconList.Add(pApp->LoadIcon(i));
  424.     }
  425.     CATCH(CMemoryException, e)
  426.     {
  427.         e->ReportError();
  428.         e->Delete();
  429.         m_IconList.RemoveAll();
  430.         return FALSE;
  431.     }
  432.     END_CATCH
  433.     return TRUE;
  434. }
  435. BOOL CSystemTray::SetIconList(HICON* pHIconList, UINT nNumIcons)
  436. {
  437.     m_IconList.RemoveAll();
  438.     TRY {
  439.     for (UINT i = 0; i < nNumIcons; i++)
  440.     m_IconList.Add(pHIconList[i]);
  441.     }
  442.     CATCH (CMemoryException, e)
  443.     {
  444.         e->ReportError();
  445.         e->Delete();
  446.         m_IconList.RemoveAll();
  447.         return FALSE;
  448.     }
  449.     END_CATCH
  450.     return TRUE;
  451. }
  452. BOOL CSystemTray::Animate(UINT nDelayMilliSeconds, int nNumSeconds /*=-1*/)
  453. {
  454. if (m_IconList.GetSize() == 0)
  455. return FALSE;
  456.     StopAnimation();
  457.     m_nCurrentIcon = 0;
  458.     m_StartTime = COleDateTime::GetCurrentTime();
  459.     m_nAnimationPeriod = nNumSeconds;
  460.     m_hSavedIcon = GetIcon();
  461. // Setup a timer for the animation
  462. m_uIDTimer = SetTimer(m_nTimerID, nDelayMilliSeconds, NULL);
  463.     return (m_uIDTimer != 0);
  464. }
  465. BOOL CSystemTray::StepAnimation()
  466. {
  467.     if (!m_IconList.GetSize())
  468.         return FALSE;
  469.     m_nCurrentIcon++;
  470.     if (m_nCurrentIcon >= m_IconList.GetSize())
  471.         m_nCurrentIcon = 0;
  472.     return SetIcon(m_IconList[m_nCurrentIcon]);
  473. }
  474. BOOL CSystemTray::StopAnimation()
  475. {
  476.     BOOL bResult = FALSE;
  477.     if (m_uIDTimer)
  478.     bResult = KillTimer(m_uIDTimer);
  479.     m_uIDTimer = 0;
  480.     if (m_hSavedIcon)
  481.         SetIcon(m_hSavedIcon);
  482.     m_hSavedIcon = NULL;
  483.     return bResult;
  484. }
  485. /////////////////////////////////////////////////////////////////////////////
  486. // CSystemTray tooltip text manipulation
  487. BOOL CSystemTray::SetTooltipText(LPCTSTR pszTip)
  488. {
  489.     ASSERT(AfxIsValidString(pszTip)); // (md)
  490.     ASSERT(_tcslen(pszTip) < m_nMaxTooltipLength);
  491.     if (!m_bEnabled) 
  492.         return FALSE;
  493.     m_tnd.uFlags = NIF_TIP;
  494.     _tcsncpy(m_tnd.szTip, pszTip, m_nMaxTooltipLength-1);
  495.     if (m_bHidden)
  496.         return TRUE;
  497.     else
  498.         return Shell_NotifyIcon(NIM_MODIFY, &m_tnd);
  499. }
  500. BOOL CSystemTray::SetTooltipText(UINT nID)
  501. {
  502.     CString strText;
  503.     VERIFY(strText.LoadString(nID));
  504.     return SetTooltipText(strText);
  505. }
  506. CString CSystemTray::GetTooltipText() const
  507. {
  508.     CString strText;
  509.     if (m_bEnabled)
  510.         strText = m_tnd.szTip;
  511.     return strText;
  512. }
  513. /////////////////////////////////////////////////////////////////////////////
  514. // CSystemTray support for Win 2K features.
  515. //////////////////////////////////////////////////////////////////////////
  516. //
  517. // Function:    ShowBalloon
  518. //
  519. // Description:
  520. //  Shows a balloon tooltip over the tray icon.
  521. //
  522. // Input:
  523. //  szText: [in] Text for the balloon tooltip.
  524. //  szTitle: [in] Title for the balloon.  This text is shown in bold above
  525. //           the tooltip text (szText).  Pass "" if you don't want a title.
  526. //  dwIcon: [in] Specifies an icon to appear in the balloon.  Legal values are:
  527. //                 NIIF_NONE: No icon
  528. //                 NIIF_INFO: Information
  529. //                 NIIF_WARNING: Exclamation
  530. //                 NIIF_ERROR: Critical error (red circle with X)
  531. //  uTimeout: [in] Number of seconds for the balloon to remain visible.  Can
  532. //            be between 10 and 30 inclusive.
  533. //
  534. // Returns:
  535. //  TRUE if successful, FALSE if not.
  536. //
  537. //////////////////////////////////////////////////////////////////////////
  538. // Added by Michael Dunn, November 1999
  539. //////////////////////////////////////////////////////////////////////////
  540. BOOL CSystemTray::ShowBalloon(LPCTSTR szText,
  541.                               LPCTSTR szTitle  /*=NULL*/,
  542.                               DWORD   dwIcon   /*=NIIF_NONE*/,
  543.                               UINT    uTimeout /*=10*/ )
  544. {
  545. #ifndef SYSTEMTRAY_USEW2K
  546.     return FALSE;
  547. #else
  548.     // Bail out if we're not on Win 2K.
  549.     if (!m_bWin2K)
  550.         return FALSE;
  551.     // Verify input parameters.
  552.     // The balloon tooltip text can be up to 255 chars long.
  553.     ASSERT(AfxIsValidString(szText));
  554.     ASSERT(lstrlen(szText) < 256);
  555.     // The balloon title text can be up to 63 chars long.
  556.     if (szTitle)
  557.     {
  558.         ASSERT(AfxIsValidString( szTitle));
  559.         ASSERT(lstrlen(szTitle) < 64);
  560.     }
  561.     // dwBalloonIcon must be valid.
  562.     ASSERT(NIIF_NONE == dwIcon    || NIIF_INFO == dwIcon ||
  563.            NIIF_WARNING == dwIcon || NIIF_ERROR == dwIcon);
  564.     // The timeout must be between 10 and 30 seconds.
  565.     ASSERT(uTimeout >= 10 && uTimeout <= 30);
  566.     m_tnd.uFlags = NIF_INFO;
  567.     _tcsncpy(m_tnd.szInfo, szText, 256);
  568.     if (szTitle)
  569.         _tcsncpy(m_tnd.szInfoTitle, szTitle, 64);
  570.     else
  571.         m_tnd.szInfoTitle[0] = _T('');
  572.     m_tnd.dwInfoFlags = dwIcon;
  573.     m_tnd.uTimeout = uTimeout * 1000;   // convert time to ms
  574.     BOOL bSuccess = Shell_NotifyIcon (NIM_MODIFY, &m_tnd);
  575.     // Zero out the balloon text string so that later operations won't redisplay
  576.     // the balloon.
  577.     m_tnd.szInfo[0] = _T('');
  578.     return bSuccess;
  579. #endif
  580. }
  581. /////////////////////////////////////////////////////////////////////////////
  582. // CSystemTray notification window stuff
  583. BOOL CSystemTray::SetNotificationWnd(CWnd* pWnd)
  584. {
  585.     if (!m_bEnabled) 
  586.         return FALSE;
  587.     // Make sure Notification window is valid
  588.     if (!pWnd || !::IsWindow(pWnd->GetSafeHwnd()))
  589.     {
  590.         ASSERT(FALSE);
  591.         return FALSE;
  592.     }
  593.     m_tnd.hWnd = pWnd->GetSafeHwnd();
  594.     m_tnd.uFlags = 0;
  595.     if (m_bHidden)
  596.         return TRUE;
  597.     else
  598.         return Shell_NotifyIcon(NIM_MODIFY, &m_tnd);
  599. }
  600. CWnd* CSystemTray::GetNotificationWnd() const
  601. {
  602.     return CWnd::FromHandle(m_tnd.hWnd);
  603. }
  604. // Hatr added
  605. // Hatr added
  606. // Change or retrive the window to send menu commands to
  607. BOOL CSystemTray::SetTargetWnd(CWnd* pTargetWnd)
  608. {
  609.     m_pTargetWnd = pTargetWnd;
  610.     return TRUE;
  611. } // CSystemTray::SetTargetWnd()
  612. CWnd* CSystemTray::GetTargetWnd() const
  613. {
  614.     if (m_pTargetWnd)
  615.         return m_pTargetWnd;
  616.     else
  617.         return AfxGetMainWnd();
  618. } // CSystemTray::GetTargetWnd()
  619. /////////////////////////////////////////////////////////////////////////////
  620. // CSystemTray notification message stuff
  621. BOOL CSystemTray::SetCallbackMessage(UINT uCallbackMessage)
  622. {
  623.     if (!m_bEnabled)
  624.         return FALSE;
  625.     // Make sure we avoid conflict with other messages
  626.     ASSERT(uCallbackMessage >= WM_APP);
  627.     m_tnd.uCallbackMessage = uCallbackMessage;
  628.     m_tnd.uFlags = NIF_MESSAGE;
  629.     if (m_bHidden)
  630.         return TRUE;
  631.     else
  632.         return Shell_NotifyIcon(NIM_MODIFY, &m_tnd);
  633. }
  634. UINT CSystemTray::GetCallbackMessage() const
  635. {
  636.     return m_tnd.uCallbackMessage;
  637. }
  638. /////////////////////////////////////////////////////////////////////////////
  639. // CSystemTray menu manipulation
  640. BOOL CSystemTray::SetMenuDefaultItem(UINT uItem, BOOL bByPos)
  641. {
  642. #ifdef _WIN32_WCE
  643.     return FALSE;
  644. #else
  645.     if ((m_DefaultMenuItemID == uItem) && (m_DefaultMenuItemByPos == bByPos)) 
  646.         return TRUE;
  647.     m_DefaultMenuItemID = uItem;
  648.     m_DefaultMenuItemByPos = bByPos;   
  649.     CMenu menu, *pSubMenu;
  650.     if (!menu.LoadMenu(m_tnd.uID))
  651.         return FALSE;
  652.     pSubMenu = menu.GetSubMenu(0);
  653.     if (!pSubMenu)
  654.         return FALSE;
  655.     ::SetMenuDefaultItem(pSubMenu->m_hMenu, m_DefaultMenuItemID, m_DefaultMenuItemByPos);
  656.     return TRUE;
  657. #endif
  658. }
  659. void CSystemTray::GetMenuDefaultItem(UINT& uItem, BOOL& bByPos)
  660. {
  661.     uItem = m_DefaultMenuItemID;
  662.     bByPos = m_DefaultMenuItemByPos;
  663. }
  664. /////////////////////////////////////////////////////////////////////////////
  665. // CSystemTray message handlers
  666. BEGIN_MESSAGE_MAP(CSystemTray, CWnd)
  667. //{{AFX_MSG_MAP(CSystemTray)
  668. ON_WM_TIMER()
  669. //}}AFX_MSG_MAP
  670. #ifndef _WIN32_WCE
  671. ON_WM_SETTINGCHANGE()
  672. #endif
  673.     ON_REGISTERED_MESSAGE(CSystemTray::m_nTaskbarCreatedMsg, OnTaskbarCreated)
  674. END_MESSAGE_MAP()
  675. void CSystemTray::OnTimer(UINT nIDEvent) 
  676. {
  677.     if (nIDEvent != m_uIDTimer)
  678.     {
  679.         ASSERT(FALSE);
  680.         return;
  681.     }
  682.     COleDateTime CurrentTime = COleDateTime::GetCurrentTime();
  683.     COleDateTimeSpan period = CurrentTime - m_StartTime;
  684.     if (m_nAnimationPeriod > 0 && m_nAnimationPeriod < period.GetTotalSeconds())
  685.     {
  686.         StopAnimation();
  687.         return;
  688.     }
  689.     StepAnimation();
  690. }
  691. // This is called whenever the taskbar is created (eg after explorer crashes
  692. // and restarts. Please note that the WM_TASKBARCREATED message is only passed
  693. // to TOP LEVEL windows (like WM_QUERYNEWPALETTE)
  694. LRESULT CSystemTray::OnTaskbarCreated(WPARAM /*wParam*/, LPARAM /*lParam*/) 
  695. {
  696.     m_bShowIconPending = TRUE; // !m_bHidden;
  697.     InstallIconPending();
  698.     return 0L;
  699. }
  700. #ifndef _WIN32_WCE
  701. void CSystemTray::OnSettingChange(UINT uFlags, LPCTSTR lpszSection) 
  702. {
  703. CWnd::OnSettingChange(uFlags, lpszSection);
  704.     if (uFlags == SPI_SETWORKAREA)
  705.     {
  706.         m_bShowIconPending = !m_bHidden;
  707.         InstallIconPending();
  708.     }
  709. }
  710. #endif
  711. LRESULT CSystemTray::OnTrayNotification(UINT wParam, LONG lParam) 
  712. {
  713.     //Return quickly if its not for this tray icon
  714.     if (wParam != m_tnd.uID)
  715.         return 0L;
  716.     CMenu menu, *pSubMenu;
  717.     CWnd *pTargetWnd = GetTargetWnd();
  718.     if (!pTargetWnd)
  719.         return 0L;
  720.     // Clicking with right button brings up a context menu
  721. #if defined(_WIN32_WCE) //&& _WIN32_WCE < 211
  722.     BOOL bAltPressed = ((GetKeyState(VK_MENU) & (1 << (sizeof(SHORT)*8-1))) != 0);
  723.     if (LOWORD(lParam) == WM_LBUTTONUP && bAltPressed)
  724. #else
  725.     if (LOWORD(lParam) == WM_RBUTTONUP)
  726. #endif
  727.     {    
  728.         if (!menu.LoadMenu(m_tnd.uID))
  729.             return 0;
  730.         
  731.         pSubMenu = menu.GetSubMenu(0);
  732.         if (!pSubMenu)
  733.             return 0;
  734. #ifndef _WIN32_WCE
  735.         // Make chosen menu item the default (bold font)
  736.         ::SetMenuDefaultItem(pSubMenu->m_hMenu, m_DefaultMenuItemID, m_DefaultMenuItemByPos);
  737. #endif
  738. CustomizeMenu(pSubMenu);
  739.         // Display and track the popup menu
  740.         CPoint pos;
  741. #ifdef _WIN32_WCE
  742.         pos = CPoint(GetMessagePos());
  743. #else
  744.         GetCursorPos(&pos);
  745. #endif
  746.         pTargetWnd->SetForegroundWindow(); 
  747.         
  748. #ifndef _WIN32_WCE
  749.         ::TrackPopupMenu(pSubMenu->m_hMenu, 0, pos.x, pos.y, 0, 
  750.                          pTargetWnd->GetSafeHwnd(), NULL);
  751. #else
  752.         pSubMenu->TrackPopupMenu(TPM_LEFTALIGN, pos.x, pos.y, pTargetWnd, NULL);
  753. #endif
  754.         // BUGFIX: See "PRB: Menus for Notification Icons Don't Work Correctly"
  755.         pTargetWnd->PostMessage(WM_NULL, 0, 0);
  756.         menu.DestroyMenu();
  757.     } 
  758. #if defined(_WIN32_WCE) //&& _WIN32_WCE < 211
  759.     if (LOWORD(lParam) == WM_LBUTTONDBLCLK && bAltPressed)
  760. #else
  761.     else if (LOWORD(lParam) == WM_LBUTTONDBLCLK) 
  762. #endif
  763.     {
  764.         // double click received, the default action is to execute default menu item
  765.         pTargetWnd->SetForegroundWindow();  
  766.         UINT uItem;
  767.         if (m_DefaultMenuItemByPos)
  768.         {
  769.             if (!menu.LoadMenu(m_tnd.uID))
  770.                 return 0;
  771.             
  772.             pSubMenu = menu.GetSubMenu(0);
  773.             if (!pSubMenu)
  774.                 return 0;
  775.             
  776.             uItem = pSubMenu->GetMenuItemID(m_DefaultMenuItemID);
  777. menu.DestroyMenu();
  778.         }
  779.         else
  780.             uItem = m_DefaultMenuItemID;
  781.         
  782.         pTargetWnd->PostMessage(WM_COMMAND, uItem, 0);
  783.     }
  784.     return 1;
  785. }
  786. LRESULT CSystemTray::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  787. {
  788.     if (message == m_tnd.uCallbackMessage)
  789.         return OnTrayNotification(wParam, lParam);
  790. return CWnd::WindowProc(message, wParam, lParam);
  791. }
  792. void CSystemTray::InstallIconPending()
  793. {
  794.     // Is the icon display pending, and it's not been set as "hidden"?
  795.     if (!m_bShowIconPending || m_bHidden)
  796.         return;
  797. // Reset the flags to what was used at creation
  798. m_tnd.uFlags = m_uCreationFlags;
  799.     // Try and recreate the icon
  800.     m_bHidden = !Shell_NotifyIcon(NIM_ADD, &m_tnd);
  801.     // If it's STILL hidden, then have another go next time...
  802.     m_bShowIconPending = m_bHidden;
  803.     ASSERT(m_bHidden == FALSE);
  804. }
  805. /////////////////////////////////////////////////////////////////////////////
  806. // For minimising/maximising from system tray
  807. BOOL CALLBACK FindTrayWnd(HWND hwnd, LPARAM lParam)
  808. {
  809.     TCHAR szClassName[256];
  810.     GetClassName(hwnd, szClassName, 255);
  811.     // Did we find the Main System Tray? If so, then get its size and keep going
  812.     if (_tcscmp(szClassName, _T("TrayNotifyWnd")) == 0)
  813.     {
  814.         CRect *pRect = (CRect*) lParam;
  815.         ::GetWindowRect(hwnd, pRect);
  816.         return TRUE;
  817.     }
  818.   
  819.       // Did we find the System Clock? If so, then adjust the size of the rectangle
  820.     // we have and quit (clock will be found after the system tray)
  821.     if (_tcscmp(szClassName, _T("TrayClockWClass")) == 0)
  822.     {
  823.         CRect *pRect = (CRect*) lParam;
  824.         CRect rectClock;
  825.         ::GetWindowRect(hwnd, rectClock);
  826.         // if clock is above system tray adjust accordingly
  827.         if (rectClock.bottom < pRect->bottom-5) // 10 = random fudge factor.
  828.             pRect->top = rectClock.bottom;
  829.         else
  830.             pRect->right = rectClock.left;
  831.         return FALSE;
  832.     }
  833.  
  834.     return TRUE;
  835. }
  836.  
  837. #ifndef _WIN32_WCE
  838. // enhanced version by Matthew Ellis <m.t.ellis@bigfoot.com>
  839. void CSystemTray::GetTrayWndRect(LPRECT lprect)
  840. {
  841. #define DEFAULT_RECT_WIDTH 150
  842. #define DEFAULT_RECT_HEIGHT 30
  843.     HWND hShellTrayWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL);
  844.     if (hShellTrayWnd)
  845.     {
  846.         ::GetWindowRect(hShellTrayWnd, lprect);
  847.         EnumChildWindows(hShellTrayWnd, FindTrayWnd, (LPARAM)lprect);
  848.         return;
  849.     }
  850.     // OK, we failed to get the rect from the quick hack. Either explorer isn't
  851.     // running or it's a new version of the shell with the window class names
  852.     // changed (how dare Microsoft change these undocumented class names!) So, we
  853.     // try to find out what side of the screen the taskbar is connected to. We
  854.     // know that the system tray is either on the right or the bottom of the
  855.     // taskbar, so we can make a good guess at where to minimize to
  856.     APPBARDATA appBarData;
  857.     appBarData.cbSize=sizeof(appBarData);
  858.     if (SHAppBarMessage(ABM_GETTASKBARPOS,&appBarData))
  859.     {
  860.         // We know the edge the taskbar is connected to, so guess the rect of the
  861.         // system tray. Use various fudge factor to make it look good
  862.         switch(appBarData.uEdge)
  863.         {
  864.         case ABE_LEFT:
  865.         case ABE_RIGHT:
  866.             // We want to minimize to the bottom of the taskbar
  867.             lprect->top    = appBarData.rc.bottom-100;
  868.             lprect->bottom = appBarData.rc.bottom-16;
  869.             lprect->left   = appBarData.rc.left;
  870.             lprect->right  = appBarData.rc.right;
  871.             break;
  872.             
  873.         case ABE_TOP:
  874.         case ABE_BOTTOM:
  875.             // We want to minimize to the right of the taskbar
  876.             lprect->top    = appBarData.rc.top;
  877.             lprect->bottom = appBarData.rc.bottom;
  878.             lprect->left   = appBarData.rc.right-100;
  879.             lprect->right  = appBarData.rc.right-16;
  880.             break;
  881.         }
  882.         return;
  883.     }
  884.     
  885.     // Blimey, we really aren't in luck. It's possible that a third party shell
  886.     // is running instead of explorer. This shell might provide support for the
  887.     // system tray, by providing a Shell_TrayWnd window (which receives the
  888.     // messages for the icons) So, look for a Shell_TrayWnd window and work out
  889.     // the rect from that. Remember that explorer's taskbar is the Shell_TrayWnd,
  890.     // and stretches either the width or the height of the screen. We can't rely
  891.     // on the 3rd party shell's Shell_TrayWnd doing the same, in fact, we can't
  892.     // rely on it being any size. The best we can do is just blindly use the
  893.     // window rect, perhaps limiting the width and height to, say 150 square.
  894.     // Note that if the 3rd party shell supports the same configuraion as
  895.     // explorer (the icons hosted in NotifyTrayWnd, which is a child window of
  896.     // Shell_TrayWnd), we would already have caught it above
  897.     if (hShellTrayWnd)
  898.     {
  899.         ::GetWindowRect(hShellTrayWnd, lprect);
  900.         if (lprect->right - lprect->left > DEFAULT_RECT_WIDTH)
  901.             lprect->left = lprect->right - DEFAULT_RECT_WIDTH;
  902.         if (lprect->bottom - lprect->top > DEFAULT_RECT_HEIGHT)
  903.             lprect->top = lprect->bottom - DEFAULT_RECT_HEIGHT;
  904.         
  905.         return;
  906.     }
  907.     
  908.     // OK. Haven't found a thing. Provide a default rect based on the current work
  909.     // area
  910.     SystemParametersInfo(SPI_GETWORKAREA,0, lprect, 0);
  911.     lprect->left = lprect->right - DEFAULT_RECT_WIDTH;
  912.     lprect->top  = lprect->bottom - DEFAULT_RECT_HEIGHT;
  913. }
  914. // Check to see if the animation has been disabled (Matthew Ellis <m.t.ellis@bigfoot.com>)
  915. BOOL CSystemTray::GetDoWndAnimation()
  916. {
  917.   ANIMATIONINFO ai;
  918.   ai.cbSize=sizeof(ai);
  919.   SystemParametersInfo(SPI_GETANIMATION,sizeof(ai),&ai,0);
  920.   return ai.iMinAnimate?TRUE:FALSE;
  921. }
  922. #endif
  923. BOOL CSystemTray::RemoveTaskbarIcon(CWnd* pWnd)
  924. {
  925.     LPCTSTR pstrOwnerClass = AfxRegisterWndClass(0);
  926.     // Create static invisible window
  927.     if (!::IsWindow(m_wndInvisible.m_hWnd))
  928.     {
  929. if (!m_wndInvisible.CreateEx(0, pstrOwnerClass, _T(""), WS_POPUP,
  930. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  931. NULL, 0))
  932. return FALSE;
  933.     }
  934.     pWnd->SetParent(&m_wndInvisible);
  935.     return TRUE;
  936. }
  937. void CSystemTray::MinimiseToTray(CWnd* pWnd, BOOL bForceAnimation /*= FALSE*/)
  938. {
  939. #ifndef _WIN32_WCE
  940.     if (bForceAnimation || GetDoWndAnimation())
  941.     {
  942.         CRect rectFrom, rectTo;
  943.         pWnd->GetWindowRect(rectFrom);
  944.         GetTrayWndRect(rectTo);
  945.         ::DrawAnimatedRects(pWnd->m_hWnd, IDANI_CAPTION, rectFrom, rectTo);
  946.     }
  947.     RemoveTaskbarIcon(pWnd);
  948.     pWnd->ModifyStyle(WS_VISIBLE, 0);
  949. #endif
  950. }
  951. void CSystemTray::MaximiseFromTray(CWnd* pWnd, BOOL bForceAnimation /*= TRUE*/)
  952. {
  953. #ifndef _WIN32_WCE
  954.     if (bForceAnimation || GetDoWndAnimation())
  955.     {
  956.         CRect rectTo;
  957.         pWnd->GetWindowRect(rectTo);
  958.         CRect rectFrom;
  959.         GetTrayWndRect(rectFrom);
  960.         pWnd->SetParent(NULL);
  961.         ::DrawAnimatedRects(pWnd->m_hWnd, IDANI_CAPTION, rectFrom, rectTo);
  962.     }
  963.     else
  964.         pWnd->SetParent(NULL);
  965.     pWnd->ModifyStyle(0, WS_VISIBLE);
  966.     pWnd->RedrawWindow(NULL, NULL, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME |
  967.                                    RDW_INVALIDATE | RDW_ERASE);
  968.     // Move focus away and back again to ensure taskbar icon is recreated
  969.     if (::IsWindow(m_wndInvisible.m_hWnd))
  970.         m_wndInvisible.SetActiveWindow();
  971.     pWnd->SetActiveWindow();
  972.     pWnd->SetForegroundWindow();
  973. #endif
  974. }