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
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
23 #include "wx/window.h"
30 #include "wx/msw/wrapshl.h"
33 #include "wx/taskbar.h"
34 #include "wx/msw/private.h"
35 #include "wx/dynlib.h"
37 #ifndef NIN_BALLOONTIMEOUT
38 #define NIN_BALLOONTIMEOUT 0x0404
39 #define NIN_BALLOONUSERCLICK 0x0405
42 #ifndef NIM_SETVERSION
43 #define NIM_SETVERSION 0x00000004
47 #define NIF_INFO 0x00000010
50 #ifndef NOTIFYICONDATA_V1_SIZE
52 #define NOTIFYICONDATA_V1_SIZE 0x0098
54 #define NOTIFYICONDATA_V1_SIZE 0x0058
58 #ifndef NOTIFYICONDATA_V2_SIZE
60 #define NOTIFYICONDATA_V2_SIZE 0x03A8
62 #define NOTIFYICONDATA_V2_SIZE 0x01E8
66 // initialized on demand
67 static UINT gs_msgTaskbar
= 0;
68 static UINT gs_msgRestartTaskbar
= 0;
71 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon
, wxEvtHandler
)
73 // ============================================================================
75 // ============================================================================
77 // wrapper around Shell_NotifyIcon(): this function is not present in Win95
78 // shell32.dll so load it dynamically to allow programs using wxTaskBarIcon to
79 // start under this OS
80 static BOOL
wxShellNotifyIcon(DWORD dwMessage
, NOTIFYICONDATA
*pData
)
82 #if wxUSE_DYNLIB_CLASS
83 typedef BOOL (WINAPI
*Shell_NotifyIcon_t
)(DWORD
, NOTIFYICONDATA
*);
85 static Shell_NotifyIcon_t s_pfnShell_NotifyIcon
= NULL
;
86 static bool s_initialized
= false;
92 wxDynamicLibrary
dllShell("shell32.dll");
93 if ( dllShell
.IsLoaded() )
95 wxDL_INIT_FUNC_AW(s_pfn
, Shell_NotifyIcon
, dllShell
);
98 // NB: it's ok to destroy dllShell here, we link to shell32.dll
99 // implicitly so it won't be unloaded
102 return s_pfnShell_NotifyIcon
? (*s_pfnShell_NotifyIcon
)(dwMessage
, pData
)
104 #else // !wxUSE_DYNLIB_CLASS
105 return Shell_NotifyIcon(dwMessage
, pData
);
106 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS
109 // ----------------------------------------------------------------------------
110 // wxTaskBarIconWindow: helper window
111 // ----------------------------------------------------------------------------
113 // We need a HWND to create a taskbar icon, so create a special hidden window
114 // just to be able to use its HWND.
115 class wxTaskBarIconWindow
: public wxFrame
118 wxTaskBarIconWindow(wxTaskBarIcon
*icon
)
119 : wxFrame(NULL
, wxID_ANY
, wxEmptyString
, wxDefaultPosition
, wxDefaultSize
, 0),
124 // This implicitly created window shouldn't prevent the application from
125 // exiting if all its other windows are closed.
126 virtual bool ShouldPreventAppExit() const { return false; }
129 WXLRESULT
MSWWindowProc(WXUINT msg
,
130 WXWPARAM wParam
, WXLPARAM lParam
)
132 if (msg
== gs_msgRestartTaskbar
|| msg
== gs_msgTaskbar
)
134 return m_icon
->WindowProc(msg
, wParam
, lParam
);
138 return wxFrame::MSWWindowProc(msg
, wParam
, lParam
);
143 wxTaskBarIcon
*m_icon
;
147 // ----------------------------------------------------------------------------
148 // NotifyIconData: wrapper around NOTIFYICONDATA
149 // ----------------------------------------------------------------------------
151 struct NotifyIconData
: public NOTIFYICONDATA
153 NotifyIconData(WXHWND hwnd
)
155 memset(this, 0, sizeof(NOTIFYICONDATA
));
157 // Do _not_ use sizeof(NOTIFYICONDATA) here, it may be too big if we're
158 // compiled with newer headers but running on an older system and while
159 // we could do complicated tests for the exact system version it's
160 // easier to just use an old size which should be supported everywhere
161 // from Windows 2000 up and which is all we need as we don't use any
162 // newer features so far. But if we're running under a really ancient
163 // system (Win9x), fall back to even smaller size -- then the balloon
164 // related features won't be available but the rest will still work.
165 cbSize
= wxTheApp
->GetShell32Version() >= 500
166 ? NOTIFYICONDATA_V2_SIZE
167 : NOTIFYICONDATA_V1_SIZE
;
170 uCallbackMessage
= gs_msgTaskbar
;
171 uFlags
= NIF_MESSAGE
;
173 // we use the same id for all taskbar icons as we don't need it to
174 // distinguish between them
179 // ----------------------------------------------------------------------------
181 // ----------------------------------------------------------------------------
183 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType
WXUNUSED(iconType
))
187 RegisterWindowMessages();
190 wxTaskBarIcon::~wxTaskBarIcon()
197 // we must use delete and not Destroy() here because the latter will
198 // only schedule the window to be deleted during the next idle event
199 // processing but we may not get any idle events if there are no other
200 // windows left in the program
206 bool wxTaskBarIcon::SetIcon(const wxIcon
& icon
, const wxString
& tooltip
)
208 // NB: we have to create the window lazily because of backward compatibility,
209 // old applications may create a wxTaskBarIcon instance before wxApp
210 // is initialized (as samples/taskbar used to do)
213 m_win
= new wxTaskBarIconWindow(this);
217 m_strTooltip
= tooltip
;
219 NotifyIconData
notifyData(GetHwndOf(m_win
));
223 notifyData
.uFlags
|= NIF_ICON
;
224 notifyData
.hIcon
= GetHiconOf(icon
);
227 // set NIF_TIP even for an empty tooltip: otherwise it would be impossible
228 // to remove an existing tooltip using this function
229 notifyData
.uFlags
|= NIF_TIP
;
230 if ( !tooltip
.empty() )
232 wxStrlcpy(notifyData
.szTip
, tooltip
.t_str(), WXSIZEOF(notifyData
.szTip
));
235 bool ok
= wxShellNotifyIcon(m_iconAdded
? NIM_MODIFY
236 : NIM_ADD
, ¬ifyData
) != 0;
240 wxLogLastError(wxT("wxShellNotifyIcon(NIM_MODIFY/ADD)"));
243 if ( !m_iconAdded
&& ok
)
249 #if wxUSE_TASKBARICON_BALLOONS
252 wxTaskBarIcon::ShowBalloon(const wxString
& title
,
253 const wxString
& text
,
257 wxCHECK_MSG( m_iconAdded
, false,
258 wxT("can't be used before the icon is created") );
260 const HWND hwnd
= GetHwndOf(m_win
);
262 // we need to enable version 5.0 behaviour to receive notifications about
263 // the balloon disappearance
264 NotifyIconData
notifyData(hwnd
);
265 notifyData
.uFlags
= 0;
266 notifyData
.uVersion
= 3 /* NOTIFYICON_VERSION for Windows 2000/XP */;
268 if ( !wxShellNotifyIcon(NIM_SETVERSION
, ¬ifyData
) )
270 wxLogLastError(wxT("wxShellNotifyIcon(NIM_SETVERSION)"));
273 // do show the balloon now
274 notifyData
= NotifyIconData(hwnd
);
275 notifyData
.uFlags
|= NIF_INFO
;
276 notifyData
.uTimeout
= msec
;
277 wxStrlcpy(notifyData
.szInfo
, text
.t_str(), WXSIZEOF(notifyData
.szInfo
));
278 wxStrlcpy(notifyData
.szInfoTitle
, title
.t_str(),
279 WXSIZEOF(notifyData
.szInfoTitle
));
281 if ( flags
& wxICON_INFORMATION
)
282 notifyData
.dwInfoFlags
|= NIIF_INFO
;
283 else if ( flags
& wxICON_WARNING
)
284 notifyData
.dwInfoFlags
|= NIIF_WARNING
;
285 else if ( flags
& wxICON_ERROR
)
286 notifyData
.dwInfoFlags
|= NIIF_ERROR
;
288 bool ok
= wxShellNotifyIcon(NIM_MODIFY
, ¬ifyData
) != 0;
291 wxLogLastError(wxT("wxShellNotifyIcon(NIM_MODIFY)"));
297 #endif // wxUSE_TASKBARICON_BALLOONS
299 bool wxTaskBarIcon::RemoveIcon()
306 NotifyIconData
notifyData(GetHwndOf(m_win
));
308 bool ok
= wxShellNotifyIcon(NIM_DELETE
, ¬ifyData
) != 0;
311 wxLogLastError(wxT("wxShellNotifyIcon(NIM_DELETE)"));
318 bool wxTaskBarIcon::PopupMenu(wxMenu
*menu
)
320 wxASSERT_MSG( m_win
!= NULL
, wxT("taskbar icon not initialized") );
322 static bool s_inPopup
= false;
330 wxGetMousePosition(&x
, &y
);
334 m_win
->PushEventHandler(this);
338 // the SetForegroundWindow() and PostMessage() calls are needed to work
339 // around Win32 bug with the popup menus shown for the notifications as
340 // documented at http://support.microsoft.com/kb/q135788/
341 ::SetForegroundWindow(GetHwndOf(m_win
));
343 bool rval
= m_win
->PopupMenu(menu
, 0, 0);
345 ::PostMessage(GetHwndOf(m_win
), WM_NULL
, 0, 0L);
347 m_win
->PopEventHandler(false);
353 #endif // wxUSE_MENUS
355 void wxTaskBarIcon::RegisterWindowMessages()
357 static bool s_registered
= false;
361 // Taskbar restart msg will be sent to us if the icon needs to be redrawn
362 gs_msgRestartTaskbar
= RegisterWindowMessage(wxT("TaskbarCreated"));
364 // Also register the taskbar message here
365 gs_msgTaskbar
= ::RegisterWindowMessage(wxT("wxTaskBarIconMessage"));
371 // ----------------------------------------------------------------------------
372 // wxTaskBarIcon window proc
373 // ----------------------------------------------------------------------------
375 long wxTaskBarIcon::WindowProc(unsigned int msg
,
376 unsigned int WXUNUSED(wParam
),
379 if ( msg
== gs_msgRestartTaskbar
) // does the icon need to be redrawn?
382 SetIcon(m_icon
, m_strTooltip
);
386 // this function should only be called for gs_msg(Restart)Taskbar messages
387 wxASSERT( msg
== gs_msgTaskbar
);
389 wxEventType eventType
= 0;
393 eventType
= wxEVT_TASKBAR_LEFT_DOWN
;
397 eventType
= wxEVT_TASKBAR_LEFT_UP
;
401 eventType
= wxEVT_TASKBAR_RIGHT_DOWN
;
405 eventType
= wxEVT_TASKBAR_RIGHT_UP
;
408 case WM_LBUTTONDBLCLK
:
409 eventType
= wxEVT_TASKBAR_LEFT_DCLICK
;
412 case WM_RBUTTONDBLCLK
:
413 eventType
= wxEVT_TASKBAR_RIGHT_DCLICK
;
417 eventType
= wxEVT_TASKBAR_MOVE
;
420 case NIN_BALLOONTIMEOUT
:
421 eventType
= wxEVT_TASKBAR_BALLOON_TIMEOUT
;
424 case NIN_BALLOONUSERCLICK
:
425 eventType
= wxEVT_TASKBAR_BALLOON_CLICK
;
431 wxTaskBarIconEvent
event(eventType
, this);
439 #endif // wxUSE_TASKBARICON