SystemTray.cpp 31 KB

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