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: taskbar.cpp 50294 2007-11-28 01:59:59Z VZ $
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 #ifndef WX_PRECOMP
21     #include "wx/window.h"
22     #include "wx/frame.h"
23     #include "wx/utils.h"
24     #include "wx/menu.h"
25 #endif
26 
27 #include "wx/msw/private.h"
28 #include "wx/msw/winundef.h"
29 
30 #include <string.h>
31 #include "wx/taskbar.h"
32 
33 #ifdef __WXWINCE__
34     #include <winreg.h>
35     #include <shellapi.h>
36 #endif
37 
38 // initialized on demand
39 UINT   gs_msgTaskbar = 0;
40 UINT   gs_msgRestartTaskbar = 0;
41 
42 #if WXWIN_COMPATIBILITY_2_4
43 BEGIN_EVENT_TABLE(wxTaskBarIcon, wxTaskBarIconBase)
44     EVT_TASKBAR_MOVE         (wxTaskBarIcon::_OnMouseMove)
45     EVT_TASKBAR_LEFT_DOWN    (wxTaskBarIcon::_OnLButtonDown)
46     EVT_TASKBAR_LEFT_UP      (wxTaskBarIcon::_OnLButtonUp)
47     EVT_TASKBAR_RIGHT_DOWN   (wxTaskBarIcon::_OnRButtonDown)
48     EVT_TASKBAR_RIGHT_UP     (wxTaskBarIcon::_OnRButtonUp)
49     EVT_TASKBAR_LEFT_DCLICK  (wxTaskBarIcon::_OnLButtonDClick)
50     EVT_TASKBAR_RIGHT_DCLICK (wxTaskBarIcon::_OnRButtonDClick)
51 END_EVENT_TABLE()
52 #endif
53 
54 
55 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
56 
57 // ============================================================================
58 // implementation
59 // ============================================================================
60 
61 // ----------------------------------------------------------------------------
62 // wxTaskBarIconWindow: helper window
63 // ----------------------------------------------------------------------------
64 
65 // NB: this class serves two purposes:
66 //     1. win32 needs a HWND associated with taskbar icon, this provides it
67 //     2. we need wxTopLevelWindow so that the app doesn't exit when
68 //        last frame is closed but there still is a taskbar icon
69 class wxTaskBarIconWindow : public wxFrame
70 {
71 public:
wxTaskBarIconWindow(wxTaskBarIcon * icon)72     wxTaskBarIconWindow(wxTaskBarIcon *icon)
73         : wxFrame(NULL, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0),
74           m_icon(icon)
75     {
76     }
77 
MSWWindowProc(WXUINT msg,WXWPARAM wParam,WXLPARAM lParam)78     WXLRESULT MSWWindowProc(WXUINT msg,
79                             WXWPARAM wParam, WXLPARAM lParam)
80     {
81         if (msg == gs_msgRestartTaskbar || msg == gs_msgTaskbar)
82         {
83             return m_icon->WindowProc(msg, wParam, lParam);
84         }
85         else
86         {
87             return wxFrame::MSWWindowProc(msg, wParam, lParam);
88         }
89     }
90 
91 private:
92     wxTaskBarIcon *m_icon;
93 };
94 
95 
96 // ----------------------------------------------------------------------------
97 // NotifyIconData: wrapper around NOTIFYICONDATA
98 // ----------------------------------------------------------------------------
99 
100 struct NotifyIconData : public NOTIFYICONDATA
101 {
NotifyIconDataNotifyIconData102     NotifyIconData(WXHWND hwnd)
103     {
104         memset(this, 0, sizeof(NOTIFYICONDATA));
105         cbSize = sizeof(NOTIFYICONDATA);
106         hWnd = (HWND) hwnd;
107         uCallbackMessage = gs_msgTaskbar;
108         uFlags = NIF_MESSAGE;
109 
110         // we use the same id for all taskbar icons as we don't need it to
111         // distinguish between them
112         uID = 99;
113     }
114 };
115 
116 // ----------------------------------------------------------------------------
117 // wxTaskBarIcon
118 // ----------------------------------------------------------------------------
119 
wxTaskBarIcon()120 wxTaskBarIcon::wxTaskBarIcon()
121 {
122     m_win = NULL;
123     m_iconAdded = false;
124     RegisterWindowMessages();
125 }
126 
~wxTaskBarIcon()127 wxTaskBarIcon::~wxTaskBarIcon()
128 {
129     if (m_iconAdded)
130         RemoveIcon();
131 
132     if (m_win)
133         m_win->Destroy();
134 }
135 
136 // Operations
SetIcon(const wxIcon & icon,const wxString & tooltip)137 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
138 {
139     // NB: we have to create the window lazily because of backward compatibility,
140     //     old applications may create a wxTaskBarIcon instance before wxApp
141     //     is initialized (as samples/taskbar used to do)
142     if (!m_win)
143     {
144         m_win = new wxTaskBarIconWindow(this);
145     }
146 
147     m_icon = icon;
148     m_strTooltip = tooltip;
149 
150     NotifyIconData notifyData(GetHwndOf(m_win));
151 
152     if (icon.Ok())
153     {
154         notifyData.uFlags |= NIF_ICON;
155         notifyData.hIcon = GetHiconOf(icon);
156     }
157 
158     // set NIF_TIP even for an empty tooltip: otherwise it would be impossible
159     // to remove an existing tooltip using this function
160     notifyData.uFlags |= NIF_TIP;
161     if ( !tooltip.empty() )
162     {
163         wxStrncpy(notifyData.szTip, tooltip.c_str(), WXSIZEOF(notifyData.szTip));
164     }
165 
166     bool ok = Shell_NotifyIcon(m_iconAdded ? NIM_MODIFY
167                                            : NIM_ADD, &notifyData) != 0;
168 
169     if ( !m_iconAdded && ok )
170         m_iconAdded = true;
171 
172     return ok;
173 }
174 
RemoveIcon()175 bool wxTaskBarIcon::RemoveIcon()
176 {
177     if (!m_iconAdded)
178         return false;
179 
180     m_iconAdded = false;
181 
182     NotifyIconData notifyData(GetHwndOf(m_win));
183 
184     return Shell_NotifyIcon(NIM_DELETE, &notifyData) != 0;
185 }
186 
PopupMenu(wxMenu * menu)187 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
188 {
189     wxASSERT_MSG( m_win != NULL, _T("taskbar icon not initialized") );
190 
191     static bool s_inPopup = false;
192 
193     if (s_inPopup)
194         return false;
195 
196     s_inPopup = true;
197 
198     int         x, y;
199     wxGetMousePosition(&x, &y);
200 
201     m_win->Move(x, y);
202 
203     m_win->PushEventHandler(this);
204 
205     menu->UpdateUI();
206 
207     // the SetForegroundWindow() and PostMessage() calls are needed to work
208     // around Win32 bug with the popup menus shown for the notifications as
209     // documented at http://support.microsoft.com/kb/q135788/
210     ::SetForegroundWindow(GetHwndOf(m_win));
211 
212     bool rval = m_win->PopupMenu(menu, 0, 0);
213 
214     ::PostMessage(GetHwndOf(m_win), WM_NULL, 0, 0L);
215 
216     m_win->PopEventHandler(false);
217 
218     s_inPopup = false;
219 
220     return rval;
221 }
222 
223 #if WXWIN_COMPATIBILITY_2_4
224 // Overridables
OnMouseMove(wxEvent & e)225 void wxTaskBarIcon::OnMouseMove(wxEvent& e)         { e.Skip(); }
OnLButtonDown(wxEvent & e)226 void wxTaskBarIcon::OnLButtonDown(wxEvent& e)       { e.Skip(); }
OnLButtonUp(wxEvent & e)227 void wxTaskBarIcon::OnLButtonUp(wxEvent& e)         { e.Skip(); }
OnRButtonDown(wxEvent & e)228 void wxTaskBarIcon::OnRButtonDown(wxEvent& e)       { e.Skip(); }
OnRButtonUp(wxEvent & e)229 void wxTaskBarIcon::OnRButtonUp(wxEvent& e)         { e.Skip(); }
OnLButtonDClick(wxEvent & e)230 void wxTaskBarIcon::OnLButtonDClick(wxEvent& e)     { e.Skip(); }
OnRButtonDClick(wxEvent & e)231 void wxTaskBarIcon::OnRButtonDClick(wxEvent& e)     { e.Skip(); }
232 
_OnMouseMove(wxTaskBarIconEvent & e)233 void wxTaskBarIcon::_OnMouseMove(wxTaskBarIconEvent& e)
234     { OnMouseMove(e);     }
_OnLButtonDown(wxTaskBarIconEvent & e)235 void wxTaskBarIcon::_OnLButtonDown(wxTaskBarIconEvent& e)
236     { OnLButtonDown(e);   }
_OnLButtonUp(wxTaskBarIconEvent & e)237 void wxTaskBarIcon::_OnLButtonUp(wxTaskBarIconEvent& e)
238     { OnLButtonUp(e);     }
_OnRButtonDown(wxTaskBarIconEvent & e)239 void wxTaskBarIcon::_OnRButtonDown(wxTaskBarIconEvent& e)
240     { OnRButtonDown(e);   }
_OnRButtonUp(wxTaskBarIconEvent & e)241 void wxTaskBarIcon::_OnRButtonUp(wxTaskBarIconEvent& e)
242     { OnRButtonUp(e);     }
_OnLButtonDClick(wxTaskBarIconEvent & e)243 void wxTaskBarIcon::_OnLButtonDClick(wxTaskBarIconEvent& e)
244     { OnLButtonDClick(e); }
_OnRButtonDClick(wxTaskBarIconEvent & e)245 void wxTaskBarIcon::_OnRButtonDClick(wxTaskBarIconEvent& e)
246     { OnRButtonDClick(e); }
247 #endif
248 
RegisterWindowMessages()249 void wxTaskBarIcon::RegisterWindowMessages()
250 {
251     static bool s_registered = false;
252 
253     if ( !s_registered )
254     {
255         // Taskbar restart msg will be sent to us if the icon needs to be redrawn
256         gs_msgRestartTaskbar = RegisterWindowMessage(wxT("TaskbarCreated"));
257 
258         // Also register the taskbar message here
259         gs_msgTaskbar = ::RegisterWindowMessage(wxT("wxTaskBarIconMessage"));
260 
261         s_registered = true;
262     }
263 }
264 
265 // ----------------------------------------------------------------------------
266 // wxTaskBarIcon window proc
267 // ----------------------------------------------------------------------------
268 
WindowProc(unsigned int msg,unsigned int WXUNUSED (wParam),long lParam)269 long wxTaskBarIcon::WindowProc(unsigned int msg,
270                                unsigned int WXUNUSED(wParam),
271                                long lParam)
272 {
273     wxEventType eventType = 0;
274 
275     if (msg == gs_msgRestartTaskbar)   // does the icon need to be redrawn?
276     {
277         m_iconAdded = false;
278         SetIcon(m_icon, m_strTooltip);
279         return 0;
280     }
281 
282     // this function should only be called for gs_msg(Restart)Taskbar messages
283     wxASSERT(msg == gs_msgTaskbar);
284 
285     switch (lParam)
286     {
287         case WM_LBUTTONDOWN:
288             eventType = wxEVT_TASKBAR_LEFT_DOWN;
289             break;
290 
291         case WM_LBUTTONUP:
292             eventType = wxEVT_TASKBAR_LEFT_UP;
293             break;
294 
295         case WM_RBUTTONDOWN:
296             eventType = wxEVT_TASKBAR_RIGHT_DOWN;
297             break;
298 
299         case WM_RBUTTONUP:
300             eventType = wxEVT_TASKBAR_RIGHT_UP;
301             break;
302 
303         case WM_LBUTTONDBLCLK:
304             eventType = wxEVT_TASKBAR_LEFT_DCLICK;
305             break;
306 
307         case WM_RBUTTONDBLCLK:
308             eventType = wxEVT_TASKBAR_RIGHT_DCLICK;
309             break;
310 
311         case WM_MOUSEMOVE:
312             eventType = wxEVT_TASKBAR_MOVE;
313             break;
314 
315         default:
316             break;
317     }
318 
319     if (eventType)
320     {
321         wxTaskBarIconEvent event(eventType, this);
322 
323         ProcessEvent(event);
324     }
325 
326     return 0;
327 }
328