X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c0c133e13b36a923c65f94499554e432bc3a0daa..ad653fa23069c5d9378247084f03c9a718c3ad62:/src/msw/notifmsg.cpp diff --git a/src/msw/notifmsg.cpp b/src/msw/notifmsg.cpp index 31a4472be4..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 @@ -134,11 +168,6 @@ public: // 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 @@ -217,7 +246,7 @@ wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon *icon) void wxNotificationIconEvtHandler::OnIconHidden() { - delete m_icon; + wxBalloonNotifMsgImpl::ReleaseIcon(); delete this; } @@ -239,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; @@ -280,10 +316,10 @@ void wxBalloonNotifMsgImpl::SetUpIcon(wxWindow *win) 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); } } @@ -293,9 +329,26 @@ wxBalloonNotifMsgImpl::DoShow(const wxString& title, 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); } // ---------------------------------------------------------------------------- @@ -310,7 +363,7 @@ wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow *win) wxManualNotifMsgImpl::~wxManualNotifMsgImpl() { - if ( m_icon ) + if ( ms_icon ) DoClose(); } @@ -321,11 +374,11 @@ wxManualNotifMsgImpl::DoShow(const wxString& title, 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 @@ -335,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; } @@ -356,9 +407,14 @@ bool wxManualNotifMsgImpl::DoClose() // ---------------------------------------------------------------------------- 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 @@ -368,7 +424,7 @@ wxAutoNotifMsgImpl::DoShow(const wxString& title, int flags) { wxASSERT_MSG( timeout != wxNotificationMessage::Timeout_Never, - _T("shouldn't be used") ); + wxT("shouldn't be used") ); if ( timeout == wxNotificationMessage::Timeout_Auto ) {