]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/taskbar.cpp
defer creating bitmap representation until it is needed
[wxWidgets.git] / src / gtk / taskbar.cpp
index ee192ae7b92c0f220f0d38145bdd5310da7a7300..8a25c000fdd528c3302110faf21d5c28655e6974 100644 (file)
@@ -1,8 +1,8 @@
 /////////////////////////////////////////////////////////////////////////
 // File:        src/gtk/taskbar.cpp
-// Purpose:     wxTaskBarIcon (src/unix/taskbarx11.cpp) helper for GTK2
+// Purpose:     wxTaskBarIcon
 // Author:      Vaclav Slavik
-// Modified by:
+// Modified by: Paul Cornett
 // Created:     2004/05/29
 // RCS-ID:      $Id$
 // Copyright:   (c) Vaclav Slavik, 2004
 
 #if wxUSE_TASKBARICON
 
-#include "wx/gtk/taskbarpriv.h"
+#include "wx/taskbar.h"
 
 #ifndef WX_PRECOMP
-    #include "wx/log.h"
-    #include "wx/frame.h"
+    #include "wx/toplevel.h"
     #include "wx/menu.h"
+    #include "wx/icon.h"
 #endif
 
+#include "eggtrayicon.h"
 #include <gtk/gtk.h>
-#include <gdk/gdkx.h>
 
-#include "eggtrayicon.h"
+#if !GTK_CHECK_VERSION(2,10,0)
+    typedef struct _GtkStatusIcon GtkStatusIcon;
+#endif
 
-wxTaskBarIconAreaBase::wxTaskBarIconAreaBase()
+class wxTaskBarIcon::Private
 {
-    if (IsProtocolSupported())
-    {
-        m_widget = GTK_WIDGET(egg_tray_icon_new("systray icon"));
-        g_object_ref(m_widget);
-        gtk_window_set_resizable(GTK_WINDOW(m_widget), false);
+public:
+    Private(wxTaskBarIcon* taskBarIcon);
+    ~Private();
+    void SetIcon();
+    void size_allocate(int width, int height);
 
-        wxLogTrace(_T("systray"), _T("using freedesktop.org systray spec"));
-    }
+    // owning wxTaskBarIcon
+    wxTaskBarIcon* m_taskBarIcon;
+    // used when GTK+ >= 2.10
+    GtkStatusIcon* m_statusIcon;
+    // used when GTK+ < 2.10
+    GtkWidget* m_eggTrayIcon;
+    // for PopupMenu
+    wxWindow* m_win;
+    // for tooltip when GTK+ < 2.10
+    GtkTooltips* m_tooltips;
+    wxBitmap m_bitmap;
+    wxString m_tipText;
+    // width and height of available space, only used when GTK+ < 2.10
+    int m_size;
+};
+//-----------------------------------------------------------------------------
 
-    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*/);
+extern "C" {
+static void
+icon_size_allocate(GtkWidget*, GtkAllocation* alloc, wxTaskBarIcon::Private* priv)
+{
+    priv->size_allocate(alloc->width, alloc->height);
+}
 
-    // WM frame extents are not useful for wxTaskBarIcon
-    m_deferShow = false;
-    gulong handler_id = g_signal_handler_find(
-        m_widget,
-        GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
-        g_signal_lookup("property_notify_event", GTK_TYPE_WIDGET),
-        0, NULL, NULL, this);
-    if (handler_id != 0)
-        g_signal_handler_disconnect(m_widget, handler_id);
+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();
+}
 
-    m_invokingWindow = NULL;
+static void
+icon_activate(void*, wxTaskBarIcon* taskBarIcon)
+{
+    // activate occurs from single click with GTK+
+    wxTaskBarIconEvent event(wxEVT_TASKBAR_LEFT_DOWN, taskBarIcon);
+    if (!taskBarIcon->SafelyProcessEvent(event))
+    {
+        // if single click not handled, send double click for compatibility
+        event.SetEventType(wxEVT_TASKBAR_LEFT_DCLICK);
+        taskBarIcon->SafelyProcessEvent(event);
+    }
+}
+
+static gboolean
+icon_popup_menu(GtkWidget*, wxTaskBarIcon* taskBarIcon)
+{
+    wxTaskBarIconEvent event(wxEVT_TASKBAR_CLICK, taskBarIcon);
+    taskBarIcon->SafelyProcessEvent(event);
+    return true;
+}
+
+static gboolean
+icon_button_press_event(GtkWidget*, GdkEventButton* event, wxTaskBarIcon* taskBarIcon)
+{
+    if (event->type == GDK_BUTTON_PRESS)
+    {
+        if (event->button == 1)
+            icon_activate(NULL, taskBarIcon);
+        else if (event->button == 3)
+            icon_popup_menu(NULL, taskBarIcon);
+    }
+    return false;
 }
 
