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