1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/notifmsg.cpp
3 // Purpose: implementation of wxNotificationMessage for Windows
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
26 // we can only use the native implementation if we have a working
27 // wxTaskBarIcon::ShowBalloon() method
28 #if wxUSE_NOTIFICATION_MESSAGE && \
29 wxUSE_TASKBARICON && wxUSE_TASKBARICON_BALLOONS
31 #include "wx/notifmsg.h"
34 #include "wx/toplevel.h"
36 #include "wx/string.h"
39 #include "wx/generic/notifmsg.h"
41 #include "wx/taskbar.h"
43 // ----------------------------------------------------------------------------
44 // different implementations used by wxNotificationMessage
45 // ----------------------------------------------------------------------------
47 // base class for all available implementations
52 virtual ~wxNotifMsgImpl() { }
54 virtual bool DoShow(const wxString
& title
,
55 const wxString
& message
,
58 virtual bool DoClose() = 0;
61 wxDECLARE_NO_COPY_CLASS(wxNotifMsgImpl
);
64 // implementation which is simply a bridge to wxGenericNotificationMessage
65 class wxGenericNotifMsgImpl
: public wxNotifMsgImpl
68 wxGenericNotifMsgImpl() : m_notif(new wxGenericNotificationMessage
) { }
69 virtual ~wxGenericNotifMsgImpl() { delete m_notif
; }
71 virtual bool DoShow(const wxString
& title
,
72 const wxString
& message
,
76 m_notif
->SetTitle(title
);
77 m_notif
->SetMessage(message
);
78 m_notif
->SetFlags(flags
);
79 return m_notif
->Show(timeout
);
82 virtual bool DoClose()
84 return m_notif
->Close();
88 wxGenericNotificationMessage
* const m_notif
;
91 // common base class for implementations using a taskbar icon and balloons
92 class wxBalloonNotifMsgImpl
: public wxNotifMsgImpl
95 // Ctor creates the associated taskbar icon (using the icon of the top
96 // level parent of the given window) unless UseTaskBarIcon() had been
97 // previously called which can be used to show an attached balloon later
98 // by the derived classes.
99 wxBalloonNotifMsgImpl(wxWindow
*win
) { SetUpIcon(win
); }
101 // implementation of wxNotificationMessage method with the same name
102 static wxTaskBarIcon
*UseTaskBarIcon(wxTaskBarIcon
*icon
);
104 virtual bool DoShow(const wxString
& title
,
105 const wxString
& message
,
110 // Returns true if we're using our own icon or false if we're hitching a
111 // ride on the application icon provided to us via UseTaskBarIcon().
112 static bool IsUsingOwnIcon()
114 return ms_refCountIcon
!= -1;
117 // Indicates that the taskbar icon we're using has been hidden and can be
120 // This is only called by wxNotificationIconEvtHandler and should only be
121 // called when using our own icon (as opposed to the one passed to us via
122 // UseTaskBarIcon()).
123 static void ReleaseIcon()
125 wxASSERT_MSG( ms_refCountIcon
!= -1,
126 wxS("Must not be called when not using own icon") );
128 if ( !--ms_refCountIcon
)
136 // Creates a new icon if necessary, see the comment below.
137 void SetUpIcon(wxWindow
*win
);
140 // We need an icon to show the notification in a balloon attached to it.
141 // It may happen that the main application already shows an icon in the
142 // taskbar notification area in which case it should call our
143 // UseTaskBarIcon() and we just use this icon without ever allocating nor
144 // deleting it and ms_refCountIcon is -1 and never changes. Otherwise, we
145 // create the icon when we need it the first time but reuse it if we need
146 // to show subsequent notifications while this icon is still alive. This is
147 // needed in order to avoid 2 or 3 or even more identical icons if a couple
148 // of notifications are shown in a row (which happens quite easily in
149 // practice because Windows helpfully buffers all the notifications that
150 // were generated while the user was away -- i.e. the screensaver was
151 // active -- and then shows them all at once when the user comes back). In
152 // this case, ms_refCountIcon is used as a normal reference counter, i.e.
153 // the icon is only destroyed when it reaches 0.
154 static wxTaskBarIcon
*ms_icon
;
155 static int ms_refCountIcon
;
158 // implementation for automatically hidden notifications
159 class wxAutoNotifMsgImpl
: public wxBalloonNotifMsgImpl
162 wxAutoNotifMsgImpl(wxWindow
*win
);
164 virtual bool DoShow(const wxString
& title
,
165 const wxString
& message
,
169 // can't close automatic notification [currently]
170 virtual bool DoClose() { return false; }
173 // implementation for manually closed notifications
174 class wxManualNotifMsgImpl
: public wxBalloonNotifMsgImpl
177 wxManualNotifMsgImpl(wxWindow
*win
);
178 virtual ~wxManualNotifMsgImpl();
180 virtual bool DoShow(const wxString
& title
,
181 const wxString
& message
,
184 virtual bool DoClose();
187 // store ctor parameter as we need it to recreate the icon later if we're
188 // closed and shown again
189 wxWindow
* const m_win
;
192 // ----------------------------------------------------------------------------
193 // custom event handler for task bar icons
194 // ----------------------------------------------------------------------------
196 // normally we'd just use a custom taskbar icon class but this is impossible
197 // because we can be asked to attach the notifications to an existing icon
198 // which we didn't create, hence we install a special event handler allowing us
199 // to get the events we need (and, crucially, to delete the icon when it's not
200 // needed any more) in any case
202 class wxNotificationIconEvtHandler
: public wxEvtHandler
205 wxNotificationIconEvtHandler(wxTaskBarIcon
*icon
);
208 void OnTimeout(wxTaskBarIconEvent
& event
);
209 void OnClick(wxTaskBarIconEvent
& event
);
214 wxTaskBarIcon
* const m_icon
;
216 wxDECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler
);
219 // ============================================================================
221 // ============================================================================
223 // ----------------------------------------------------------------------------
224 // wxNotificationIconEvtHandler
225 // ----------------------------------------------------------------------------
227 wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon
*icon
)
232 wxEVT_TASKBAR_BALLOON_TIMEOUT
,
233 wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnTimeout
),
240 wxEVT_TASKBAR_BALLOON_CLICK
,
241 wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnClick
),
247 void wxNotificationIconEvtHandler::OnIconHidden()
249 wxBalloonNotifMsgImpl::ReleaseIcon();
255 wxNotificationIconEvtHandler::OnTimeout(wxTaskBarIconEvent
& WXUNUSED(event
))
260 void wxNotificationIconEvtHandler::OnClick(wxTaskBarIconEvent
& WXUNUSED(event
))
262 // TODO: generate an event notifying the user code?
267 // ----------------------------------------------------------------------------
268 // wxBalloonNotifMsgImpl
269 // ----------------------------------------------------------------------------
271 wxTaskBarIcon
*wxBalloonNotifMsgImpl::ms_icon
= NULL
;
272 int wxBalloonNotifMsgImpl::ms_refCountIcon
= 0;
275 wxTaskBarIcon
*wxBalloonNotifMsgImpl::UseTaskBarIcon(wxTaskBarIcon
*icon
)
277 wxTaskBarIcon
* const iconOld
= ms_icon
;
280 // Don't use reference counting for the provided icon, we don't own it.
281 ms_refCountIcon
= icon
? -1 : 0;
286 void wxBalloonNotifMsgImpl::SetUpIcon(wxWindow
*win
)
290 // Increment the reference count if we manage the icon on our own.
291 if ( ms_refCountIcon
!= -1 )
294 else // Create a new icon.
296 wxASSERT_MSG( ms_refCountIcon
== 0,
297 wxS("Shouldn't reference not existent icon") );
299 ms_icon
= new wxTaskBarIcon
;
302 // use the icon of the associated (or main, if none) frame
305 win
= wxGetTopLevelParent(win
);
307 win
= wxTheApp
->GetTopWindow();
310 const wxTopLevelWindow
* const
311 tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
313 icon
= tlw
->GetIcon();
318 // we really must have some icon
319 icon
= wxIcon(wxT("wxICON_AAA"));
322 ms_icon
->SetIcon(icon
);
327 wxBalloonNotifMsgImpl::DoShow(const wxString
& title
,
328 const wxString
& message
,
332 if ( !ms_icon
->IsIconInstalled() )
334 // If we failed to install the icon (which does happen sometimes,
335 // although only in unusual circumstances, e.g. it happens regularly,
336 // albeit not constantly, if we're used soon after resume from suspend
337 // under Windows 7), we should not call ShowBalloon() because it would
338 // just assert and return and we must delete the icon ourselves because
339 // otherwise its associated wxTaskBarIconWindow would remain alive
340 // forever because we're not going to receive a notification about icon
341 // disappearance from the system if we failed to install it in the
349 timeout
*= 1000; // Windows expresses timeout in milliseconds
351 return ms_icon
->ShowBalloon(title
, message
, timeout
, flags
);
354 // ----------------------------------------------------------------------------
355 // wxManualNotifMsgImpl
356 // ----------------------------------------------------------------------------
358 wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow
*win
)
359 : wxBalloonNotifMsgImpl(win
),
364 wxManualNotifMsgImpl::~wxManualNotifMsgImpl()
371 wxManualNotifMsgImpl::DoShow(const wxString
& title
,
372 const wxString
& message
,
373 int WXUNUSED_UNLESS_DEBUG(timeout
),
376 wxASSERT_MSG( timeout
== wxNotificationMessage::Timeout_Never
,
377 wxT("shouldn't be used") );
379 // base class creates the icon for us initially but we could have destroyed
380 // it in DoClose(), recreate it if this was the case
384 // use maximal (in current Windows versions) timeout (but it will still
385 // disappear on its own)
386 return wxBalloonNotifMsgImpl::DoShow(title
, message
, 30, flags
);
389 bool wxManualNotifMsgImpl::DoClose()
391 if ( IsUsingOwnIcon() )
393 // we don't need the icon any more
396 else // using an existing icon
398 // just hide the balloon
399 ms_icon
->ShowBalloon("", "");
405 // ----------------------------------------------------------------------------
406 // wxAutoNotifMsgImpl
407 // ----------------------------------------------------------------------------
409 wxAutoNotifMsgImpl::wxAutoNotifMsgImpl(wxWindow
*win
)
410 : wxBalloonNotifMsgImpl(win
)
412 if ( ms_refCountIcon
!= -1 )
414 // This object will self-destruct and decrease the ref count of the
415 // icon when the notification is hidden.
416 new wxNotificationIconEvtHandler(ms_icon
);
421 wxAutoNotifMsgImpl::DoShow(const wxString
& title
,
422 const wxString
& message
,
426 wxASSERT_MSG( timeout
!= wxNotificationMessage::Timeout_Never
,
427 wxT("shouldn't be used") );
429 if ( timeout
== wxNotificationMessage::Timeout_Auto
)
431 // choose a value more or less in the middle of the allowed range
435 return wxBalloonNotifMsgImpl::DoShow(title
, message
, timeout
, flags
);
438 // ----------------------------------------------------------------------------
439 // wxNotificationMessage
440 // ----------------------------------------------------------------------------
443 bool wxNotificationMessage::ms_alwaysUseGeneric
= false;
446 wxTaskBarIcon
*wxNotificationMessage::UseTaskBarIcon(wxTaskBarIcon
*icon
)
448 return wxBalloonNotifMsgImpl::UseTaskBarIcon(icon
);
451 bool wxNotificationMessage::Show(int timeout
)
455 if ( !ms_alwaysUseGeneric
&& wxTheApp
->GetShell32Version() >= 500 )
457 if ( timeout
== Timeout_Never
)
458 m_impl
= new wxManualNotifMsgImpl(GetParent());
460 m_impl
= new wxAutoNotifMsgImpl(GetParent());
462 else // no support for balloon tooltips
464 m_impl
= new wxGenericNotifMsgImpl
;
467 //else: reuse the same implementation for the subsequent calls, it would
468 // be too confusing if it changed
470 return m_impl
->DoShow(GetTitle(), GetMessage(), timeout
, GetFlags());
473 bool wxNotificationMessage::Close()
475 wxCHECK_MSG( m_impl
, false, "must show the notification first" );
477 return m_impl
->DoClose();
480 wxNotificationMessage::~wxNotificationMessage()
485 #endif // wxUSE_NOTIFICATION_MESSAGE && wxUSE_TASKBARICON