X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/065f010fe343a5a0f20c4658b053882a213a3be3..bb996f289574defb0ae4339ae8e46ff3cf6fa54c:/src/gtk/taskbar.cpp?ds=sidebyside diff --git a/src/gtk/taskbar.cpp b/src/gtk/taskbar.cpp index 1badc0a089..1aee6ca5b9 100644 --- a/src/gtk/taskbar.cpp +++ b/src/gtk/taskbar.cpp @@ -1,147 +1,342 @@ ///////////////////////////////////////////////////////////////////////// -// File: taskbar.cpp -// Purpose: wxTaskBarIcon (src/unix/taskbarx11.cpp) helper for GTK2 +// File: src/gtk/taskbar.cpp +// Purpose: wxTaskBarIcon // Author: Vaclav Slavik -// Modified by: +// Modified by: Paul Cornett // Created: 2004/05/29 // RCS-ID: $Id$ // Copyright: (c) Vaclav Slavik, 2004 // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////// -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) -#pragma implementation "taskbarpriv.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" -#include "wx/gtk/taskbarpriv.h" -#include "wx/log.h" -#include "wx/frame.h" -#include "wx/menu.h" +#if wxUSE_TASKBARICON + +#include "wx/taskbar.h" -#include +#ifndef WX_PRECOMP + #include "wx/toplevel.h" + #include "wx/menu.h" + #include "wx/icon.h" +#endif + +#include +#ifdef GDK_WINDOWING_X11 + #include +#endif +#ifndef __WXGTK3__ + #include "eggtrayicon.h" +#endif + +#if !GTK_CHECK_VERSION(2,10,0) + typedef struct _GtkStatusIcon GtkStatusIcon; +#endif + +class wxTaskBarIcon::Private +{ +public: + Private(wxTaskBarIcon* taskBarIcon); + ~Private(); + void SetIcon(); + void size_allocate(int width, int height); -#ifdef __WXGTK20__ -#include -#if GTK_CHECK_VERSION(2, 1, 0) + // owning wxTaskBarIcon + wxTaskBarIcon* m_taskBarIcon; + // used when GTK+ >= 2.10 + GtkStatusIcon* m_statusIcon; + // for PopupMenu + wxWindow* m_win; + wxBitmap m_bitmap; + wxString m_tipText; +#ifndef __WXGTK3__ + // used when GTK+ < 2.10 + GtkWidget* m_eggTrayIcon; + // for tooltip when GTK+ < 2.10 + GtkTooltips* m_tooltips; + // width and height of available space, only used when GTK+ < 2.10 + int m_size; +#endif +}; +//----------------------------------------------------------------------------- -#include "gtk/gtk.h" +extern "C" { +#ifndef __WXGTK3__ +static void +icon_size_allocate(GtkWidget*, GtkAllocation* alloc, wxTaskBarIcon::Private* priv) +{ + priv->size_allocate(alloc->width, alloc->height); +} -#include "eggtrayicon.h" +static void +icon_destroy(GtkWidget*, wxTaskBarIcon::Private* priv) +{ + // Icon window destroyed, probably because tray program has died. + // Recreate icon so it will appear if tray program is restarted. + priv->m_eggTrayIcon = NULL; + priv->SetIcon(); +} +#endif -wxTaskBarIconAreaBase::wxTaskBarIconAreaBase() +static void +icon_activate(void*, wxTaskBarIcon* taskBarIcon) { - if (IsProtocolSupported()) + // activate occurs from single click with GTK+ + wxTaskBarIconEvent event(wxEVT_TASKBAR_LEFT_DOWN, taskBarIcon); + if (!taskBarIcon->SafelyProcessEvent(event)) { - m_widget = GTK_WIDGET(egg_tray_icon_new("systray icon")); - gtk_window_set_resizable(GTK_WINDOW(m_widget), false); - - wxLogTrace(_T("systray"), _T("using freedesktop.org systray spec")); + // if single click not handled, send double click for compatibility + event.SetEventType(wxEVT_TASKBAR_LEFT_DCLICK); + taskBarIcon->SafelyProcessEvent(event); } - - wxTopLevelWindow::Create( - NULL, wxID_ANY, _T("systray icon"), - wxDefaultPosition, wxDefaultSize, - wxDEFAULT_FRAME_STYLE | wxFRAME_NO_TASKBAR | wxSIMPLE_BORDER | - wxFRAME_SHAPED, - wxEmptyString /*eggtray doesn't like setting wmclass*/); - - m_invokingWindow = NULL; } -bool wxTaskBarIconAreaBase::IsProtocolSupported() +static gboolean +icon_popup_menu(GtkWidget*, wxTaskBarIcon* taskBarIcon) +{ + wxTaskBarIconEvent event(wxEVT_TASKBAR_CLICK, taskBarIcon); + taskBarIcon->SafelyProcessEvent(event); + return true; +} + +#ifndef __WXGTK3__ +static gboolean +icon_button_press_event(GtkWidget*, GdkEventButton* event, wxTaskBarIcon* taskBarIcon) { - static int s_supported = -1; - if (s_supported == -1) + if (event->type == GDK_BUTTON_PRESS) { - Display *display = GDK_DISPLAY(); - Screen *screen = DefaultScreenOfDisplay(display); - - wxString name; - name.Printf(_T("_NET_SYSTEM_TRAY_S%d"), XScreenNumberOfScreen(screen)); - Atom atom = XInternAtom(display, name.ToAscii(), False); - - Window manager = XGetSelectionOwner(display, atom); - - s_supported = (manager != None); + if (event->button == 1) + icon_activate(NULL, taskBarIcon); + else if (event->button == 3) + icon_popup_menu(NULL, taskBarIcon); } - - return (bool)s_supported; + return false; } +#endif +#if GTK_CHECK_VERSION(2,10,0) +static void +status_icon_popup_menu(GtkStatusIcon*, guint, guint, wxTaskBarIcon* taskBarIcon) +{ + icon_popup_menu(NULL, taskBarIcon); +} +#endif +} // extern "C" //----------------------------------------------------------------------------- -// Pop-up menu stuff -//----------------------------------------------------------------------------- - -extern "C" void gtk_pop_hide_callback( GtkWidget *widget, bool* is_waiting ); -extern void SetInvokingWindow( wxMenu *menu, wxWindow* win ); - -extern "C" void wxPopupMenuPositionCallback( GtkMenu *menu, - gint *x, gint *y, - gboolean * WXUNUSED(whatever), - gpointer user_data ); - -#if wxUSE_MENUS_NATIVE -bool wxTaskBarIconAreaBase::DoPopupMenu( wxMenu *menu, int x, int y ) +bool wxTaskBarIconBase::IsAvailable() { - wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") ); - - wxCHECK_MSG( menu != NULL, false, wxT("invalid popup-menu") ); +#ifdef GDK_WINDOWING_X11 + char name[32]; + g_snprintf(name, sizeof(name), "_NET_SYSTEM_TRAY_S%d", + gdk_x11_get_default_screen()); + Atom atom = gdk_x11_get_xatom_by_name(name); - // NOTE: if you change this code, you need to update - // the same code in window.cpp as well. This - // is ugly code duplication, I know, + Window manager = XGetSelectionOwner(gdk_x11_get_default_xdisplay(), atom); - SetInvokingWindow( menu, this ); + return manager != None; +#else + return true; +#endif +} +//----------------------------------------------------------------------------- - menu->UpdateUI( m_invokingWindow ); +wxTaskBarIcon::Private::Private(wxTaskBarIcon* taskBarIcon) +{ + m_taskBarIcon = taskBarIcon; + m_statusIcon = NULL; + m_win = NULL; +#ifndef __WXGTK3__ + m_eggTrayIcon = NULL; + m_tooltips = NULL; + m_size = 0; +#endif +} - bool is_waiting = true; +wxTaskBarIcon::Private::~Private() +{ + if (m_statusIcon) + g_object_unref(m_statusIcon); +#ifndef __WXGTK3__ + else if (m_eggTrayIcon) + { + g_signal_handlers_disconnect_by_func(m_eggTrayIcon, (void*)icon_destroy, this); + gtk_widget_destroy(m_eggTrayIcon); + } +#endif + if (m_win) + { + m_win->PopEventHandler(); + m_win->Destroy(); + } +#ifndef __WXGTK3__ + if (m_tooltips) + { + gtk_object_destroy(GTK_OBJECT(m_tooltips)); + g_object_unref(m_tooltips); + } +#endif +} - gulong handler = gtk_signal_connect( GTK_OBJECT(menu->m_menu), - "hide", - GTK_SIGNAL_FUNC(gtk_pop_hide_callback), - (gpointer)&is_waiting ); +void wxTaskBarIcon::Private::SetIcon() +{ +#if GTK_CHECK_VERSION(2,10,0) + if (GTK_CHECK_VERSION(3,0,0) || gtk_check_version(2,10,0) == NULL) + { + if (m_statusIcon) + gtk_status_icon_set_from_pixbuf(m_statusIcon, m_bitmap.GetPixbuf()); + else + { + m_statusIcon = gtk_status_icon_new_from_pixbuf(m_bitmap.GetPixbuf()); + g_signal_connect(m_statusIcon, "activate", + G_CALLBACK(icon_activate), m_taskBarIcon); + g_signal_connect(m_statusIcon, "popup_menu", + G_CALLBACK(status_icon_popup_menu), m_taskBarIcon); + } + } + else +#endif + { +#ifndef __WXGTK3__ + m_size = 0; + if (m_eggTrayIcon) + { + GtkWidget* image = gtk_bin_get_child(GTK_BIN(m_eggTrayIcon)); + gtk_image_set_from_pixbuf(GTK_IMAGE(image), m_bitmap.GetPixbuf()); + } + else + { + m_eggTrayIcon = GTK_WIDGET(egg_tray_icon_new("wxTaskBarIcon")); + gtk_widget_add_events(m_eggTrayIcon, GDK_BUTTON_PRESS_MASK); + g_signal_connect(m_eggTrayIcon, "size_allocate", + G_CALLBACK(icon_size_allocate), this); + g_signal_connect(m_eggTrayIcon, "destroy", + G_CALLBACK(icon_destroy), this); + g_signal_connect(m_eggTrayIcon, "button_press_event", + G_CALLBACK(icon_button_press_event), m_taskBarIcon); + g_signal_connect(m_eggTrayIcon, "popup_menu", + G_CALLBACK(icon_popup_menu), m_taskBarIcon); + GtkWidget* image = gtk_image_new_from_pixbuf(m_bitmap.GetPixbuf()); + gtk_container_add(GTK_CONTAINER(m_eggTrayIcon), image); + gtk_widget_show_all(m_eggTrayIcon); + } +#endif + } +#if wxUSE_TOOLTIPS + const char *tip_text = NULL; + if (!m_tipText.empty()) + tip_text = m_tipText.utf8_str(); - wxPoint pos; - gpointer userdata; - GtkMenuPositionFunc posfunc; - if ( x == -1 && y == -1 ) +#if GTK_CHECK_VERSION(2,10,0) + if (m_statusIcon) { - // use GTK's default positioning algorithm - userdata = NULL; - posfunc = NULL; +#if GTK_CHECK_VERSION(2,16,0) + if (GTK_CHECK_VERSION(3,0,0) || gtk_check_version(2,16,0) == NULL) + gtk_status_icon_set_tooltip_text(m_statusIcon, tip_text); + else +#endif + { +#ifndef __WXGTK3__ + gtk_status_icon_set_tooltip(m_statusIcon, tip_text); +#endif + } } else +#endif // GTK_CHECK_VERSION(2,10,0) { - pos = ClientToScreen(wxPoint(x, y)); - userdata = &pos; - posfunc = wxPopupMenuPositionCallback; +#ifndef __WXGTK3__ + if (tip_text && m_tooltips == NULL) + { + m_tooltips = gtk_tooltips_new(); + g_object_ref(m_tooltips); + gtk_object_sink(GTK_OBJECT(m_tooltips)); + } + if (m_tooltips) + gtk_tooltips_set_tip(m_tooltips, m_eggTrayIcon, tip_text, ""); +#endif } +#endif // wxUSE_TOOLTIPS +} - gtk_menu_popup( - GTK_MENU(menu->m_menu), - (GtkWidget *) NULL, // parent menu shell - (GtkWidget *) NULL, // parent menu item - posfunc, // function to position it - userdata, // client data - 0, // button used to activate it - gtk_get_current_event_time() - ); - - while (is_waiting) +#ifndef __WXGTK3__ +void wxTaskBarIcon::Private::size_allocate(int width, int height) +{ + int size = height; + EggTrayIcon* icon = EGG_TRAY_ICON(m_eggTrayIcon); + if (egg_tray_icon_get_orientation(icon) == GTK_ORIENTATION_VERTICAL) + size = width; + if (m_size == size) + return; + m_size = size; + int w = m_bitmap.GetWidth(); + int h = m_bitmap.GetHeight(); + if (w > size || h > size) { - gtk_main_iteration(); + if (w > size) w = size; + if (h > size) h = size; + GdkPixbuf* pixbuf = + gdk_pixbuf_scale_simple(m_bitmap.GetPixbuf(), w, h, GDK_INTERP_BILINEAR); + GtkImage* image = GTK_IMAGE(gtk_bin_get_child(GTK_BIN(m_eggTrayIcon))); + gtk_image_set_from_pixbuf(image, pixbuf); + g_object_unref(pixbuf); } +} +#endif +//----------------------------------------------------------------------------- - gtk_signal_disconnect(GTK_OBJECT(menu->m_menu), handler); +IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler) +wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType WXUNUSED(iconType)) +{ + m_priv = new Private(this); +} + +wxTaskBarIcon::~wxTaskBarIcon() +{ + delete m_priv; +} + +bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) +{ + m_priv->m_bitmap = icon; + m_priv->m_tipText = tooltip; + m_priv->SetIcon(); + return true; +} + +bool wxTaskBarIcon::RemoveIcon() +{ + delete m_priv; + m_priv = new Private(this); + return true; +} + +bool wxTaskBarIcon::IsIconInstalled() const +{ +#ifdef __WXGTK3__ + return m_priv->m_statusIcon != NULL; +#else + return m_priv->m_statusIcon || m_priv->m_eggTrayIcon; +#endif +} + +bool wxTaskBarIcon::PopupMenu(wxMenu* menu) +{ +#if wxUSE_MENUS + if (m_priv->m_win == NULL) + { + m_priv->m_win = new wxTopLevelWindow( + NULL, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, 0); + m_priv->m_win->PushEventHandler(this); + } + wxPoint point(-1, -1); +#ifdef __WXUNIVERSAL__ + point = wxGetMousePosition(); +#endif + m_priv->m_win->PopupMenu(menu, point); +#endif // wxUSE_MENUS return true; } -#endif // wxUSE_MENUS_NATIVE -#endif // __WXGTK20__ -#endif // GTK_CHECK_VERSION(2, 1, 0) +#endif // wxUSE_TASKBARICON