]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/notifmsg.cpp
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / msw / notifmsg.cpp
index 136a1bc9c79221c72b5b47cc1e0f501da2f24e2a..9db2177dfa1d6781dd9064173d6b6673fe39ab15 100644 (file)
     #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());