X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e2d5abbf527ae760cf65467bf94b914ba9974657..1fd850ab550a30bc600d5a10c5aed5386bf3624b:/src/msw/notifmsg.cpp diff --git a/src/msw/notifmsg.cpp b/src/msw/notifmsg.cpp index 136a1bc9c7..9db2177dfa 100644 --- a/src/msw/notifmsg.cpp +++ b/src/msw/notifmsg.cpp @@ -23,13 +23,19 @@ #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" @@ -52,7 +58,7 @@ public: virtual bool DoClose() = 0; private: - DECLARE_NO_COPY_CLASS(wxNotifMsgImpl) + wxDECLARE_NO_COPY_CLASS(wxNotifMsgImpl); }; // implementation which is simply a bridge to wxGenericNotificationMessage @@ -86,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 @@ -99,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; + } - static wxTaskBarIcon *ms_iconToUse; + // 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; + } + } + +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 @@ -128,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 @@ -178,7 +213,7 @@ private: wxTaskBarIcon * const m_icon; - DECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler) + wxDECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler); }; // ============================================================================ @@ -203,7 +238,7 @@ wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon *icon) m_icon->Connect ( wxEVT_TASKBAR_BALLOON_CLICK, - wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnTimeout), + wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnClick), NULL, this ); @@ -211,7 +246,7 @@ wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon *icon) void wxNotificationIconEvtHandler::OnIconHidden() { - delete m_icon; + wxBalloonNotifMsgImpl::ReleaseIcon(); delete this; } @@ -233,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; @@ -274,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); } } @@ -287,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); } // ---------------------------------------------------------------------------- @@ -304,22 +363,22 @@ wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow *win) 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 @@ -329,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; } @@ -350,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 @@ -362,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 ) { @@ -377,6 +439,9 @@ wxAutoNotifMsgImpl::DoShow(const wxString& title, // wxNotificationMessage // ---------------------------------------------------------------------------- +/* static */ +bool wxNotificationMessage::ms_alwaysUseGeneric = false; + /* static */ wxTaskBarIcon *wxNotificationMessage::UseTaskBarIcon(wxTaskBarIcon *icon) { @@ -387,7 +452,7 @@ bool wxNotificationMessage::Show(int timeout) { if ( !m_impl ) { - if ( wxTheApp->GetShell32Version() >= 500 ) + if ( !ms_alwaysUseGeneric && wxTheApp->GetShell32Version() >= 500 ) { if ( timeout == Timeout_Never ) m_impl = new wxManualNotifMsgImpl(GetParent());