-bool wxTaskBarIconAreaBase::IsProtocolSupported()
+#if GTK_CHECK_VERSION(2,10,0)
+static void
+status_icon_popup_menu(GtkStatusIcon*, guint, guint, wxTaskBarIcon* taskBarIcon)
 {
-    Display *display = GDK_DISPLAY();
-    Screen *screen = DefaultScreenOfDisplay(display);
+    icon_popup_menu(NULL, taskBarIcon);
+}
+#endif
+} // extern "C"
+//-----------------------------------------------------------------------------
 
+bool wxTaskBarIconBase::IsAvailable()
+{
     char name[32];
     g_snprintf(name, sizeof(name), "_NET_SYSTEM_TRAY_S%d",
-        XScreenNumberOfScreen(screen));
-    Atom atom = XInternAtom(display, name, False);
+        gdk_x11_get_default_screen());
+    Atom atom = gdk_x11_get_xatom_by_name(name);
 
-    Window manager = XGetSelectionOwner(display, atom);
+    Window manager = XGetSelectionOwner(gdk_x11_get_default_xdisplay(), atom);
 
-    return (manager != None);
+    return manager != None;
 }
-
 //-----------------------------------------------------------------------------
-// Pop-up menu stuff
+
+wxTaskBarIcon::Private::Private(wxTaskBarIcon* taskBarIcon)
+{
+    m_taskBarIcon = taskBarIcon;
+    m_statusIcon = NULL;
+    m_eggTrayIcon = NULL;
+    m_win = NULL;
+    m_tooltips = NULL;
+    m_size = 0;
+}
+
+wxTaskBarIcon::Private::~Private()
+{
+    if (m_statusIcon)
+        g_object_unref(m_statusIcon);
+    else if (m_eggTrayIcon)
+    {
+        g_signal_handlers_disconnect_by_func(m_eggTrayIcon, (void*)icon_destroy, this);
+        gtk_widget_destroy(m_eggTrayIcon);
+    }
+    if (m_win)
+    {
+        m_win->PopEventHandler();
+        m_win->Destroy();
+    }
+    if (m_tooltips)
+    {
+        gtk_object_destroy(GTK_OBJECT(m_tooltips));
+        g_object_unref(m_tooltips);
+    }
+}
+
+void wxTaskBarIcon::Private::SetIcon()
+{
+#if GTK_CHECK_VERSION(2,10,0)
+    if (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
+    {
+        m_size = 0;
+        if (m_eggTrayIcon)
+        {
+            GtkWidget* image = GTK_BIN(m_eggTrayIcon)->child;
+            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);
+        }
+    }
+#if wxUSE_TOOLTIPS
+    const char *tip_text = NULL;
+    if (!m_tipText.empty())
+        tip_text = m_tipText.c_str();
+
+#if GTK_CHECK_VERSION(2,10,0)
+    if (m_statusIcon)
+        gtk_status_icon_set_tooltip(m_statusIcon, tip_text);
+    else
+#endif
+    {
+        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 // wxUSE_TOOLTIPS
+}
+
+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)
+    {
+        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(m_eggTrayIcon)->child);
+        gtk_image_set_from_pixbuf(image, pixbuf);
+        g_object_unref(pixbuf);
+    }
+}
 //-----------------------------------------------------------------------------
 
-#if wxUSE_MENUS_NATIVE
-void wxTaskBarIconAreaBase::DoPopupMenuUpdateUI(wxMenu* menu)
+IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
+
+wxTaskBarIcon::wxTaskBarIcon()
 {
-    menu->UpdateUI(m_invokingWindow);
+    m_priv = new Private(this);
 }
-#endif // wxUSE_MENUS_NATIVE
+
+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
+{
+    return m_priv->m_statusIcon || m_priv->m_eggTrayIcon;
+}
+
+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_TASKBARICON