]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/notifmsg.cpp
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / msw / notifmsg.cpp
... / ...
CommitLineData
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/msw/notifmsg.cpp
3// Purpose: implementation of wxNotificationMessage for Windows
4// Author: Vadim Zeitlin
5// Created: 2007-12-01
6// RCS-ID: $Id$
7// Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
8// Licence: wxWindows licence
9///////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19// for compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
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
30
31#include "wx/notifmsg.h"
32
33#ifndef WX_PRECOMP
34 #include "wx/toplevel.h"
35 #include "wx/app.h"
36 #include "wx/string.h"
37#endif // WX_PRECOMP
38
39#include "wx/generic/notifmsg.h"
40
41#include "wx/taskbar.h"
42
43// ----------------------------------------------------------------------------
44// different implementations used by wxNotificationMessage
45// ----------------------------------------------------------------------------
46
47// base class for all available implementations
48class wxNotifMsgImpl
49{
50public:
51 wxNotifMsgImpl() { }
52 virtual ~wxNotifMsgImpl() { }
53
54 virtual bool DoShow(const wxString& title,
55 const wxString& message,
56 int timeout,
57 int flags) = 0;
58 virtual bool DoClose() = 0;
59
60private:
61 wxDECLARE_NO_COPY_CLASS(wxNotifMsgImpl);
62};
63
64// implementation which is simply a bridge to wxGenericNotificationMessage
65class wxGenericNotifMsgImpl : public wxNotifMsgImpl
66{
67public:
68 wxGenericNotifMsgImpl() : m_notif(new wxGenericNotificationMessage) { }
69 virtual ~wxGenericNotifMsgImpl() { delete m_notif; }
70
71 virtual bool DoShow(const wxString& title,
72 const wxString& message,
73 int timeout,
74 int flags)
75 {
76 m_notif->SetTitle(title);
77 m_notif->SetMessage(message);
78 m_notif->SetFlags(flags);
79 return m_notif->Show(timeout);
80 }
81
82 virtual bool DoClose()
83 {
84 return m_notif->Close();
85 }
86
87private:
88 wxGenericNotificationMessage * const m_notif;
89};
90
91// common base class for implementations using a taskbar icon and balloons
92class wxBalloonNotifMsgImpl : public wxNotifMsgImpl
93{
94public:
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); }
100
101 // implementation of wxNotificationMessage method with the same name
102 static wxTaskBarIcon *UseTaskBarIcon(wxTaskBarIcon *icon);
103
104 virtual bool DoShow(const wxString& title,
105 const wxString& message,
106 int timeout,
107 int flags);
108
109
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()
113 {
114 return ms_refCountIcon != -1;
115 }
116
117 // Indicates that the taskbar icon we're using has been hidden and can be
118 // deleted.
119 //
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()
124 {
125 wxASSERT_MSG( ms_refCountIcon != -1,
126 wxS("Must not be called when not using own icon") );
127
128 if ( !--ms_refCountIcon )
129 {
130 delete ms_icon;
131 ms_icon = NULL;
132 }
133 }
134
135protected:
136 // Creates a new icon if necessary, see the comment below.
137 void SetUpIcon(wxWindow *win);
138
139
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;
156};
157
158// implementation for automatically hidden notifications
159class wxAutoNotifMsgImpl : public wxBalloonNotifMsgImpl
160{
161public:
162 wxAutoNotifMsgImpl(wxWindow *win);
163
164 virtual bool DoShow(const wxString& title,
165 const wxString& message,
166 int timeout,
167 int flags);
168
169 // can't close automatic notification [currently]
170 virtual bool DoClose() { return false; }
171};
172
173// implementation for manually closed notifications
174class wxManualNotifMsgImpl : public wxBalloonNotifMsgImpl
175{
176public:
177 wxManualNotifMsgImpl(wxWindow *win);
178 virtual ~wxManualNotifMsgImpl();
179
180 virtual bool DoShow(const wxString& title,
181 const wxString& message,
182 int timeout,
183 int flags);
184 virtual bool DoClose();
185
186private:
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;
190};
191
192// ----------------------------------------------------------------------------
193// custom event handler for task bar icons
194// ----------------------------------------------------------------------------
195
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
201
202class wxNotificationIconEvtHandler : public wxEvtHandler
203{
204public:
205 wxNotificationIconEvtHandler(wxTaskBarIcon *icon);
206
207private:
208 void OnTimeout(wxTaskBarIconEvent& event);
209 void OnClick(wxTaskBarIconEvent& event);
210
211 void OnIconHidden();
212
213
214 wxTaskBarIcon * const m_icon;
215
216 wxDECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler);
217};
218
219// ============================================================================
220// implementation
221// ============================================================================
222
223// ----------------------------------------------------------------------------
224// wxNotificationIconEvtHandler
225// ----------------------------------------------------------------------------
226
227wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon *icon)
228 : m_icon(icon)
229{
230 m_icon->Connect
231 (
232 wxEVT_TASKBAR_BALLOON_TIMEOUT,
233 wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnTimeout),
234 NULL,
235 this
236 );
237
238 m_icon->Connect
239 (
240 wxEVT_TASKBAR_BALLOON_CLICK,
241 wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnClick),
242 NULL,
243 this
244 );
245}
246
247void wxNotificationIconEvtHandler::OnIconHidden()
248{
249 wxBalloonNotifMsgImpl::ReleaseIcon();
250
251 delete this;
252}
253
254void
255wxNotificationIconEvtHandler::OnTimeout(wxTaskBarIconEvent& WXUNUSED(event))
256{
257 OnIconHidden();
258}
259
260void wxNotificationIconEvtHandler::OnClick(wxTaskBarIconEvent& WXUNUSED(event))
261{
262 // TODO: generate an event notifying the user code?
263
264 OnIconHidden();
265}
266
267// ----------------------------------------------------------------------------
268// wxBalloonNotifMsgImpl
269// ----------------------------------------------------------------------------
270
271wxTaskBarIcon *wxBalloonNotifMsgImpl::ms_icon = NULL;
272int wxBalloonNotifMsgImpl::ms_refCountIcon = 0;
273
274/* static */
275wxTaskBarIcon *wxBalloonNotifMsgImpl::UseTaskBarIcon(wxTaskBarIcon *icon)
276{
277 wxTaskBarIcon * const iconOld = ms_icon;
278 ms_icon = icon;
279
280 // Don't use reference counting for the provided icon, we don't own it.
281 ms_refCountIcon = icon ? -1 : 0;
282
283 return iconOld;
284}
285
286void wxBalloonNotifMsgImpl::SetUpIcon(wxWindow *win)
287{
288 if ( ms_icon )
289 {
290 // Increment the reference count if we manage the icon on our own.
291 if ( ms_refCountIcon != -1 )
292 ms_refCountIcon++;
293 }
294 else // Create a new icon.
295 {
296 wxASSERT_MSG( ms_refCountIcon == 0,
297 wxS("Shouldn't reference not existent icon") );
298
299 ms_icon = new wxTaskBarIcon;
300 ms_refCountIcon = 1;
301
302 // use the icon of the associated (or main, if none) frame
303 wxIcon icon;
304 if ( win )
305 win = wxGetTopLevelParent(win);
306 if ( !win )
307 win = wxTheApp->GetTopWindow();
308 if ( win )
309 {
310 const wxTopLevelWindow * const
311 tlw = wxDynamicCast(win, wxTopLevelWindow);
312 if ( tlw )
313 icon = tlw->GetIcon();
314 }
315
316 if ( !icon.IsOk() )
317 {
318 // we really must have some icon
319 icon = wxIcon(wxT("wxICON_AAA"));
320 }
321
322 ms_icon->SetIcon(icon);
323 }
324}
325
326bool
327wxBalloonNotifMsgImpl::DoShow(const wxString& title,
328 const wxString& message,
329 int timeout,
330 int flags)
331{
332 if ( !ms_icon->IsIconInstalled() )
333 {
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
342 // first place.
343 delete ms_icon;
344 ms_icon = NULL;
345
346 return false;
347 }
348
349 timeout *= 1000; // Windows expresses timeout in milliseconds
350
351 return ms_icon->ShowBalloon(title, message, timeout, flags);
352}
353
354// ----------------------------------------------------------------------------
355// wxManualNotifMsgImpl
356// ----------------------------------------------------------------------------
357
358wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow *win)
359 : wxBalloonNotifMsgImpl(win),
360 m_win(win)
361{
362}
363
364wxManualNotifMsgImpl::~wxManualNotifMsgImpl()
365{
366 if ( ms_icon )
367 DoClose();
368}
369
370bool
371wxManualNotifMsgImpl::DoShow(const wxString& title,
372 const wxString& message,
373 int WXUNUSED_UNLESS_DEBUG(timeout),
374 int flags)
375{
376 wxASSERT_MSG( timeout == wxNotificationMessage::Timeout_Never,
377 wxT("shouldn't be used") );
378
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
381 if ( !ms_icon )
382 SetUpIcon(m_win);
383
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);
387}
388
389bool wxManualNotifMsgImpl::DoClose()
390{
391 if ( IsUsingOwnIcon() )
392 {
393 // we don't need the icon any more
394 ReleaseIcon();
395 }
396 else // using an existing icon
397 {
398 // just hide the balloon
399 ms_icon->ShowBalloon("", "");
400 }
401
402 return true;
403}
404
405// ----------------------------------------------------------------------------
406// wxAutoNotifMsgImpl
407// ----------------------------------------------------------------------------
408
409wxAutoNotifMsgImpl::wxAutoNotifMsgImpl(wxWindow *win)
410 : wxBalloonNotifMsgImpl(win)
411{
412 if ( ms_refCountIcon != -1 )
413 {
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);
417 }
418}
419
420bool
421wxAutoNotifMsgImpl::DoShow(const wxString& title,
422 const wxString& message,
423 int timeout,
424 int flags)
425{
426 wxASSERT_MSG( timeout != wxNotificationMessage::Timeout_Never,
427 wxT("shouldn't be used") );
428
429 if ( timeout == wxNotificationMessage::Timeout_Auto )
430 {
431 // choose a value more or less in the middle of the allowed range
432 timeout = 1;
433 }
434
435 return wxBalloonNotifMsgImpl::DoShow(title, message, timeout, flags);
436}
437
438// ----------------------------------------------------------------------------
439// wxNotificationMessage
440// ----------------------------------------------------------------------------
441
442/* static */
443bool wxNotificationMessage::ms_alwaysUseGeneric = false;
444
445/* static */
446wxTaskBarIcon *wxNotificationMessage::UseTaskBarIcon(wxTaskBarIcon *icon)
447{
448 return wxBalloonNotifMsgImpl::UseTaskBarIcon(icon);
449}
450
451bool wxNotificationMessage::Show(int timeout)
452{
453 if ( !m_impl )
454 {
455 if ( !ms_alwaysUseGeneric && wxTheApp->GetShell32Version() >= 500 )
456 {
457 if ( timeout == Timeout_Never )
458 m_impl = new wxManualNotifMsgImpl(GetParent());
459 else
460 m_impl = new wxAutoNotifMsgImpl(GetParent());
461 }
462 else // no support for balloon tooltips
463 {
464 m_impl = new wxGenericNotifMsgImpl;
465 }
466 }
467 //else: reuse the same implementation for the subsequent calls, it would
468 // be too confusing if it changed
469
470 return m_impl->DoShow(GetTitle(), GetMessage(), timeout, GetFlags());
471}
472
473bool wxNotificationMessage::Close()
474{
475 wxCHECK_MSG( m_impl, false, "must show the notification first" );
476
477 return m_impl->DoClose();
478}
479
480wxNotificationMessage::~wxNotificationMessage()
481{
482 delete m_impl;
483}
484
485#endif // wxUSE_NOTIFICATION_MESSAGE && wxUSE_TASKBARICON