]>
Commit | Line | Data |
---|---|---|
e2d5abbf VZ |
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 | ||
01b7f8f2 VZ |
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 | |
e2d5abbf | 30 | |
11a0827d PC |
31 | #include "wx/notifmsg.h" |
32 | ||
e2d5abbf | 33 | #ifndef WX_PRECOMP |
11a0827d PC |
34 | #include "wx/toplevel.h" |
35 | #include "wx/app.h" | |
e2d5abbf VZ |
36 | #include "wx/string.h" |
37 | #endif // WX_PRECOMP | |
38 | ||
e2d5abbf VZ |
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 | |
48 | class wxNotifMsgImpl | |
49 | { | |
50 | public: | |
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 | ||
60 | private: | |
c0c133e1 | 61 | wxDECLARE_NO_COPY_CLASS(wxNotifMsgImpl); |
e2d5abbf VZ |
62 | }; |
63 | ||
64 | // implementation which is simply a bridge to wxGenericNotificationMessage | |
65 | class wxGenericNotifMsgImpl : public wxNotifMsgImpl | |
66 | { | |
67 | public: | |
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 | ||
87 | private: | |
88 | wxGenericNotificationMessage * const m_notif; | |
89 | }; | |
90 | ||
91 | // common base class for implementations using a taskbar icon and balloons | |
92 | class wxBalloonNotifMsgImpl : public wxNotifMsgImpl | |
93 | { | |
94 | public: | |
3b4b952d VZ |
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. | |
e2d5abbf VZ |
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 | ||
e2d5abbf | 109 | |
3b4b952d VZ |
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 | } | |
e2d5abbf | 134 | |
3b4b952d VZ |
135 | protected: |
136 | // Creates a new icon if necessary, see the comment below. | |
137 | void SetUpIcon(wxWindow *win); | |
e2d5abbf | 138 | |
e2d5abbf | 139 | |
3b4b952d VZ |
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; | |
e2d5abbf VZ |
156 | }; |
157 | ||
158 | // implementation for automatically hidden notifications | |
159 | class wxAutoNotifMsgImpl : public wxBalloonNotifMsgImpl | |
160 | { | |
161 | public: | |
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; } | |
e2d5abbf VZ |
171 | }; |
172 | ||
173 | // implementation for manually closed notifications | |
174 | class wxManualNotifMsgImpl : public wxBalloonNotifMsgImpl | |
175 | { | |
176 | public: | |
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 | ||
186 | private: | |
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 | ||
202 | class wxNotificationIconEvtHandler : public wxEvtHandler | |
203 | { | |
204 | public: | |
205 | wxNotificationIconEvtHandler(wxTaskBarIcon *icon); | |
206 | ||
207 | private: | |
208 | void OnTimeout(wxTaskBarIconEvent& event); | |
209 | void OnClick(wxTaskBarIconEvent& event); | |
210 | ||
211 | void OnIconHidden(); | |
212 | ||
213 | ||
214 | wxTaskBarIcon * const m_icon; | |
215 | ||
c0c133e1 | 216 | wxDECLARE_NO_COPY_CLASS(wxNotificationIconEvtHandler); |
e2d5abbf VZ |
217 | }; |
218 | ||
219 | // ============================================================================ | |
220 | // implementation | |
221 | // ============================================================================ | |
222 | ||
223 | // ---------------------------------------------------------------------------- | |
224 | // wxNotificationIconEvtHandler | |
225 | // ---------------------------------------------------------------------------- | |
226 | ||
227 | wxNotificationIconEvtHandler::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, | |
64336927 | 241 | wxTaskBarIconEventHandler(wxNotificationIconEvtHandler::OnClick), |
e2d5abbf VZ |
242 | NULL, |
243 | this | |
244 | ); | |
245 | } | |
246 | ||
247 | void wxNotificationIconEvtHandler::OnIconHidden() | |
248 | { | |
3b4b952d | 249 | wxBalloonNotifMsgImpl::ReleaseIcon(); |
e2d5abbf VZ |
250 | |
251 | delete this; | |
252 | } | |
253 | ||
254 | void | |
255 | wxNotificationIconEvtHandler::OnTimeout(wxTaskBarIconEvent& WXUNUSED(event)) | |
256 | { | |
257 | OnIconHidden(); | |
258 | } | |
259 | ||
260 | void 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 | ||
3b4b952d VZ |
271 | wxTaskBarIcon *wxBalloonNotifMsgImpl::ms_icon = NULL; |
272 | int wxBalloonNotifMsgImpl::ms_refCountIcon = 0; | |
e2d5abbf VZ |
273 | |
274 | /* static */ | |
275 | wxTaskBarIcon *wxBalloonNotifMsgImpl::UseTaskBarIcon(wxTaskBarIcon *icon) | |
276 | { | |
3b4b952d VZ |
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 | ||
e2d5abbf VZ |
283 | return iconOld; |
284 | } | |
285 | ||
286 | void wxBalloonNotifMsgImpl::SetUpIcon(wxWindow *win) | |
287 | { | |
3b4b952d | 288 | if ( ms_icon ) |
e2d5abbf | 289 | { |
3b4b952d VZ |
290 | // Increment the reference count if we manage the icon on our own. |
291 | if ( ms_refCountIcon != -1 ) | |
292 | ms_refCountIcon++; | |
e2d5abbf | 293 | } |
3b4b952d | 294 | else // Create a new icon. |
e2d5abbf | 295 | { |
3b4b952d VZ |
296 | wxASSERT_MSG( ms_refCountIcon == 0, |
297 | wxS("Shouldn't reference not existent icon") ); | |
298 | ||
299 | ms_icon = new wxTaskBarIcon; | |
300 | ms_refCountIcon = 1; | |
e2d5abbf VZ |
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 | |
9a83f860 | 319 | icon = wxIcon(wxT("wxICON_AAA")); |
e2d5abbf VZ |
320 | } |
321 | ||
3b4b952d | 322 | ms_icon->SetIcon(icon); |
e2d5abbf VZ |
323 | } |
324 | } | |
325 | ||
326 | bool | |
327 | wxBalloonNotifMsgImpl::DoShow(const wxString& title, | |
328 | const wxString& message, | |
329 | int timeout, | |
330 | int flags) | |
331 | { | |
3b4b952d | 332 | if ( !ms_icon->IsIconInstalled() ) |
ac664fea VZ |
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. | |
3b4b952d VZ |
343 | delete ms_icon; |
344 | ms_icon = NULL; | |
ac664fea VZ |
345 | |
346 | return false; | |
347 | } | |
348 | ||
e2d5abbf VZ |
349 | timeout *= 1000; // Windows expresses timeout in milliseconds |
350 | ||
3b4b952d | 351 | return ms_icon->ShowBalloon(title, message, timeout, flags); |
e2d5abbf VZ |
352 | } |
353 | ||
354 | // ---------------------------------------------------------------------------- | |
355 | // wxManualNotifMsgImpl | |
356 | // ---------------------------------------------------------------------------- | |
357 | ||
358 | wxManualNotifMsgImpl::wxManualNotifMsgImpl(wxWindow *win) | |
359 | : wxBalloonNotifMsgImpl(win), | |
360 | m_win(win) | |
361 | { | |
362 | } | |
363 | ||
364 | wxManualNotifMsgImpl::~wxManualNotifMsgImpl() | |
365 | { | |
3b4b952d | 366 | if ( ms_icon ) |
e2d5abbf VZ |
367 | DoClose(); |
368 | } | |
369 | ||
370 | bool | |
371 | wxManualNotifMsgImpl::DoShow(const wxString& title, | |
372 | const wxString& message, | |
68753711 | 373 | int WXUNUSED_UNLESS_DEBUG(timeout), |
e2d5abbf VZ |
374 | int flags) |
375 | { | |
376 | wxASSERT_MSG( timeout == wxNotificationMessage::Timeout_Never, | |
9a83f860 | 377 | wxT("shouldn't be used") ); |
e2d5abbf VZ |
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 | |
3b4b952d | 381 | if ( !ms_icon ) |
e2d5abbf VZ |
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 | ||
389 | bool wxManualNotifMsgImpl::DoClose() | |
390 | { | |
3b4b952d | 391 | if ( IsUsingOwnIcon() ) |
e2d5abbf VZ |
392 | { |
393 | // we don't need the icon any more | |
3b4b952d | 394 | ReleaseIcon(); |
e2d5abbf VZ |
395 | } |
396 | else // using an existing icon | |
397 | { | |
398 | // just hide the balloon | |
3b4b952d | 399 | ms_icon->ShowBalloon("", ""); |
e2d5abbf VZ |
400 | } |
401 | ||
e2d5abbf VZ |
402 | return true; |
403 | } | |
404 | ||
405 | // ---------------------------------------------------------------------------- | |
406 | // wxAutoNotifMsgImpl | |
407 | // ---------------------------------------------------------------------------- | |
408 | ||
409 | wxAutoNotifMsgImpl::wxAutoNotifMsgImpl(wxWindow *win) | |
4dd8339b | 410 | : wxBalloonNotifMsgImpl(win) |
e2d5abbf | 411 | { |
3b4b952d | 412 | if ( ms_refCountIcon != -1 ) |
4dd8339b | 413 | { |
3b4b952d VZ |
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); | |
4dd8339b | 417 | } |
e2d5abbf VZ |
418 | } |
419 | ||
420 | bool | |
421 | wxAutoNotifMsgImpl::DoShow(const wxString& title, | |
422 | const wxString& message, | |
423 | int timeout, | |
424 | int flags) | |
425 | { | |
426 | wxASSERT_MSG( timeout != wxNotificationMessage::Timeout_Never, | |
9a83f860 | 427 | wxT("shouldn't be used") ); |
e2d5abbf VZ |
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 | ||
5e6b39c9 VZ |
442 | /* static */ |
443 | bool wxNotificationMessage::ms_alwaysUseGeneric = false; | |
444 | ||
e2d5abbf VZ |
445 | /* static */ |
446 | wxTaskBarIcon *wxNotificationMessage::UseTaskBarIcon(wxTaskBarIcon *icon) | |
447 | { | |
448 | return wxBalloonNotifMsgImpl::UseTaskBarIcon(icon); | |
449 | } | |
450 | ||
451 | bool wxNotificationMessage::Show(int timeout) | |
452 | { | |
453 | if ( !m_impl ) | |
454 | { | |
5e6b39c9 | 455 | if ( !ms_alwaysUseGeneric && wxTheApp->GetShell32Version() >= 500 ) |
e2d5abbf VZ |
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 | ||
473 | bool wxNotificationMessage::Close() | |
474 | { | |
475 | wxCHECK_MSG( m_impl, false, "must show the notification first" ); | |
476 | ||
477 | return m_impl->DoClose(); | |
478 | } | |
479 | ||
480 | wxNotificationMessage::~wxNotificationMessage() | |
481 | { | |
482 | delete m_impl; | |
483 | } | |
484 | ||
485 | #endif // wxUSE_NOTIFICATION_MESSAGE && wxUSE_TASKBARICON |