#pragma hdrstop
#endif
-#if wxUSE_NOTIFICATION_MESSAGE && wxUSE_TASKBARICON
+// we can only use the native implementation if we have a working
+// wxTaskBarIcon::ShowBalloon() method
+#if wxUSE_NOTIFICATION_MESSAGE && \
+ wxUSE_TASKBARICON && wxUSE_TASKBARICON_BALLOONS
+
+#include "wx/notifmsg.h"
#ifndef WX_PRECOMP
+ #include "wx/toplevel.h"
+ #include "wx/app.h"
#include "wx/string.h"
#endif // WX_PRECOMP
-#include "wx/notifmsg.h"
#include "wx/generic/notifmsg.h"
#include "wx/taskbar.h"
virtual bool DoClose() = 0;
private:
- DECLARE_NO_COPY_CLASS(wxNotifMsgImpl)
+ wxDECLARE_NO_COPY_CLASS(wxNotifMsgImpl);
};
// implementation which is simply a bridge to wxGenericNotificationMessage
class wxBalloonNotifMsgImpl : public wxNotifMsgImpl
{
public:
- // ctor sets up m_icon (using the icon of the top level parent of the given
- // window) which can be used to show an attached balloon later by the
- // derived classes
+ // Ctor creates the associated taskbar icon (using the icon of the top
+ // level parent of the given window) unless UseTaskBarIcon() had been
+ // previously called which can be used to show an attached balloon later
+ // by the derived classes.
wxBalloonNotifMsgImpl(wxWindow *win) { SetUpIcon(win); }
// implementation of wxNotificationMessage method with the same name
int timeout,
int flags);
-protected:
- // sets up m_icon (doesn't do anything with the old value, caller beware)
- void SetUpIcon(wxWindow *win);
+ // Returns true if we're using our own icon or false if we're hitching a
+ // ride on the application icon provided to us via UseTaskBarIcon().
+ static bool IsUsingOwnIcon()
+ {
+ return ms_refCountIcon != -1;
+ }
+
+ // Indicates that the taskbar icon we're using has been hidden and can be
+ // deleted.
+ //
+ // This is only called by wxNotificationIconEvtHandler and should only be
+ // called when using our own icon (as opposed to the one passed to us via
+ // UseTaskBarIcon()).
+ static void ReleaseIcon()
+ {
+ wxASSERT_MSG( ms_refCountIcon != -1,
+ wxS("Must not be called when not using own icon") );
- static wxTaskBarIcon *ms_iconToUse;
+ if ( !--ms_refCountIcon )
+ {
+ delete ms_icon;
+ ms_icon = NULL;
+ }
+ }
+
+protected:
+ // Creates a new icon if necessary, see the comment below.
+ void SetUpIcon(wxWindow *win);
- // the icon we attach our notification to, either ms_iconToUse or a
- // temporary one which we will destroy when done
- wxTaskBarIcon *m_icon;
- // should be only used if m_icon != NULL and indicates whether we should
- // delete it
- bool m_ownsIcon;
+ // We need an icon to show the notification in a balloon attached to it.
+ // It may happen that the main application already shows an icon in the
+ // taskbar notification area in which case it should call our
+ // UseTaskBarIcon() and we just use this icon without ever allocating nor
+ // deleting it and ms_refCountIcon is -1 and never changes. Otherwise, we
+ // create the icon when we need it the first time but reuse it if we need
+ // to show subsequent notifications while this icon is still alive. This is
+ // needed in order to avoid 2 or 3 or even more identical icons if a couple
+ // of notifications are shown in a row (which happens quite easily in
+ // practice because Windows helpfully buffers all the notifications that
+ // were generated while the user was away -- i.e. the screensaver was
+ // active -- and then shows them all at once when the user comes back). In
+ // this case, ms_refCountIcon is used as a normal reference counter, i.e.
+ // the icon is only destroyed when it reaches 0.
+ static wxTaskBarIcon *ms_icon;
+ static int ms_refCountIcon;
};
// implementation for automatically hidden notifications
// can't close automatic notification [currently]
virtual bool DoClose() { return false; }
-
-private:
- // custom event handler connected to m_icon which will receive the icon
- // close event and delete it and itself when it happens
- wxEvtHandler * const m_iconEvtHandler;
};
// implementation for manually closed notifications
wxTaskBarIcon * const m_icon;
- DECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler)
+ wxDECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler);
};
// ============================================================================
void wxNotificationIconEvtHandler::OnIconHidden()
{
- delete m_icon;
+ wxBalloonNotifMsgImpl::ReleaseIcon();
delete this;
}
// wxBalloonNotifMsgImpl
// ----------------------------------------------------------------------------
-wxTaskBarIcon *wxBalloonNotifMsgImpl::ms_iconToUse = NULL;
+wxTaskBarIcon *wxBalloonNotifMsgImpl::ms_icon = NULL;
+int wxBalloonNotifMsgImpl::ms_refCountIcon = 0;
/* static */
wxTaskBarIcon *wxBalloonNotifMsgImpl::UseTaskBarIcon(wxTaskBarIcon *icon)
{
- wxTaskBarIcon * const iconOld = ms_iconToUse;
- ms_iconToUse = icon;
+ wxTaskBarIcon * const iconOld = ms_icon;
+ ms_icon = icon;
+
+ // Don't use reference counting for the provided icon, we don't own it.
+ ms_refCountIcon = icon ? -1 : 0;
+
return iconOld;
}
void wxBalloonNotifMsgImpl::SetUpIcon(wxWindow *win)
{
- if ( ms_iconToUse )
+ if ( ms_icon )
{
- // use an existing icon
- m_ownsIcon = false;
- m_icon = ms_iconToUse;
+ // Increment the reference count if we manage the icon on our own.
+ if ( ms_refCountIcon != -1 )
+ ms_refCountIcon++;
}
- else // no user-specified icon to attach to
+ else // Create a new icon.
{
- // create our own one
- m_ownsIcon = true;
- m_icon = new wxTaskBarIcon;
+ wxASSERT_MSG( ms_refCountIcon == 0,
+ wxS("Shouldn't reference not existent icon") );
+
+ ms_icon = new wxTaskBarIcon;
+ ms_refCountIcon = 1;
// use the icon of the associated (or main, if none) frame
wxIcon icon;
if ( !icon.IsOk() )
{
// we really must have some icon
- icon = wxIcon(_T("wxICON_AAA"));
+ icon = wxIcon(wxT("wxICON_AAA"));
}
- m_icon->SetIcon(icon);
+ ms_icon->SetIcon(icon);
}
}
int timeout,
int flags)
{
+ if ( !ms_icon->IsIconInstalled() )
+ {
+ // If we failed to install the icon (which does happen sometimes,
+ // although only in unusual circumstances, e.g. it happens regularly,
+ // albeit not constantly, if we're used soon after resume from suspend
+ // under Windows 7), we should not call ShowBalloon() because it would
+ // just assert and return and we must delete the icon ourselves because
+ // otherwise its associated wxTaskBarIconWindow would remain alive
+ // forever because we're not going to receive a notification about icon
+ // disappearance from the system if we failed to install it in the
+ // first place.
+ delete ms_icon;
+ ms_icon = NULL;
+
+ return false;
+ }
+
timeout *= 1000; // Windows expresses timeout in milliseconds
- return m_icon->ShowBalloon(title, message, timeout, flags);
+ return ms_icon->ShowBalloon(title, message, timeout, flags);
}
// ----------------------------------------------------------------------------
wxManualNotifMsgImpl::~wxManualNotifMsgImpl()
{
- if ( m_icon )
+ if ( ms_icon )
DoClose();
}
bool
wxManualNotifMsgImpl::DoShow(const wxString& title,
const wxString& message,
- int timeout,
+ int WXUNUSED_UNLESS_DEBUG(timeout),
int flags)
{
wxASSERT_MSG( timeout == wxNotificationMessage::Timeout_Never,
- _T("shouldn't be used") );
+ wxT("shouldn't be used") );
// base class creates the icon for us initially but we could have destroyed
// it in DoClose(), recreate it if this was the case
- if ( !m_icon )
+ if ( !ms_icon )
SetUpIcon(m_win);
// use maximal (in current Windows versions) timeout (but it will still
bool wxManualNotifMsgImpl::DoClose()
{
- if ( m_ownsIcon )
+ if ( IsUsingOwnIcon() )
{
// we don't need the icon any more
- delete m_icon;
+ ReleaseIcon();
}
else // using an existing icon
{
// just hide the balloon
- m_icon->ShowBalloon("", "");
+ ms_icon->ShowBalloon("", "");
}
- m_icon = NULL;
-
return true;
}
// ----------------------------------------------------------------------------
wxAutoNotifMsgImpl::wxAutoNotifMsgImpl(wxWindow *win)
- : wxBalloonNotifMsgImpl(win),
- m_iconEvtHandler(new wxNotificationIconEvtHandler(m_icon))
+ : wxBalloonNotifMsgImpl(win)
{
+ if ( ms_refCountIcon != -1 )
+ {
+ // This object will self-destruct and decrease the ref count of the
+ // icon when the notification is hidden.
+ new wxNotificationIconEvtHandler(ms_icon);
+ }
}
bool
int flags)
{
wxASSERT_MSG( timeout != wxNotificationMessage::Timeout_Never,
- _T("shouldn't be used") );
+ wxT("shouldn't be used") );
if ( timeout == wxNotificationMessage::Timeout_Auto )
{
// wxNotificationMessage
// ----------------------------------------------------------------------------
+/* static */
+bool wxNotificationMessage::ms_alwaysUseGeneric = false;
+
/* static */
wxTaskBarIcon *wxNotificationMessage::UseTaskBarIcon(wxTaskBarIcon *icon)
{
{
if ( !m_impl )
{
- if ( wxTheApp->GetShell32Version() >= 500 )
+ if ( !ms_alwaysUseGeneric && wxTheApp->GetShell32Version() >= 500 )
{
if ( timeout == Timeout_Never )
m_impl = new wxManualNotifMsgImpl(GetParent());