prevent crash if panel/kicker is killed, bug 1872724
[wxWidgets.git] / src / unix / taskbarx11.cpp
1 /////////////////////////////////////////////////////////////////////////
2 // File: src/unix/taskbarx11.cpp
3 // Purpose: wxTaskBarIcon class for common Unix desktops
4 // Author: Vaclav Slavik
5 // Modified by:
6 // Created: 04/04/2003
7 // RCS-ID: $Id$
8 // Copyright: (c) Vaclav Slavik, 2003
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////
11
12 // NB: This implementation does *not* work with every X11 window manager.
13 // Currently only GNOME 1.2 and KDE 1,2,3 methods are implemented here.
14 // Freedesktop.org's System Tray specification is implemented in
15 // src/gtk/taskbar.cpp and used from here under wxGTK.
16 //
17 // Thanks to Ian Campbell, author of XMMS Status Docklet, for publishing
18 // KDE and GNOME 1.2 methods.
19
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #if wxUSE_TASKBARICON
25
26 #include "wx/taskbar.h"
27
28 #ifndef WX_PRECOMP
29 #include "wx/log.h"
30 #include "wx/frame.h"
31 #include "wx/dcclient.h"
32 #include "wx/statbmp.h"
33 #include "wx/sizer.h"
34 #include "wx/bitmap.h"
35 #include "wx/image.h"
36 #endif
37
38 #ifdef __VMS
39 #pragma message disable nosimpint
40 #endif
41 #include <X11/Xlib.h>
42 #include <X11/Xatom.h>
43 #ifdef __VMS
44 #pragma message enable nosimpint
45 #endif
46
47 // ----------------------------------------------------------------------------
48 // base class that implements toolkit-specific method:
49 // ----------------------------------------------------------------------------
50
51 #ifdef __WXGTK20__
52 #include <gtk/gtk.h>
53 #if GTK_CHECK_VERSION(2,1,0)
54 #include "wx/gtk/taskbarpriv.h"
55 #define TASKBAR_ICON_AREA_BASE_INCLUDED
56 #endif
57 #endif
58
59 #ifndef TASKBAR_ICON_AREA_BASE_INCLUDED
60 class WXDLLIMPEXP_ADV wxTaskBarIconAreaBase : public wxFrame
61 {
62 public:
63 wxTaskBarIconAreaBase()
64 : wxFrame(NULL, wxID_ANY, _T("systray icon"),
65 wxDefaultPosition, wxDefaultSize,
66 wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR |
67 wxSIMPLE_BORDER | wxFRAME_SHAPED) {}
68
69 bool IsProtocolSupported() const { return false; }
70 };
71 #endif
72
73
74 // ----------------------------------------------------------------------------
75 // toolkit dependent methods to set properties on helper window:
76 // ----------------------------------------------------------------------------
77
78 #if defined(__WXGTK__)
79 #include <gdk/gdk.h>
80 #include <gdk/gdkx.h>
81 #include <gtk/gtk.h>
82 #define GetDisplay() GDK_DISPLAY()
83 #define GetXWindow(wxwin) GDK_WINDOW_XWINDOW((wxwin)->m_widget->window)
84 #elif defined(__WXX11__) || defined(__WXMOTIF__)
85 #include "wx/x11/privx.h"
86 #define GetDisplay() ((Display*)wxGlobalDisplay())
87 #define GetXWindow(wxwin) ((Window)(wxwin)->GetHandle())
88 #else
89 #error "You must define X11 accessors for this port!"
90 #endif
91
92
93 // ----------------------------------------------------------------------------
94 // wxTaskBarIconArea is the real window that shows the icon:
95 // ----------------------------------------------------------------------------
96
97 class WXDLLIMPEXP_ADV wxTaskBarIconArea : public wxTaskBarIconAreaBase
98 {
99 public:
100 wxTaskBarIconArea(wxTaskBarIcon *icon, const wxBitmap &bmp);
101 void SetTrayIcon(const wxBitmap& bmp);
102 bool IsOk() { return true; }
103
104 protected:
105 void SetLegacyWMProperties();
106
107 void OnSizeChange(wxSizeEvent& event);
108 void OnPaint(wxPaintEvent& evt);
109 void OnMouseEvent(wxMouseEvent& event);
110 void OnMenuEvent(wxCommandEvent& event);
111
112 wxTaskBarIcon *m_icon;
113 wxPoint m_pos;
114 wxBitmap m_bmp;
115
116 DECLARE_EVENT_TABLE()
117 };
118
119 BEGIN_EVENT_TABLE(wxTaskBarIconArea, wxTaskBarIconAreaBase)
120 EVT_SIZE(wxTaskBarIconArea::OnSizeChange)
121 EVT_MOUSE_EVENTS(wxTaskBarIconArea::OnMouseEvent)
122 EVT_MENU(wxID_ANY, wxTaskBarIconArea::OnMenuEvent)
123 EVT_PAINT(wxTaskBarIconArea::OnPaint)
124 END_EVENT_TABLE()
125
126 wxTaskBarIconArea::wxTaskBarIconArea(wxTaskBarIcon *icon, const wxBitmap &bmp)
127 : wxTaskBarIconAreaBase(), m_icon(icon), m_bmp(bmp)
128 {
129 #if defined(__WXGTK20__) && defined(TASKBAR_ICON_AREA_BASE_INCLUDED)
130 m_invokingWindow = icon;
131 #endif
132
133 // Set initial size to bitmap size (tray manager may and often will
134 // change it):
135 SetClientSize(wxSize(bmp.GetWidth(), bmp.GetHeight()));
136
137 SetTrayIcon(bmp);
138
139 if (!IsProtocolSupported())
140 {
141 wxLogTrace(_T("systray"),
142 _T("using legacy KDE1,2 and GNOME 1.2 methods"));
143 SetLegacyWMProperties();
144 }
145 }
146
147 void wxTaskBarIconArea::SetTrayIcon(const wxBitmap& bmp)
148 {
149 m_bmp = bmp;
150
151 // determine suitable bitmap size:
152 wxSize winsize(GetClientSize());
153 wxSize bmpsize(m_bmp.GetWidth(), m_bmp.GetHeight());
154 wxSize iconsize(wxMin(winsize.x, bmpsize.x), wxMin(winsize.y, bmpsize.y));
155
156 // rescale the bitmap to fit into the tray icon window:
157 if (bmpsize != iconsize)
158 {
159 wxImage img = m_bmp.ConvertToImage();
160 img.Rescale(iconsize.x, iconsize.y);
161 m_bmp = wxBitmap(img);
162 }
163
164 wxRegion region;
165 region.Union(m_bmp);
166
167 // if the bitmap is smaller than the window, offset it:
168 if (winsize != iconsize)
169 {
170 m_pos.x = (winsize.x - iconsize.x) / 2;
171 m_pos.y = (winsize.y - iconsize.y) / 2;
172 region.Offset(m_pos.x, m_pos.y);
173 }
174
175 // set frame's shape to correct value and redraw:
176 SetShape(region);
177 Refresh();
178 }
179
180 void wxTaskBarIconArea::SetLegacyWMProperties()
181 {
182 #ifdef __WXGTK__
183 gtk_widget_realize(m_widget);
184 #endif
185
186 long data[1];
187
188 // KDE 2 & KDE 3:
189 Atom _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR =
190 XInternAtom(GetDisplay(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False);
191 data[0] = 0;
192 XChangeProperty(GetDisplay(), GetXWindow(this),
193 _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
194 XA_WINDOW, 32,
195 PropModeReplace, (unsigned char*)data, 1);
196
197 // GNOME 1.2 & KDE 1:
198 Atom KWM_DOCKWINDOW =
199 XInternAtom(GetDisplay(), "KWM_DOCKWINDOW", False);
200 data[0] = 1;
201 XChangeProperty(GetDisplay(), GetXWindow(this),
202 KWM_DOCKWINDOW,
203 KWM_DOCKWINDOW, 32,
204 PropModeReplace, (unsigned char*)data, 1);
205 }
206
207 void wxTaskBarIconArea::OnSizeChange(wxSizeEvent& WXUNUSED(event))
208 {
209 wxLogTrace(_T("systray"), _T("icon size changed to %i x %i"),
210 GetSize().x, GetSize().y);
211 // rescale or reposition the icon as needed:
212 wxBitmap bmp(m_bmp);
213 SetTrayIcon(bmp);
214 }
215
216 void wxTaskBarIconArea::OnPaint(wxPaintEvent& WXUNUSED(event))
217 {
218 wxPaintDC dc(this);
219 dc.DrawBitmap(m_bmp, m_pos.x, m_pos.y, true);
220 }
221
222 void wxTaskBarIconArea::OnMouseEvent(wxMouseEvent& event)
223 {
224 wxEventType type = 0;
225 wxEventType mtype = event.GetEventType();
226
227 if (mtype == wxEVT_LEFT_DOWN)
228 type = wxEVT_TASKBAR_LEFT_DOWN;
229 else if (mtype == wxEVT_LEFT_UP)
230 type = wxEVT_TASKBAR_LEFT_UP;
231 else if (mtype == wxEVT_LEFT_DCLICK)
232 type = wxEVT_TASKBAR_LEFT_DCLICK;
233 else if (mtype == wxEVT_RIGHT_DOWN)
234 type = wxEVT_TASKBAR_RIGHT_DOWN;
235 else if (mtype == wxEVT_RIGHT_UP)
236 type = wxEVT_TASKBAR_RIGHT_UP;
237 else if (mtype == wxEVT_RIGHT_DCLICK)
238 type = wxEVT_TASKBAR_RIGHT_DCLICK;
239 else if (mtype == wxEVT_MOTION)
240 type = wxEVT_TASKBAR_MOVE;
241 else
242 return;
243
244 wxTaskBarIconEvent e(type, m_icon);
245 m_icon->ProcessEvent(e);
246 }
247
248 void wxTaskBarIconArea::OnMenuEvent(wxCommandEvent& event)
249 {
250 m_icon->ProcessEvent(event);
251 }
252
253 // ----------------------------------------------------------------------------
254 // wxTaskBarIcon class:
255 // ----------------------------------------------------------------------------
256
257 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
258
259 wxTaskBarIcon::wxTaskBarIcon() : m_iconWnd(NULL)
260 {
261 }
262
263 wxTaskBarIcon::~wxTaskBarIcon()
264 {
265 if (m_iconWnd)
266 RemoveIcon();
267 }
268
269 bool wxTaskBarIcon::IsOk() const
270 {
271 return true;
272 }
273
274 bool wxTaskBarIcon::IsIconInstalled() const
275 {
276 return m_iconWnd != NULL;
277 }
278
279 // Destroy event from wxTaskBarIconArea
280 void wxTaskBarIcon::OnDestroy(wxWindowDestroyEvent&)
281 {
282 // prevent crash if wxTaskBarIconArea is destroyed by something else,
283 // for example if panel/kicker is killed
284 m_iconWnd = NULL;
285 }
286
287 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
288 {
289 wxBitmap bmp;
290 bmp.CopyFromIcon(icon);
291
292 if (!m_iconWnd)
293 {
294 m_iconWnd = new wxTaskBarIconArea(this, bmp);
295 if (m_iconWnd->IsOk())
296 {
297 m_iconWnd->Connect(wxEVT_DESTROY,
298 wxWindowDestroyEventHandler(wxTaskBarIcon::OnDestroy),
299 NULL, this);
300 m_iconWnd->Show();
301 }
302 else
303 {
304 m_iconWnd->Destroy();
305 m_iconWnd = NULL;
306 return false;
307 }
308 }
309 else
310 {
311 m_iconWnd->SetTrayIcon(bmp);
312 }
313
314 #if wxUSE_TOOLTIPS
315 if (!tooltip.empty())
316 m_iconWnd->SetToolTip(tooltip);
317 else
318 m_iconWnd->SetToolTip(NULL);
319 #else
320 wxUnusedVar(tooltip);
321 #endif
322 return true;
323 }
324
325 bool wxTaskBarIcon::RemoveIcon()
326 {
327 if (!m_iconWnd)
328 return false;
329 m_iconWnd->Destroy();
330 m_iconWnd = NULL;
331 return true;
332 }
333
334 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
335 {
336 if (!m_iconWnd)
337 return false;
338 m_iconWnd->PopupMenu(menu);
339 return true;
340 }
341
342 #endif // wxUSE_TASKBARICON