Click here to Skip to main content
15,884,976 members
Articles / Programming Languages / C++

Easy Animated Tray Icon

Rate me:
Please Sign up or sign in to vote.
4.64/5 (12 votes)
4 Feb 20023 min read 214.4K   5.8K   69  
This is a class (SS_TrayIcon) that allows for the easy addition of a tray icon into any project (whether you use MFC or not).
// ----------------------------------------------------------------------- //
//
//  FILENAME:	SS_TrayIcon.cpp
//  AUTHOR:		Steve Schaneville
//  CREATED:	22 Jan 2002, 09:23
//
//  PURPOSE:	Easily create an animated icon in the system tray.
//
//  Copyright (c) 2002
//
// ----------------------------------------------------------------------- //

// ------------------[       Pre-Include Defines       ]------------------ //
// ------------------[          Include Files          ]------------------ //
#include "SS_TrayIcon.h"
#include <list>
using namespace std;

// ------------------[      Macros/Constants/Types     ]------------------ //

// structure for a message map entry
typedef struct SSTiMessageMapEntry
{
    UINT            nMessage;
    SS_FnMessageMap pFn;
    
} SSTI_MESSAGE_MAP_ENTRY;

// create the list types for the message map
typedef list<SSTiMessageMapEntry> MessageMap;
typedef list<SSTiMessageMapEntry>::iterator MessageMapIterator;

// this is used as an nResourceID value.  It will be passed into the 
// SS_TrayIcon::LoadIcon() function when a blank icon is needed (for blinking).
#define IDI_TRAYICON_BLANK -1

// ------------------[         Global Variables        ]------------------ //

// the global message map
MessageMap g_MessageMap;

// aid in the pause for sending the WM_LBUTTONDOWN message.
WPARAM g_wParam = 0;
LPARAM g_lParam = 0;

// ------------------[         Global Functions        ]------------------ //

// look up the function pointer based on the message (support for the 
// message map)
BOOL FindMessageFunction(UINT nMessage, SS_FnMessageMap* pFn)
{
    BOOL bResult = FALSE;

    SS_Wnd* pWnd = NULL;
    MessageMapIterator it = g_MessageMap.begin();
    while( it != g_MessageMap.end() )
    {
        if( it->nMessage == nMessage )
        {
            *pFn = it->pFn;
            bResult = TRUE;
            break;
        }

        it++;
    }
    
    return bResult;
}

// ------------------[    Class Function Definitions   ]------------------ //

// ----------------------------------------------------------------------- //
//  Class:			SS_TrayIconWnd
//  Author:			Steve Schaneville
//  Notes:			This class is derived from the SS_Wnd class.  It is 
//                  here for the sole purpose of handling the Windows
//                  messages generated by the SS_TrayIcon.  The class is 
//                  used if and only if the user of the SS_TrayIcon class
//                  does not provide an hWnd in the SS_TrayIcon constructor.
// ----------------------------------------------------------------------- //

// standard constructor
SS_TrayIconWnd::SS_TrayIconWnd(HINSTANCE hInstance) :
    SS_Wnd(hInstance)
{} // we don't need to do anything here

