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