icon in systray (it represents state and changes when we recieve a message not poped up)

This commit is contained in:
Yann Leboulanger 2004-06-09 14:22:27 +00:00
parent 27c5cd358e
commit c95a979544
10 changed files with 607 additions and 8 deletions

View File

@ -1,4 +1,13 @@
MODULES = common plugins/gtkgui
all:
python setup.py build_ext -i
mv idle.so common/
msgfmt Messages/fr/LC_MESSAGES/gajim.po -o Messages/fr/LC_MESSAGES/gajim.mo
for dir in ${MODULES}; do \
(cd $$dir; make all); \
done
clean:
find -name *.pyc -exec rm {} \;
for dir in ${MODULES}; do \
(cd $$dir; make clean); \
done

0
common/i18n.py Executable file → Normal file
View File

26
plugins/gtkgui/Makefile Normal file
View File

@ -0,0 +1,26 @@
# Set the C flags to include the GTK+ and Python libraries
CFLAGS = `pkg-config --cflags gtk+-2.0 pygtk-2.0` -I/usr/include/python2.3/ -I.
LDFLAGS = `pkg-config --libs gtk+-2.0 pygtk-2.0`
# Build the shared object
#trayicon.so: trayicon.o eggtrayicon.o trayiconmodule.o
all: trayicon.o eggtrayicon.o trayiconmodule.o
$(CC) $(LDFLAGS) -shared $^ -o trayicon.so
# The path to the GTK+ python types
DEFS=`pkg-config --variable=defsdir pygtk-2.0`
# Generate the C wrapper from the defs and our override file
trayicon.c: trayicon.defs trayicon.override
pygtk-codegen-2.0 --prefix trayicon \
--register $(DEFS)/gdk-types.defs \
--register $(DEFS)/gtk-types.defs \
--override trayicon.override \
trayicon.defs > $@
# A rule to clean the generated files
clean:
rm -f trayicon.so *.o trayicon.c *~
.PHONY: clean

View File

@ -0,0 +1,344 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* eggtrayicon.c
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
*
* 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 <string.h>
#include <gdk/gdkx.h>
#include "eggtrayicon.h"
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
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_update_manager_window (EggTrayIcon *icon);
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;
gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
}
static void
egg_tray_icon_class_init (EggTrayIconClass *klass)
{
parent_class = g_type_class_peek_parent (klass);
}
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);
}
else if (xev->xany.window == icon->manager_window)
{
if (xev->xany.type == DestroyNotify)
{
egg_tray_icon_update_manager_window (icon);
}
}
return GDK_FILTER_CONTINUE;
}
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;
#if HAVE_GTK_MULTIHEAD
display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
#else
display = gdk_display;
#endif
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)
{
Display *xdisplay;
#if HAVE_GTK_MULTIHEAD
xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
#else
xdisplay = gdk_display;
#endif
if (icon->manager_window != None)
{
GdkWindow *gdkwin;
#if HAVE_GTK_MULTIHEAD
gdkwin = gdk_window_lookup_for_display (display,
icon->manager_window);
#else
gdkwin = gdk_window_lookup (icon->manager_window);
#endif
gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
}
XGrabServer (xdisplay);
icon->manager_window = XGetSelectionOwner (xdisplay,
icon->selection_atom);
if (icon->manager_window != None)
XSelectInput (xdisplay,
icon->manager_window, StructureNotifyMask);
XUngrabServer (xdisplay);
XFlush (xdisplay);
if (icon->manager_window != None)
{
GdkWindow *gdkwin;
#if HAVE_GTK_MULTIHEAD
gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
icon->manager_window);
#else
gdkwin = gdk_window_lookup (icon->manager_window);
#endif
gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
/* Send a request that we'd like to dock */
egg_tray_icon_send_dock_request (icon);
}
}
EggTrayIcon *
egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name);
EggTrayIcon *
egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
{
EggTrayIcon *icon;
char buffer[256];
GdkWindow *root_window;
g_return_val_if_fail (xscreen != NULL, NULL);
icon = g_object_new (EGG_TYPE_TRAY_ICON, NULL);
gtk_window_set_title (GTK_WINDOW (icon), name);
#if HAVE_GTK_MULTIHEAD
gtk_plug_construct_for_display (GTK_PLUG (icon),
gdk_screen_get_display (screen), 0);
#else
gtk_plug_construct (GTK_PLUG (icon), 0);
#endif
gtk_widget_realize (GTK_WIDGET (icon));
/* Now see if there's a manager window around */
g_snprintf (buffer, sizeof (buffer),
"_NET_SYSTEM_TRAY_S%d",
XScreenNumberOfScreen (xscreen));
icon->selection_atom = XInternAtom (DisplayOfScreen (xscreen),
buffer, False);
icon->manager_atom = XInternAtom (DisplayOfScreen (xscreen),
"MANAGER", False);
icon->system_tray_opcode_atom = XInternAtom (DisplayOfScreen (xscreen),
"_NET_SYSTEM_TRAY_OPCODE", False);
egg_tray_icon_update_manager_window (icon);
#if HAVE_GTK_MULTIHEAD
root_window = gdk_screen_get_root_window (screen);
#else
root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
#endif
/* Add a root window filter so that we get changes on MANAGER */
gdk_window_add_filter (root_window,
egg_tray_icon_manager_filter, icon);
return icon;
}
#if HAVE_GTK_MULTIHEAD
EggTrayIcon *
egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
{
EggTrayIcon *icon;
char buffer[256];
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
return egg_tray_icon_new_for_xscreen (GDK_SCREEN_XSCREEN (screen), name);
}
#endif
EggTrayIcon*
egg_tray_icon_new (const gchar *name)
{
return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_display), name);
}
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);
if (icon->manager_window == None)
return 0;
if (len < 0)
len = strlen (message);
stamp = icon->stamp++;
/* 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;
#if HAVE_GTK_MULTIHEAD
xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
#else
xdisplay = gdk_display;
#endif
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 ();
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);
egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
(Window)gtk_plug_get_id (GTK_PLUG (icon)),
id, 0, 0);
}

View File

@ -0,0 +1,76 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* eggtrayicon.h
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
*
* 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 <gtk/gtkplug.h>
#include <gdk/gdkx.h>
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;
Atom selection_atom;
Atom manager_atom;
Atom system_tray_opcode_atom;
Window manager_window;
};
struct _EggTrayIconClass
{
GtkPlugClass parent_class;
};
GType egg_tray_icon_get_type (void);
#if EGG_TRAY_ENABLE_MULTIHEAD
EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen *screen,
const gchar *name);
#endif
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);
G_END_DECLS
#endif /* __EGG_TRAY_ICON_H__ */