// standard destructor
SS_TrayIconWnd::~SS_TrayIconWnd()
{} // nothing to destruct

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIconWnd::WindowProc()
//  Author:			Steve Schaneville
//  Parameters:		message [in] - The message that generated the WindowProc
//                                 call.  Compare the parameter to 
//                                 SSTI_CALLBACK_MESSAGE to see if the message
//                                 came from our SS_TrayIcon.
//                  wParam [in]  - The windows message ID (WM_LBUTTONDOWN, etc).
//                  lParam [in]  - Message dependant... 
//  Return Values:	Message dependant...
//  Notes:			First we check the message to see if it originated from 
//                  our tray icon and it is a WM_LBUTTONDOWN message.  If
//                  it is, we start a timer and exit.  The timer will wait
//                  until just after a double-click could occur.  If the
//                  user *does* double-click, the timer is cancelled before
//                  it is ever signalled and we process the WM_LBUTTONDBLCLK
//                  message.  If the user does *not* double-click, the timer
//                  will signal and the SS_TrayIconWnd::OnTimer function will 
//                  handle the original WM_LBUTTONDOWN message.
//
//                  If the message is anything other than a WM_LBUTTONDOWN
//                  message, we search the message map for a user defined
//                  function related to the message.  If we find one, we
//                  call the function passing in the wParam and lParam.
// ----------------------------------------------------------------------- //
LRESULT SS_TrayIconWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // If the icon was single clicked, start a timer so we don't actually
    // send the single-click message on until we are sure that the user
    // won't double-click.  If the icon was double-clicked, cancel the timer
    // so that the OnTimer function never has a chance to process our original
    // WM_LBUTTONDOWN message.
    switch( message )
    {
    case SSTI_CALLBACK_MESSAGE:
        {
            int i=0;
            switch( (UINT)lParam )
            {
            case WM_LBUTTONDOWN:
                {
                    g_wParam = wParam;
                    g_lParam = lParam;
                    SetTimer(SSTI_TIMER_DBLCLICK, GetDoubleClickTime()+25);
                    return 0;
                }
                break;

            case WM_LBUTTONDBLCLK:
                {
                    KillTimer(SSTI_TIMER_DBLCLICK);
                }
                break;

            default:
                ;
            }
        }
        break;
    
    default:
        ;
    }

    // check the message map to see if the user of the SS_TrayIcon class
    // has mapped the current message to a function.  If so, call that
    // function and exit.
    SS_FnMessageMap pFn = NULL;
    if( SSTI_CALLBACK_MESSAGE == message )
        if( FindMessageFunction((UINT)lParam, &pFn) )
            pFn(wParam, lParam);        

    // let the base class pass the message on to others who might need it.
    return SS_Wnd::WindowProc(message, wParam, lParam);
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIconWnd::OnTimer()
//  Author:			Steve Schaneville
//  Parameters:		uMsg [in]    -
//                  idEvent [in] - the timer ID, given by the user to the 
//                                 SetTimer function.
//                  dwTime [in]  -
//  Return Values:	none
//  Notes:			This function checks to see if the timer has the 
//                  SSTI_TIMER_DBLCLICK id.  If so, it is assumed that we
//                  need to process a WM_LBUTTONDOWN message that was 
//                  delayed because the user might double-click (see the 
//                  SS_TrayIconWnd::WindowProc function for more details).
//                  If the timer id is not SSTI_TIMER_DBLCLICK, let the
//                  SS_TrayIcon class handle the OnTimer event.
// ----------------------------------------------------------------------- //
VOID SS_TrayIconWnd::OnTimer(UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    if( SSTI_TIMER_DBLCLICK == idEvent )
    {
        KillTimer(SSTI_TIMER_DBLCLICK);

        // perform single-click operation
        SS_FnMessageMap pFn = NULL;
        if( FindMessageFunction(WM_LBUTTONDOWN, &pFn) )
            pFn(g_wParam, g_lParam);
    }   
    else
        TrayIcon()->OnTimer(idEvent);
}


