]> git.saurik.com Git - wxWidgets.git/blob - src/msw/taskbar.cpp
check that the version of __sync_sub_and_fetch that returns a value is supported...
[wxWidgets.git] / src / msw / taskbar.cpp
1 /////////////////////////////////////////////////////////////////////////
2 // File: src/msw/taskbar.cpp
3 // Purpose: Implements wxTaskBarIcon class for manipulating icons on
4 // the Windows task bar.
5 // Author: Julian Smart
6 // Modified by: Vaclav Slavik
7 // Created: 24/3/98
8 // RCS-ID: $Id$
9 // Copyright: (c)
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////
12
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
15
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19
20 #if wxUSE_TASKBARICON
21
22 #ifndef WX_PRECOMP
23 #include "wx/window.h"
24 #include "wx/frame.h"
25 #include "wx/utils.h"
26 #include "wx/menu.h"
27 #endif
28
29 #include "wx/msw/wrapshl.h"
30
31 #include <string.h>
32 #include "wx/taskbar.h"
33 #include "wx/dynlib.h"
34
35 #ifndef NIN_BALLOONTIMEOUT
36 #define NIN_BALLOONTIMEOUT 0x0404
37 #define NIN_BALLOONUSERCLICK 0x0405
38 #endif
39
40 // initialized on demand
41 static UINT gs_msgTaskbar = 0;
42 static UINT gs_msgRestartTaskbar = 0;
43
44
45 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
46
47 // ============================================================================
48 // implementation
49 // ============================================================================
50
51 // wrapper around Shell_NotifyIcon(): this function is not present in Win95
52 // shell32.dll so load it dynamically to allow programs using wxTaskBarIcon to
53 // start under this OS
54 static BOOL wxShellNotifyIcon(DWORD dwMessage, NOTIFYICONDATA *pData)
55 {
56 #if wxUSE_DYNLIB_CLASS
57 typedef BOOL (WINAPI *Shell_NotifyIcon_t)(DWORD, NOTIFYICONDATA *);
58
59 static Shell_NotifyIcon_t s_pfnShell_NotifyIcon = NULL;
60 static bool s_initialized = false;
61 if ( !s_initialized )
62 {
63 s_initialized = true;
64
65 wxLogNull noLog;
66 wxDynamicLibrary dllShell("shell32.dll");
67 if ( dllShell.IsLoaded() )
68 {
69 wxDL_INIT_FUNC_AW(s_pfn, Shell_NotifyIcon, dllShell);
70 }
71
72 // NB: it's ok to destroy dllShell here, we link to shell32.dll
73 // implicitly so it won't be unloaded
74 }
75
76 return s_pfnShell_NotifyIcon ? (*s_pfnShell_NotifyIcon)(dwMessage, pData)
77 : FALSE;
78 #else // !wxUSE_DYNLIB_CLASS
79 return Shell_NotifyIcon(dwMessage, pData);
80 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
81 }
82
83 // ----------------------------------------------------------------------------
84 // wxTaskBarIconWindow: helper window
85 // ----------------------------------------------------------------------------
86
87 // NB: this class serves two purposes:
88 // 1. win32 needs a HWND associated with taskbar icon, this provides it
89 // 2. we need wxTopLevelWindow so that the app doesn't exit when
90 // last frame is closed but there still is a taskbar icon
91 class wxTaskBarIconWindow : public wxFrame
92 {
93 public:
94 wxTaskBarIconWindow(wxTaskBarIcon *icon)
95 : wxFrame(NULL, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0),
96 m_icon(icon)
97 {
98 }
99
100 WXLRESULT MSWWindowProc(WXUINT msg,
101 WXWPARAM wParam, WXLPARAM lParam)
102 {
103 if (msg == gs_msgRestartTaskbar || msg == gs_msgTaskbar)
104 {
105 return m_icon->WindowProc(msg, wParam, lParam);
106 }
107 else
108 {
109 return wxFrame::MSWWindowProc(msg, wParam, lParam);
110 }
111 }
112
113 private:
114 wxTaskBarIcon *m_icon;
115 };
116
117
118 // ----------------------------------------------------------------------------
119 // NotifyIconData: wrapper around NOTIFYICONDATA
120 // ----------------------------------------------------------------------------
121
122 struct NotifyIconData : public NOTIFYICONDATA
123 {
124 NotifyIconData(WXHWND hwnd)
125 {
126 memset(this, 0, sizeof(NOTIFYICONDATA));
127 cbSize = sizeof(NOTIFYICONDATA);
128 hWnd = (HWND) hwnd;
129 uCallbackMessage = gs_msgTaskbar;
130 uFlags = NIF_MESSAGE;
131
132 // we use the same id for all taskbar icons as we don't need it to
133 // distinguish between them
134 uID = 99;
135 }
136 };
137
138 // ----------------------------------------------------------------------------
139 // wxTaskBarIcon
140 // ----------------------------------------------------------------------------
141
142 wxTaskBarIcon::wxTaskBarIcon()
143 {
144 m_win = NULL;
145 m_iconAdded = false;
146 RegisterWindowMessages();
147 }
148
149 wxTaskBarIcon::~wxTaskBarIcon()
150 {
151 if ( m_iconAdded )
152 RemoveIcon();
153
154 if ( m_win )
155 {
156 // we must use delete and not Destroy() here because the latter will
157 // only schedule the window to be deleted during the next idle event
158 // processing but we may not get any idle events if there are no other
159 // windows left in the program
160 delete m_win;
161 }
162 }
163
164 // Operations
165 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
166 {
167 // NB: we have to create the window lazily because of backward compatibility,
168 // old applications may create a wxTaskBarIcon instance before wxApp
169 // is initialized (as samples/taskbar used to do)
170 if (!m_win)
171 {
172 m_win = new wxTaskBarIconWindow(this);
173 }
174
175 m_icon = icon;
176 m_strTooltip = tooltip;
177
178 NotifyIconData notifyData(GetHwndOf(m_win));
179
180 if (icon.Ok())
181 {
182 notifyData.uFlags |= NIF_ICON;
183 notifyData.hIcon = GetHiconOf(icon);
184 }
185
186 // set NIF_TIP even for an empty tooltip: otherwise it would be impossible
187 // to remove an existing tooltip using this function
188 notifyData.uFlags |= NIF_TIP;
189 if ( !tooltip.empty() )
190 {
191 wxStrncpy(notifyData.szTip, tooltip.wx_str(), WXSIZEOF(notifyData.szTip));
192 }
193
194 bool ok = wxShellNotifyIcon(m_iconAdded ? NIM_MODIFY
195 : NIM_ADD, &notifyData) != 0;
196
197 if ( !m_iconAdded && ok )
198 m_iconAdded = true;
199
200 return ok;
201 }
202
203 bool
204 wxTaskBarIcon::ShowBalloon(const wxString& title,
205 const wxString& text,
206 unsigned msec,
207 int flags)
208 {
209 wxCHECK_MSG( m_iconAdded, false,
210 _T("can't be used before the icon is created") );
211
212 const HWND hwnd = GetHwndOf(m_win);
213
214 // we need to enable version 5.0 behaviour to receive notifications about
215 // the balloon disappearance
216 NotifyIconData notifyData(hwnd);
217 notifyData.uFlags = 0;
218 notifyData.uVersion = 3 /* NOTIFYICON_VERSION for Windows XP */;
219
220 wxShellNotifyIcon(NIM_SETVERSION, &notifyData);
221
222
223 // do show the balloon now
224 notifyData = NotifyIconData(hwnd);
225 notifyData.uFlags |= NIF_INFO;
226 notifyData.uTimeout = msec;
227 wxStrncpy(notifyData.szInfo, text.wx_str(), WXSIZEOF(notifyData.szInfo));
228 wxStrncpy(notifyData.szInfoTitle, title.wx_str(),
229 WXSIZEOF(notifyData.szInfoTitle));
230
231 if ( flags & wxICON_INFORMATION )
232 notifyData.dwInfoFlags |= NIIF_INFO;
233 else if ( flags & wxICON_WARNING )
234 notifyData.dwInfoFlags |= NIIF_WARNING;
235 else if ( flags & wxICON_ERROR )
236 notifyData.dwInfoFlags |= NIIF_ERROR;
237
238 return wxShellNotifyIcon(NIM_MODIFY, &notifyData) != 0;
239 }
240
241 bool wxTaskBarIcon::RemoveIcon()
242 {
243 if (!m_iconAdded)
244 return false;
245
246 m_iconAdded = false;
247
248 NotifyIconData notifyData(GetHwndOf(m_win));
249
250 return wxShellNotifyIcon(NIM_DELETE, &notifyData) != 0;
251 }
252
253 #if wxUSE_MENUS
254 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
255 {
256 wxASSERT_MSG( m_win != NULL, _T("taskbar icon not initialized") );
257
258 static bool s_inPopup = false;
259
260 if (s_inPopup)
261 return false;
262
263 s_inPopup = true;
264
265 int x, y;
266 wxGetMousePosition(&x, &y);
267
268 m_win->Move(x, y);
269
270 m_win->PushEventHandler(this);
271
272 menu->UpdateUI();
273
274 // the SetForegroundWindow() and PostMessage() calls are needed to work
275 // around Win32 bug with the popup menus shown for the notifications as
276 // documented at http://support.microsoft.com/kb/q135788/
277 ::SetForegroundWindow(GetHwndOf(m_win));
278
279 bool rval = m_win->PopupMenu(menu, 0, 0);
280
281 ::PostMessage(GetHwndOf(m_win), WM_NULL, 0, 0L);
282
283 m_win->PopEventHandler(false);
284
285 s_inPopup = false;
286
287 return rval;
288 }
289 #endif // wxUSE_MENUS
290
291 void wxTaskBarIcon::RegisterWindowMessages()
292 {
293 static bool s_registered = false;
294
295 if ( !s_registered )
296 {
297 // Taskbar restart msg will be sent to us if the icon needs to be redrawn
298 gs_msgRestartTaskbar = RegisterWindowMessage(wxT("TaskbarCreated"));
299
300 // Also register the taskbar message here
301 gs_msgTaskbar = ::RegisterWindowMessage(wxT("wxTaskBarIconMessage"));
302
303 s_registered = true;
304 }
305 }
306
307 // ----------------------------------------------------------------------------
308 // wxTaskBarIcon window proc
309 // ----------------------------------------------------------------------------
310
311 long wxTaskBarIcon::WindowProc(unsigned int msg,
312 unsigned int WXUNUSED(wParam),
313 long lParam)
314 {
315 if ( msg == gs_msgRestartTaskbar ) // does the icon need to be redrawn?
316 {
317 m_iconAdded = false;
318 SetIcon(m_icon, m_strTooltip);
319 return 0;
320 }
321
322 // this function should only be called for gs_msg(Restart)Taskbar messages
323 wxASSERT( msg == gs_msgTaskbar );
324
325 wxEventType eventType = 0;
326 switch ( lParam )
327 {
328 case WM_LBUTTONDOWN:
329 eventType = wxEVT_TASKBAR_LEFT_DOWN;
330 break;
331
332 case WM_LBUTTONUP:
333 eventType = wxEVT_TASKBAR_LEFT_UP;
334 break;
335
336 case WM_RBUTTONDOWN:
337 eventType = wxEVT_TASKBAR_RIGHT_DOWN;
338 break;
339
340 case WM_RBUTTONUP:
341 eventType = wxEVT_TASKBAR_RIGHT_UP;
342 break;
343
344 case WM_LBUTTONDBLCLK:
345 eventType = wxEVT_TASKBAR_LEFT_DCLICK;
346 break;
347
348 case WM_RBUTTONDBLCLK:
349 eventType = wxEVT_TASKBAR_RIGHT_DCLICK;
350 break;
351
352 case WM_MOUSEMOVE:
353 eventType = wxEVT_TASKBAR_MOVE;
354 break;
355
356 case NIN_BALLOONTIMEOUT:
357 eventType = wxEVT_TASKBAR_BALLOON_TIMEOUT;
358 break;
359
360 case NIN_BALLOONUSERCLICK:
361 eventType = wxEVT_TASKBAR_BALLOON_CLICK;
362 break;
363 }
364
365 if ( eventType )
366 {
367 wxTaskBarIconEvent event(eventType, this);
368
369 ProcessEvent(event);
370 }
371
372 return 0;
373 }
374
375 #endif // wxUSE_TASKBARICON
376