]>
Commit | Line | Data |
---|---|---|
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 |