Files
wxWidgets/src/msw/notifmsg.cpp
Vadim Zeitlin 51871b7b5a Reset icon ref count when deleting the icon in wxMSW balloon icons code.
This avoids the assert which happened when trying to show the next
notification message in SetUpIcon() as the icon was null while the reference
count was positive.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@76447 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2014-05-04 22:12:05 +00:00

486 lines
15 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/msw/notifmsg.cpp
// Purpose: implementation of wxNotificationMessage for Windows
// Author: Vadim Zeitlin
// Created: 2007-12-01
// Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// 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/generic/notifmsg.h"
#include "wx/taskbar.h"
// ----------------------------------------------------------------------------
// different implementations used by wxNotificationMessage
// ----------------------------------------------------------------------------
// base class for all available implementations
class wxNotifMsgImpl
{
public:
wxNotifMsgImpl() { }
virtual ~wxNotifMsgImpl() { }
virtual bool DoShow(const wxString& title,
const wxString& message,
int timeout,
int flags) = 0;
virtual bool DoClose() = 0;
private:
wxDECLARE_NO_COPY_CLASS(wxNotifMsgImpl);
};
// implementation which is simply a bridge to wxGenericNotificationMessage
class wxGenericNotifMsgImpl : public wxNotifMsgImpl
{
public:
wxGenericNotifMsgImpl() : m_notif(new wxGenericNotificationMessage) { }
virtual ~wxGenericNotifMsgImpl() { delete m_notif; }
virtual bool DoShow(const wxString& title,
const wxString& message,
int timeout,
int flags)
{
m_notif->SetTitle(title);
m_notif->SetMessage(message);
m_notif->SetFlags(flags);
return m_notif->Show(timeout);
}
virtual bool DoClose()
{
return m_notif->Close();
}
private:
wxGenericNotificationMessage * const m_notif;
};
// common base class for implementations using a taskbar icon and balloons
class wxBalloonNotifMsgImpl : public wxNotifMsgImpl
{
public:
// 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
static wxTaskBarIcon *UseTaskBarIcon(wxTaskBarIcon *icon);
virtual bool DoShow(const wxString& title,
const wxString& message,
int timeout,
int flags);
// 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;
}
}
protected:
// Creates a new icon if necessary, see the comment below.
void SetUpIcon(wxWindow *win);
// 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
class wxAutoNotifMsgImpl : public wxBalloonNotifMsgImpl
{
public:
wxAutoNotifMsgImpl(wxWindow *win);
virtual bool DoShow(const wxString& title,
const wxString& message,
int timeout,
int flags);
// can't close automatic notification [currently]
virtual bool DoClose() { return false; }
};
// implementation for manually closed notifications
class wxManualNotifMsgImpl : public wxBalloonNotifMsgImpl
{
public:
wxManualNotifMsgImpl(wxWindow *win);
virtual ~wxManualNotifMsgImpl();
virtual bool DoShow(const wxString& title,
const wxString& message,
int timeout,
int flags);
virtual bool DoClose();
private:
// store ctor parameter as we need it to recreate the icon later if we're
// closed and shown again
wxWindow * const m_win;
};
// ----------------------------------------------------------------------------
// custom event handler for task bar icons
// ----------------------------------------------------------------------------
// normally we'd just use a custom taskbar icon class but this is impossible
// because we can be asked to attach the notifications to an existing icon
// which we didn't create, hence we install a special event handler allowing us
// to get the events we need (and, crucially, to delete the icon when it's not
// needed any more) in any case
class wxNotificationIconEvtHandler : public wxEvtHandler
{
public:
wxNotificationIconEvtHandler(wxTaskBarIcon *icon);
private:
void OnTimeout(wxTaskBarIconEvent& event);
void OnClick(wxTaskBarIconEvent& event);
void OnIconHidden();
wxTaskBarIcon * const m_icon;
wxDECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler);
};
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxNotificationIconEvtHandler
// ----------------------------------------------------------------------------
wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon *icon)
: m_icon(icon)
{
m_icon->Connect
(
wxEVT_TASKBAR_BALLOON_TIMEOUT,
wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnTimeout),
NULL,
this
);
m_icon->Connect
(
wxEVT_TASKBAR_BALLOON_CLICK,
wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnClick),
NULL,
this
);
}
void wxNotificationIconEvtHandler::OnIconHidden()
{
wxBalloonNotifMsgImpl::ReleaseIcon();
delete this;
}
void
wxNotificationIconEvtHandler::OnTimeout(wxTaskBarIconEvent& WXUNUSED(event))
{
OnIconHidden();
}
void wxNotificationIconEvtHandler::OnClick(wxTaskBarIconEvent& WXUNUSED(event))
{
// TODO: generate an event notifying the user code?
OnIconHidden();
}
// ----------------------------------------------------------------------------
// wxBalloonNotifMsgImpl
// ----------------------------------------------------------------------------
wxTaskBarIcon *wxBalloonNotifMsgImpl::ms_icon = NULL;
int wxBalloonNotifMsgImpl::ms_refCountIcon = 0;
/* static */
wxTaskBarIcon *wxBalloonNotifMsgImpl::UseTaskBarIcon(wxTaskBarIcon *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_icon )
{
// Increment the reference count if we manage the icon on our own.
if ( ms_refCountIcon != -1 )
ms_refCountIcon++;
}
else // Create a new icon.
{
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 ( win )
win = wxGetTopLevelParent(win);
if ( !win )
win = wxTheApp->GetTopWindow();
if ( win )
{
const wxTopLevelWindow * const
tlw = wxDynamicCast(win, wxTopLevelWindow);
if ( tlw )
icon = tlw->GetIcon();
}
if ( !icon.IsOk() )
{
// we really must have some icon
icon = wxIcon(wxT("wxICON_AAA"));
}
ms_icon->SetIcon(icon);
}
}
bool
wxBalloonNotifMsgImpl::DoShow(const wxString& title,
const wxString& message,
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;
ms_refCountIcon = 0;
return false;
}
timeout *= 1000; // Windows expresses timeout in milliseconds
return ms_icon->ShowBalloon(title, message, timeout, flags);
}
// ----------------------------------------------------------------------------
// wxManualNotifMsgImpl
// ----------------------------------------------------------------------------
wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow *win)
: wxBalloonNotifMsgImpl(win),
m_win(win)
{
}
wxManualNotifMsgImpl::~wxManualNotifMsgImpl()
{
if ( ms_icon )
DoClose();
}
bool
wxManualNotifMsgImpl::DoShow(const wxString& title,
const wxString& message,
int WXUNUSED_UNLESS_DEBUG(timeout),
int flags)
{
wxASSERT_MSG( timeout == wxNotificationMessage::Timeout_Never,
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 ( !ms_icon )
SetUpIcon(m_win);
// use maximal (in current Windows versions) timeout (but it will still
// disappear on its own)
return wxBalloonNotifMsgImpl::DoShow(title, message, 30, flags);
}
bool wxManualNotifMsgImpl::DoClose()
{
if ( IsUsingOwnIcon() )
{
// we don't need the icon any more
ReleaseIcon();
}
else // using an existing icon
{
// just hide the balloon
ms_icon->ShowBalloon("", "");
}
return true;
}
// ----------------------------------------------------------------------------
// wxAutoNotifMsgImpl
// ----------------------------------------------------------------------------
wxAutoNotifMsgImpl::wxAutoNotifMsgImpl(wxWindow *win)
: 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
wxAutoNotifMsgImpl::DoShow(const wxString& title,
const wxString& message,
int timeout,
int flags)
{
wxASSERT_MSG( timeout != wxNotificationMessage::Timeout_Never,
wxT("shouldn't be used") );
if ( timeout == wxNotificationMessage::Timeout_Auto )
{
// choose a value more or less in the middle of the allowed range
timeout = 1;
}
return wxBalloonNotifMsgImpl::DoShow(title, message, timeout, flags);
}
// ----------------------------------------------------------------------------
// wxNotificationMessage
// ----------------------------------------------------------------------------
/* static */
bool wxNotificationMessage::ms_alwaysUseGeneric = false;
/* static */
wxTaskBarIcon *wxNotificationMessage::UseTaskBarIcon(wxTaskBarIcon *icon)
{
return wxBalloonNotifMsgImpl::UseTaskBarIcon(icon);
}
bool wxNotificationMessage::Show(int timeout)
{
if ( !m_impl )
{
if ( !ms_alwaysUseGeneric && wxTheApp->GetShell32Version() >= 500 )
{
if ( timeout == Timeout_Never )
m_impl = new wxManualNotifMsgImpl(GetParent());
else
m_impl = new wxAutoNotifMsgImpl(GetParent());
}
else // no support for balloon tooltips
{
m_impl = new wxGenericNotifMsgImpl;
}
}
//else: reuse the same implementation for the subsequent calls, it would
// be too confusing if it changed
return m_impl->DoShow(GetTitle(), GetMessage(), timeout, GetFlags());
}
bool wxNotificationMessage::Close()
{
wxCHECK_MSG( m_impl, false, "must show the notification first" );
return m_impl->DoClose();
}
wxNotificationMessage::~wxNotificationMessage()
{
delete m_impl;
}
#endif // wxUSE_NOTIFICATION_MESSAGE && wxUSE_TASKBARICON