From 3b4b952d42608ff90b03c85f961ba10b93bde916 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 28 Dec 2012 16:02:04 +0000 Subject: [PATCH] Use a single taskbar icon for all notifications in wxMSW. Allocating a new icon for every notification could result in showing many identical icons in the taskbar notification area if several notification messages were generated which looked like a bug to the user. It was also inconsistent with the behaviour in the case when UseTaskBarIcon() was called. Always behave as in the latter case now, i.e. any subsequent notification replaces the previous one instead of being shown in addition to it. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73287 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/msw/notifmsg.cpp | 121 ++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/src/msw/notifmsg.cpp b/src/msw/notifmsg.cpp index b1a91d86d6..9db2177dfa 100644 --- a/src/msw/notifmsg.cpp +++ b/src/msw/notifmsg.cpp @@ -92,9 +92,10 @@ private: 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 @@ -105,20 +106,53 @@ public: 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") ); + + if ( !--ms_refCountIcon ) + { + delete ms_icon; + ms_icon = NULL; + } + } - static wxTaskBarIcon *ms_iconToUse; +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 @@ -212,7 +246,7 @@ wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon *icon) void wxNotificationIconEvtHandler::OnIconHidden() { - delete m_icon; + wxBalloonNotifMsgImpl::ReleaseIcon(); delete this; } @@ -234,29 +268,36 @@ void wxNotificationIconEvtHandler::OnClick(wxTaskBarIconEvent& WXUNUSED(event)) // 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; @@ -278,7 +319,7 @@ void wxBalloonNotifMsgImpl::SetUpIcon(wxWindow *win) icon = wxIcon(wxT("wxICON_AAA")); } - m_icon->SetIcon(icon); + ms_icon->SetIcon(icon); } } @@ -288,7 +329,7 @@ wxBalloonNotifMsgImpl::DoShow(const wxString& title, int timeout, int flags) { - if ( !m_icon->IsIconInstalled() ) + 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, @@ -299,15 +340,15 @@ wxBalloonNotifMsgImpl::DoShow(const wxString& title, // 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 m_icon; - m_icon = NULL; + 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); } // ---------------------------------------------------------------------------- @@ -322,7 +363,7 @@ wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow *win) wxManualNotifMsgImpl::~wxManualNotifMsgImpl() { - if ( m_icon ) + if ( ms_icon ) DoClose(); } @@ -337,7 +378,7 @@ wxManualNotifMsgImpl::DoShow(const wxString& title, // 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 @@ -347,19 +388,17 @@ wxManualNotifMsgImpl::DoShow(const wxString& title, 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; } @@ -370,11 +409,11 @@ bool wxManualNotifMsgImpl::DoClose() wxAutoNotifMsgImpl::wxAutoNotifMsgImpl(wxWindow *win) : wxBalloonNotifMsgImpl(win) { - if ( m_ownsIcon ) + if ( ms_refCountIcon != -1 ) { - // This object will self-destruct and also delete the icon when the - // notification is hidden. - new wxNotificationIconEvtHandler(m_icon); + // This object will self-destruct and decrease the ref count of the + // icon when the notification is hidden. + new wxNotificationIconEvtHandler(ms_icon); } } -- 2.45.2