]> git.saurik.com Git - wxWidgets.git/blob - src/msw/taskbar.cpp
define NIN_BALLOONXXX constants if the SDK doesn't
[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 m_win->Destroy();
156 }
157
158 // Operations
159 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
160 {
161 // NB: we have to create the window lazily because of backward compatibility,
162 // old applications may create a wxTaskBarIcon instance before wxApp
163 // is initialized (as samples/taskbar used to do)
164 if (!m_win)
165 {
166 m_win = new wxTaskBarIconWindow(this);
167 }
168
169 m_icon = icon;
170 m_strTooltip = tooltip;
171
172 NotifyIconData notifyData(GetHwndOf(m_win));
173
174 if (icon.Ok())
175 {
176 notifyData.uFlags |= NIF_ICON;
177 notifyData.hIcon = GetHiconOf(icon);
178 }
179
180 // set NIF_TIP even for an empty tooltip: otherwise it would be impossible
181 // to remove an existing tooltip using this function
182 notifyData.uFlags |= NIF_TIP;
183 if ( !tooltip.empty() )
184 {
185 wxStrncpy(notifyData.szTip, tooltip.wx_str(), WXSIZEOF(notifyData.szTip));
186 }
187
188 bool ok = wxShellNotifyIcon(m_iconAdded ? NIM_MODIFY
189 : NIM_ADD, &notifyData) != 0;
190
191 if ( !m_iconAdded && ok )
192 m_iconAdded = true;
193
194 return ok;
195 }
196
197 bool
198 wxTaskBarIcon::ShowBalloon(const wxString& title,
199 const wxString& text,
200 unsigned msec,
201 int flags)
202 {
203 wxCHECK_MSG( m_iconAdded, false,
204 _T("can't be used before the icon is created") );
205
206 const HWND hwnd = GetHwndOf(m_win);
207
208 // we need to enable version 5.0 behaviour to receive notifications about
209 // the balloon disappearance
210 NotifyIconData notifyData(hwnd);
211 notifyData.uFlags = 0;
212 notifyData.uVersion = 3 /* NOTIFYICON_VERSION for Windows XP */;
213
214 wxShellNotifyIcon(NIM_SETVERSION, &notifyData);
215
216
217 // do show the balloon now
218 notifyData = NotifyIconData(hwnd);
219 notifyData.uFlags |= NIF_INFO;
220 notifyData.uTimeout = msec;
221 wxStrncpy(notifyData.szInfo, text.wx_str(), WXSIZEOF(notifyData.szInfo));
222 wxStrncpy(notifyData.szInfoTitle, title.wx_str(),
223 WXSIZEOF(notifyData.szInfoTitle));
224
225 if ( flags & wxICON_INFORMATION )
226 notifyData.dwInfoFlags |= NIIF_INFO;
227 else if ( flags & wxICON_WARNING )
228 notifyData.dwInfoFlags |= NIIF_WARNING;
229 else if ( flags & wxICON_ERROR )
230 notifyData.dwInfoFlags |= NIIF_ERROR;
231
232 return wxShellNotifyIcon(NIM_MODIFY, &notifyData) != 0;
233 }
234
235 bool wxTaskBarIcon::RemoveIcon()
236 {
237 if (!m_iconAdded)
238 return false;
239
240 m_iconAdded = false;
241
242 NotifyIconData notifyData(GetHwndOf(m_win));
243
244 return wxShellNotifyIcon(NIM_DELETE, &notifyData) != 0;
245 }
246
247 #if wxUSE_MENUS
248 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
249 {
250 wxASSERT_MSG( m_win != NULL, _T("taskbar icon not initialized") );
251
252 static bool s_inPopup = false;
253
254 if (s_inPopup)
255 return false;
256
257 s_inPopup = true;
258
259 int x, y;
260 wxGetMousePosition(&x, &y);
261
262 m_win->Move(x, y);
263
264 m_win->PushEventHandler(this);
265
266 menu->UpdateUI();
267
268 // the SetForegroundWindow() and PostMessage() calls are needed to work
269 // around Win32 bug with the popup menus shown for the notifications as
270 // documented at http://support.microsoft.com/kb/q135788/
271 ::SetForegroundWindow(GetHwndOf(m_win));
272
273 bool rval = m_win->PopupMenu(menu, 0, 0);
274
275 ::PostMessage(GetHwndOf(m_win), WM_NULL, 0, 0L);
276
277 m_win->PopEventHandler(false);
278
279 s_inPopup = false;
280
281 return rval;
282 }
283 #endif // wxUSE_MENUS
284
285 void wxTaskBarIcon::RegisterWindowMessages()
286 {
287 static bool s_registered = false;
288
289 if ( !s_registered )
290 {
291 // Taskbar restart msg will be sent to us if the icon needs to be redrawn
292 gs_msgRestartTaskbar = RegisterWindowMessage(wxT("TaskbarCreated"));
293
294 // Also register the taskbar message here
295 gs_msgTaskbar = ::RegisterWindowMessage(wxT("wxTaskBarIconMessage"));
296
297 s_registered = true;
298 }
299 }
300
301 // ----------------------------------------------------------------------------
302 // wxTaskBarIcon window proc
303 // ----------------------------------------------------------------------------
304
305 long wxTaskBarIcon::WindowProc(unsigned int msg,
306 unsigned int WXUNUSED(wParam),
307 long lParam)
308 {
309 if ( msg == gs_msgRestartTaskbar ) // does the icon need to be redrawn?
310 {
311 m_iconAdded = false;
312 SetIcon(m_icon, m_strTooltip);
313 return 0;
314 }
315
316 // this function should only be called for gs_msg(Restart)Taskbar messages
317 wxASSERT( msg == gs_msgTaskbar );
318
319 wxEventType eventType = 0;
320 switch ( lParam )
321 {
322 case WM_LBUTTONDOWN:
323 eventType = wxEVT_TASKBAR_LEFT_DOWN;
324 break;
325
326 case WM_LBUTTONUP:
327 eventType = wxEVT_TASKBAR_LEFT_UP;
328 break;
329
330 case WM_RBUTTONDOWN:
331 eventType = wxEVT_TASKBAR_RIGHT_DOWN;
332 break;
333
334 case WM_RBUTTONUP:
335 eventType = wxEVT_TASKBAR_RIGHT_UP;
336 break;
337
338 case WM_LBUTTONDBLCLK:
339 eventType = wxEVT_TASKBAR_LEFT_DCLICK;
340 break;
341
342 case WM_RBUTTONDBLCLK:
343 eventType = wxEVT_TASKBAR_RIGHT_DCLICK;
344 break;
345
346 case WM_MOUSEMOVE:
347 eventType = wxEVT_TASKBAR_MOVE;
348 break;
349
350 case NIN_BALLOONTIMEOUT:
351 eventType = wxEVT_TASKBAR_BALLOON_TIMEOUT;
352 break;
353
354 case NIN_BALLOONUSERCLICK:
355 eventType = wxEVT_TASKBAR_BALLOON_CLICK;
356 break;
357 }
358
359 if ( eventType )
360 {
361 wxTaskBarIconEvent event(eventType, this);
362
363 ProcessEvent(event);
364 }
365
366 return 0;
367 }
368
369 #endif // wxUSE_TASKBARICON
370