1 /////////////////////////////////////////////////////////////////////////
2 // File: src/gtk/taskbar.cpp
3 // Purpose: wxTaskBarIcon
4 // Author: Vaclav Slavik
5 // Modified by: Paul Cornett
6 // Created: 2004/05/29
7 // Copyright: (c) Vaclav Slavik, 2004
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #if wxUSE_TASKBARICON
15
16 #include "wx/taskbar.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/toplevel.h"
20 #include "wx/menu.h"
21 #include "wx/icon.h"
22 #endif
23
24 #include <gtk/gtk.h>
25 #ifdef GDK_WINDOWING_X11
26 #include <gdk/gdkx.h>
27 #endif
28 #ifndef __WXGTK3__
29 #include "eggtrayicon.h"
30 #endif
31 #include "wx/gtk/private/gtk2-compat.h"
32
33 #if !GTK_CHECK_VERSION(2,10,0)
34 typedef struct _GtkStatusIcon GtkStatusIcon;
35 #endif
36
37 class wxTaskBarIcon::Private
38 {
39 public:
40 Private(wxTaskBarIcon* taskBarIcon);
41 ~Private();
42 void SetIcon();
43 void size_allocate(int width, int height);
44
45 // owning wxTaskBarIcon
46 wxTaskBarIcon* m_taskBarIcon;
47 // used when GTK+ >= 2.10
48 GtkStatusIcon* m_statusIcon;
49 // for PopupMenu
50 wxWindow* m_win;
51 wxBitmap m_bitmap;
52 wxString m_tipText;
53 #ifndef __WXGTK3__
54 // used when GTK+ < 2.10
55 GtkWidget* m_eggTrayIcon;
56 // for tooltip when GTK+ < 2.10
57 GtkTooltips* m_tooltips;
58 // width and height of available space, only used when GTK+ < 2.10
59 int m_size;
60 #endif
61 };
62 //-----------------------------------------------------------------------------
63
64 extern "C" {
65 #ifndef __WXGTK3__
66 static void
icon_size_allocate(GtkWidget *,GtkAllocation * alloc,wxTaskBarIcon::Private * priv)67 icon_size_allocate(GtkWidget*, GtkAllocation* alloc, wxTaskBarIcon::Private* priv)
68 {
69 priv->size_allocate(alloc->width, alloc->height);
70 }
71
72 static void
icon_destroy(GtkWidget *,wxTaskBarIcon::Private * priv)73 icon_destroy(GtkWidget*, wxTaskBarIcon::Private* priv)
74 {
75 // Icon window destroyed, probably because tray program has died.
76 // Recreate icon so it will appear if tray program is restarted.
77 priv->m_eggTrayIcon = NULL;
78 priv->SetIcon();
79 }
80 #endif
81
82 static void
icon_activate(void *,wxTaskBarIcon * taskBarIcon)83 icon_activate(void*, wxTaskBarIcon* taskBarIcon)
84 {
85 // activate occurs from single click with GTK+
86 wxTaskBarIconEvent event(wxEVT_TASKBAR_LEFT_DOWN, taskBarIcon);
87 if (!taskBarIcon->SafelyProcessEvent(event))
88 {
89 // if single click not handled, send double click for compatibility
90 event.SetEventType(wxEVT_TASKBAR_LEFT_DCLICK);
91 taskBarIcon->SafelyProcessEvent(event);
92 }
93 }
94
95 static gboolean
icon_popup_menu(GtkWidget *,wxTaskBarIcon * taskBarIcon)96 icon_popup_menu(GtkWidget*, wxTaskBarIcon* taskBarIcon)
97 {
98 wxTaskBarIconEvent event(wxEVT_TASKBAR_CLICK, taskBarIcon);
99 taskBarIcon->SafelyProcessEvent(event);
100 return true;
101 }
102
103 #ifndef __WXGTK3__
104 static gboolean
icon_button_press_event(GtkWidget *,GdkEventButton * event,wxTaskBarIcon * taskBarIcon)105 icon_button_press_event(GtkWidget*, GdkEventButton* event, wxTaskBarIcon* taskBarIcon)
106 {
107 if (event->type == GDK_BUTTON_PRESS)
108 {
109 if (event->button == 1)
110 icon_activate(NULL, taskBarIcon);
111 else if (event->button == 3)
112 icon_popup_menu(NULL, taskBarIcon);
113 }
114 return false;
115 }
116 #endif
117
118 #if GTK_CHECK_VERSION(2,10,0)
119 static void
status_icon_popup_menu(GtkStatusIcon *,guint,guint,wxTaskBarIcon * taskBarIcon)120 status_icon_popup_menu(GtkStatusIcon*, guint, guint, wxTaskBarIcon* taskBarIcon)
121 {
122 icon_popup_menu(NULL, taskBarIcon);
123 }
124 #endif
125 } // extern "C"
126 //-----------------------------------------------------------------------------
127
IsAvailable()128 bool wxTaskBarIconBase::IsAvailable()
129 {
130 #ifdef GDK_WINDOWING_X11
131 if (!GDK_IS_X11_DISPLAY(gdk_display_get_default()))
132 return false;
133
134 char name[32];
135 g_snprintf(name, sizeof(name), "_NET_SYSTEM_TRAY_S%d",
136 gdk_x11_get_default_screen());
137 Atom atom = gdk_x11_get_xatom_by_name(name);
138
139 Window manager = XGetSelectionOwner(gdk_x11_get_default_xdisplay(), atom);
140
141 return manager != None;
142 #else
143 return true;
144 #endif
145 }
146 //-----------------------------------------------------------------------------
147
Private(wxTaskBarIcon * taskBarIcon)148 wxTaskBarIcon::Private::Private(wxTaskBarIcon* taskBarIcon)
149 {
150 m_taskBarIcon = taskBarIcon;
151 m_statusIcon = NULL;
152 m_win = NULL;
153 #ifndef __WXGTK3__
154 m_eggTrayIcon = NULL;
155 m_tooltips = NULL;
156 m_size = 0;
157 #endif
158 }
159
~Private()160 wxTaskBarIcon::Private::~Private()
161 {
162 if (m_statusIcon)
163 g_object_unref(m_statusIcon);
164 #ifndef __WXGTK3__
165 else if (m_eggTrayIcon)
166 {
167 g_signal_handlers_disconnect_by_func(m_eggTrayIcon, (void*)icon_destroy, this);
168 gtk_widget_destroy(m_eggTrayIcon);
169 }
170 #endif
171 if (m_win)
172 {
173 m_win->PopEventHandler();
174 m_win->Destroy();
175 }
176 #ifndef __WXGTK3__
177 if (m_tooltips)
178 {
179 gtk_object_destroy(GTK_OBJECT(m_tooltips));
180 g_object_unref(m_tooltips);
181 }
182 #endif
183 }
184
SetIcon()185 void wxTaskBarIcon::Private::SetIcon()
186 {
187 #if GTK_CHECK_VERSION(2,10,0)
188 if (GTK_CHECK_VERSION(3,0,0) || gtk_check_version(2,10,0) == NULL)
189 {
190 if (m_statusIcon)
191 gtk_status_icon_set_from_pixbuf(m_statusIcon, m_bitmap.GetPixbuf());
192 else
193 {
194 m_statusIcon = gtk_status_icon_new_from_pixbuf(m_bitmap.GetPixbuf());
195 g_signal_connect(m_statusIcon, "activate",
196 G_CALLBACK(icon_activate), m_taskBarIcon);
197 g_signal_connect(m_statusIcon, "popup_menu",
198 G_CALLBACK(status_icon_popup_menu), m_taskBarIcon);
199 }
200 }
201 else
202 #endif
203 {
204 #ifndef __WXGTK3__
205 m_size = 0;
206 if (m_eggTrayIcon)
207 {
208 GtkWidget* image = gtk_bin_get_child(GTK_BIN(m_eggTrayIcon));
209 gtk_image_set_from_pixbuf(GTK_IMAGE(image), m_bitmap.GetPixbuf());
210 }
211 else
212 {
213 m_eggTrayIcon = GTK_WIDGET(egg_tray_icon_new("wxTaskBarIcon"));
214 gtk_widget_add_events(m_eggTrayIcon, GDK_BUTTON_PRESS_MASK);
215 g_signal_connect(m_eggTrayIcon, "size_allocate",
216 G_CALLBACK(icon_size_allocate), this);
217 g_signal_connect(m_eggTrayIcon, "destroy",
218 G_CALLBACK(icon_destroy), this);
219 g_signal_connect(m_eggTrayIcon, "button_press_event",
220 G_CALLBACK(icon_button_press_event), m_taskBarIcon);
221 g_signal_connect(m_eggTrayIcon, "popup_menu",
222 G_CALLBACK(icon_popup_menu), m_taskBarIcon);
223 GtkWidget* image = gtk_image_new_from_pixbuf(m_bitmap.GetPixbuf());
224 gtk_container_add(GTK_CONTAINER(m_eggTrayIcon), image);
225 gtk_widget_show_all(m_eggTrayIcon);
226 }
227 #endif
228 }
229 #if wxUSE_TOOLTIPS
230 const char *tip_text = NULL;
231 if (!m_tipText.empty())
232 tip_text = m_tipText.utf8_str();
233
234 #if GTK_CHECK_VERSION(2,10,0)
235 if (m_statusIcon)
236 {
237 #if GTK_CHECK_VERSION(2,16,0)
238 if (GTK_CHECK_VERSION(3,0,0) || gtk_check_version(2,16,0) == NULL)
239 gtk_status_icon_set_tooltip_text(m_statusIcon, tip_text);
240 else
241 #endif
242 {
243 #ifndef __WXGTK3__
244 gtk_status_icon_set_tooltip(m_statusIcon, tip_text);
245 #endif
246 }
247 }
248 else
249 #endif // GTK_CHECK_VERSION(2,10,0)
250 {
251 #ifndef __WXGTK3__
252 if (tip_text && m_tooltips == NULL)
253 {
254 m_tooltips = gtk_tooltips_new();
255 g_object_ref(m_tooltips);
256 gtk_object_sink(GTK_OBJECT(m_tooltips));
257 }
258 if (m_tooltips)
259 gtk_tooltips_set_tip(m_tooltips, m_eggTrayIcon, tip_text, "");
260 #endif
261 }
262 #endif // wxUSE_TOOLTIPS
263 }
264
265 #ifndef __WXGTK3__
size_allocate(int width,int height)266 void wxTaskBarIcon::Private::size_allocate(int width, int height)
267 {
268 int size = height;
269 EggTrayIcon* icon = EGG_TRAY_ICON(m_eggTrayIcon);
270 if (egg_tray_icon_get_orientation(icon) == GTK_ORIENTATION_VERTICAL)
271 size = width;
272 if (m_size == size)
273 return;
274 m_size = size;
275 int w = m_bitmap.GetWidth();
276 int h = m_bitmap.GetHeight();
277 if (w > size || h > size)
278 {
279 if (w > size) w = size;
280 if (h > size) h = size;
281 GdkPixbuf* pixbuf =
282 gdk_pixbuf_scale_simple(m_bitmap.GetPixbuf(), w, h, GDK_INTERP_BILINEAR);
283 GtkImage* image = GTK_IMAGE(gtk_bin_get_child(GTK_BIN(m_eggTrayIcon)));
284 gtk_image_set_from_pixbuf(image, pixbuf);
285 g_object_unref(pixbuf);
286 }
287 }
288 #endif
289 //-----------------------------------------------------------------------------
290
IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon,wxEvtHandler)291 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
292
293 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType WXUNUSED(iconType))
294 {
295 m_priv = new Private(this);
296 }
297
~wxTaskBarIcon()298 wxTaskBarIcon::~wxTaskBarIcon()
299 {
300 delete m_priv;
301 }
302
SetIcon(const wxIcon & icon,const wxString & tooltip)303 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
304 {
305 m_priv->m_bitmap = icon;
306 m_priv->m_tipText = tooltip;
307 m_priv->SetIcon();
308 return true;
309 }
310
RemoveIcon()311 bool wxTaskBarIcon::RemoveIcon()
312 {
313 delete m_priv;
314 m_priv = new Private(this);
315 return true;
316 }
317
IsIconInstalled() const318 bool wxTaskBarIcon::IsIconInstalled() const
319 {
320 #ifdef __WXGTK3__
321 return m_priv->m_statusIcon != NULL;
322 #else
323 return m_priv->m_statusIcon || m_priv->m_eggTrayIcon;
324 #endif
325 }
326
PopupMenu(wxMenu * menu)327 bool wxTaskBarIcon::PopupMenu(wxMenu* menu)
328 {
329 #if wxUSE_MENUS
330 if (m_priv->m_win == NULL)
331 {
332 m_priv->m_win = new wxTopLevelWindow(
333 NULL, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, 0);
334 m_priv->m_win->PushEventHandler(this);
335 }
336 wxPoint point(-1, -1);
337 #ifdef __WXUNIVERSAL__
338 point = wxGetMousePosition();
339 #endif
340 m_priv->m_win->PopupMenu(menu, point);
341 #endif // wxUSE_MENUS
342 return true;
343 }
344
345 #endif // wxUSE_TASKBARICON
346