View File

@ -2,7 +2,6 @@
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<requires lib="gnome"/>
<widget class="GtkWindow" id="Gajim">
<property name="visible">True</property>
@ -10,8 +9,8 @@
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">100</property>
<property name="default_height">300</property>
<property name="default_width">150</property>
<property name="default_height">400</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
@ -4062,7 +4061,7 @@ when NOT onlie</property>
<property name="can_focus">True</property>
<property name="editable">True</property>
<property name="overwrite">False</property>
<property name="accepts_tab">True</property>
<property name="accepts_tab">False</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="cursor_visible">True</property>

View File

@ -21,6 +21,7 @@ import pygtk
pygtk.require('2.0')
import gtk
from gtk import TRUE, FALSE
import trayicon
import gtk.glade,gobject
import os,string,time,Queue
import common.optparser,common.sleepy
@ -102,6 +103,7 @@ class message_Window:
if self.plugin.roster.pixbufs.has_key(self.user.show):
self.plugin.roster.tree.get_model().set_value(i, 0, \
self.plugin.roster.pixbufs[self.user.show])
self.plugin.set_systray()
def on_msg_key_press_event(self, widget, event):
"""When a key is pressed :
@ -839,13 +841,15 @@ class roster_Window:
def set_optionmenu(self):
#table to change index in plugin.connected to index in optionmenu
table = {0:6, 1:0, 2:1, 3:2, 4:3, 5:4}
mini = min(self.plugin.connected.values())
maxi = max(self.plugin.connected.values())
optionmenu = self.xml.get_widget('optionmenu')
#temporarily block signal in order not to send status that we show
#in the optionmenu
optionmenu.handler_block(self.id_signal_optionmenu)
optionmenu.set_history(table[mini])
optionmenu.set_history(table[maxi])
optionmenu.handler_unblock(self.id_signal_optionmenu)
statuss = ['offline', 'online', 'away', 'xa', 'dnd', 'invisible']
self.plugin.set_systray(statuss[maxi])
def on_status_changed(self, account, status):
"""the core tells us that our status has changed"""
@ -891,6 +895,7 @@ class roster_Window:
self.plugin.queues[account][jid] = Queue.Queue(50)
for i in self.get_user_iter(jid, account):
model.set_value(i, 0, self.pixbufs['message'])
self.plugin.set_systray('message')
tim = time.strftime("[%H:%M:%S]")
self.plugin.queues[account][jid].put((msg, tim))
if path:
@ -1402,6 +1407,27 @@ class plugin:
self.sleeper_state[account] = 3
return 1
def set_systray_img(self, status):
if not self.roster.pixbufs.has_key(status):
return
pix = self.roster.pixbufs[status]
if isinstance(pix, gtk.gdk.PixbufAnimation):
self.img_tray.set_from_animation(pix)
else:
self.img_tray.set_from_pixbuf(pix)
def set_systray(self, status=None):
if not status:
self.nb_msg -= 1
if self.nb_msg == 0:
self.set_systray_img(self.status)
elif status == 'message':
self.nb_msg += 1
self.set_systray_img('message')
else:
self.status = status
self.set_systray_img(status)
def __init__(self, quIN, quOUT):
gtk.threads_init()
gtk.threads_enter()
@ -1420,6 +1446,7 @@ class plugin:
self.config = self.wait('CONFIG')
self.send('ASK_CONFIG', None, ('GtkGui', 'accounts'))
self.accounts = self.wait('CONFIG')
self.nb_msg = 0
self.windows = {'logs':{}}
self.queues = {}
self.connected = {}
@ -1440,6 +1467,13 @@ class plugin:
gtk.timeout_add(100, self.read_queue)
gtk.timeout_add(1000, self.read_sleepy)
self.sleeper = None
t = trayicon.TrayIcon("Gajim")
tip = gtk.Tooltips()
tip.set_tip(t, 'Gajim')
self.img_tray = gtk.Image()
t.add(self.img_tray)
t.show_all()
self.set_systray('offline')
gtk.main()
gtk.threads_leave()

View File

@ -0,0 +1,58 @@
;; -*- 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")
)
)

View File

@ -0,0 +1,28 @@
/* -*- Mode: C; c-basic-offset: 4 -*- */
%%
headers
#include <Python.h>
#include "pygobject.h"
#include "eggtrayicon.h"
%%
modulename trayicon
%%
import gtk.Plug as PyGtkPlug_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);
}

View File

@ -0,0 +1,25 @@
/* -*- Mode: C; c-basic-offset: 4 -*- */
/* include this first, before NO_IMPORT_PYGOBJECT is defined */
#include <pygobject.h>
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 :(");
}
}