]> git.saurik.com Git - wxWidgets.git/blob - src/msw/notifmsg.cpp
No changes, just use wxRecursionGuard instead of manual boolean flag.
[wxWidgets.git] / src / msw / notifmsg.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/notifmsg.cpp
3 // Purpose: implementation of wxNotificationMessage for Windows
4 // Author: Vadim Zeitlin
5 // Created: 2007-12-01
6 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 // we can only use the native implementation if we have a working
26 // wxTaskBarIcon::ShowBalloon() method
27 #if wxUSE_NOTIFICATION_MESSAGE && \
28 wxUSE_TASKBARICON && wxUSE_TASKBARICON_BALLOONS
29
30 #include "wx/notifmsg.h"
31
32 #ifndef WX_PRECOMP
33 #include "wx/toplevel.h"
34 #include "wx/app.h"
35 #include "wx/string.h"
36 #endif // WX_PRECOMP
37
38 #include "wx/generic/notifmsg.h"
39
40 #include "wx/taskbar.h"
41
42 // ----------------------------------------------------------------------------
43 // different implementations used by wxNotificationMessage
44 // ----------------------------------------------------------------------------
45
46 // base class for all available implementations
47 class wxNotifMsgImpl
48 {
49 public:
50 wxNotifMsgImpl() { }
51 virtual ~wxNotifMsgImpl() { }
52
53 virtual bool DoShow(const wxString& title,
54 const wxString& message,
55 int timeout,
56 int flags) = 0;
57 virtual bool DoClose() = 0;
58
59 private:
60 wxDECLARE_NO_COPY_CLASS(wxNotifMsgImpl);
61 };
62
63 // implementation which is simply a bridge to wxGenericNotificationMessage
64 class wxGenericNotifMsgImpl : public wxNotifMsgImpl
65 {
66 public:
67 wxGenericNotifMsgImpl() : m_notif(new wxGenericNotificationMessage) { }
68 virtual ~wxGenericNotifMsgImpl() { delete m_notif; }
69
70 virtual bool DoShow(const wxString& title,
71 const wxString& message,
72 int timeout,
73 int flags)
74 {
75 m_notif->SetTitle(title);
76 m_notif->SetMessage(message);
77 m_notif->SetFlags(flags);
78 return m_notif->Show(timeout);
79 }
80
81 virtual bool DoClose()
82 {
83 return m_notif->Close();
84 }
85
86 private:
87 wxGenericNotificationMessage * const m_notif;
88 };
89
90 // common base class for implementations using a taskbar icon and balloons
91 class wxBalloonNotifMsgImpl : public wxNotifMsgImpl
92 {
93 public:
94 // Ctor creates the associated taskbar icon (using the icon of the top
95 // level parent of the given window) unless UseTaskBarIcon() had been
96 // previously called which can be used to show an attached balloon later
97 // by the derived classes.
98 wxBalloonNotifMsgImpl(wxWindow *win) { SetUpIcon(win); }
99
100 // implementation of wxNotificationMessage method with the same name
101 static wxTaskBarIcon *UseTaskBarIcon(wxTaskBarIcon *icon);
102
103 virtual bool DoShow(const wxString& title,
104 const wxString& message,
105 int timeout,
106 int flags);
107
108
109 // Returns true if we're using our own icon or false if we're hitching a
110 // ride on the application icon provided to us via UseTaskBarIcon().
111 static bool IsUsingOwnIcon()
112 {
113 return ms_refCountIcon != -1;
114 }
115
116 // Indicates that the taskbar icon we're using has been hidden and can be
117 // deleted.
118 //
119 // This is only called by wxNotificationIconEvtHandler and should only be
120 // called when using our own icon (as opposed to the one passed to us via
121 // UseTaskBarIcon()).
122 static void ReleaseIcon()
123 {
124 wxASSERT_MSG( ms_refCountIcon != -1,
125 wxS("Must not be called when not using own icon") );
126
127 if ( !--ms_refCountIcon )
128 {
129 delete ms_icon;
130 ms_icon = NULL;
131 }
132 }
133
134 protected:
135 // Creates a new icon if necessary, see the comment below.
136 void SetUpIcon(wxWindow *win);
137
138
139 // We need an icon to show the notification in a balloon attached to it.
140 // It may happen that the main application already shows an icon in the
141 // taskbar notification area in which case it should call our
142 // UseTaskBarIcon() and we just use this icon without ever allocating nor
143 // deleting it and ms_refCountIcon is -1 and never changes. Otherwise, we
144 // create the icon when we need it the first time but reuse it if we need
145 // to show subsequent notifications while this icon is still alive. This is
146 // needed in order to avoid 2 or 3 or even more identical icons if a couple
147 // of notifications are shown in a row (which happens quite easily in
148 // practice because Windows helpfully buffers all the notifications that
149 // were generated while the user was away -- i.e. the screensaver was
150 // active -- and then shows them all at once when the user comes back). In
151 // this case, ms_refCountIcon is used as a normal reference counter, i.e.
152 // the icon is only destroyed when it reaches 0.
153 static wxTaskBarIcon *ms_icon;
154 static int ms_refCountIcon;
155 };
156
157 // implementation for automatically hidden notifications
158 class wxAutoNotifMsgImpl : public wxBalloonNotifMsgImpl
159 {
160 public:
161 wxAutoNotifMsgImpl(wxWindow *win);
162
163 virtual bool DoShow(const wxString& title,
164 const wxString& message,
165 int timeout,
166 int flags);
167
168 // can't close automatic notification [currently]
169 virtual bool DoClose() { return false; }
170 };
171
172 // implementation for manually closed notifications
173 class wxManualNotifMsgImpl : public wxBalloonNotifMsgImpl
174 {
175 public:
176 wxManualNotifMsgImpl(wxWindow *win);
177 virtual ~wxManualNotifMsgImpl();
178
179 virtual bool DoShow(const wxString& title,
180 const wxString& message,
181 int timeout,
182 int flags);
183 virtual bool DoClose();
184
185 private:
186 // store ctor parameter as we need it to recreate the icon later if we're
187 // closed and shown again
188 wxWindow * const m_win;
189 };
190
191 // ----------------------------------------------------------------------------
192 // custom event handler for task bar icons
193 // ----------------------------------------------------------------------------
194
195 // normally we'd just use a custom taskbar icon class but this is impossible
196 // because we can be asked to attach the notifications to an existing icon
197 // which we didn't create, hence we install a special event handler allowing us
198 // to get the events we need (and, crucially, to delete the icon when it's not
199 // needed any more) in any case
200
201 class wxNotificationIconEvtHandler : public wxEvtHandler
202 {
203 public:
204 wxNotificationIconEvtHandler(wxTaskBarIcon *icon);
205
206 private:
207 void OnTimeout(wxTaskBarIconEvent& event);
208 void OnClick(wxTaskBarIconEvent& event);
209
210 void OnIconHidden();
211
212
213 wxTaskBarIcon * const m_icon;
214
215 wxDECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler);
216 };
217
218 // ============================================================================
219 // implementation
220 // ============================================================================
221
222 // ----------------------------------------------------------------------------
223 // wxNotificationIconEvtHandler
224 // ----------------------------------------------------------------------------
225
226 wxNotificationIconEvtHandler::wxNotificationIconEvtHandler(wxTaskBarIcon *icon)
227 : m_icon(icon)
228 {
229 m_icon->Connect
230 (
231 wxEVT_TASKBAR_BALLOON_TIMEOUT,
232 wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnTimeout),
233 NULL,
234 this
235 );
236
237 m_icon->Connect
238 (
239 wxEVT_TASKBAR_BALLOON_CLICK,
240 wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnClick),
241 NULL,
242 this
243 );
244 }
245
246 void wxNotificationIconEvtHandler::OnIconHidden()
247 {
248 wxBalloonNotifMsgImpl::ReleaseIcon();
249
250 delete this;
251 }
252
253 void
254 wxNotificationIconEvtHandler::OnTimeout(wxTaskBarIconEvent& WXUNUSED(event))
255 {
256 OnIconHidden();
257 }
258
259 void wxNotificationIconEvtHandler::OnClick(wxTaskBarIconEvent& WXUNUSED(event))
260 {
261 // TODO: generate an event notifying the user code?
262
263 OnIconHidden();
264 }
265
266 // ----------------------------------------------------------------------------
267 // wxBalloonNotifMsgImpl
268 // ----------------------------------------------------------------------------
269
270 wxTaskBarIcon *wxBalloonNotifMsgImpl::ms_icon = NULL;
271 int wxBalloonNotifMsgImpl::ms_refCountIcon = 0;
272
273 /* static */
274 wxTaskBarIcon *wxBalloonNotifMsgImpl::UseTaskBarIcon(wxTaskBarIcon *icon)
275 {
276 wxTaskBarIcon * const iconOld = ms_icon;
277 ms_icon = icon;
278
279 // Don't use reference counting for the provided icon, we don't own it.
280 ms_refCountIcon = icon ? -1 : 0;
281
282 return iconOld;
283 }
284
285 void wxBalloonNotifMsgImpl::SetUpIcon(wxWindow *win)
286 {
287 if ( ms_icon )
288 {
289 // Increment the reference count if we manage the icon on our own.
290 if ( ms_refCountIcon != -1 )
291 ms_refCountIcon++;
292 }
293 else // Create a new icon.
294 {
295 wxASSERT_MSG( ms_refCountIcon == 0,
296 wxS("Shouldn't reference not existent icon") );
297
298 ms_icon = new wxTaskBarIcon;
299 ms_refCountIcon = 1;
300
301 // use the icon of the associated (or main, if none) frame
302 wxIcon icon;
303 if ( win )
304 win = wxGetTopLevelParent(win);
305 if ( !win )
306 win = wxTheApp->GetTopWindow();
307 if ( win )
308 {
309 const wxTopLevelWindow * const
310 tlw = wxDynamicCast(win, wxTopLevelWindow);
311 if ( tlw )
312 icon = tlw->GetIcon();
313 }
314
315 if ( !icon.IsOk() )
316 {
317 // we really must have some icon
318 icon = wxIcon(wxT("wxICON_AAA"));
319 }
320
321 ms_icon->SetIcon(icon);
322 }
323 }
324
325 bool
326 wxBalloonNotifMsgImpl::DoShow(const wxString& title,
327 const wxString& message,
328 int timeout,
329 int flags)
330 {
331 if ( !ms_icon->IsIconInstalled() )
332 {
333 // If we failed to install the icon (which does happen sometimes,
334 // although only in unusual circumstances, e.g. it happens regularly,
335 // albeit not constantly, if we're used soon after resume from suspend
336 // under Windows 7), we should not call ShowBalloon() because it would
337 // just assert and return and we must delete the icon ourselves because
338 // otherwise its associated wxTaskBarIconWindow would remain alive
339 // forever because we're not going to receive a notification about icon
340 // disappearance from the system if we failed to install it in the
341 // first place.
342 delete ms_icon;
343 ms_icon = NULL;
344
345 return false;
346 }
347
348 timeout *= 1000; // Windows expresses timeout in milliseconds
349
350 return ms_icon->ShowBalloon(title, message, timeout, flags);
351 }
352
353 // ----------------------------------------------------------------------------
354 // wxManualNotifMsgImpl
355 // ----------------------------------------------------------------------------
356
357 wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow *win)
358 : wxBalloonNotifMsgImpl(win),
359 m_win(win)
360 {
361 }
362
363 wxManualNotifMsgImpl::~wxManualNotifMsgImpl()
364 {
365 if ( ms_icon )
366 DoClose();
367 }
368
369 bool
370 wxManualNotifMsgImpl::DoShow(const wxString& title,
371 const wxString& message,
372 int WXUNUSED_UNLESS_DEBUG(timeout),
373 int flags)
374 {
375 wxASSERT_MSG( timeout == wxNotificationMessage::Timeout_Never,
376 wxT("shouldn't be used") );
377
378 // base class creates the icon for us initially but we could have destroyed
379 // it in DoClose(), recreate it if this was the case
380 if ( !ms_icon )
381 SetUpIcon(m_win);
382
383 // use maximal (in current Windows versions) timeout (but it will still
384 // disappear on its own)
385 return wxBalloonNotifMsgImpl::DoShow(title, message, 30, flags);
386 }
387
388 bool wxManualNotifMsgImpl::DoClose()
389 {
390 if ( IsUsingOwnIcon() )
391 {
392 // we don't need the icon any more
393 ReleaseIcon();
394 }
395 else // using an existing icon
396 {
397 // just hide the balloon
398 ms_icon->ShowBalloon("", "");
399 }
400
401 return true;
402 }
403
404 // ----------------------------------------------------------------------------
405 // wxAutoNotifMsgImpl
406 // ----------------------------------------------------------------------------
407
408 wxAutoNotifMsgImpl::wxAutoNotifMsgImpl(wxWindow *win)
409 : wxBalloonNotifMsgImpl(win)
410 {
411 if ( ms_refCountIcon != -1 )
412 {
413 // This object will self-destruct and decrease the ref count of the
414 // icon when the notification is hidden.
415 new wxNotificationIconEvtHandler(ms_icon);
416 }
417 }
418
419 bool
420 wxAutoNotifMsgImpl::DoShow(const wxString& title,
421 const wxString& message,
422 int timeout,
423 int flags)
424 {
425 wxASSERT_MSG( timeout != wxNotificationMessage::Timeout_Never,
426 wxT("shouldn't be used") );
427
428 if ( timeout == wxNotificationMessage::Timeout_Auto )
429 {
430 // choose a value more or less in the middle of the allowed range
431 timeout = 1;
432 }
433
434 return wxBalloonNotifMsgImpl::DoShow(title, message, timeout, flags);
435 }
436
437 // ----------------------------------------------------------------------------
438 // wxNotificationMessage
439 // ----------------------------------------------------------------------------
440
441 /* static */
442 bool wxNotificationMessage::ms_alwaysUseGeneric = false;
443
444 /* static */
445 wxTaskBarIcon *wxNotificationMessage::UseTaskBarIcon(wxTaskBarIcon *icon)
446 {
447 return wxBalloonNotifMsgImpl::UseTaskBarIcon(icon);
448 }
449
450 bool wxNotificationMessage::Show(int timeout)
451 {
452 if ( !m_impl )
453 {
454 if ( !ms_alwaysUseGeneric && wxTheApp->GetShell32Version() >= 500 )
455 {
456 if ( timeout == Timeout_Never )
457 m_impl = new wxManualNotifMsgImpl(GetParent());
458 else
459 m_impl = new wxAutoNotifMsgImpl(GetParent());
460 }
461 else // no support for balloon tooltips
462 {
463 m_impl = new wxGenericNotifMsgImpl;
464 }
465 }
466 //else: reuse the same implementation for the subsequent calls, it would
467 // be too confusing if it changed
468
469 return m_impl->DoShow(GetTitle(), GetMessage(), timeout, GetFlags());
470 }
471
472 bool wxNotificationMessage::Close()
473 {
474 wxCHECK_MSG( m_impl, false, "must show the notification first" );
475
476 return m_impl->DoClose();
477 }
478
479 wxNotificationMessage::~wxNotificationMessage()
480 {
481 delete m_impl;
482 }
483
484 #endif // wxUSE_NOTIFICATION_MESSAGE && wxUSE_TASKBARICON