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