// ----------------------------------------------------------------------- //
//  Class:			SS_TrayIcon
//  Author:			Steve Schaneville
//  Notes:			
// ----------------------------------------------------------------------- //

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::SS_TrayIcon()
//  Author:			Steve Schaneville
//  Parameters:		hInstance [in]       - The app's Instance.
//                  nMaxIconSets [in]    - How many sets of icons do you want?
//                                         A 'set' can really be thought of as
//                                         a single icon, or icon animation.
//                  nMaxIconsPerSet [in] - How many icons do you want in each
//                                         set?  You will only need 1 icon
//                                         in any set if the icon will only
//                                         blink or stand still.  If you want
//                                         to animate it, however, you will
//                                         want to specify as many icons in 
//                                         each set as you have "frames" in
//                                         your animation.
//                  hWnd [in]            - The handle to the window that will
//                                         receive the Windows messages generated
//                                         by the tray icon.  This greatly affects
//                                         how you will use the class.  If this
//                                         parameter is NULL (or simply not
//                                         included), the SS_TrayIcon class will
//                                         create its own window to handle the
//                                         messages, and you will have to use
//                                         the MapMessageToFunction(...) call
//                                         to respond to the tray icon's 
//                                         Windows messages.  If, on the other
//                                         hand, you *do* supply window handle,
//                                         all Windows messages generated by the
//                                         tray icon will be sent to that window's
//                                         WindowProc function.  You will then 
//                                         do a switch and look for the 
//                                         SSTI_CALLBACK_MESSAGE to respond
//                                         to the tray icon messages.
//  Return Values:	                       none
//  Notes:			                       Important information in the hWnd
//                                         parameter description.
// ----------------------------------------------------------------------- //
SS_TrayIcon::SS_TrayIcon(HINSTANCE hInstance, INT nMaxIconSets, INT nMaxIconsPerSet, HWND hWnd)
{
    ASSERT( nMaxIconSets > 0 );
    ASSERT( nMaxIconsPerSet > 0 );
 
    // we want to have a least 2 Icons per set so that we can blink 
    // them if requested, so increment this if it is only 1.
    if( 1 == nMaxIconsPerSet )
        nMaxIconsPerSet++;
    
    InitObject();
    MaxIconSets(nMaxIconSets);
    MaxIconsPerSet(nMaxIconsPerSet);
    Instance(hInstance);
    
    // create the icons.
    m_pIconSet = new SSTiIconSet[MaxIconSets()];
    for( INT i=0; i<MaxIconSets(); i++ )
    {
        IconSetNC()[i].pIcon = new SSTiIcon[MaxIconsPerSet()];
        for( INT j=0; j<MaxIconsPerSet(); j++ )
        {
            IconSetNC()[i].pIcon[j].hIcon = NULL;
            IconSetNC()[i].pIcon[j].nTime = SSTI_DEFAULT_BLINK_RATE;
        }
        IconSetNC()[i].nMode = SSTI_MODE_SHOWNORMAL;
    }
    
    // If the user did not supply an hWnd, we'll create a window.
    // (see the hWnd parameter description above in the function header)
    if( NULL == hWnd )
    {
        SSWnd( new SS_TrayIconWnd(Instance()) );
        SSWnd()->Create();
        SSWnd()->TrayIcon(this);

        hWnd = SSWnd()->WindowHandle();
        UsingSSWnd(TRUE);
    }
    else
        UsingSSWnd(FALSE);
    
    WindowHandle(hWnd);
}

// Standard Destructor
SS_TrayIcon::~SS_TrayIcon()
{
    // hide the icon
    HideIcon();

    // release the memory for the icons
    for( INT i=0; i<MaxIconSets(); i++ )
        delete [] IconSetNC()[i].pIcon;

    delete [] m_pIconSet;
    m_pIconSet = NULL;

    delete m_pSSWnd;
    m_pSSWnd = NULL;
}

