]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/taskbar.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[wxWidgets.git] / src / gtk / taskbar.cpp
index 1badc0a0894773c03b47ebfc86a65fdd3d670cc4..1aee6ca5b9287cb455c5f1d7bd812b6d0b3c40bd 100644 (file)
 /////////////////////////////////////////////////////////////////////////
-// 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 <gdk/gdkx.h>
+#ifndef WX_PRECOMP
+    #include "wx/toplevel.h"
+    #include "wx/menu.h"
+    #include "wx/icon.h"
+#endif
+
+#include <gtk/gtk.h>
+#ifdef GDK_WINDOWING_X11
+    #include <gdk/gdkx.h>
+#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 <gtk/gtkversion.h>
-#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