SystemTray.cpp 33 KB

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