// Object Initialization
VOID SS_TrayIcon::InitObject()
{
    m_pIconSet = NULL;
    m_bCurrentlyVisible = FALSE;
    MaxIconSets(0);
    MaxIconsPerSet(0);
    Instance(NULL);
    CurrentSet(0);
    BlankIcon(NULL);
    WindowHandle(NULL);
    UsingSSWnd(TRUE);
    SSWnd(NULL);
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::Show()
//  Author:			Steve Schaneville
//  Parameters:		nIconSet [in] - Which IconSet do you want to show?
//                  nMode [in]    - Select a mode from the SsTiMode enum.
//                                  Choosing SSTI_MODE_DEFAULT will cause
//                                  the IconSet to use its associated
//                                  display mode (set with the Mode() function).
//                                  Note that choosing anything other
//                                  than SSTI_MODE_DEFAULT will change
//                                  the default display mode for that 
//                                  particular IconSet.
//  Return Values:	none
//  Notes:			Though you can call this function with the SSTI_MODE_HIDE
//                  mode and it will properly hide the icon, this changes
//                  that IconSet's default display mode to "hide".  This
//                  is probably undesirable, so you might want to call
//                  HideIcon() instead, which has no effect on an IconSet's
//                  default display mode.
// ----------------------------------------------------------------------- //
INT SS_TrayIcon::ShowIcon(INT nIconSet, SsTiMode nMode)
{
    ASSERT( nIconSet < MaxIconSets() );

    // record the current set id
    if( -1 == nIconSet )
        nIconSet = CurrentSet();

    CurrentSet(nIconSet);
    ASSERT( IconSet(nIconSet).pIcon[IconSet(nIconSet).nCurrentIcon].hIcon != NULL );
    
    // set the proper mode
    if( SSTI_MODE_DEFAULT != nMode )
        Mode(nIconSet, nMode);

    // are we modifying, adding, or deleting the icon?
    DWORD dwUpdate = NIM_MODIFY;
    if( !CurrentlyVisible() )
        dwUpdate = NIM_ADD;
    if( SSTI_MODE_HIDE == IconSet(nIconSet).nMode )
        dwUpdate = NIM_DELETE;
    
    // do the deed
    NOTIFYICONDATA nif;
    nif.cbSize = sizeof(NOTIFYICONDATA);
    nif.hWnd = WindowHandle();
    nif.hIcon = IconSet(nIconSet).pIcon[IconSet(nIconSet).nCurrentIcon].hIcon;
    nif.uID = 1;
#if(_WIN32_IE>=0x0500)
        nif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
#else
        nif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
#endif
    _tcscpy(nif.szTip, IconSet(nIconSet).szToolTip);
    nif.uCallbackMessage = SSTI_CALLBACK_MESSAGE;
    
    INT nResult = Shell_NotifyIcon(dwUpdate, &nif);
    
    // set the currentvisible flag
    if( SSTI_MODE_HIDE == IconSet(nIconSet).nMode )
        CurrentlyVisible(FALSE);
    else
        CurrentlyVisible(TRUE);
    
    // now we need to set up a timer if the display mode is animate or blink
    switch( IconSet(nIconSet).nMode )
    {
    case SSTI_MODE_BLINK:
    case SSTI_MODE_ANIMATE:
        {
            ::SetTimer(WindowHandle(), SSTI_TIMER_ANIMATE, IconSet(nIconSet).pIcon[0].nTime, NULL);
        }
        break;
        
    case SSTI_MODE_SHOWNORMAL:
    case SSTI_MODE_HIDE:
    case SSTI_MODE_DEFAULT:
        break;
    }
    
    return nResult;
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::HideIcon()
//  Author:			Steve Schaneville
//  Parameters:		none
//  Return Values:	none
//  Notes:			Hide the currently displayed IconSet.  If no IconSet is
//                  currently visible, this function has no effect.
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::HideIcon()
{
    if( CurrentlyVisible() )
    {
        // we kill the animation time just in case it is running, as we
        // wouldn't want the icon to re-appear when the timer signals after
        // we've just hidden it.
        ::KillTimer(WindowHandle(), SSTI_TIMER_ANIMATE);

        // hide it!
        NOTIFYICONDATA nif;
        nif.cbSize = sizeof(NOTIFYICONDATA);
        nif.hWnd = WindowHandle();
        nif.uID = 1;
        nif.uFlags = NULL;
        
        INT nResult = Shell_NotifyIcon(NIM_DELETE, &nif);

        // now set the currentlyvisible flag
        CurrentlyVisible(FALSE);
    }
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::LoadIcon()
//  Author:			Steve Schaneville
//  Parameters:		nIconSet [in]     - Into which IconSet do you want to load
//                                      an icon?  (zero-based list)
//                  nIndex [in]       - Into which "frame" or "slot" in the
//                                      IconSet do you want to load the icon?
//                                      If your icon will not be animated
//                                      (i.e. it will blink or sit still), you
//                                      will only ever load an icon into index
//                                      '0'.  If you want to animate your
//                                      icon, you will need to load a different
//                                      icon into each frame of the IconSet.  
//                                      This nIndex represents the frame 
//                                      number.  (zero-based list)
//                  nResourceID [in]  - an icon Resource ID.
//                  nDisplayTime [in] - If you are animating your icon, each
//                                      frame of the animation can have a 
//                                      different display length.  This 
//                                      parameter determined the display length
//                                      in milliseconds for the frame being
//                                      loaded.
//  Return Values:	                    a handle to the newly loaded icon, or
//                                      NULL if no icon was loaded
//  Notes:			If you are creating an animated IconSet and you load icons
//                  into indexes 1, 2, 3, and 5, but you skip 4, this
//                  class will not be able to help you.  When the class
//                  gets to index 4 while animating the icons, the ShowIcon()
//                  function will cause an assertion.
// ----------------------------------------------------------------------- //
HICON SS_TrayIcon::LoadIcon(INT nIconSet, INT nIndex, UINT nResourceID, INT nDisplayTime)
{
    ASSERT( nIconSet < MaxIconSets() );
    ASSERT( nIndex < MaxIconsPerSet() );
    
    // load the icon
    HICON hIcon = NULL;
    if( -1 == nResourceID )
        hIcon = CreateBlankIcon();
    else
        hIcon = ::LoadIcon(Instance(), MAKEINTRESOURCE(nResourceID));
    
    // record the information in the IconSet
    IconSet(nIconSet).pIcon[nIndex].hIcon = hIcon;
    IconSet(nIconSet).pIcon[nIndex].nTime = nDisplayTime;
    
    // increase the nLastIcon value in the set if necessary
    if( nIndex > IconSet(nIconSet).nLastIcon )
        IconSetNC()[nIconSet].nLastIcon = nIndex;

    return hIcon;
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::ShowMenuAtMouse()
//  Author:			Steve Schaneville
//  Parameters:		nResourceID [in] - A menu resource ID.  Make sure that 
//                                     the menu resource has the "popup"
//                                     flag specified.
//                  hWnd [in]        - A handle to the window that will 
//                                     receive the Windows messages generated
//                                     by the popup menu.  The Windows messages
//                                     will be sent to that window's 
//                                     WindowProc function.
//  Return Values:	none
//  Notes:			
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::ShowMenuAtMouse(UINT nResourceID, HWND hWnd) const
{
    if( NULL == hWnd )
        hWnd = WindowHandle();

    HMENU hMenu;
    hMenu = ::LoadMenu(Instance(), MAKEINTRESOURCE(nResourceID));
    HMENU hPopup = ::GetSubMenu(hMenu, 0);
    ASSERT(hPopup != NULL);
    
    POINT point;
    GetCursorPos( &point );
    RECT rect;

    ::TrackPopupMenu(hPopup, TPM_LEFTALIGN|TPM_RIGHTBUTTON, 
                     point.x, point.y, 0, hWnd, 
                     &rect);
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::MapMessageToFunction()
//  Author:			Steve Schaneville
//  Parameters:		message [in] - The Windows message that you want to 
//                                 map. (ex. WM_LBUTTONDOWN, WM_MOUSEMOVE, etc.)
//                  pFn [in]     - A pointer to a user-defined callback function.
//                                 This function will be called whenever the
//                                 tray icon generates the message given in
//                                 the first parameter of this function.  The
//                                 callback function must be defined as follows:
//
//                                 LRESULT CALLBACK MyFunction(WPARAM wParam, LPARAM lParam)
//
//                                 The function *cannot* be a member function of
//                                 any class (as is the case will callback
//                                 functions in general).  If you need access
//                                 to class members, you will need to create
//                                 a global pointer to your instance of the 
//                                 class and access that pointer from the
//                                 callback function.
//  Return Values:	none
//  Notes:			
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::MapMessageToFunction(UINT message, 
                                       SS_FnMessageMap pFn )
{
    // We do the assert below because any maps created here will only
    // work if the user did not specified an hWnd in the constructor.
    // If the user *did* specified an hWnd in the constructor, he must
    // handle the message mapping himself in the window's WindowProc(...)
    // function.
    ASSERT(UsingSSWnd());

    // create an entry for the message map.
    SSTiMessageMapEntry mme;
    mme.nMessage = message;
    mme.pFn = pFn;

    g_MessageMap.push_back(mme);
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::Mode()
//  Author:			Steve Schaneville
//  Parameters:		nIconSet [in] - Which IconSet do you want to change the
//                                  mode to?
//                  nMode [in]    - What should the new display mode be?
//                                  Select a value from the SsTiMode enum.
//  Return Values:	none
//  Notes:			If you have an animated IconSet with more than one frame,
//                  loading icons into several frames, and then to set that
//                  IconSet's mode to SSTI_MODE_BLINK, the second frame will
//                  be overwritten.  You will need to re-load that frame
//                  manually if/when you set the icon's mode back to 
//                  SSTI_MODE_ANIMATE.
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::Mode(INT nIconSet, SsTiMode nMode)
{
    ASSERT( nIconSet < MaxIconSets() );
    IconSetNC()[nIconSet].nMode = nMode;

    // if we are in blink mode, make sure that the second icon 
    // in the set is IDI_TRAYICON_BLANK, and make it blink at the 
    // same rate as the first icon.
    if( SSTI_MODE_BLINK == nMode )
    {
        INT nBlinkRate = IconSetNC()[nIconSet].pIcon[0].nTime;
        LoadIcon(nIconSet, 1, IDI_TRAYICON_BLANK, nBlinkRate);
        IconSetNC()[nIconSet].nLastIcon = 1;
    }
}

// return the IconSet's current mode.
const SsTiMode SS_TrayIcon::Mode(INT nIconSet) const
{
    return IconSet(nIconSet).nMode;
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::SetAnimateSpeed()
//  Author:			Steve Schaneville
//  Parameters:		nIconSet [in]      - For which IconSet do you want to
//                                       set the speed?
//                  nMilliseconds [in] - This is the speed of every frame 
//                                       of an IconSet's animation.  This 
//                                       will make all frames the same length,
//                                       overwriting any values that were 
//                                       given in the LoadIcon function.
//  Return Values:	none
//  Notes:			This function affects both SSTI_MODE_ANIMATE and 
//                  SSTI_MODE_BLINK display modes.
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::SetAnimateSpeed(INT nIconSet, INT nMilliseconds)
{
    ASSERT( nIconSet < MaxIconSets() );
    
    for( INT i=0; i<MaxIconsPerSet(); i++ )
        IconSetNC()[nIconSet].pIcon[i].nTime = nMilliseconds;
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::IncrementIcon()
//  Author:			Steve Schaneville
//  Parameters:		nIconSet [in] - Which IconSet to you want to move to the
//                                  next frame?
//  Return Values:	none
//  Notes:			This function moves current IconSet's frame (or index)
//                  to the next one in line.  It "wraps", so if we pass the
//                  end, the index is set back to the first frame.
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::IncrementIcon(INT nIconSet)
{
    IconSetNC()[nIconSet].nCurrentIcon += 1;

    if( IconSet(nIconSet).nCurrentIcon > IconSet(nIconSet).nLastIcon || 
        IconSet(nIconSet).nCurrentIcon > MaxIconsPerSet() )
    {
        IconSetNC()[nIconSet].nCurrentIcon = 0;
    }
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::DecrementIcon()
//  Author:			Steve Schaneville
//  Parameters:		nIconSet [in] - Which IconSet to you want to move to the
//                                  previous frame?
//  Return Values:	none
//  Notes:			This function moves current IconSet's frame (or index)
//                  to the previous one in line.  It "wraps", so if we pass the
//                  beginning, the index is set to the last frame.
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::DecrementIcon(INT nIconSet)
{
    IconSetNC()[nIconSet].nCurrentIcon -= 1;
    
    if( IconSet(nIconSet).nCurrentIcon < 0 )
    {
        IconSetNC()[nIconSet].nCurrentIcon = IconSet(nIconSet).nLastIcon;
    }
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::OnTimer()
//  Author:			Steve Schaneville
//  Parameters:		idEvent [in] - Which timer id is signaling this call?
//  Return Values:	none
//  Notes:			This function does the animating of the currently 
//                  displayed icon.  If you provided your own hWnd in the 
//                  SS_TrayIcon constructor, make sure that you override
//                  your window's OnTimer function and call this OnTimer 
//                  function from within it if you want your icons to
//                  properly animate or blink.
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::OnTimer(UINT idEvent)
{
    switch( idEvent )
    {
    case SSTI_TIMER_ANIMATE:
        {
            ::KillTimer(WindowHandle(), SSTI_TIMER_ANIMATE);
            
            // show next icon
            IncrementIcon( CurrentSet() );
            ShowIcon();
        }
        break;
        
    default:
        ;
    }
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::ToolTip()
//  Author:			Steve Schaneville
//  Parameters:		nIconSet [in]  - For which IconSet do you want to set
//                                   the tooltip?
//                  szToolTip [in] - A null-terminated string that is your
//                                   tooltip text.  This can be 64 characters
//                                   on Win95, 98, ME, and NT4 systems, and 
//                                   it can be 256 characters on Win2000, 
//                                   WinXP and later systems.
//  Return Values:	none
//  Notes:			
// ----------------------------------------------------------------------- //
VOID SS_TrayIcon::ToolTip(INT nIconSet, TCHAR* szToolTip)
{
#if(_WIN32_IE>=0x0500)
    ASSERT( _tcslen(szToolTip) < 256 );
#else
    ASSERT( _tcslen(szToolTip) < 64 );
#endif

    _tcscpy( IconSetNC()[nIconSet].szToolTip, szToolTip );
    
    if( CurrentlyVisible() )
    {
        NOTIFYICONDATA nif;
        nif.cbSize = sizeof(NOTIFYICONDATA);
        nif.hWnd = WindowHandle();
        nif.uID = 1;
#if(_WIN32_IE>=0x0500)
        nif.uFlags = NIF_TIP | NIF_INFO;
#else
        nif.uFlags = NIF_TIP;
#endif
        _tcscpy(nif.szTip, szToolTip);
        
        INT nResult = Shell_NotifyIcon(NIM_MODIFY, &nif);
    }
}

// return the requested IconSet's tooltip.
const TCHAR* SS_TrayIcon::ToolTip(INT nIconSet) const
{
    return IconSet(nIconSet).szToolTip;
}

// ----------------------------------------------------------------------- //
//  Function:		SS_TrayIcon::CreateBlankIcon()
//  Author:			Steve Schaneville
//  Parameters:		none
//  Return Values:	A handle to the blank icon.
//  Notes:			This function creates a blank icon (i.e. one that is 
//                  completely clear).  It helps to create the appearance
//                  of a blinking icon (to make an icon blink, we continually
//                  swap back and forth between the regular and clear icons).
// ----------------------------------------------------------------------- //
HICON SS_TrayIcon::CreateBlankIcon()
{
    if( NULL == BlankIcon() )
    {
        BYTE ANDmaskIcon[] = {  0xFF, 0xFF, 0xFF, 0xFF,   // line 1 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 2 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 3 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 4 
            
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 5 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 6 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 7 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 8 
            
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 9 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 10 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 11 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 12 
            
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 13 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 14 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 15 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 16 
            
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 17 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 18 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 19 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 20 
            
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 21 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 22 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 23 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 24 
            
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 25 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 26 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 27 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 28 
            
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 29 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 30 
                                0xFF, 0xFF, 0xFF, 0xFF,   // line 31 
                                0xFF, 0xFF, 0xFF, 0xFF  };// line 32 
        
        BYTE XORmaskIcon[] = {  0x00, 0x00, 0x00, 0x00,   // line 1 
                                0x00, 0x00, 0x00, 0x00,   // line 2 
                                0x00, 0x00, 0x00, 0x00,   // line 3 
                                0x00, 0x00, 0x00, 0x00,   // line 4 
            
                                0x00, 0x00, 0x00, 0x00,   // line 5 
                                0x00, 0x00, 0x00, 0x00,   // line 6 
                                0x00, 0x00, 0x00, 0x00,   // line 7 
                                0x00, 0x00, 0x00, 0x00,   // line 8 
            
                                0x00, 0x00, 0x00, 0x00,   // line 9 
                                0x00, 0x00, 0x00, 0x00,   // line 10 
                                0x00, 0x00, 0x00, 0x00,   // line 11 
                                0x00, 0x00, 0x00, 0x00,   // line 12 
            
                                0x00, 0x00, 0x00, 0x00,   // line 13 
                                0x00, 0x00, 0x00, 0x00,   // line 14 
                                0x00, 0x00, 0x00, 0x00,   // line 15 
                                0x00, 0x00, 0x00, 0x00,   // line 16 
            
                                0x00, 0x00, 0x00, 0x00,   // line 17 
                                0x00, 0x00, 0x00, 0x00,   // line 18 
                                0x00, 0x00, 0x00, 0x00,   // line 19 
                                0x00, 0x00, 0x00, 0x00,   // line 20 
            
                                0x00, 0x00, 0x00, 0x00,   // line 21 
                                0x00, 0x00, 0x00, 0x00,   // line 22 
                                0x00, 0x00, 0x00, 0x00,   // line 23 
                                0x00, 0x00, 0x00, 0x00,   // line 24 
            
                                0x00, 0x00, 0x00, 0x00,   // line 25 
                                0x00, 0x00, 0x00, 0x00,   // line 26 
                                0x00, 0x00, 0x00, 0x00,   // line 27 
                                0x00, 0x00, 0x00, 0x00,   // line 28 
            
                                0x00, 0x00, 0x00, 0x00,   // line 29 
                                0x00, 0x00, 0x00, 0x00,   // line 30 
                                0x00, 0x00, 0x00, 0x00,   // line 31 
                                0x00, 0x00, 0x00, 0x00  };// line 32 
        
        
        HICON hIcon = CreateIcon(   Instance(),      // application instance  
                                    32,              // icon width 
                                    32,              // icon height 
                                    1,               // number of XOR planes 
                                    1,               // number of bits per pixel 
                                    ANDmaskIcon,     // AND bitmask  
                                    XORmaskIcon );   // XOR bitmask  

        BlankIcon(hIcon);
    }

    return BlankIcon();
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Amedisys
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions