source: trunk/src/WTLx/NTray.cpp @ 493

Revision 493, 45.0 KB checked in by Eoin, 12 years ago (diff)

Freedom from automatic ATL and/or WTL namespaces.

Line 
1/*
2Module : NTray.CPP
3Purpose: implementation for a MFC class to encapsulate Shell_NotifyIcon
4Created: PJN / 14-05-1997
5History: PJN / 25-11-1997 Addition of the following
6                          1. HideIcon(), ShowIcon() & MoveToExtremeRight()
7                          2. Support for animated tray icons
8         PJN / 23-06-1998 Class now supports the new Taskbar Creation Notification
9                          message which comes with IE 4. This allows the tray icon
10                          to be recreated whenever the explorer restarts (Crashes!!)
11         PJN / 22-07-1998 1. Code now compiles cleanly at warning level 4
12                          2. Code is now UNICODE enabled + build configurations are provided
13                          3. The documentation for the class has been updated
14         PJN / 27-01-1999 1. Code first tries to load a 16*16 icon before loading the 32*32
15                          version. This removes the blurryness which was previously occuring
16         PJN / 28-01-1999 1. Fixed a number of level 4 warnings which were occurring.
17         PJN / 09-05-1999 1. Fixed a problem as documented in KB article "PRB: Menus for
18                          Notification Icons Do Not Work Correctly", Article ID: Q135788
19         PJN / 15-05-1999 1. Now uses the author's hookwnd class. This prevents the need to
20                          create the two hidden windows namely CTrayRessurectionWnd and
21                          CTrayTimerWnd
22                          2. Code now compiles cleanly at warning level 4
23                          3. General code tidy up and rearrangement
24                          4. Added numerous ASSERT's to improve the code's robustness
25                          5. Added functions to allow context menu to be customized
26         PJN / 01-01-2001 1. Now includes copyright message in the source code and documentation.
27                          2. Fixed problem where the window does not get focus after double clicking
28                          on the tray icon
29                          3. Now fully supports the Windows 2000 balloon style tooltips
30                          4. Fixed a off by one problem in some of the ASSERT's
31                          5. Fixed problems with Unicode build configurations
32                          6. Provided Win2k specific build configurations
33         PJN / 10-02-2001 1. Now fully supports creation of 2 tray icons at the same time
34         PJN / 13-06-2001 1. Now removes windows hook upon call to RemoveIcon
35         PJN / 26-08-2001 1. Fixed memory leak in RemoveIcon.
36                          2. Fixed GPF in RemoveIcon by removing call to Unhook
37         PJN / 28-08-2001 1. Added support for direct access to the System Tray's HDC. This allows
38                          you to generate an icon for the tray on the fly using GDI calls. The idea
39                          came from the article by Jeff Heaton in the April issue of WDJ. Also added
40                          are overriden Create methods to allow you to easily costruct a dynamic
41                          tray icon given a BITMAP instead of an ICON.
42         PJN / 21-03-2003 1. Fixed icon resource leaks in SetIcon(LPCTSTR lpIconName) and
43                          SetIcon(UINT nIDResource). Thanks to Egor Pervouninski for reporting this.
44                          2. Fixed unhooking of the tray icon when the notification window is being
45                          closed.
46         PJN / 31-03-2003 1. Now uses V1.05 of my Hookwnd class
47         PJN / 02-04-2003 1. Now uses v1.06 of my Hookwnd class
48                          2. Fixed a bug in the sample app for this class where the hooks should
49                          have been created as global instances rather than as member variables of
50                          the mainframe window. This ensures that the hooks remain valid even after
51                          calling DefWindowProc on the mainframe.
52         PJN / 23-07-2004 1. Minor update to remove unnecessary include of "resource.h"
53         PJN / 03-03-2006 1. Updated copyright details.
54                          2. Updated the documentation to use the same style as the web site.
55                          3. Did a spell check of the documentation.
56                          4. Fixed some issues when the code is compiled using /Wp64. Please note that
57                          to support this the code now requires a recentish Platform SDK to be installed
58                          if the code is compiled with Visual C++ 6.
59                          5. Replaced all calls to ZeroMemory with memset.
60                          6. Fixed an issue where SetBalloonDetails was not setting the cbSize parameter.
61                          Thanks to Enrique Granda for reporting this issue.
62                          7. Added support for NIIF_USER and NIIF_NONE flags.
63                          8. Now includes support for NIM_NIMSETVERSION via SetVersion. In addition this
64                          is now automatically set in the Create() calls if the Win2k boolean parameter
65                          is set.
66                          9. Removed derivation from CObject as it was not really needed.
67                          10. Now includes support for NIM_SETFOCUS
68                          11. Added support for NIS_HIDDEN via the ShowIcon and HideIcon methods.
69                          12. Added support for NIIF_NOSOUND
70         PJN / 27-06-2006 1. Code now uses new C++ style casts rather than old style C casts where necessary.
71                          2. The class framework now requires the Platform SDK if compiled using VC 6.
72                          3. Updated the logic of the ASSERTs which validate the various string lengths.
73                          4. Fixed a bug in CTrayNotifyIcon::SetFocus() where the cbSize value was not being
74                          set correctly.
75                          5. CTrayIconHooker class now uses ATL's CWindowImpl class in preference to the author's
76                          CHookWnd class. This does mean that for MFC only client projects, you will need to add
77                          ATL support to your project.
78                          6. Optimized CTrayIconHooker constructor code
79                          7. Updated code to compile cleanly using VC 2005. Thanks to "Itamar" for prompting this
80                          update.
81                          8. Addition of a CTRAYNOTIFYICON_EXT_CLASS and CTRAYNOTIFYICON_EXT_API macros which makes
82                          the class easier to use in an extension dll.
83                          9. Made CTrayNotifyIcon destructor virtual
84         PJN / 03-07-2005 1. Fixed a bug where the HideIcon functionality did not work on Windows 2000. This was
85                          related to how the cbSize member of the NOTIFYICONDATA structure was initialized. The code
86                          now dynamically determines the correct size to set at runtime according to the instructions
87                          provided by the MSDN documentation for this structure. As a result of this, all "bWin2k"
88                          parameters which were previously exposed via CTrayNotifyIcon have now been removed as there
89                          is no need for them. Thanks to Edwin Geng for reporting this important bug. Client code will
90                          still need to intelligently make decisions on what is supported by the OS. For example balloon
91                          tray icons are only supported on Shell v5 (nominally Windows 2000 or later). CTrayNotifyIcon
92                          will ASSERT if for example calls are made to it to create a balloon tray icon on operating
93                          systems < Windows 2000.
94         PJN / 04-07-2006 1. Fixed a bug where the menu may pop up a second time after a menu item is chosen on
95                          Windows 2000. The problem was tracked down to the code in CTrayNotifyIcon::OnTrayNotification.
96                          During testing of this bug, I was unable to get a workable solution using the new shell
97                          messages of WM_CONTEXTMENU, NIN_KEYSELECT & NIN_SELECT on Windows 2000 and Windows XP.
98                          This means that the code in CTrayNotifyIcon::OnTrayNotification uses the old way of handling
99                          notifications (WM_RBUTTDOWN*). This does mean that by default, client apps which use the
100                          CTrayNotifyIcon class will not support the new keyboard and mouse semantics for tray icons
101                          (IMHO this is no big loss!). Client code is of course free to handle their own notifications.
102                          If you go down this route then I would advise you to thoroughly test your application on
103                          Windows 2000 and Windows XP as my testing has shown that there is significant differences in
104                          how tray icons handle their messaging on these 2 operating systems. Thanks to Edwin Geng for
105                          reporting this issue.
106                          2. Class now displays the menu based on the current message's screen coordinates, rather than
107                          the current cursor screen coordinates.
108                          3. Fixed bug in sample app where if the about dialog is already up and it is reactivated
109                          from the tray menu, it did not bring itself into the foreground
110         PJN / 06-07-2006 1. Reverted the change made for v1.53 where the screen coordinates used to show the context
111                          menu use the current message's screen coordinates. Instead the pre v1.53 mechanism which
112                          uses the current cursor's screen coordinates is now used. Thanks to Itamar Syn-Hershko for
113                          reporting this issue.
114         PJN / 19-07-2006 1. The default menu item can now be customized via SetDefaultMenuItem and
115                          GetDefaultMenuItem. Thanks to Mikhail Bykanov for suggesting this nice update.
116                          2. Optimized CTrayNotifyIcon constructor code
117         PJN / 19-08-2005 1. Updated the code to operate independent of MFC if so desired. This requires WTL which is an
118                          open source library extension for ATL to provide UI support along the lines of MFC. Thanks to
119                          zhiguo zhao for providing this very nice addition.
120         PJN / 15-09-2006 1. Fixed a bug where WM_DESTROY messages were not been handled correctly for the top level window
121                          which the CTrayIconHooker class subclasses in order to handle the tray resurrection message,
122                          the animation timers and auto destroying of the icons when the top level window is destroyed.
123                          Thanks to Edward Livingston for reporting this bug.
124                          2. Fixed a bug where the tray icons were not being recreated correctly when we receive the
125                          "TaskbarCreated" when Explorer is restarted. Thanks to Nuno Esculcas for reporting this bug.
126                          3. Split the functionality of hiding versus deleting and showing versus creating of the tray
127                          icon into 4 separate functions, namely Delete(), Create(), Hide() and Show(). Note that Hide
128                          and Show functionality is only available on Shell v5 or later.
129                          4. Fixed an issue with recreation of tray icons which use a dynamic icon created from a bitmap
130                          (through the use of BitmapToIcon).
131                          5. CTrayNotifyIcon::LoadIconResource now loads up an icon as a shared icon resource using
132                          LoadImage. This should avoid resource leaks using this function.
133
134Copyright (c) 1997 - 2006 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com)
135
136All rights reserved.
137
138Copyright / Usage Details:
139
140You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
141when your product is released in binary form. You are allowed to modify the source code in any way you want
142except you cannot modify the copyright details at the top of each module. If you want to distribute source
143code with your application, then you are only allowed to distribute versions released by the author. This is
144to maintain a single distribution point for the source code.
145
146*/
147
148/////////////////////////////////  Includes  //////////////////////////////////
149
150#include "stdAfx.hpp"
151#include "NTray.hpp"
152#ifndef _INC_SHELLAPI
153#pragma message("To avoid this message, please put ShellApi.h in your PCH (normally stdafx.h)")
154#include <ShellApi.h>
155#endif
156
157
158/////////////////////////////////  Macros /////////////////////////////////////
159
160#ifdef _AFX
161#ifdef _DEBUG
162#define new DEBUG_NEW
163#endif
164#endif
165
166//Defines our own versions of various constants we use from ShellApi.h. This allows us to operate in a mode
167//where we do not depend on value set for _WIN32_IE
168#ifndef NIIF_USER
169#define NIIF_USER 0x00000004
170#endif
171
172#ifndef NIF_STATE
173#define NIF_STATE 0x00000008
174#endif
175
176#ifndef NIF_INFO
177#define NIF_INFO 0x00000010
178#endif
179
180#ifndef NIS_HIDDEN
181#define NIS_HIDDEN              0x00000001
182#endif
183
184#ifndef NOTIFYICON_VERSION
185#define NOTIFYICON_VERSION 3
186#endif
187
188#ifndef NIM_SETVERSION
189#define NIM_SETVERSION 0x00000004
190#endif
191
192#ifndef NIIF_NONE
193#define NIIF_NONE 0x00000000
194#endif
195
196#ifndef NIIF_INFO
197#define NIIF_INFO 0x00000001
198#endif
199
200#ifndef NIIF_WARNING
201#define NIIF_WARNING 0x00000002
202#endif
203
204#ifndef NIIF_ERROR
205#define NIIF_ERROR 0x00000003
206#endif
207
208#ifndef NIIF_USER
209#define NIIF_USER 0x00000004
210#endif
211
212#ifndef NIIF_NOSOUND
213#define NIIF_NOSOUND 0x00000010
214#endif
215
216#ifndef NIM_SETFOCUS
217#define NIM_SETFOCUS 0x00000003
218#endif
219
220DWORD CTrayNotifyIcon::sm_dwShellVersion = 0;
221
222
223///////////////////////////////// Implementation //////////////////////////////
224
225const UINT wm_TaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
226
227CTrayIconHooker::CTrayIconHooker() : m_pTrayIcon(NULL),
228                                     m_phIcons(NULL),
229                                     m_nNumIcons(0),
230                                     m_nTimerID(0),
231                                     m_nCurrentIconIndex(0)
232{
233}
234
235CTrayIconHooker::~CTrayIconHooker()
236{
237  StopUsingAnimation();
238}
239
240#ifdef _AFX
241BOOL CTrayIconHooker::Init(CTrayNotifyIcon* pTrayIcon, CWnd* pNotifyWnd)
242#else
243BOOL CTrayIconHooker::Init(CTrayNotifyIcon* pTrayIcon, CWindow* pNotifyWnd)
244#endif
245{
246  //Validate our parameters
247  ATLASSERT(pTrayIcon); //must have a valid tray notify instance
248#ifdef _AFX
249  ATLASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->GetSafeHwnd()));
250#else
251  ATLASSERT(pNotifyWnd && pNotifyWnd->IsWindow());
252#endif
253
254  //Hive away the input parameter
255  m_pTrayIcon = pTrayIcon;
256
257  //Hook the top level frame of the notify window in preference
258  //to the notify window itself. This will ensure that we get
259  //the taskbar created message
260#ifdef _AFX
261  CWnd* pTopLevelWnd = pNotifyWnd->GetTopLevelFrame();
262  if (pTopLevelWnd)
263    return SubclassWindow(pTopLevelWnd->operator HWND());
264  else
265    return SubclassWindow(pNotifyWnd->GetSafeHwnd());
266#else
267  CWindow TopLevelWnd = pNotifyWnd->GetTopLevelWindow();
268  if (TopLevelWnd.IsWindow())
269    return SubclassWindow(TopLevelWnd.operator HWND());
270  else
271    return SubclassWindow(pNotifyWnd->m_hWnd);
272#endif
273}
274
275void CTrayIconHooker::StartUsingAnimation(HICON* phIcons, int nNumIcons, DWORD dwDelay)
276{
277  //Validate our parameters
278  ATLASSERT(nNumIcons >= 2); //must be using at least 2 icons if you are using animation
279  ATLASSERT(phIcons);        //array of icon handles must be valid
280  ATLASSERT(dwDelay);        //must be non zero timer interval
281  ATLASSERT(m_pTrayIcon);
282
283  //Stop the animation if already started
284  StopUsingAnimation();
285
286  //Hive away all the values locally
287  ATLASSERT(m_phIcons == NULL);
288  m_phIcons = new HICON[nNumIcons];
289  for (int i=0; i<nNumIcons; i++)
290    m_phIcons[i] = phIcons[i];
291  m_nNumIcons = nNumIcons;
292
293  //Start up the timer
294  m_nTimerID = SetTimer(m_pTrayIcon->m_NotifyIconData.uID, dwDelay);
295}
296
297void CTrayIconHooker::StopUsingAnimation()
298{
299  //Kill the timer
300  if (m_nTimerID)
301  {
302    if (::IsWindow(m_hWnd))
303      KillTimer(m_nTimerID);
304    m_nTimerID = 0;
305  }
306
307  //Free up the memory
308  if (m_phIcons)
309  {
310    delete [] m_phIcons;
311    m_phIcons = NULL;
312  }
313
314  //Reset the other animation related variables
315  m_nCurrentIconIndex = 0;
316  m_nNumIcons = 0;
317}
318
319BOOL CTrayIconHooker::UsingAnimatedIcon() const
320{
321  return (m_nNumIcons != 0);
322}
323
324HICON CTrayIconHooker::GetCurrentIcon() const
325{
326  ATLASSERT(UsingAnimatedIcon());
327  ATLASSERT(m_phIcons);
328  return m_phIcons[m_nCurrentIconIndex];
329}
330
331BOOL CTrayIconHooker::ProcessWindowMessage(HWND /*hWnd*/, UINT nMsg, WPARAM wParam, LPARAM /*lParam*/, LRESULT& lResult, DWORD /*dwMsgMapID*/)
332{
333  //Validate our parameters
334  ATLASSERT(m_pTrayIcon);
335
336  lResult = 0;
337  BOOL bHandled = FALSE;
338
339  if (nMsg == wm_TaskbarCreated)
340  {
341    //Refresh the tray icon if necessary
342    m_pTrayIcon->Delete();
343    m_pTrayIcon->Create();
344  }
345  else if ((nMsg == WM_TIMER) && (wParam == m_pTrayIcon->m_NotifyIconData.uID))
346  {
347    OnTimer(m_pTrayIcon->m_NotifyIconData.uID);
348    bHandled = TRUE; //Do not allow this message to go any further because we have fully handled the message
349  }
350  else if (nMsg == WM_DESTROY)
351    m_pTrayIcon->Delete();
352
353  return bHandled;
354}
355
356#ifdef _DEBUG
357void CTrayIconHooker::OnTimer(UINT_PTR nIDEvent)
358#else
359void CTrayIconHooker::OnTimer(UINT_PTR /*nIDEvent*/)  //Just to avoid a compiler warning
360#endif                                                //when being built for release
361{
362  ATLASSERT(nIDEvent == m_nTimerID);
363
364  //increment the icon index
365  ++m_nCurrentIconIndex;
366  m_nCurrentIconIndex = m_nCurrentIconIndex % m_nNumIcons;
367
368  //update the tray icon
369  m_pTrayIcon->m_NotifyIconData.uFlags = NIF_ICON;
370  m_pTrayIcon->m_NotifyIconData.hIcon = m_phIcons[m_nCurrentIconIndex];
371  Shell_NotifyIcon(NIM_MODIFY, reinterpret_cast<PNOTIFYICONDATA>(&m_pTrayIcon->m_NotifyIconData));
372}
373
374
375CTrayNotifyIcon::CTrayNotifyIcon() : m_bCreated(FALSE),
376                                     m_bHidden(FALSE),
377                                     m_pNotificationWnd(NULL),
378                                     m_bDefaultMenuItemByPos(TRUE),
379                                     m_nDefaultMenuItem(0),
380                                     m_hDynamicIcon(NULL)
381{
382  memset(&m_NotifyIconData, 0, sizeof(m_NotifyIconData));
383  m_NotifyIconData.cbSize = GetNOTIFYICONDATASizeForOS();
384}
385
386CTrayNotifyIcon::~CTrayNotifyIcon()
387{
388  //Delete the tray icon
389  Delete();
390
391  //Free up any dynamic icon we may have
392  if (m_hDynamicIcon)
393  {
394    DestroyIcon(m_hDynamicIcon);
395    m_hDynamicIcon = NULL;
396  }
397}
398
399BOOL CTrayNotifyIcon::Delete()
400{
401  //What will be the return value from this function (assume the best)
402  BOOL bSuccess = TRUE;
403
404  if (m_bCreated)
405  {
406    m_NotifyIconData.uFlags = 0;
407    bSuccess = Shell_NotifyIcon(NIM_DELETE, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
408    m_bCreated = FALSE;
409  }
410  return bSuccess;
411}
412
413BOOL CTrayNotifyIcon::Create()
414{
415  m_NotifyIconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
416  BOOL bSuccess = Shell_NotifyIcon(NIM_ADD, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
417  if (bSuccess)
418    m_bCreated = TRUE;
419  return bSuccess;
420}
421
422BOOL CTrayNotifyIcon::Hide()
423{
424  //Validate our parameters
425  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
426  ATLASSERT(!m_bHidden); //Only makes sense to hide the icon if it is not already hidden
427
428  m_NotifyIconData.uFlags = NIF_STATE;
429  m_NotifyIconData.dwState = NIS_HIDDEN;
430  m_NotifyIconData.dwStateMask = NIS_HIDDEN;
431  BOOL bSuccess = Shell_NotifyIcon(NIM_MODIFY, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
432  if (bSuccess)
433    m_bHidden = TRUE;
434  return bSuccess;
435}
436
437BOOL CTrayNotifyIcon::Show()
438{
439  //Validate our parameters
440  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
441  ATLASSERT(m_bHidden); //Only makes sense to show the icon if it has been previously hidden
442
443  ATLASSERT(m_bCreated);
444  m_NotifyIconData.uFlags = NIF_STATE;
445  m_NotifyIconData.dwState = 0;
446  m_NotifyIconData.dwStateMask = NIS_HIDDEN;
447  BOOL bSuccess = Shell_NotifyIcon(NIM_MODIFY, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
448  if (bSuccess)
449    m_bHidden = FALSE;
450  return bSuccess;
451}
452
453void CTrayNotifyIcon::SetMenu(HMENU hMenu)
454{
455  //Validate our parameters
456  ATLASSERT(hMenu);
457
458  m_Menu.DestroyMenu();
459  m_Menu.Attach(hMenu);
460
461#ifdef _AFX
462  WTL::CMenu* pSubMenu = m_Menu.GetSubMenu(0);
463  ATLASSERT(pSubMenu); //Your menu resource has been designed incorrectly
464
465  //Make the specified menu item the default (bold font)
466  pSubMenu->SetDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
467#else
468  WTL::CMenuHandle subMenu = m_Menu.GetSubMenu(0);
469  ATLASSERT(subMenu.IsMenu()); //Your menu resource has been designed incorrectly
470
471  //Make the specified menu item the default (bold font)
472  subMenu.SetMenuDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
473#endif
474}
475
476WTL::CMenu& CTrayNotifyIcon::GetMenu()
477{
478  return m_Menu;
479}
480
481void CTrayNotifyIcon::SetDefaultMenuItem(UINT uItem, BOOL fByPos)
482{
483  m_nDefaultMenuItem = uItem;
484  m_bDefaultMenuItemByPos = fByPos;
485
486  //Also update in the live menu if it is present
487  if (m_Menu.operator HMENU())
488  {
489  #ifdef _AFX
490    WTL::CMenu* pSubMenu = m_Menu.GetSubMenu(0);
491    ATLASSERT(pSubMenu); //Your menu resource has been designed incorrectly
492
493    pSubMenu->SetDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
494  #else
495    WTL::CMenuHandle subMenu = m_Menu.GetSubMenu(0);
496    ATLASSERT(subMenu.IsMenu()); //Your menu resource has been designed incorrectly
497
498    subMenu.SetMenuDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
499  #endif
500  }
501}
502
503#ifdef _AFX
504BOOL CTrayNotifyIcon::Create(CWnd* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, HICON hIcon, UINT nNotifyMessage, UINT uMenuID)
505#else
506BOOL CTrayNotifyIcon::Create(ATL::CWindow* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, HICON hIcon, UINT nNotifyMessage, UINT uMenuID)
507#endif
508{
509  //Validate our parameters
510  ATLASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->operator HWND()));
511#ifdef _DEBUG
512  if (GetShellVersion() >= 5) //If on Shell v5 or higher, then use the larger size tooltip
513  {
514    NOTIFYICONDATA_2 dummy;
515    ATLASSERT(_tcslen(pszTooltipText) < sizeof(dummy.szTip)/sizeof(TCHAR));
516    DBG_UNREFERENCED_LOCAL_VARIABLE(dummy);
517  }
518  else
519  {
520    NOTIFYICONDATA_1 dummy;
521    ATLASSERT(_tcslen(pszTooltipText) < sizeof(dummy.szTip)/sizeof(TCHAR));
522    DBG_UNREFERENCED_LOCAL_VARIABLE(dummy);
523  }
524#endif
525  ATLASSERT(hIcon);
526  ATLASSERT(nNotifyMessage >= WM_USER); //Make sure we avoid conflict with other messages
527
528  //Load up the menu resource which is to be used as the context menu
529  if (!m_Menu.LoadMenu(uMenuID == 0 ? uID : uMenuID))
530  {
531    ATLASSERT(FALSE);
532    return FALSE;
533  }
534#ifdef _AFX
535  WTL::CMenu* pSubMenu = m_Menu.GetSubMenu(0);
536  if (!pSubMenu)
537  {
538    ATLASSERT(FALSE); //Your menu resource has been designed incorrectly
539    return FALSE;
540  }
541  //Make the specified menu item the default (bold font)
542  pSubMenu->SetDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
543#else
544  WTL::CMenuHandle subMenu = m_Menu.GetSubMenu(0);
545  if (!subMenu.IsMenu())
546  {
547    ATLASSERT(FALSE); //Your menu resource has been designed incorrectly
548    return FALSE;
549  }
550  subMenu.SetMenuDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
551#endif
552
553  //Install the hook
554  if (!m_HookWnd.Init(this, pNotifyWnd))
555    return FALSE;
556
557  //Call the Shell_NotifyIcon function
558  m_pNotificationWnd = pNotifyWnd;
559  m_NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
560  m_NotifyIconData.hWnd = pNotifyWnd->operator HWND();
561  m_NotifyIconData.uID = uID;
562  m_NotifyIconData.uCallbackMessage = nNotifyMessage;
563  m_NotifyIconData.hIcon = hIcon;
564#if (_MSC_VER >= 1400)
565  _tcscpy_s(m_NotifyIconData.szTip, sizeof(m_NotifyIconData.szTip)/sizeof(TCHAR), pszTooltipText);
566#else
567  _tcscpy(m_NotifyIconData.szTip, pszTooltipText);
568#endif
569  m_bCreated = Shell_NotifyIcon(NIM_ADD, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
570
571  //Turn on Shell v5 style behaviour if supported
572  if (GetShellVersion() >= 5)
573    SetVersion(NOTIFYICON_VERSION);
574
575  return m_bCreated;
576}
577
578BOOL CTrayNotifyIcon::SetVersion(UINT uVersion)
579{
580  //Validate our parameters
581  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
582
583  //Call the Shell_NotifyIcon function
584  m_NotifyIconData.uVersion = uVersion;
585  return Shell_NotifyIcon(NIM_SETVERSION, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
586}
587
588HICON CTrayNotifyIcon::BitmapToIcon(WTL::CBitmap* pBitmap)
589{
590  //Validate our parameters
591  ATLASSERT(pBitmap);
592
593  //Get the width and height of a small icon
594  int w = GetSystemMetrics(SM_CXSMICON);
595  int h = GetSystemMetrics(SM_CYSMICON);
596
597  //Create a 0 mask
598  int nMaskSize = h*(w/8);
599  unsigned char* pMask = new unsigned char[nMaskSize];
600  memset(pMask, 0, nMaskSize);
601
602  //Create a mask bitmap
603  WTL::CBitmap maskBitmap;
604#ifdef _AFX
605  BOOL bSuccess = maskBitmap.CreateBitmap(w, h, 1, 1, pMask);
606#else
607  maskBitmap.CreateBitmap(w, h, 1, 1, pMask);
608  BOOL bSuccess = !maskBitmap.IsNull();
609#endif
610
611  //Free up the heap memory now that we have created the mask bitmap
612  delete [] pMask;
613
614  //Handle the error
615  if (!bSuccess)
616    return NULL;
617
618  //Create an ICON base on the bitmap just created
619  ICONINFO iconInfo;
620  iconInfo.fIcon = TRUE;
621  iconInfo.xHotspot = 0;
622  iconInfo.yHotspot = 0;
623  iconInfo.hbmMask = maskBitmap;
624  iconInfo.hbmColor = *pBitmap;
625  return CreateIconIndirect(&iconInfo);
626}
627
628#ifdef _AFX
629BOOL CTrayNotifyIcon::Create(CWnd* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, CBitmap* pBitmap, UINT nNotifyMessage, UINT uMenuID)
630#else
631BOOL CTrayNotifyIcon::Create(ATL::CWindow* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, WTL::CBitmap* pBitmap, UINT nNotifyMessage, UINT uMenuID)
632#endif
633{
634  //Convert the bitmap to an ICON
635  if (m_hDynamicIcon)
636    DestroyIcon(m_hDynamicIcon);
637  m_hDynamicIcon = BitmapToIcon(pBitmap);
638
639  //Pass the buck to the other function to do the work
640  return Create(pNotifyWnd, uID, pszTooltipText, m_hDynamicIcon, nNotifyMessage, uMenuID);
641}
642
643#ifdef _AFX
644BOOL CTrayNotifyIcon::Create(CWnd* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, HICON* phIcons, int nNumIcons, DWORD dwDelay, UINT nNotifyMessage, UINT uMenuID)
645#else
646BOOL CTrayNotifyIcon::Create(ATL::CWindow* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, HICON* phIcons, int nNumIcons, DWORD dwDelay, UINT nNotifyMessage, UINT uMenuID)
647#endif
648{
649  //Validate our parameters
650  ATLASSERT(phIcons);
651  ATLASSERT(nNumIcons >= 2); //must be using at least 2 icons if you are using animation
652  ATLASSERT(dwDelay);
653
654  //let the normal Create function do its stuff
655  BOOL bSuccess = Create(pNotifyWnd, uID, pszTooltipText, phIcons[0], nNotifyMessage, uMenuID);
656
657  //tell the hook class to do the animation
658  m_HookWnd.StartUsingAnimation(phIcons, nNumIcons, dwDelay);
659
660  return bSuccess;
661}
662
663#ifdef _AFX
664BOOL CTrayNotifyIcon::Create(CWnd* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, LPCTSTR pszBalloonText, LPCTSTR pszBalloonCaption, UINT nTimeout, BalloonStyle style, HICON hIcon, UINT nNotifyMessage, UINT uMenuID, BOOL bNoSound)
665#else
666BOOL CTrayNotifyIcon::Create(ATL::CWindow* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, LPCTSTR pszBalloonText, LPCTSTR pszBalloonCaption, UINT nTimeout, BalloonStyle style, HICON hIcon, UINT nNotifyMessage, UINT uMenuID, BOOL bNoSound)
667#endif
668{
669  //Validate our parameters
670  ATLASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->operator HWND()));
671  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
672#ifdef _DEBUG
673  NOTIFYICONDATA_2 dummy;
674  DBG_UNREFERENCED_LOCAL_VARIABLE(dummy);
675  ATLASSERT(_tcslen(pszTooltipText) < sizeof(dummy.szTip)/sizeof(TCHAR));
676  ATLASSERT(_tcslen(pszBalloonText) < sizeof(dummy.szInfo)/sizeof(TCHAR));
677  ATLASSERT(_tcslen(pszBalloonCaption) < sizeof(dummy.szInfoTitle)/sizeof(TCHAR));
678  ATLASSERT(hIcon);
679  ATLASSERT(nNotifyMessage >= WM_USER); //Make sure we avoid conflict with other messages
680#endif
681
682  //Load up the menu resource which is to be used as the context menu
683  if (!m_Menu.LoadMenu(uMenuID == 0 ? uID : uMenuID))
684  {
685    ATLASSERT(FALSE);
686    return FALSE;
687  }
688#ifdef _AFX
689  WTL::CMenu* pSubMenu = m_Menu.GetSubMenu(0);
690  if (!pSubMenu)
691  {
692    ATLASSERT(FALSE); //Your menu resource has been designed incorrectly
693    return FALSE;
694  }
695  //Make the specified menu item the default (bold font)
696  pSubMenu->SetDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
697#else
698  WTL::CMenuHandle subMenu = m_Menu.GetSubMenu(0);
699  if (!subMenu.IsMenu())
700  {
701    ATLASSERT(FALSE); //Your menu resource has been designed incorrectly
702    return FALSE;
703  }
704  //Make the specified menu item the default (bold font)
705  subMenu.SetMenuDefaultItem(m_nDefaultMenuItem, m_bDefaultMenuItemByPos);
706#endif
707
708  //Install the hook
709  if (!m_HookWnd.Init(this, pNotifyWnd))
710    return FALSE;
711
712  //Call the Shell_NotifyIcon function
713  m_pNotificationWnd = pNotifyWnd;
714  m_NotifyIconData.hWnd = pNotifyWnd->operator HWND();
715  m_NotifyIconData.uID = uID;
716  m_NotifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
717  m_NotifyIconData.uCallbackMessage = nNotifyMessage;
718  m_NotifyIconData.hIcon = hIcon;
719#if (_MSC_VER >= 1400)
720  _tcscpy_s(m_NotifyIconData.szTip, sizeof(m_NotifyIconData.szTip)/sizeof(TCHAR), pszTooltipText);
721  _tcscpy_s(m_NotifyIconData.szInfo, sizeof(m_NotifyIconData.szInfo)/sizeof(TCHAR), pszBalloonText);
722  _tcscpy_s(m_NotifyIconData.szInfoTitle, sizeof(m_NotifyIconData.szInfoTitle)/sizeof(TCHAR), pszBalloonCaption);
723#else
724  _tcscpy(m_NotifyIconData.szTip, pszTooltipText);
725  _tcscpy(m_NotifyIconData.szInfo, pszBalloonText);
726  _tcscpy(m_NotifyIconData.szInfoTitle, pszBalloonCaption);
727#endif
728  m_NotifyIconData.uTimeout = nTimeout;
729  switch (style)
730  {
731    case Warning:
732    {
733      m_NotifyIconData.dwInfoFlags = NIIF_WARNING;
734      break;
735    }
736    case Error:
737    {
738      m_NotifyIconData.dwInfoFlags = NIIF_ERROR;
739      break;
740    }
741    case Info:
742    {
743      m_NotifyIconData.dwInfoFlags = NIIF_INFO;
744      break;
745    }
746    case None:
747    {
748      m_NotifyIconData.dwInfoFlags = NIIF_NONE;
749      break;
750    }
751    case User:
752    {
753      ATLASSERT(hIcon != NULL); //You forget to provide a user icon
754      m_NotifyIconData.dwInfoFlags = NIIF_USER;
755      break;
756    }
757    default:
758    {
759      ATLASSERT(FALSE);
760      break;
761    }
762  }
763  if (bNoSound)
764    m_NotifyIconData.dwInfoFlags |= NIIF_NOSOUND;
765
766  m_bCreated = Shell_NotifyIcon(NIM_ADD, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
767
768  //Turn on Shell v5 tray icon behaviour
769  SetVersion(NOTIFYICON_VERSION);
770
771  return m_bCreated;
772}
773
774#ifdef _AFX
775BOOL CTrayNotifyIcon::Create(CWnd* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, LPCTSTR pszBalloonText, LPCTSTR pszBalloonCaption, UINT nTimeout, BalloonStyle style, WTL::CBitmap* pBitmap, UINT nNotifyMessage, UINT uMenuID, BOOL bNoSound)
776#else
777BOOL CTrayNotifyIcon::Create(ATL::CWindow* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, LPCTSTR pszBalloonText, LPCTSTR pszBalloonCaption, UINT nTimeout, BalloonStyle style, WTL::CBitmap* pBitmap, UINT nNotifyMessage, UINT uMenuID, BOOL bNoSound)
778#endif
779{
780  //Convert the bitmap to an ICON
781  if (m_hDynamicIcon)
782    DestroyIcon(m_hDynamicIcon);
783  m_hDynamicIcon = BitmapToIcon(pBitmap);
784
785  //Pass the buck to the other function to do the work
786  return Create(pNotifyWnd, uID, pszTooltipText, pszBalloonText, pszBalloonCaption, nTimeout, style, m_hDynamicIcon, nNotifyMessage, uMenuID, bNoSound);
787}
788
789#ifdef _AFX
790BOOL CTrayNotifyIcon::Create(CWnd* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, LPCTSTR pszBalloonText, LPCTSTR pszBalloonCaption, UINT nTimeout, BalloonStyle style, HICON* phIcons, int nNumIcons, DWORD dwDelay, UINT nNotifyMessage, UINT uMenuID, BOOL bNoSound)
791#else
792BOOL CTrayNotifyIcon::Create(ATL::CWindow* pNotifyWnd, UINT uID, LPCTSTR pszTooltipText, LPCTSTR pszBalloonText, LPCTSTR pszBalloonCaption, UINT nTimeout, BalloonStyle style, HICON* phIcons, int nNumIcons, DWORD dwDelay, UINT nNotifyMessage, UINT uMenuID, BOOL bNoSound)
793#endif
794{
795  //Validate our parameters
796  ATLASSERT(phIcons);
797  ATLASSERT(nNumIcons >= 2); //must be using at least 2 icons if you are using animation
798  ATLASSERT(dwDelay);
799
800  //let the normal Create function do its stuff
801  BOOL bSuccess = Create(pNotifyWnd, uID, pszTooltipText, pszBalloonText, pszBalloonCaption, nTimeout, style, phIcons[0], nNotifyMessage, uMenuID, bNoSound);
802
803  //tell the hook class to do the animation
804  m_HookWnd.StartUsingAnimation(phIcons, nNumIcons, dwDelay);
805
806  return bSuccess;
807}
808
809BOOL CTrayNotifyIcon::SetBalloonDetails(LPCTSTR pszBalloonText, LPCTSTR pszBalloonCaption, BalloonStyle style, UINT nTimeout, HICON hUserIcon, BOOL bNoSound)
810{
811  if (!m_bCreated)
812    return FALSE;
813
814  //Validate our parameters
815  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
816#ifdef _DEBUG
817  NOTIFYICONDATA_2 dummy;
818  DBG_UNREFERENCED_LOCAL_VARIABLE(dummy);
819  ATLASSERT(_tcslen(pszBalloonText) < sizeof(dummy.szInfo)/sizeof(TCHAR));
820  ATLASSERT(_tcslen(pszBalloonCaption) < sizeof(dummy.szInfoTitle)/sizeof(TCHAR));
821#endif
822
823  //Call the Shell_NotifyIcon function
824  m_NotifyIconData.uFlags = NIF_INFO;
825#if (_MSC_VER >= 1400)
826  _tcscpy_s(m_NotifyIconData.szInfo, sizeof(m_NotifyIconData.szInfo)/sizeof(TCHAR), pszBalloonText);
827  _tcscpy_s(m_NotifyIconData.szInfoTitle, sizeof(m_NotifyIconData.szInfoTitle)/sizeof(TCHAR), pszBalloonCaption);
828#else
829  _tcscpy(m_NotifyIconData.szInfo, pszBalloonText);
830  _tcscpy(m_NotifyIconData.szInfoTitle, pszBalloonCaption);
831#endif
832  m_NotifyIconData.uTimeout = nTimeout;
833  switch (style)
834  {
835    case Warning:
836    {
837      m_NotifyIconData.dwInfoFlags = NIIF_WARNING;
838      break;
839    }
840    case Error:
841    {
842      m_NotifyIconData.dwInfoFlags = NIIF_ERROR;
843      break;
844    }
845    case Info:
846    {
847      m_NotifyIconData.dwInfoFlags = NIIF_INFO;
848      break;
849    }
850    case None:
851    {
852      m_NotifyIconData.dwInfoFlags = NIIF_NONE;
853      break;
854    }
855    case User:
856    {
857      ATLASSERT(hUserIcon != NULL); //You forget to provide a user icon
858      m_NotifyIconData.dwInfoFlags = NIIF_USER;
859      m_NotifyIconData.hIcon = hUserIcon;
860      break;
861    }
862    default:
863    {
864      ATLASSERT(FALSE);
865      break;
866    }
867  }
868  if (bNoSound)
869    m_NotifyIconData.dwInfoFlags |= NIIF_NOSOUND;
870
871  return Shell_NotifyIcon(NIM_MODIFY, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
872}
873
874CTrayNotifyIconString CTrayNotifyIcon::GetBalloonText() const
875{
876  //Validate our parameters
877  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
878
879  CTrayNotifyIconString sText;
880  if (m_bCreated)
881    sText = m_NotifyIconData.szInfo;
882
883  return sText;
884}
885
886CTrayNotifyIconString CTrayNotifyIcon::GetBalloonCaption() const
887{
888  //Validate our parameters
889  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
890
891  CTrayNotifyIconString sText;
892  if (m_bCreated)
893    sText = m_NotifyIconData.szInfoTitle;
894
895  return sText;
896}
897
898UINT CTrayNotifyIcon::GetBalloonTimeout() const
899{
900  //Validate our parameters
901  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or later
902
903  UINT nTimeout = 0;
904  if (m_bCreated)
905    nTimeout = m_NotifyIconData.uTimeout;
906
907  return nTimeout;
908}
909
910BOOL CTrayNotifyIcon::SetTooltipText(LPCTSTR pszTooltipText)
911{
912  if (!m_bCreated)
913    return FALSE;
914
915  if (GetShellVersion() >= 5) //Allow the larger size tooltip text if on Shell v5 or later
916  {
917  #ifdef _DEBUG
918    NOTIFYICONDATA_2 dummy;
919    DBG_UNREFERENCED_LOCAL_VARIABLE(dummy);
920    ATLASSERT(_tcslen(pszTooltipText) < sizeof(dummy.szTip)/sizeof(TCHAR));
921  #endif
922  }
923  else
924  {
925  #ifdef _DEBUG
926    NOTIFYICONDATA_1 dummy;
927    ATLASSERT(_tcslen(pszTooltipText) < sizeof(dummy.szTip)/sizeof(TCHAR));
928    DBG_UNREFERENCED_LOCAL_VARIABLE(dummy);
929  #endif
930  }
931
932  //Call the Shell_NotifyIcon function
933  m_NotifyIconData.uFlags = NIF_TIP;
934#if (_MSC_VER >= 1400)
935  _tcscpy_s(m_NotifyIconData.szTip, sizeof(m_NotifyIconData.szTip)/sizeof(TCHAR), pszTooltipText);
936#else
937  _tcscpy(m_NotifyIconData.szTip, pszTooltipText);
938#endif
939  return Shell_NotifyIcon(NIM_MODIFY, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
940}
941
942BOOL CTrayNotifyIcon::SetTooltipText(UINT nID)
943{
944  CTrayNotifyIconString sToolTipText;
945  sToolTipText.LoadString(nID);
946
947  //Let the other version of the function handle the rest
948  return SetTooltipText(sToolTipText);
949}
950
951BOOL CTrayNotifyIcon::SetIcon(WTL::CBitmap* pBitmap)
952{
953  //Convert the bitmap to an ICON
954  if (m_hDynamicIcon)
955    DestroyIcon(m_hDynamicIcon);
956  m_hDynamicIcon = BitmapToIcon(pBitmap);
957
958  //Pass the buck to the other function to do the work
959  return SetIcon(m_hDynamicIcon);
960}
961
962BOOL CTrayNotifyIcon::SetIcon(HICON hIcon)
963{
964  //Validate our parameters
965  ATLASSERT(hIcon);
966
967  if (!m_bCreated)
968    return FALSE;
969
970  //Since we are going to use one icon, stop any animation
971  m_HookWnd.StopUsingAnimation();
972
973  //Call the Shell_NotifyIcon function
974  m_NotifyIconData.uFlags = NIF_ICON;
975  m_NotifyIconData.hIcon = hIcon;
976  return Shell_NotifyIcon(NIM_MODIFY, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
977}
978
979BOOL CTrayNotifyIcon::SetIcon(LPCTSTR lpIconName)
980{
981  return SetIcon(LoadIconResource(lpIconName));
982}
983
984BOOL CTrayNotifyIcon::SetIcon(UINT nIDResource)
985{
986  return SetIcon(LoadIconResource(nIDResource));
987}
988
989BOOL CTrayNotifyIcon::SetStandardIcon(LPCTSTR lpIconName)
990{
991  return SetIcon(::LoadIcon(NULL, lpIconName));
992}
993
994BOOL CTrayNotifyIcon::SetStandardIcon(UINT nIDResource)
995{
996  return SetIcon(::LoadIcon(NULL, MAKEINTRESOURCE(nIDResource)));
997}
998
999BOOL CTrayNotifyIcon::SetIcon(HICON* phIcons, int nNumIcons, DWORD dwDelay)
1000{
1001  //Validate our parameters
1002  ATLASSERT(nNumIcons >= 2); //must be using at least 2 icons if you are using animation
1003  ATLASSERT(phIcons);
1004  ATLASSERT(dwDelay);
1005
1006  if (!SetIcon(phIcons[0]))
1007    return FALSE;
1008
1009  //Install the hook
1010  m_HookWnd.StartUsingAnimation(phIcons, nNumIcons, dwDelay);
1011
1012  return TRUE;
1013}
1014
1015HICON CTrayNotifyIcon::LoadIconResource(LPCTSTR lpIconName)
1016{
1017  //First try to load a small icon using LoadImage, if this fails, they fall back on the using LoadIcon which will just load the standard size
1018#ifdef _AFX
1019  HICON hIcon = static_cast<HICON>(::LoadImage(AfxGetResourceHandle(), lpIconName, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED));
1020#else
1021  HICON hIcon = static_cast<HICON>(::LoadImage(_Module.GetResourceInstance(), lpIconName, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED));
1022#endif
1023  if (hIcon == NULL)
1024  {
1025  #ifdef _AFX
1026    hIcon = AfxGetApp()->LoadIcon(lpIconName);
1027  #else
1028    hIcon = ::LoadIcon(_Module.GetResourceInstance(), lpIconName);
1029  #endif
1030  }
1031
1032  //Return the icon handle
1033  return hIcon;
1034}
1035
1036HICON CTrayNotifyIcon::LoadIconResource(UINT nIDResource)
1037{
1038  return LoadIconResource(MAKEINTRESOURCE(nIDResource));
1039}
1040
1041#ifdef _AFX
1042BOOL CTrayNotifyIcon::SetNotificationWnd(CWnd* pNotifyWnd)
1043#else
1044BOOL CTrayNotifyIcon::SetNotificationWnd(ATL::CWindow* pNotifyWnd)
1045#endif
1046{
1047  //Validate our parameters
1048  ATLASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->operator HWND()));
1049
1050  if (!m_bCreated)
1051    return FALSE;
1052
1053  //Call the Shell_NotifyIcon function
1054  m_pNotificationWnd = pNotifyWnd;
1055  m_NotifyIconData.hWnd = pNotifyWnd->operator HWND();
1056  m_NotifyIconData.uFlags = 0;
1057  return Shell_NotifyIcon(NIM_MODIFY, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
1058}
1059
1060CTrayNotifyIconString CTrayNotifyIcon::GetTooltipText() const
1061{
1062  CTrayNotifyIconString sText;
1063  if (m_bCreated)
1064    sText = m_NotifyIconData.szTip;
1065
1066  return sText;
1067}
1068
1069HICON CTrayNotifyIcon::GetIcon() const
1070{
1071  HICON hIcon = NULL;
1072  if (m_bCreated)
1073  {
1074    if (UsingAnimatedIcon())
1075      hIcon = m_HookWnd.GetCurrentIcon();
1076    else
1077      hIcon = m_NotifyIconData.hIcon;
1078  }
1079
1080  return hIcon;
1081}
1082
1083BOOL CTrayNotifyIcon::UsingAnimatedIcon() const
1084{
1085  return m_HookWnd.UsingAnimatedIcon();
1086}
1087
1088#ifdef _AFX
1089CWnd* CTrayNotifyIcon::GetNotificationWnd() const
1090#else
1091ATL::CWindow* CTrayNotifyIcon::GetNotificationWnd() const
1092#endif
1093{
1094  return m_pNotificationWnd;
1095}
1096
1097BOOL CTrayNotifyIcon::SetFocus()
1098{
1099  ATLASSERT(GetShellVersion() >= 5); //Only supported on Shell v5 or greater
1100
1101  //Call the Shell_NotifyIcon function
1102  return Shell_NotifyIcon(NIM_SETFOCUS, reinterpret_cast<PNOTIFYICONDATA>(&m_NotifyIconData));
1103}
1104
1105LRESULT CTrayNotifyIcon::OnTrayNotification(WPARAM wID, LPARAM lEvent)
1106{
1107  //Return quickly if its not for this tray icon
1108  if (wID != m_NotifyIconData.uID)
1109    return 0L;
1110
1111#ifdef _AFX
1112  CMenu* pSubMenu = m_Menu.GetSubMenu(0);
1113  ATLASSERT(pSubMenu); //Your menu resource has been designed incorrectly
1114#else
1115  WTL::CMenuHandle subMenu = m_Menu.GetSubMenu(0);
1116  ATLASSERT(subMenu.IsMenu());
1117#endif
1118
1119  if (lEvent == WM_RBUTTONUP)
1120  {
1121    WTL::CPoint ptCursor;
1122    GetCursorPos(&ptCursor);
1123    ::SetForegroundWindow(m_NotifyIconData.hWnd);
1124  #ifdef _AFX
1125    ::TrackPopupMenu(pSubMenu->m_hMenu, TPM_LEFTBUTTON, ptCursor.x, ptCursor.y, 0, m_NotifyIconData.hWnd, NULL);
1126  #else
1127    ::TrackPopupMenu(subMenu, TPM_LEFTBUTTON, ptCursor.x, ptCursor.y, 0, m_NotifyIconData.hWnd, NULL);
1128  #endif
1129    ::PostMessage(m_NotifyIconData.hWnd, WM_NULL, 0, 0);
1130  }
1131  else if (lEvent == WM_LBUTTONDBLCLK) //double click received, the default action is to execute first menu item
1132  {
1133    ::SetForegroundWindow(m_NotifyIconData.hWnd);
1134  #ifdef _AFX
1135    UINT nDefaultItem = pSubMenu->GetDefaultItem(GMDI_GOINTOPOPUPS, FALSE);
1136  #else
1137    UINT nDefaultItem = subMenu.GetMenuDefaultItem(FALSE, GMDI_GOINTOPOPUPS);
1138  #endif
1139    if (nDefaultItem != -1)
1140      ::SendMessage(m_NotifyIconData.hWnd, WM_COMMAND, nDefaultItem, 0);
1141  }
1142
1143  return 1; // handled
1144}
1145
1146BOOL CTrayNotifyIcon::GetDynamicDCAndBitmap(WTL::CDC* pDC, WTL::CBitmap* pBitmap)
1147{
1148  //Validate our parameters
1149  ATLASSERT(pDC != NULL);
1150  ATLASSERT(pBitmap != NULL);
1151
1152  //Get the HWND for the desktop
1153#ifdef _AFX
1154  CWnd* pWndScreen = CWnd::GetDesktopWindow();
1155  if (pWndScreen == NULL)
1156    return FALSE;
1157#else
1158  ATL::CWindow WndScreen(::GetDesktopWindow());
1159  if (!WndScreen.IsWindow())
1160    return FALSE;
1161#endif
1162
1163  //Get the desktop HDC to create a compatible bitmap from
1164#ifdef _AFX
1165  CDC* pDCScreen = pWndScreen->GetDC();
1166  if (pDCScreen == NULL)
1167    return FALSE;
1168#else
1169  WTL::CDC DCScreen(WndScreen.GetDC());
1170  if (DCScreen.IsNull())
1171    return FALSE;
1172#endif
1173
1174  //Get the width and height of a small icon
1175  int w = GetSystemMetrics(SM_CXSMICON);
1176  int h = GetSystemMetrics(SM_CYSMICON);
1177
1178  //Create an off-screen bitmap that the dynamic tray icon
1179  //can be drawn into. (Compatible with the desktop DC).
1180#ifdef _AFX
1181  BOOL bSuccess = pBitmap->CreateCompatibleBitmap(pDCScreen, w, h);
1182#else
1183  BOOL bSuccess = (pBitmap->CreateCompatibleBitmap(DCScreen.operator HDC(), w, h) != NULL);
1184#endif
1185  if (!bSuccess)
1186  {
1187  #ifdef _AFX
1188    pWndScreen->ReleaseDC(pDCScreen);
1189  #else
1190    WndScreen.ReleaseDC(DCScreen);
1191  #endif
1192    return FALSE;
1193  }
1194
1195  //Get a HDC to the newly created off-screen bitmap
1196#ifdef _AFX
1197  bSuccess = pDC->CreateCompatibleDC(pDCScreen);
1198#else
1199  bSuccess = (pDC->CreateCompatibleDC(DCScreen.operator HDC()) != NULL);
1200#endif
1201  if (!bSuccess)
1202  {
1203  //Release the Screen DC now that we are finished with it
1204  #ifdef _AFX
1205    pWndScreen->ReleaseDC(pDCScreen);
1206  #else
1207    WndScreen.ReleaseDC(DCScreen);
1208  #endif
1209
1210    //Free up the bitmap now that we are finished with it
1211    pBitmap->DeleteObject();
1212
1213    return FALSE;
1214  }
1215
1216  //Select the bitmap into the offscreen DC
1217#ifdef _AFX
1218  pDC->SelectObject(pBitmap);
1219#else
1220  pDC->SelectBitmap(pBitmap->operator HBITMAP());
1221#endif
1222
1223  //Release the Screen DC now that we are finished with it
1224#ifdef _AFX
1225  pWndScreen->ReleaseDC(pDCScreen);
1226#else
1227  WndScreen.ReleaseDC(DCScreen);
1228#endif
1229
1230  return TRUE;
1231}
1232
1233DWORD CTrayNotifyIcon::GetShellVersion()
1234{
1235  if (sm_dwShellVersion)
1236    return sm_dwShellVersion;
1237  else
1238  {
1239    typedef HRESULT (CALLBACK DLLGETVERSION)(DLLVERSIONINFO*);
1240    typedef DLLGETVERSION* LPDLLGETVERSION;
1241
1242    //What will be the return value
1243    sm_dwShellVersion = 4; //Assume version 4 of the shell
1244
1245    //Try to get the details with DllGetVersion
1246    HMODULE hShell32 = GetModuleHandle(_T("shell32.dll"));
1247    LPDLLGETVERSION lpfnDllGetVersion = reinterpret_cast<LPDLLGETVERSION>(GetProcAddress(hShell32, "DllGetVersion"));
1248    if (lpfnDllGetVersion)
1249    {
1250      DLLVERSIONINFO vinfo;
1251      vinfo.cbSize = sizeof(DLLVERSIONINFO);
1252      if (SUCCEEDED(lpfnDllGetVersion(&vinfo)))
1253        sm_dwShellVersion = vinfo.dwMajorVersion;
1254    }
1255  }
1256
1257  return sm_dwShellVersion;
1258}
1259
1260DWORD CTrayNotifyIcon::GetNOTIFYICONDATASizeForOS()
1261{
1262  DWORD dwVersion = GetShellVersion();
1263  if (dwVersion >= 6)
1264    return sizeof(NOTIFYICONDATA_3);
1265  else if (dwVersion >= 5)
1266    return sizeof(NOTIFYICONDATA_2);
1267  else
1268    return sizeof(NOTIFYICONDATA_1);
1269}
Note: See TracBrowser for help on using the repository browser.