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