From eb11c24eea6b367d1774cfa8ae3b38c03e72085a Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Tue, 24 Nov 2009 14:33:49 +0100 Subject: egg.trayicon is dead. Light a candle :'( Fixes #3021, #5246 --- Makefile.am | 1 - README.html | 5 +- configure.ac | 16 +- scripts/gajim.in | 1 - src/Makefile.am | 31 +-- src/eggtrayicon.c | 584 ------------------------------------------------- src/eggtrayicon.h | 80 ------- src/features_window.py | 13 -- src/gui_interface.py | 20 +- src/statusicon.py | 354 +++++++++++++++++++++++++++++- src/systray.py | 459 -------------------------------------- src/tooltips.py | 4 +- src/trayicon.defs | 59 ----- src/trayicon.override | 47 ---- src/trayiconmodule.c | 43 ---- 15 files changed, 354 insertions(+), 1363 deletions(-) delete mode 100644 src/eggtrayicon.c delete mode 100644 src/eggtrayicon.h delete mode 100644 src/systray.py delete mode 100644 src/trayicon.defs delete mode 100644 src/trayicon.override delete mode 100644 src/trayiconmodule.c diff --git a/Makefile.am b/Makefile.am index 019f7a1d9..f708a0855 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,7 +50,6 @@ MAINTAINERCLEANFILES = \ aclocal.m4 \ libtool \ po/POTFILES.in \ - src/trayicon_la-eggtrayicon.loT \ m4/intltool.m4 MAINTAINERCLEANDIRS = \ diff --git a/README.html b/README.html index 558d23a80..8ddade813 100644 --- a/README.html +++ b/README.html @@ -16,7 +16,7 @@ Welcome to Gajim and thank you for trying out our client.

Runtime Requirements

@@ -34,7 +34,6 @@ Gajim is a GTK+ app that loves GNOME. You can do 'make' so you don't require gno
  • For zeroconf (bonjour), the "enable link-local messaging" checkbox, you need dbus-glib, python-avahi
  • dnsutils (or whatever package provides the nslookup binary) for SRV support
  • gtkspell and aspell-LANG where lang is your locale eg. en, fr etc
  • -
  • GnomePythonExtras 2.10 or above (aka gnome-python-desktop) so you can avoid compiling trayicon and gtkspell
  • gnome-python-desktop (for GnomeKeyring support)
  • notification-daemon or notify-python (and D-Bus) to get cooler popups
  • D-Bus running to have gajim-remote working. Some distributions split dbus-x11, which is needed for dbus to work with Gajim. Version >= 0.80 is required.
  • @@ -53,8 +52,6 @@ the xml lib that *comes* with python and not pyxml or whatever. diff --git a/configure.ac b/configure.ac index 6363cb90b..af9840989 100644 --- a/configure.ac +++ b/configure.ac @@ -39,7 +39,7 @@ AM_NLS dnl **** dnl pygtk and gtk+ dnl **** -PKG_CHECK_MODULES([PYGTK], [gtk+-2.0 >= 2.12.0 pygtk-2.0 >= 2.12.0]) +PKG_CHECK_MODULES([PYGTK], [gtk+-2.0 >= 2.16.0 pygtk-2.0 >= 2.16.0]) AC_SUBST(PYGTK_CFLAGS) AC_SUBST(PYGTK_LIBS) PYGTK_DEFS=`$PKG_CONFIG --variable=defsdir pygtk-2.0` @@ -50,15 +50,6 @@ if test "x$PYTHON" = "x:"; then AC_MSG_ERROR([Python not found]) fi -dnl **** -dnl tray icon -dnl **** -AC_ARG_ENABLE(trayicon, - [ --disable-trayicon do not build trayicon module [default yes]], - enable_trayicon=$enableval, enable_trayicon=yes) -test "x$enable_trayicon" = "xyes" && have_trayicon=true || have_trayicon=false -AM_CONDITIONAL(BUILD_TRAYICON, $have_trayicon) - ACLOCAL_AMFLAGS="\${ACLOCAL_FLAGS}" AC_SUBST(ACLOCAL_AMFLAGS) @@ -91,8 +82,3 @@ AC_CONFIG_FILES([ po/Makefile.in ]) AC_OUTPUT -echo " -***************************** - Build features: - trayicon ......... ${have_trayicon} -*****************************" diff --git a/scripts/gajim.in b/scripts/gajim.in index 711ddac1d..178d69b16 100644 --- a/scripts/gajim.in +++ b/scripts/gajim.in @@ -33,5 +33,4 @@ export datadir=@DATADIR@/gajim PYTHON_EXEC=@PYTHON@ cd ${datadir}/src -export PYTHONPATH="$PYTHONPATH:@LIBDIR@/gajim" exec ${PYTHON_EXEC} -OO $APP.py "$@" diff --git a/src/Makefile.am b/src/Makefile.am index 24e7e2234..9c5e23b85 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,31 +1,7 @@ -CLEANFILES = \ - trayicon.c INCLUDES = \ $(PYTHON_INCLUDES) export MACOSX_DEPLOYMENT_TARGET=10.4 -if BUILD_TRAYICON -trayiconlib_LTLIBRARIES = trayicon.la -trayiconlibdir = $(pkglibdir) -trayicon_la_LIBADD = $(PYGTK_LIBS) -trayicon_la_SOURCES = \ - eggtrayicon.c \ - trayiconmodule.c - -nodist_trayicon_la_SOURCES = \ - trayicon.c - -trayicon_la_LDFLAGS = \ - -module -avoid-version -trayicon_la_CFLAGS = $(PYGTK_CFLAGS) - -trayicon.c: - pygtk-codegen-2.0 --prefix trayicon \ - --register $(PYGTK_DEFS)/gdk-types.defs \ - --register $(PYGTK_DEFS)/gtk-types.defs \ - --override $(srcdir)/trayicon.override \ - $(srcdir)/trayicon.defs > $@ -endif gajimsrcdir = $(pkgdatadir)/src gajimsrc_PYTHON = $(srcdir)/*.py @@ -56,12 +32,7 @@ EXTRA_DIST = $(gajimsrc_PYTHON) \ $(gajimsrc2_PYTHON) \ $(gajimsrc3_PYTHON) \ $(gajimsrc4_PYTHON) \ - $(gajimsrc5_PYTHON) \ - eggtrayicon.c \ - trayiconmodule.c \ - eggtrayicon.h \ - trayicon.defs \ - trayicon.override + $(gajimsrc5_PYTHON) dist-hook: rm -f $(distdir)/ipython_view.py diff --git a/src/eggtrayicon.c b/src/eggtrayicon.c deleted file mode 100644 index 56b3a0fb9..000000000 --- a/src/eggtrayicon.c +++ /dev/null @@ -1,584 +0,0 @@ -/* eggtrayicon.c - * Copyright (C) 2002 Anders Carlsson - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "eggtrayicon.h" - -#include -#if defined (GDK_WINDOWING_X11) -#include -#include -#elif defined (GDK_WINDOWING_WIN32) -#include -#endif - -#ifndef EGG_COMPILATION -#ifndef _ -#define _(x) dgettext (GETTEXT_PACKAGE, x) -#define N_(x) x -#endif -#else -#define _(x) x -#define N_(x) x -#endif - -#define SYSTEM_TRAY_REQUEST_DOCK 0 -#define SYSTEM_TRAY_BEGIN_MESSAGE 1 -#define SYSTEM_TRAY_CANCEL_MESSAGE 2 - -#define SYSTEM_TRAY_ORIENTATION_HORZ 0 -#define SYSTEM_TRAY_ORIENTATION_VERT 1 - -enum { - PROP_0, - PROP_ORIENTATION -}; - -static GtkPlugClass *parent_class = NULL; - -static void egg_tray_icon_init (EggTrayIcon *icon); -static void egg_tray_icon_class_init (EggTrayIconClass *klass); - -static void egg_tray_icon_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -static void egg_tray_icon_add (GtkContainer *container, GtkWidget *widget); - -static void egg_tray_icon_realize (GtkWidget *widget); -static void egg_tray_icon_unrealize (GtkWidget *widget); - -#ifdef GDK_WINDOWING_X11 -static void egg_tray_icon_update_manager_window (EggTrayIcon *icon, - gboolean dock_if_realized); -static void egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon); -#endif - -GType -egg_tray_icon_get_type (void) -{ - static GType our_type = 0; - - if (our_type == 0) - { - static const GTypeInfo our_info = - { - sizeof (EggTrayIconClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) egg_tray_icon_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (EggTrayIcon), - 0, /* n_preallocs */ - (GInstanceInitFunc) egg_tray_icon_init - }; - - our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0); - } - - return our_type; -} - -static void -egg_tray_icon_init (EggTrayIcon *icon) -{ - icon->stamp = 1; - icon->orientation = GTK_ORIENTATION_HORIZONTAL; - - gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK); -} - -static void -egg_tray_icon_class_init (EggTrayIconClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass *)klass; - GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; - GtkContainerClass *container_class = (GtkContainerClass *)klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->get_property = egg_tray_icon_get_property; - - widget_class->realize = egg_tray_icon_realize; - widget_class->unrealize = egg_tray_icon_unrealize; - - container_class->add = egg_tray_icon_add; - - g_object_class_install_property (gobject_class, - PROP_ORIENTATION, - g_param_spec_enum ("orientation", - _("Orientation"), - _("The orientation of the tray."), - GTK_TYPE_ORIENTATION, - GTK_ORIENTATION_HORIZONTAL, - G_PARAM_READABLE)); - -#if defined (GDK_WINDOWING_X11) - /* Nothing */ -#elif defined (GDK_WINDOWING_WIN32) - g_warning ("Port eggtrayicon to Win32"); -#else - g_warning ("Port eggtrayicon to this GTK+ backend"); -#endif -} - -static void -egg_tray_icon_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - EggTrayIcon *icon = EGG_TRAY_ICON (object); - - switch (prop_id) - { - case PROP_ORIENTATION: - g_value_set_enum (value, icon->orientation); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -#ifdef GDK_WINDOWING_X11 - -static Display * -egg_tray_icon_get_x_display(EggTrayIcon *icon) -{ - Display *xdisplay = NULL; - - GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (icon)); - if (!GDK_IS_DISPLAY (display)) - display = gdk_display_get_default (); - - xdisplay = GDK_DISPLAY_XDISPLAY (display); - - return xdisplay; -} - -static void -egg_tray_icon_get_orientation_property (EggTrayIcon *icon) -{ - Display *xdisplay; - Atom type; - int format; - union { - gulong *prop; - guchar *prop_ch; - } prop = { NULL }; - gulong nitems; - gulong bytes_after; - int error, result; - - g_assert (icon->manager_window != None); - - xdisplay = egg_tray_icon_get_x_display(icon); - if (xdisplay == NULL) - return; - - gdk_error_trap_push (); - type = None; - result = XGetWindowProperty (xdisplay, - icon->manager_window, - icon->orientation_atom, - 0, G_MAXLONG, FALSE, - XA_CARDINAL, - &type, &format, &nitems, - &bytes_after, &(prop.prop_ch)); - error = gdk_error_trap_pop (); - - if (error || result != Success) - return; - - if (type == XA_CARDINAL) - { - GtkOrientation orientation; - - orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ? - GTK_ORIENTATION_HORIZONTAL : - GTK_ORIENTATION_VERTICAL; - - if (icon->orientation != orientation) - { - icon->orientation = orientation; - - g_object_notify (G_OBJECT (icon), "orientation"); - } - } - - if (prop.prop) - XFree (prop.prop); -} - -static GdkFilterReturn -egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data) -{ - EggTrayIcon *icon = user_data; - XEvent *xev = (XEvent *)xevent; - - if (xev->xany.type == ClientMessage && - xev->xclient.message_type == icon->manager_atom && - xev->xclient.data.l[1] == icon->selection_atom) - { - egg_tray_icon_update_manager_window (icon, TRUE); - } - else if (xev->xany.window == icon->manager_window) - { - if (xev->xany.type == PropertyNotify && - xev->xproperty.atom == icon->orientation_atom) - { - egg_tray_icon_get_orientation_property (icon); - } - if (xev->xany.type == DestroyNotify) - { - egg_tray_icon_manager_window_destroyed (icon); - } - } - return GDK_FILTER_CONTINUE; -} - -#endif - -static void -egg_tray_icon_unrealize (GtkWidget *widget) -{ -#ifdef GDK_WINDOWING_X11 - EggTrayIcon *icon = EGG_TRAY_ICON (widget); - GdkWindow *root_window; - - if (icon->manager_window != None) - { - GdkWindow *gdkwin; - - gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget), - icon->manager_window); - - gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); - } - - root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); - - gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon); - - if (GTK_WIDGET_CLASS (parent_class)->unrealize) - (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); -#endif -} - -#ifdef GDK_WINDOWING_X11 - -static void -egg_tray_icon_send_manager_message (EggTrayIcon *icon, - long message, - Window window, - long data1, - long data2, - long data3) -{ - XClientMessageEvent ev; - Display *display; - - ev.type = ClientMessage; - ev.window = window; - ev.message_type = icon->system_tray_opcode_atom; - ev.format = 32; - ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window); - ev.data.l[1] = message; - ev.data.l[2] = data1; - ev.data.l[3] = data2; - ev.data.l[4] = data3; - - display = egg_tray_icon_get_x_display(icon); - - if (display == NULL) - return; - - gdk_error_trap_push (); - XSendEvent (display, - icon->manager_window, False, NoEventMask, (XEvent *)&ev); - XSync (display, False); - gdk_error_trap_pop (); -} - -static void -egg_tray_icon_send_dock_request (EggTrayIcon *icon) -{ - egg_tray_icon_send_manager_message (icon, - SYSTEM_TRAY_REQUEST_DOCK, - icon->manager_window, - gtk_plug_get_id (GTK_PLUG (icon)), - 0, 0); -} - -static void -egg_tray_icon_update_manager_window (EggTrayIcon *icon, - gboolean dock_if_realized) -{ - Display *xdisplay; - - if (icon->manager_window != None) - return; - - xdisplay = egg_tray_icon_get_x_display(icon); - - if (xdisplay == NULL) - return; - - XGrabServer (xdisplay); - - icon->manager_window = XGetSelectionOwner (xdisplay, - icon->selection_atom); - - if (icon->manager_window != None) - XSelectInput (xdisplay, - icon->manager_window, StructureNotifyMask|PropertyChangeMask); - - XUngrabServer (xdisplay); - XFlush (xdisplay); - - if (icon->manager_window != None) - { - GdkWindow *gdkwin; - - gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), - icon->manager_window); - - gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon); - - if (dock_if_realized && GTK_WIDGET_REALIZED (icon)) - egg_tray_icon_send_dock_request (icon); - - egg_tray_icon_get_orientation_property (icon); - } -} - -static gboolean -transparent_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) -{ - gdk_window_clear_area (widget->window, event->area.x, event->area.y, - event->area.width, event->area.height); - return FALSE; -} - -static void -make_transparent_again (GtkWidget *widget, GtkStyle *previous_style, - gpointer user_data) -{ - gdk_window_set_back_pixmap (widget->window, NULL, TRUE); -} - -static void -make_transparent (GtkWidget *widget, gpointer user_data) -{ - if (GTK_WIDGET_NO_WINDOW (widget) || GTK_WIDGET_APP_PAINTABLE (widget)) - return; - - gtk_widget_set_app_paintable (widget, TRUE); - gtk_widget_set_double_buffered (widget, FALSE); - gdk_window_set_back_pixmap (widget->window, NULL, TRUE); - g_signal_connect (widget, "expose_event", - G_CALLBACK (transparent_expose_event), NULL); - g_signal_connect_after (widget, "style_set", - G_CALLBACK (make_transparent_again), NULL); -} - -static void -egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon) -{ - GdkWindow *gdkwin; - - g_return_if_fail (icon->manager_window != None); - - gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), - icon->manager_window); - - gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); - - icon->manager_window = None; - - egg_tray_icon_update_manager_window (icon, TRUE); -} - -#endif - -static void -egg_tray_icon_realize (GtkWidget *widget) -{ -#ifdef GDK_WINDOWING_X11 - EggTrayIcon *icon = EGG_TRAY_ICON (widget); - GdkScreen *screen; - Display *xdisplay; - char buffer[256]; - GdkWindow *root_window; - - if (GTK_WIDGET_CLASS (parent_class)->realize) - GTK_WIDGET_CLASS (parent_class)->realize (widget); - - make_transparent (widget, NULL); - - xdisplay = egg_tray_icon_get_x_display(icon); - - if (xdisplay == NULL) - return; - - screen = gtk_widget_get_screen (widget); - - /* Now see if there's a manager window around */ - g_snprintf (buffer, sizeof (buffer), - "_NET_SYSTEM_TRAY_S%d", - gdk_screen_get_number (screen)); - - icon->selection_atom = XInternAtom (xdisplay, buffer, False); - - icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False); - - icon->system_tray_opcode_atom = XInternAtom (xdisplay, - "_NET_SYSTEM_TRAY_OPCODE", - False); - - icon->orientation_atom = XInternAtom (xdisplay, - "_NET_SYSTEM_TRAY_ORIENTATION", - False); - - egg_tray_icon_update_manager_window (icon, FALSE); - egg_tray_icon_send_dock_request (icon); - - root_window = gdk_screen_get_root_window (screen); - - /* Add a root window filter so that we get changes on MANAGER */ - gdk_window_add_filter (root_window, - egg_tray_icon_manager_filter, icon); -#endif -} - -static void -egg_tray_icon_add (GtkContainer *container, GtkWidget *widget) -{ - g_signal_connect (widget, "realize", - G_CALLBACK (make_transparent), NULL); - GTK_CONTAINER_CLASS (parent_class)->add (container, widget); -} - -EggTrayIcon * -egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name) -{ - g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); - - return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL); -} - -EggTrayIcon* -egg_tray_icon_new (const gchar *name) -{ - return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL); -} - -guint -egg_tray_icon_send_message (EggTrayIcon *icon, - gint timeout, - const gchar *message, - gint len) -{ - guint stamp; - - g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0); - g_return_val_if_fail (timeout >= 0, 0); - g_return_val_if_fail (message != NULL, 0); - -#ifdef GDK_WINDOWING_X11 - if (icon->manager_window == None) - return 0; -#endif - - if (len < 0) - len = strlen (message); - - stamp = icon->stamp++; - -#ifdef GDK_WINDOWING_X11 - /* Get ready to send the message */ - egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE, - (Window)gtk_plug_get_id (GTK_PLUG (icon)), - timeout, len, stamp); - - /* Now to send the actual message */ - gdk_error_trap_push (); - while (len > 0) - { - XClientMessageEvent ev; - Display *xdisplay; - - xdisplay = egg_tray_icon_get_x_display(icon); - - if (xdisplay == NULL) - return 0; - - ev.type = ClientMessage; - ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon)); - ev.format = 8; - ev.message_type = XInternAtom (xdisplay, - "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); - if (len > 20) - { - memcpy (&ev.data, message, 20); - len -= 20; - message += 20; - } - else - { - memcpy (&ev.data, message, len); - len = 0; - } - - XSendEvent (xdisplay, - icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev); - XSync (xdisplay, False); - } - gdk_error_trap_pop (); -#endif - - return stamp; -} - -void -egg_tray_icon_cancel_message (EggTrayIcon *icon, - guint id) -{ - g_return_if_fail (EGG_IS_TRAY_ICON (icon)); - g_return_if_fail (id > 0); -#ifdef GDK_WINDOWING_X11 - egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE, - (Window)gtk_plug_get_id (GTK_PLUG (icon)), - id, 0, 0); -#endif -} - -GtkOrientation -egg_tray_icon_get_orientation (EggTrayIcon *icon) -{ - g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL); - - return icon->orientation; -} diff --git a/src/eggtrayicon.h b/src/eggtrayicon.h deleted file mode 100644 index 557fdb20c..000000000 --- a/src/eggtrayicon.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* eggtrayicon.h - * Copyright (C) 2002 Anders Carlsson - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __EGG_TRAY_ICON_H__ -#define __EGG_TRAY_ICON_H__ - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -G_BEGIN_DECLS - -#define EGG_TYPE_TRAY_ICON (egg_tray_icon_get_type ()) -#define EGG_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon)) -#define EGG_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) -#define EGG_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON)) -#define EGG_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON)) -#define EGG_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) - -typedef struct _EggTrayIcon EggTrayIcon; -typedef struct _EggTrayIconClass EggTrayIconClass; - -struct _EggTrayIcon -{ - GtkPlug parent_instance; - - guint stamp; - -#ifdef GDK_WINDOWING_X11 - Atom selection_atom; - Atom manager_atom; - Atom system_tray_opcode_atom; - Atom orientation_atom; - Window manager_window; -#endif - GtkOrientation orientation; -}; - -struct _EggTrayIconClass -{ - GtkPlugClass parent_class; -}; - -GType egg_tray_icon_get_type (void); - -EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen *screen, - const gchar *name); - -EggTrayIcon *egg_tray_icon_new (const gchar *name); - -guint egg_tray_icon_send_message (EggTrayIcon *icon, - gint timeout, - const char *message, - gint len); -void egg_tray_icon_cancel_message (EggTrayIcon *icon, - guint id); - -GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon); - -G_END_DECLS - -#endif /* __EGG_TRAY_ICON_H__ */ diff --git a/src/features_window.py b/src/features_window.py index 609959e65..c89b597cb 100644 --- a/src/features_window.py +++ b/src/features_window.py @@ -83,10 +83,6 @@ class FeaturesWindow: _('Passive popups notifying for new events.'), _('Requires python-notify or instead python-dbus in conjunction with notification-daemon.'), _('Feature not available under Windows.')), - _('Trayicon'): (self.trayicon_available, - _('A icon in systemtray reflecting the current presence.'), - _('Requires python-gnome2-extras or compiled trayicon module from Gajim sources.'), - _('Requires PyGTK >= 2.10.')), _('Automatic status'): (self.idle_available, _('Ability to measure idle time, in order to set auto status.'), _('Requires libxss library.'), @@ -240,15 +236,6 @@ class FeaturesWindow: return False return True - def trayicon_available(self): - if os.name == 'nt': - return True - try: - import systray - except Exception: - return False - return True - def idle_available(self): from common import sleepy return sleepy.SUPPORTED diff --git a/src/gui_interface.py b/src/gui_interface.py index 98c1f3298..dc8959f91 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -3122,7 +3122,7 @@ class Interface: gajim.ipython_window = window def run(self): - if self.systray_capabilities and gajim.config.get('trayicon') != 'never': + if gajim.config.get('trayicon') != 'never': self.show_systray() self.roster = roster_window.RosterWindow() @@ -3357,21 +3357,9 @@ class Interface: gtkgui_helpers.make_jabber_state_images() self.systray_enabled = False - self.systray_capabilities = False - - if (os.name == 'nt'): - import statusicon - self.systray = statusicon.StatusIcon() - self.systray_capabilities = True - else: # use ours, not GTK+ one - # [FIXME: remove this when we migrate to 2.10 and we can do - # cool tooltips somehow and (not dying to keep) animation] - import systray - self.systray_capabilities = systray.HAS_SYSTRAY_CAPABILITIES - if self.systray_capabilities: - self.systray = systray.Systray() - else: - gajim.config.set('trayicon', 'never') + + import statusicon + self.systray = statusicon.StatusIcon() path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'gajim.png') pix = gtk.gdk.pixbuf_new_from_file(path_to_file) diff --git a/src/statusicon.py b/src/statusicon.py index 90bdb8c91..e161d18a4 100644 --- a/src/statusicon.py +++ b/src/statusicon.py @@ -25,26 +25,62 @@ import sys import gtk -import systray +import gobject +import os + +import dialogs +import config +import tooltips +import gtkgui_helpers +import tooltips from common import gajim from common import helpers +from common import pep -class StatusIcon(systray.Systray): +class StatusIcon: '''Class for the notification area icon''' - #NOTE: gtk api does NOT allow: - # leave, enter motion notify - # and can't do cool tooltips we use def __init__(self): - systray.Systray.__init__(self) + self.single_message_handler_id = None + self.new_chat_handler_id = None + # click somewhere else does not popdown menu. workaround this. + self.added_hide_menuitem = False + self.status = 'offline' + self.xml = gtkgui_helpers.get_glade('systray_context_menu.glade') + self.systray_context_menu = self.xml.get_widget('systray_context_menu') + self.xml.signal_autoconnect(self) + self.popup_menus = [] self.status_icon = None + self.tooltip = tooltips.NotificationAreaTooltip() + + def subscribe_events(self): + '''Register listeners to the events class''' + gajim.events.event_added_subscribe(self.on_event_added) + gajim.events.event_removed_subscribe(self.on_event_removed) + + def unsubscribe_events(self): + '''Unregister listeners to the events class''' + gajim.events.event_added_unsubscribe(self.on_event_added) + gajim.events.event_removed_unsubscribe(self.on_event_removed) + + def on_event_added(self, event): + '''Called when an event is added to the event list''' + if event.show_in_systray: + self.set_img() + + def on_event_removed(self, event_list): + '''Called when one or more events are removed from the event list''' + self.set_img() def show_icon(self): if not self.status_icon: self.status_icon = gtk.StatusIcon() + self.status_icon.set_property('has-tooltip', True) self.status_icon.connect('activate', self.on_status_icon_left_clicked) self.status_icon.connect('popup-menu', self.on_status_icon_right_clicked) + self.status_icon.connect('query-tooltip', + self.on_status_icon_query_tooltip) self.set_img() self.status_icon.set_visible(True) @@ -53,6 +89,11 @@ class StatusIcon(systray.Systray): def on_status_icon_right_clicked(self, widget, event_button, event_time): self.make_menu(event_button, event_time) + def on_status_icon_query_tooltip(self, widget, x, y, keyboard_mode, tooltip): + self.tooltip.populate() + tooltip.set_custom(self.tooltip.hbox) + return True + def hide_icon(self): self.status_icon.set_visible(False) self.unsubscribe_events() @@ -64,8 +105,6 @@ class StatusIcon(systray.Systray): '''apart from image, we also update tooltip text here''' if not gajim.interface.systray_enabled: return - text = helpers.get_notification_icon_tooltip_text() - self.status_icon.set_tooltip(text) if gajim.events.get_nb_systray_events(): state = 'event' self.status_icon.set_blinking(True) @@ -79,7 +118,304 @@ class StatusIcon(systray.Systray): self.status_icon.set_from_pixbuf(image.get_pixbuf()) #FIXME: oops they forgot to support GIF animation? #or they were lazy to get it to work under Windows! WTF! - #elif image.get_storage_type() == gtk.IMAGE_ANIMATION: + elif image.get_storage_type() == gtk.IMAGE_ANIMATION: + self.status_icon.set_from_pixbuf( + image.get_animation().get_static_image()) # self.img_tray.set_from_animation(image.get_animation()) + def change_status(self, global_status): + ''' set tray image to 'global_status' ''' + # change image and status, only if it is different + if global_status is not None and self.status != global_status: + self.status = global_status + self.set_img() + + def start_chat(self, widget, account, jid): + contact = gajim.contacts.get_first_contact_from_jid(account, jid) + if gajim.interface.msg_win_mgr.has_window(jid, account): + gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab( + jid, account) + elif contact: + gajim.interface.new_chat(contact, account) + gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab( + jid, account) + + def on_single_message_menuitem_activate(self, widget, account): + dialogs.SingleMessageWindow(account, action='send') + + def on_new_chat(self, widget, account): + dialogs.NewChatDialog(account) + + def make_menu(self, event_button, event_time): + '''create chat with and new message (sub) menus/menuitems''' + for m in self.popup_menus: + m.destroy() + + chat_with_menuitem = self.xml.get_widget('chat_with_menuitem') + single_message_menuitem = self.xml.get_widget( + 'single_message_menuitem') + status_menuitem = self.xml.get_widget('status_menu') + join_gc_menuitem = self.xml.get_widget('join_gc_menuitem') + sounds_mute_menuitem = self.xml.get_widget('sounds_mute_menuitem') + + if self.single_message_handler_id: + single_message_menuitem.handler_disconnect( + self.single_message_handler_id) + self.single_message_handler_id = None + if self.new_chat_handler_id: + chat_with_menuitem.disconnect(self.new_chat_handler_id) + self.new_chat_handler_id = None + + sub_menu = gtk.Menu() + self.popup_menus.append(sub_menu) + status_menuitem.set_submenu(sub_menu) + + gc_sub_menu = gtk.Menu() # gc is always a submenu + join_gc_menuitem.set_submenu(gc_sub_menu) + + # We need our own set of status icons, let's make 'em! + iconset = gajim.config.get('iconset') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') + state_images = gtkgui_helpers.load_iconset(path) + + if 'muc_active' in state_images: + join_gc_menuitem.set_image(state_images['muc_active']) + + for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'): + uf_show = helpers.get_uf_show(show, use_mnemonic = True) + item = gtk.ImageMenuItem(uf_show) + item.set_image(state_images[show]) + sub_menu.append(item) + item.connect('activate', self.on_show_menuitem_activate, show) + + item = gtk.SeparatorMenuItem() + sub_menu.append(item) + + item = gtk.ImageMenuItem(_('_Change Status Message...')) + path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png') + img = gtk.Image() + img.set_from_file(path) + item.set_image(img) + sub_menu.append(item) + item.connect('activate', self.on_change_status_message_activate) + + connected_accounts = gajim.get_number_of_connected_accounts() + if connected_accounts < 1: + item.set_sensitive(False) + + connected_accounts_with_private_storage = 0 + + item = gtk.SeparatorMenuItem() + sub_menu.append(item) + + uf_show = helpers.get_uf_show('offline', use_mnemonic = True) + item = gtk.ImageMenuItem(uf_show) + item.set_image(state_images['offline']) + sub_menu.append(item) + item.connect('activate', self.on_show_menuitem_activate, 'offline') + + iskey = connected_accounts > 0 and not (connected_accounts == 1 and + gajim.connections[gajim.connections.keys()[0]].is_zeroconf) + chat_with_menuitem.set_sensitive(iskey) + single_message_menuitem.set_sensitive(iskey) + join_gc_menuitem.set_sensitive(iskey) + + accounts_list = sorted(gajim.contacts.get_accounts()) + # items that get shown whether an account is zeroconf or not + if connected_accounts > 1: # 2 or more connections? make submenus + account_menu_for_chat_with = gtk.Menu() + chat_with_menuitem.set_submenu(account_menu_for_chat_with) + self.popup_menus.append(account_menu_for_chat_with) + + for account in accounts_list: + if gajim.account_is_connected(account): + # for chat_with + item = gtk.MenuItem(_('using account %s') % account) + account_menu_for_chat_with.append(item) + item.connect('activate', self.on_new_chat, account) + + elif connected_accounts == 1: # one account + # one account connected, no need to show 'as jid' + for account in gajim.connections: + if gajim.connections[account].connected > 1: + # for start chat + self.new_chat_handler_id = chat_with_menuitem.connect( + 'activate', self.on_new_chat, account) + break # No other connected account + + # menu items that don't apply to zeroconf connections + if connected_accounts == 1 or (connected_accounts == 2 and \ + gajim.zeroconf_is_connected()): + # only one 'real' (non-zeroconf) account is connected, don't need + # submenus + for account in gajim.connections: + if gajim.account_is_connected(account) and \ + not gajim.config.get_per('accounts', account, 'is_zeroconf'): + if gajim.connections[account].private_storage_supported: + connected_accounts_with_private_storage += 1 + + # for single message + single_message_menuitem.remove_submenu() + self.single_message_handler_id = single_message_menuitem.\ + connect('activate', + self.on_single_message_menuitem_activate, account) + # join gc + gajim.interface.roster.add_bookmarks_list(gc_sub_menu, + account) + break # No other account connected + else: + # 2 or more 'real' accounts are connected, make submenus + account_menu_for_single_message = gtk.Menu() + single_message_menuitem.set_submenu( + account_menu_for_single_message) + self.popup_menus.append(account_menu_for_single_message) + + for account in accounts_list: + if gajim.connections[account].is_zeroconf or \ + not gajim.account_is_connected(account): + continue + if gajim.connections[account].private_storage_supported: + connected_accounts_with_private_storage += 1 + # for single message + item = gtk.MenuItem(_('using account %s') % account) + item.connect('activate', + self.on_single_message_menuitem_activate, account) + account_menu_for_single_message.append(item) + + # join gc + gc_item = gtk.MenuItem(_('using account %s') % account, False) + gc_sub_menu.append(gc_item) + gc_menuitem_menu = gtk.Menu() + gajim.interface.roster.add_bookmarks_list(gc_menuitem_menu, + account) + gc_item.set_submenu(gc_menuitem_menu) + gc_sub_menu.show_all() + + newitem = gtk.SeparatorMenuItem() # separator + gc_sub_menu.append(newitem) + newitem = gtk.ImageMenuItem(_('_Manage Bookmarks...')) + img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) + newitem.set_image(img) + newitem.connect('activate', + gajim.interface.roster.on_manage_bookmarks_menuitem_activate) + gc_sub_menu.append(newitem) + if connected_accounts_with_private_storage == 0: + newitem.set_sensitive(False) + + sounds_mute_menuitem.set_active(not gajim.config.get('sounds_on')) + + if os.name == 'nt': + if self.added_hide_menuitem is False: + self.systray_context_menu.prepend(gtk.SeparatorMenuItem()) + item = gtk.MenuItem(_('Hide this menu')) + self.systray_context_menu.prepend(item) + self.added_hide_menuitem = True + + self.systray_context_menu.show_all() + self.systray_context_menu.popup(None, None, None, 0, + event_time) + + def on_show_all_events_menuitem_activate(self, widget): + events = gajim.events.get_systray_events() + for account in events: + for jid in events[account]: + for event in events[account][jid]: + gajim.interface.handle_event(account, jid, event.type_) + + def on_sounds_mute_menuitem_activate(self, widget): + gajim.config.set('sounds_on', not widget.get_active()) + gajim.interface.save_config() + + def on_show_roster_menuitem_activate(self, widget): + win = gajim.interface.roster.window + win.present() + + def on_preferences_menuitem_activate(self, widget): + if 'preferences' in gajim.interface.instances: + gajim.interface.instances['preferences'].window.present() + else: + gajim.interface.instances['preferences'] = config.PreferencesWindow() + + def on_quit_menuitem_activate(self, widget): + gajim.interface.roster.on_quit_request() + + def on_left_click(self): + win = gajim.interface.roster.window + if len(gajim.events.get_systray_events()) == 0: + # No pending events, so toggle visible/hidden for roster window + if not win.iconify_initially and (win.get_property( + 'has-toplevel-focus') or os.name == 'nt'): + # visible in ANY virtual desktop? + + # we could be in another VD right now. eg vd2 + # and we want to show it in vd2 + if not gtkgui_helpers.possibly_move_window_in_current_desktop(win): + win.set_property('skip-taskbar-hint', False) + win.iconify() # else we hide it from VD that was visible in + win.set_property('skip-taskbar-hint', True) + else: + win.deiconify() + if not gajim.config.get('roster_window_skip_taskbar'): + win.set_property('skip-taskbar-hint', False) + win.present_with_time(gtk.get_current_event_time()) + else: + self.handle_first_event() + + def handle_first_event(self): + account, jid, event = gajim.events.get_first_systray_event() + if not event: + return + gajim.interface.handle_event(account, jid, event.type_) + + def on_middle_click(self): + '''middle click raises window to have complete focus (fe. get kbd events) + but if already raised, it hides it''' + win = gajim.interface.roster.window + if win.is_active(): # is it fully raised? (eg does it receive kbd events?) + win.hide() + else: + win.present() + + def on_clicked(self, widget, event): + self.on_tray_leave_notify_event(widget, None) + if event.type != gtk.gdk.BUTTON_PRESS: + return + if event.button == 1: # Left click + self.on_left_click() + elif event.button == 2: # middle click + self.on_middle_click() + elif event.button == 3: # right click + self.make_menu(event.button, event.time) + + def on_show_menuitem_activate(self, widget, show): + # we all add some fake (we cannot select those nor have them as show) + # but this helps to align with roster's status_combobox index positions + l = ['online', 'chat', 'away', 'xa', 'dnd', 'invisible', 'SEPARATOR', + 'CHANGE_STATUS_MSG_MENUITEM', 'SEPARATOR', 'offline'] + index = l.index(show) + if not helpers.statuses_unified(): + gajim.interface.roster.status_combobox.set_active(index + 2) + return + current = gajim.interface.roster.status_combobox.get_active() + if index != current: + gajim.interface.roster.status_combobox.set_active(index) + + def on_change_status_message_activate(self, widget): + model = gajim.interface.roster.status_combobox.get_model() + active = gajim.interface.roster.status_combobox.get_active() + status = model[active][2].decode('utf-8') + def on_response(message, pep_dict): + if message is None: # None if user press Cancel + return + accounts = gajim.connections.keys() + for acct in accounts: + if not gajim.config.get_per('accounts', acct, + 'sync_with_global_status'): + continue + show = gajim.SHOW_LIST[gajim.connections[acct].connected] + gajim.interface.roster.send_status(acct, show, message) + gajim.interface.roster.send_pep(acct, pep_dict) + dlg = dialogs.ChangeStatusMessageDialog(on_response, status) + dlg.dialog.present() + # vim: se ts=3: diff --git a/src/systray.py b/src/systray.py deleted file mode 100644 index e2c09331b..000000000 --- a/src/systray.py +++ /dev/null @@ -1,459 +0,0 @@ -# -*- coding:utf-8 -*- -## src/systray.py -## -## Copyright (C) 2003-2005 Vincent Hanquez -## Copyright (C) 2003-2008 Yann Leboulanger -## Copyright (C) 2005 Norman Rasmussen -## Copyright (C) 2005-2006 Dimitur Kirov -## Travis Shirk -## Copyright (C) 2005-2007 Nikos Kouremenos -## Copyright (C) 2006 Stefan Bethge -## Copyright (C) 2006-2008 Jean-Marie Traissard -## Copyright (C) 2007 Lukas Petrovicky -## Julien Pivotto -## -## This file is part of Gajim. -## -## Gajim is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published -## by the Free Software Foundation; version 3 only. -## -## Gajim is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with Gajim. If not, see . -## - -import gtk -import gobject -import os - -import dialogs -import config -import tooltips -import gtkgui_helpers - -from common import gajim -from common import helpers -from common import pep - -HAS_SYSTRAY_CAPABILITIES = True - -try: - import egg.trayicon as trayicon # gnomepythonextras trayicon -except Exception: - try: - import trayicon # our trayicon - except Exception: - gajim.log.debug('No trayicon module available') - HAS_SYSTRAY_CAPABILITIES = False - - -class Systray: - '''Class for icon in the notification area - This class is both base class (for statusicon.py) and normal class - for trayicon in GNU/Linux''' - - def __init__(self): - self.single_message_handler_id = None - self.new_chat_handler_id = None - self.t = None - # click somewhere else does not popdown menu. workaround this. - self.added_hide_menuitem = False - self.img_tray = gtk.Image() - self.status = 'offline' - self.xml = gtkgui_helpers.get_glade('systray_context_menu.glade') - self.systray_context_menu = self.xml.get_widget('systray_context_menu') - self.xml.signal_autoconnect(self) - self.popup_menus = [] - - def subscribe_events(self): - '''Register listeners to the events class''' - gajim.events.event_added_subscribe(self.on_event_added) - gajim.events.event_removed_subscribe(self.on_event_removed) - - def unsubscribe_events(self): - '''Unregister listeners to the events class''' - gajim.events.event_added_unsubscribe(self.on_event_added) - gajim.events.event_removed_unsubscribe(self.on_event_removed) - - def on_event_added(self, event): - '''Called when an event is added to the event list''' - if event.show_in_systray: - self.set_img() - - def on_event_removed(self, event_list): - '''Called when one or more events are removed from the event list''' - self.set_img() - - def set_img(self): - if not gajim.interface.systray_enabled: - return - if gajim.events.get_nb_systray_events(): - state = 'event' - else: - state = self.status - if state != 'event' and gajim.config.get('trayicon') == 'on_event': - self.t.hide() - else: - self.t.show() - image = gajim.interface.jabber_state_images['16'][state] - if image.get_storage_type() == gtk.IMAGE_ANIMATION: - self.img_tray.set_from_animation(image.get_animation()) - elif image.get_storage_type() == gtk.IMAGE_PIXBUF: - self.img_tray.set_from_pixbuf(image.get_pixbuf()) - - def change_status(self, global_status): - ''' set tray image to 'global_status' ''' - # change image and status, only if it is different - if global_status is not None and self.status != global_status: - self.status = global_status - self.set_img() - - def start_chat(self, widget, account, jid): - contact = gajim.contacts.get_first_contact_from_jid(account, jid) - if gajim.interface.msg_win_mgr.has_window(jid, account): - gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab( - jid, account) - elif contact: - gajim.interface.new_chat(contact, account) - gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab( - jid, account) - - def on_single_message_menuitem_activate(self, widget, account): - dialogs.SingleMessageWindow(account, action = 'send') - - def on_new_chat(self, widget, account): - dialogs.NewChatDialog(account) - - def make_menu(self, event_button, event_time): - '''create chat with and new message (sub) menus/menuitems''' - for m in self.popup_menus: - m.destroy() - - chat_with_menuitem = self.xml.get_widget('chat_with_menuitem') - single_message_menuitem = self.xml.get_widget( - 'single_message_menuitem') - status_menuitem = self.xml.get_widget('status_menu') - join_gc_menuitem = self.xml.get_widget('join_gc_menuitem') - sounds_mute_menuitem = self.xml.get_widget('sounds_mute_menuitem') - - if self.single_message_handler_id: - single_message_menuitem.handler_disconnect( - self.single_message_handler_id) - self.single_message_handler_id = None - if self.new_chat_handler_id: - chat_with_menuitem.disconnect(self.new_chat_handler_id) - self.new_chat_handler_id = None - - sub_menu = gtk.Menu() - self.popup_menus.append(sub_menu) - status_menuitem.set_submenu(sub_menu) - - gc_sub_menu = gtk.Menu() # gc is always a submenu - join_gc_menuitem.set_submenu(gc_sub_menu) - - # We need our own set of status icons, let's make 'em! - iconset = gajim.config.get('iconset') - path = os.path.join(helpers.get_iconset_path(iconset), '16x16') - state_images = gtkgui_helpers.load_iconset(path) - - if 'muc_active' in state_images: - join_gc_menuitem.set_image(state_images['muc_active']) - - for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'): - uf_show = helpers.get_uf_show(show, use_mnemonic = True) - item = gtk.ImageMenuItem(uf_show) - item.set_image(state_images[show]) - sub_menu.append(item) - item.connect('activate', self.on_show_menuitem_activate, show) - - item = gtk.SeparatorMenuItem() - sub_menu.append(item) - - item = gtk.ImageMenuItem(_('_Change Status Message...')) - path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png') - img = gtk.Image() - img.set_from_file(path) - item.set_image(img) - sub_menu.append(item) - item.connect('activate', self.on_change_status_message_activate) - - connected_accounts = gajim.get_number_of_connected_accounts() - if connected_accounts < 1: - item.set_sensitive(False) - - connected_accounts_with_private_storage = 0 - - item = gtk.SeparatorMenuItem() - sub_menu.append(item) - - uf_show = helpers.get_uf_show('offline', use_mnemonic = True) - item = gtk.ImageMenuItem(uf_show) - item.set_image(state_images['offline']) - sub_menu.append(item) - item.connect('activate', self.on_show_menuitem_activate, 'offline') - - iskey = connected_accounts > 0 and not (connected_accounts == 1 and - gajim.connections[gajim.connections.keys()[0]].is_zeroconf) - chat_with_menuitem.set_sensitive(iskey) - single_message_menuitem.set_sensitive(iskey) - join_gc_menuitem.set_sensitive(iskey) - - accounts_list = sorted(gajim.contacts.get_accounts()) - # items that get shown whether an account is zeroconf or not - if connected_accounts > 1: # 2 or more connections? make submenus - account_menu_for_chat_with = gtk.Menu() - chat_with_menuitem.set_submenu(account_menu_for_chat_with) - self.popup_menus.append(account_menu_for_chat_with) - - for account in accounts_list: - if gajim.account_is_connected(account): - # for chat_with - item = gtk.MenuItem(_('using account %s') % account) - account_menu_for_chat_with.append(item) - item.connect('activate', self.on_new_chat, account) - - elif connected_accounts == 1: # one account - # one account connected, no need to show 'as jid' - for account in gajim.connections: - if gajim.connections[account].connected > 1: - # for start chat - self.new_chat_handler_id = chat_with_menuitem.connect( - 'activate', self.on_new_chat, account) - break # No other connected account - - # menu items that don't apply to zeroconf connections - if connected_accounts == 1 or (connected_accounts == 2 and \ - gajim.zeroconf_is_connected()): - # only one 'real' (non-zeroconf) account is connected, don't need - # submenus - for account in gajim.connections: - if gajim.account_is_connected(account) and \ - not gajim.config.get_per('accounts', account, 'is_zeroconf'): - if gajim.connections[account].private_storage_supported: - connected_accounts_with_private_storage += 1 - - # for single message - single_message_menuitem.remove_submenu() - self.single_message_handler_id = single_message_menuitem.\ - connect('activate', - self.on_single_message_menuitem_activate, account) - # join gc - gajim.interface.roster.add_bookmarks_list(gc_sub_menu, - account) - break # No other account connected - else: - # 2 or more 'real' accounts are connected, make submenus - account_menu_for_single_message = gtk.Menu() - single_message_menuitem.set_submenu( - account_menu_for_single_message) - self.popup_menus.append(account_menu_for_single_message) - - for account in accounts_list: - if gajim.connections[account].is_zeroconf or \ - not gajim.account_is_connected(account): - continue - if gajim.connections[account].private_storage_supported: - connected_accounts_with_private_storage += 1 - # for single message - item = gtk.MenuItem(_('using account %s') % account) - item.connect('activate', - self.on_single_message_menuitem_activate, account) - account_menu_for_single_message.append(item) - - # join gc - gc_item = gtk.MenuItem(_('using account %s') % account, False) - gc_sub_menu.append(gc_item) - gc_menuitem_menu = gtk.Menu() - gajim.interface.roster.add_bookmarks_list(gc_menuitem_menu, - account) - gc_item.set_submenu(gc_menuitem_menu) - gc_sub_menu.show_all() - - newitem = gtk.SeparatorMenuItem() # separator - gc_sub_menu.append(newitem) - newitem = gtk.ImageMenuItem(_('_Manage Bookmarks...')) - img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) - newitem.set_image(img) - newitem.connect('activate', - gajim.interface.roster.on_manage_bookmarks_menuitem_activate) - gc_sub_menu.append(newitem) - if connected_accounts_with_private_storage == 0: - newitem.set_sensitive(False) - - sounds_mute_menuitem.set_active(not gajim.config.get('sounds_on')) - - if os.name == 'nt': - if self.added_hide_menuitem is False: - self.systray_context_menu.prepend(gtk.SeparatorMenuItem()) - item = gtk.MenuItem(_('Hide this menu')) - self.systray_context_menu.prepend(item) - self.added_hide_menuitem = True - - self.systray_context_menu.show_all() - self.systray_context_menu.popup(None, None, None, 0, - event_time) - - def on_show_all_events_menuitem_activate(self, widget): - events = gajim.events.get_systray_events() - for account in events: - for jid in events[account]: - for event in events[account][jid]: - gajim.interface.handle_event(account, jid, event.type_) - - def on_sounds_mute_menuitem_activate(self, widget): - gajim.config.set('sounds_on', not widget.get_active()) - gajim.interface.save_config() - - def on_show_roster_menuitem_activate(self, widget): - win = gajim.interface.roster.window - win.present() - - def on_preferences_menuitem_activate(self, widget): - if 'preferences' in gajim.interface.instances: - gajim.interface.instances['preferences'].window.present() - else: - gajim.interface.instances['preferences'] = config.PreferencesWindow() - - def on_quit_menuitem_activate(self, widget): - gajim.interface.roster.on_quit_request() - - def on_left_click(self): - win = gajim.interface.roster.window - if len(gajim.events.get_systray_events()) == 0: - # No pending events, so toggle visible/hidden for roster window - if not win.iconify_initially and (win.get_property( - 'has-toplevel-focus') or os.name == 'nt'): - # visible in ANY virtual desktop? - - # we could be in another VD right now. eg vd2 - # and we want to show it in vd2 - if not gtkgui_helpers.possibly_move_window_in_current_desktop(win): - win.set_property('skip-taskbar-hint', False) - win.iconify() # else we hide it from VD that was visible in - win.set_property('skip-taskbar-hint', True) - else: - win.deiconify() - if not gajim.config.get('roster_window_skip_taskbar'): - win.set_property('skip-taskbar-hint', False) - win.present_with_time(gtk.get_current_event_time()) - else: - self.handle_first_event() - - def handle_first_event(self): - account, jid, event = gajim.events.get_first_systray_event() - if not event: - return - gajim.interface.handle_event(account, jid, event.type_) - - def on_middle_click(self): - '''middle click raises window to have complete focus (fe. get kbd events) - but if already raised, it hides it''' - win = gajim.interface.roster.window - if win.is_active(): # is it fully raised? (eg does it receive kbd events?) - win.hide() - else: - win.present() - - def on_clicked(self, widget, event): - self.on_tray_leave_notify_event(widget, None) - if event.type != gtk.gdk.BUTTON_PRESS: - return - if event.button == 1: # Left click - self.on_left_click() - elif event.button == 2: # middle click - self.on_middle_click() - elif event.button == 3: # right click - self.make_menu(event.button, event.time) - - def on_show_menuitem_activate(self, widget, show): - # we all add some fake (we cannot select those nor have them as show) - # but this helps to align with roster's status_combobox index positions - l = ['online', 'chat', 'away', 'xa', 'dnd', 'invisible', 'SEPARATOR', - 'CHANGE_STATUS_MSG_MENUITEM', 'SEPARATOR', 'offline'] - index = l.index(show) - if not helpers.statuses_unified(): - gajim.interface.roster.status_combobox.set_active(index + 2) - return - current = gajim.interface.roster.status_combobox.get_active() - if index != current: - gajim.interface.roster.status_combobox.set_active(index) - - def on_change_status_message_activate(self, widget): - model = gajim.interface.roster.status_combobox.get_model() - active = gajim.interface.roster.status_combobox.get_active() - status = model[active][2].decode('utf-8') - def on_response(message, pep_dict): - if message is None: # None if user press Cancel - return - accounts = gajim.connections.keys() - for acct in accounts: - if not gajim.config.get_per('accounts', acct, - 'sync_with_global_status'): - continue - show = gajim.SHOW_LIST[gajim.connections[acct].connected] - gajim.interface.roster.send_status(acct, show, message) - gajim.interface.roster.send_pep(acct, pep_dict) - dlg = dialogs.ChangeStatusMessageDialog(on_response, status) - dlg.dialog.present() - - def show_tooltip(self, widget): - position = widget.window.get_origin() - if self.tooltip.id == position: - size = widget.window.get_size() - self.tooltip.show_tooltip('', size[1], position[1]) - - def on_tray_motion_notify_event(self, widget, event): - position = widget.window.get_origin() - if self.tooltip.timeout > 0: - if self.tooltip.id != position: - self.tooltip.hide_tooltip() - if self.tooltip.timeout == 0 and \ - self.tooltip.id != position: - self.tooltip.id = position - self.tooltip.timeout = gobject.timeout_add(500, - self.show_tooltip, widget) - - def on_tray_leave_notify_event(self, widget, event): - position = widget.window.get_origin() - if self.tooltip.timeout > 0 and \ - self.tooltip.id == position: - self.tooltip.hide_tooltip() - - def on_tray_destroyed(self, widget): - '''re-add trayicon when systray is destroyed''' - self.t = None - if gajim.interface.systray_enabled: - self.show_icon() - - def show_icon(self): - if not self.t: - self.t = trayicon.TrayIcon('Gajim') - self.t.connect('destroy', self.on_tray_destroyed) - eb = gtk.EventBox() - # avoid draw seperate bg color in some gtk themes - eb.set_visible_window(False) - eb.set_events(gtk.gdk.POINTER_MOTION_MASK) - eb.connect('button-press-event', self.on_clicked) - eb.connect('motion-notify-event', self.on_tray_motion_notify_event) - eb.connect('leave-notify-event', self.on_tray_leave_notify_event) - self.tooltip = tooltips.NotificationAreaTooltip() - - self.img_tray = gtk.Image() - eb.add(self.img_tray) - self.t.add(eb) - self.set_img() - self.subscribe_events() - self.t.show_all() - - def hide_icon(self): - if self.t: - self.t.destroy() - self.t = None - self.unsubscribe_events() - -# vim: se ts=3: diff --git a/src/tooltips.py b/src/tooltips.py index 847e6a17a..5e55166c0 100644 --- a/src/tooltips.py +++ b/src/tooltips.py @@ -269,7 +269,7 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): for line in acct['event_lines']: self.add_text_row(' ' + line, 1) - def populate(self, data): + def populate(self, data=''): self.create_window() self.create_table() @@ -280,7 +280,7 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): self.table.set_property('column-spacing', 1) self.hbox.add(self.table) - self.win.add(self.hbox) + self.hbox.show_all() class GCTooltip(BaseTooltip): ''' Tooltip that is shown in the GC treeview ''' diff --git a/src/trayicon.defs b/src/trayicon.defs deleted file mode 100644 index 6d253cf83..000000000 --- a/src/trayicon.defs +++ /dev/null @@ -1,59 +0,0 @@ -;; -*- scheme -*- - -; object definitions ... -(define-object TrayIcon - (in-module "Egg") - (parent "GtkPlug") - (c-name "EggTrayIcon") - (gtype-id "EGG_TYPE_TRAY_ICON") -) - -;; Enumerations and flags ... - - -;; From eggtrayicon.h - -(define-function egg_tray_icon_get_type - (c-name "egg_tray_icon_get_type") - (return-type "GType") -) - -(define-function egg_tray_icon_new_for_screen - (c-name "egg_tray_icon_new_for_screen") - (return-type "EggTrayIcon*") - (parameters - '("GdkScreen*" "screen") - '("const-gchar*" "name") - ) -) - -(define-function egg_tray_icon_new - (c-name "egg_tray_icon_new") - (is-constructor-of "EggTrayIcon") - (return-type "EggTrayIcon*") - (parameters - '("const-gchar*" "name") - ) -) - -(define-method send_message - (of-object "EggTrayIcon") - (c-name "egg_tray_icon_send_message") - (return-type "guint") - (parameters - '("gint" "timeout") - '("const-char*" "message") - '("gint" "len") - ) -) - -(define-method cancel_message - (of-object "EggTrayIcon") - (c-name "egg_tray_icon_cancel_message") - (return-type "none") - (parameters - '("guint" "id") - ) -) - - diff --git a/src/trayicon.override b/src/trayicon.override deleted file mode 100644 index 75bd7ed8f..000000000 --- a/src/trayicon.override +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- Mode: C; c-basic-offset: 4 -*- - * src/trayicon.override - * - * Copyright (C) 2004-2005 Yann Leboulanger - * - * This file is part of Gajim. - * - * Gajim is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; version 3 only. - * - * Gajim is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Gajim. If not, see . - */ -%% -headers -#include - -#include "pygobject.h" -#include "eggtrayicon.h" -%% -modulename trayicon -%% -import gtk.Plug as PyGtkPlug_Type -import gtk.gdk.Screen as PyGdkScreen_Type -%% -ignore-glob - *_get_type -%% -override egg_tray_icon_send_message kwargs -static PyObject* -_wrap_egg_tray_icon_send_message(PyGObject *self, PyObject *args, PyObject *kwargs) -{ - static char *kwlist[] = {"timeout", "message", NULL}; - int timeout, len, ret; - char *message; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "is#:TrayIcon.send_message", kwlist, &timeout, &message, &len)) - return NULL; - ret = egg_tray_icon_send_message(EGG_TRAY_ICON(self->obj), timeout, message, len); - return PyInt_FromLong(ret); -} diff --git a/src/trayiconmodule.c b/src/trayiconmodule.c deleted file mode 100644 index 7a2b1206f..000000000 --- a/src/trayiconmodule.c +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: C; c-basic-offset: 4 -*- - * src/trayiconmodule.c - * - * Copyright (C) 2004-2005 Yann Leboulanger - * - * This file is part of Gajim. - * - * Gajim is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; version 3 only. - * - * Gajim is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Gajim. If not, see . - */ - -/* include this first, before NO_IMPORT_PYGOBJECT is defined */ -#include - -void trayicon_register_classes (PyObject *d); - -extern PyMethodDef trayicon_functions[]; - -DL_EXPORT(void) -inittrayicon(void) -{ - PyObject *m, *d; - - init_pygobject (); - - m = Py_InitModule ("trayicon", trayicon_functions); - d = PyModule_GetDict (m); - - trayicon_register_classes (d); - - if (PyErr_Occurred ()) { - Py_FatalError ("can't initialise module trayicon :("); - } -} -- cgit v1.2.3