From: Christoph Hohmann Date: Sat, 22 Mar 2003 23:16:41 +0000 (+0000) Subject: 0.8.11claws39 X-Git-Tag: rel_0_9_0~178 X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=commitdiff_plain;h=fcfe8c2bc310fdc32e5b445d97dac4cc7994fcd1 0.8.11claws39 * src/imap.c * src/common/session.[ch] Init session data to make sure we don't have invalid pointers for read_ch and write_ch when new multi process sessions are not used * src/main.c * src/common/plugin.c * src/common/sylpheed.c * src/plugins/demo/demo.c * src/plugins/dillo_viewer/dillo_viewer.c * src/plugins/mathml_viewer/mathml_viewer.c * src/plugins/spamassassin/spamassassin.c * src/plugins/spamassassin/spamassassin_gtk.c add plugin types to allow loading plugins at different times (and maybe for different frontends), because it's not good to load GTK-Plugins gefore gtk_init was called * configure.ac * src/plugins/Makefile.am * src/plugins/trayicon/.cvsignore ** NEW ** * src/plugins/trayicon/Makefile.am ** NEW ** * src/plugins/trayicon/eggtrayicon.c ** NEW ** * src/plugins/trayicon/eggtrayicon.h ** NEW ** * src/plugins/trayicon/gnome-mail.xpm ** NEW ** * src/plugins/trayicon/gnome-nomail.xpm ** NEW ** * src/plugins/trayicon/trayicon.c ** NEW ** New Trayicon plugin that shows an icon in a systray that uses XEMBED protocol like Gnome's systray. Icon shows if there are new mails and a tooltip shows information about new, unread and total messages. --- diff --git a/ChangeLog.claws b/ChangeLog.claws index f77c655bc..5199642f7 100644 --- a/ChangeLog.claws +++ b/ChangeLog.claws @@ -1,3 +1,37 @@ +2003-03-22 [christoph] 0.8.11claws39 + + * src/imap.c + * src/common/session.[ch] + Init session data to make sure we don't have invalid pointers + for read_ch and write_ch when new multi process sessions are + not used + + * src/main.c + * src/common/plugin.c + * src/common/sylpheed.c + * src/plugins/demo/demo.c + * src/plugins/dillo_viewer/dillo_viewer.c + * src/plugins/mathml_viewer/mathml_viewer.c + * src/plugins/spamassassin/spamassassin.c + * src/plugins/spamassassin/spamassassin_gtk.c + add plugin types to allow loading plugins at different times + (and maybe for different frontends), because it's not good to + load GTK-Plugins gefore gtk_init was called + + * configure.ac + * src/plugins/Makefile.am + * src/plugins/trayicon/.cvsignore ** NEW ** + * src/plugins/trayicon/Makefile.am ** NEW ** + * src/plugins/trayicon/eggtrayicon.c ** NEW ** + * src/plugins/trayicon/eggtrayicon.h ** NEW ** + * src/plugins/trayicon/gnome-mail.xpm ** NEW ** + * src/plugins/trayicon/gnome-nomail.xpm ** NEW ** + * src/plugins/trayicon/trayicon.c ** NEW ** + New Trayicon plugin that shows an icon in a systray that uses + XEMBED protocol like Gnome's systray. Icon shows if there + are new mails and a tooltip shows information about new, + unread and total messages. + 2003-03-22 [paul] 0.8.11claws38 * src/common/smtp.c diff --git a/configure.ac b/configure.ac index eee3eb241..041dbcd46 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,7 @@ MINOR_VERSION=8 MICRO_VERSION=11 INTERFACE_AGE=0 BINARY_AGE=0 -EXTRA_VERSION=claws38 +EXTRA_VERSION=claws39 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION dnl set $target @@ -415,6 +415,13 @@ if test x"$ac_cv_enable_dillo_plugin" = xyes; then PLUGINS="dillo $PLUGINS" fi +AC_ARG_ENABLE(trayicon-plugin, + [ --enable-trayicon-plugin System Tray Icon [default=no]], + [ac_cv_enable_trayicon_plugin=$enableval], [ac_cv_enable_trayicon_plugin=no]) +AM_CONDITIONAL(BUILD_TRAYICON_PLUGIN, test x"$ac_cv_enable_trayicon_plugin" = xyes) +if test x"$ac_cv_enable_trayicon_plugin" = xyes; then + PLUGINS="trayicon $PLUGINS" +fi dnl **************************** dnl ** Final configure output ** @@ -436,6 +443,7 @@ src/plugins/demo/Makefile src/plugins/spamassassin/Makefile src/plugins/mathml_viewer/Makefile src/plugins/dillo_viewer/Makefile +src/plugins/trayicon/Makefile faq/Makefile faq/de/Makefile faq/en/Makefile diff --git a/src/common/plugin.c b/src/common/plugin.c index adb484f37..c8a3d154c 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -34,39 +34,51 @@ struct _Plugin GModule *module; gchar *(*name) (); gchar *(*desc) (); + gchar *(*type) (); }; /** * List of all loaded plugins */ GSList *plugins = NULL; +GSList *plugin_types = NULL; + +static gint list_find_by_string(gconstpointer data, gconstpointer str) +{ + return strcmp((gchar *)data, (gchar *)str) ? TRUE : FALSE; +} void plugin_save_list() { - gchar *rcpath; + gchar *rcpath, *block; PrefFile *pfile; - GSList *cur; + GSList *type_cur, *plugin_cur; Plugin *plugin; - rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL); - if ((pfile = prefs_write_open(rcpath)) == NULL || - (prefs_set_block_label(pfile, "Plugins") < 0)) { - g_warning("failed to write plugin list\n"); - g_free(rcpath); - return; - } - - for (cur = plugins; cur != NULL; cur = g_slist_next(cur)) { - plugin = (Plugin *)cur->data; + for (type_cur = plugin_types; type_cur != NULL; type_cur = g_slist_next(type_cur)) { + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL); + block = g_strconcat("Plugins_", type_cur->data, NULL); + if ((pfile = prefs_write_open(rcpath)) == NULL || + (prefs_set_block_label(pfile, block) < 0)) { + g_warning("failed to write plugin list\n"); + g_free(rcpath); + return; + } + g_free(block); + + for (plugin_cur = plugins; plugin_cur != NULL; plugin_cur = g_slist_next(plugin_cur)) { + plugin = (Plugin *) plugin_cur->data; - fprintf(pfile->fp, "%s\n", plugin->filename); - } - fprintf(pfile->fp, "\n"); + if (!strcmp(plugin->type(), type_cur->data)) + fprintf(pfile->fp, "%s\n", plugin->filename); + } + fprintf(pfile->fp, "\n"); - if (prefs_file_close(pfile) < 0) - g_warning("failed to write plugin list\n"); + if (prefs_file_close(pfile) < 0) + g_warning("failed to write plugin list\n"); - g_free(rcpath); + g_free(rcpath); + } } /** @@ -80,7 +92,7 @@ gint plugin_load(const gchar *filename, gchar **error) { Plugin *plugin; gint (*plugin_init) (gchar **error); - gchar *plugin_name, *plugin_desc; + gchar *plugin_name, *plugin_desc, *plugin_type; gint ok; g_return_val_if_fail(filename != NULL, -1); @@ -101,6 +113,7 @@ gint plugin_load(const gchar *filename, gchar **error) if (!g_module_symbol(plugin->module, "plugin_name", (gpointer *)&plugin_name) || !g_module_symbol(plugin->module, "plugin_desc", (gpointer *)&plugin_desc) || + !g_module_symbol(plugin->module, "plugin_type", (gpointer *)&plugin_type) || !g_module_symbol(plugin->module, "plugin_init", (gpointer *)&plugin_init)) { *error = g_strdup(g_module_error()); g_module_close(plugin->module); @@ -116,6 +129,7 @@ gint plugin_load(const gchar *filename, gchar **error) plugin->name = plugin_name; plugin->desc = plugin_desc; + plugin->type = plugin_type; plugin->filename = g_strdup(filename); plugins = g_slist_append(plugins, plugin); @@ -138,19 +152,23 @@ void plugin_unload(Plugin *plugin) g_free(plugin); } -void plugin_load_all() +void plugin_load_all(gchar *type) { gchar *rcpath; gchar buf[BUFFSIZE]; PrefFile *pfile; - gchar *error; + gchar *error, *block; + + plugin_types = g_slist_append(plugin_types, g_strdup(type)); rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL); + block = g_strconcat("Plugins_", type, NULL); if ((pfile = prefs_read_open(rcpath)) == NULL || - (prefs_set_block_label(pfile, "Plugins") < 0)) { + (prefs_set_block_label(pfile, block) < 0)) { g_free(rcpath); return; } + g_free(block); while (fgets(buf, sizeof(buf), pfile->fp) != NULL) { if (buf[0] == '[') @@ -167,7 +185,7 @@ void plugin_load_all() g_free(rcpath); } -void plugin_unload_all() +void plugin_unload_all(gchar *type) { GSList *list, *cur; @@ -177,9 +195,16 @@ void plugin_unload_all() for(cur = list; cur != NULL; cur = g_slist_next(cur)) { Plugin *plugin = (Plugin *) cur->data; - plugin_unload(plugin); + if (!strcmp(type, plugin->type())) + plugin_unload(plugin); } g_slist_free(list); + + cur = g_slist_find_custom(plugin_types, type, list_find_by_string); + if (cur) { + g_free(cur->data); + g_slist_remove(plugin_types, cur); + } } GSList *plugin_get_list() diff --git a/src/common/session.c b/src/common/session.c index e3141223c..fefb9f16e 100644 --- a/src/common/session.c +++ b/src/common/session.c @@ -56,6 +56,21 @@ gboolean session_parent_input_cb (GIOChannel *source, gboolean session_child_input (Session *session); +void session_init(Session *session) +{ + session->type = 0; + session->sock = NULL; + + session->server = NULL; + session->port = 0; + session->state = SESSION_READY; + session->last_access_time = time(NULL); + session->data = NULL; + + session->read_ch = NULL; + session->write_ch = NULL; +} + gint session_connect(Session *session, const gchar *server, gushort port) { pid_t pid; diff --git a/src/common/session.h b/src/common/session.h index 0f58f2b0f..f846e5f50 100644 --- a/src/common/session.h +++ b/src/common/session.h @@ -131,6 +131,7 @@ struct _Session gpointer send_data_notify_data; }; +void session_init (Session *session); gint session_connect (Session *session, const gchar *server, gushort port); diff --git a/src/common/sylpheed.c b/src/common/sylpheed.c index 2587f00f8..da8b1426c 100644 --- a/src/common/sylpheed.c +++ b/src/common/sylpheed.c @@ -104,7 +104,7 @@ gboolean sylpheed_init(int *argc, char ***argv) ssl_init(); #endif - plugin_load_all(); + plugin_load_all("Common"); sylpheed_initialized = TRUE; @@ -113,7 +113,7 @@ gboolean sylpheed_init(int *argc, char ***argv) void sylpheed_done() { - plugin_unload_all(); + plugin_unload_all("Common"); #if USE_OPENSSL ssl_done(); diff --git a/src/imap.c b/src/imap.c index c8dfd712c..9454fd5d1 100644 --- a/src/imap.c +++ b/src/imap.c @@ -556,11 +556,10 @@ Session *imap_session_new(const PrefsAccount *account) (is_preauth) ? "pre" : "un"); session = g_new(IMAPSession, 1); + session_init(SESSION(session)); SESSION(session)->type = SESSION_IMAP; SESSION(session)->server = g_strdup(account->recv_server); SESSION(session)->sock = imap_sock; - SESSION(session)->last_access_time = time(NULL); - SESSION(session)->data = NULL; SESSION(session)->destroy = imap_session_destroy; diff --git a/src/main.c b/src/main.c index 2cd5a0130..af18903d3 100644 --- a/src/main.c +++ b/src/main.c @@ -72,6 +72,7 @@ #include "gtkutils.h" #include "log.h" #include "prefs_toolbar.h" +#include "plugin.h" #if USE_GPGME # include "rfc2015.h" @@ -356,10 +357,14 @@ int main(int argc, char *argv[]) main_window_toggle_work_offline(mainwin, FALSE); prefs_toolbar_init(); + + plugin_load_all("GTK"); static_mainwindow = mainwin; gtk_main(); + plugin_unload_all("GTK"); + prefs_toolbar_done(); addressbook_destroy(); diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 0830d8670..ab83c20cc 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -14,7 +14,12 @@ if BUILD_DILLO_VIEWER_PLUGIN dillo_viewer_dir = dillo_viewer endif +if BUILD_TRAYICON_PLUGIN +trayicon_dir = trayicon +endif -SUBDIRS = $(demo_dir) $(spamassasssin_dir) $(mathml_viewer_dir) \ - $(dillo_viewer_dir) - +SUBDIRS = $(demo_dir) \ + $(spamassasssin_dir) \ + $(mathml_viewer_dir) \ + $(dillo_viewer_dir) \ + $(trayicon_dir) diff --git a/src/plugins/demo/demo.c b/src/plugins/demo/demo.c index caa134d90..b59917e4c 100644 --- a/src/plugins/demo/demo.c +++ b/src/plugins/demo/demo.c @@ -65,3 +65,8 @@ const gchar *plugin_desc() "\n\n" "It is not really usefull"; } + +const gchar *plugin_type() +{ + return "Common"; +} diff --git a/src/plugins/dillo_viewer/dillo_viewer.c b/src/plugins/dillo_viewer/dillo_viewer.c index 389d79570..bd272973f 100644 --- a/src/plugins/dillo_viewer/dillo_viewer.c +++ b/src/plugins/dillo_viewer/dillo_viewer.c @@ -166,3 +166,8 @@ const gchar *plugin_desc() return "This plugin renders HTML mail using the Dillo " "web browser."; } + +const gchar *plugin_type() +{ + return "GTK"; +} diff --git a/src/plugins/mathml_viewer/mathml_viewer.c b/src/plugins/mathml_viewer/mathml_viewer.c index 4f8836eae..adaad1f6a 100644 --- a/src/plugins/mathml_viewer/mathml_viewer.c +++ b/src/plugins/mathml_viewer/mathml_viewer.c @@ -147,3 +147,8 @@ const gchar *plugin_desc() { return ""; } + +const gchar *plugin_type() +{ + return "GTK"; +} diff --git a/src/plugins/spamassassin/spamassassin.c b/src/plugins/spamassassin/spamassassin.c index 99ed7cff2..719ca7a55 100644 --- a/src/plugins/spamassassin/spamassassin.c +++ b/src/plugins/spamassassin/spamassassin.c @@ -234,3 +234,8 @@ const gchar *plugin_desc() "a User Interface plugin too, otherwise you will have to " "manually write the plugin configuration.\n"; } + +const gchar *plugin_type() +{ + return "Common"; +} diff --git a/src/plugins/spamassassin/spamassassin_gtk.c b/src/plugins/spamassassin/spamassassin_gtk.c index fe04f52d0..bab8dad27 100644 --- a/src/plugins/spamassassin/spamassassin_gtk.c +++ b/src/plugins/spamassassin/spamassassin_gtk.c @@ -303,3 +303,8 @@ const gchar *plugin_desc() "(default: Yes) and select the folder where spam mail will be " "saved.\n"; } + +const gchar *plugin_type() +{ + return "GTK"; +} diff --git a/src/plugins/trayicon/.cvsignore b/src/plugins/trayicon/.cvsignore new file mode 100644 index 000000000..2a6ab490b --- /dev/null +++ b/src/plugins/trayicon/.cvsignore @@ -0,0 +1,7 @@ +.deps +.libs +Makefile +Makefile.in +*.o +*.lo +*.la diff --git a/src/plugins/trayicon/Makefile.am b/src/plugins/trayicon/Makefile.am new file mode 100644 index 000000000..27bce04c7 --- /dev/null +++ b/src/plugins/trayicon/Makefile.am @@ -0,0 +1,21 @@ +plugindir = $(pkglibdir)/plugins + +plugin_LTLIBRARIES = trayicon.la + +trayicon_la_SOURCES = \ + trayicon.c \ + eggtrayicon.c eggtrayicon.h + +trayicon_la_LDFLAGS = \ + -avoid-version -module \ + $(GTK_LIBS) + +INCLUDES = \ + -I../../ \ + -I../../common \ + -I../../gtk + +CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(GTK_CFLAGS) \ + $(ASPELL_CFLAGS) diff --git a/src/plugins/trayicon/eggtrayicon.c b/src/plugins/trayicon/eggtrayicon.c new file mode 100644 index 000000000..6a0d8c264 --- /dev/null +++ b/src/plugins/trayicon/eggtrayicon.c @@ -0,0 +1,388 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* 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 "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_unrealize (GtkWidget *widget); + +static void egg_tray_icon_update_manager_window (EggTrayIcon *icon); + +Window gtk_plug_get_id(GtkPlug *widget) +{ + return (guint32) GDK_WINDOW_XWINDOW(GTK_WIDGET(widget)->window); +} + +GtkType +egg_tray_icon_get_type (void) +{ + static GtkType our_type = 0; + + if (our_type == 0) + { + GtkTypeInfo our_info = + { + "EggTrayIcon", + sizeof (EggTrayIcon), + sizeof (EggTrayIconClass), + (GtkClassInitFunc) egg_tray_icon_class_init, + (GtkObjectInitFunc) egg_tray_icon_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, /* n_preallocs */ + }; + + our_type = gtk_type_unique (gtk_plug_get_type(), &our_info); + } + + 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) +{ + GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; + + parent_class = gtk_type_class(gtk_plug_get_type()); + + widget_class->unrealize = egg_tray_icon_unrealize; +} + +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_unrealize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + GdkWindow *root_window; + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + +#if HAVE_GTK_MULTIHEAD + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget), + icon->manager_window); +#else + gdkwin = gdk_window_lookup (icon->manager_window); +#endif + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + } + +#if HAVE_GTK_MULTIHEAD + root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); +#else + root_window = gdk_window_lookup (GDK_ROOT_WINDOW()); +#endif + + 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); +} + +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_time_get(); /* 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 (gtk_widget_get_display (GTK_WIDGET (icon)), + 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 *icon; + char buffer[256]; + GdkWindow *root_window; + + g_return_val_if_fail (xscreen != NULL, NULL); + + icon = gtk_type_new (EGG_TYPE_TRAY_ICON); + gtk_window_set_title (GTK_WINDOW (icon), name); + +#if HAVE_GTK_MULTIHEAD + /* FIXME: this code does not compile, screen is undefined. Now try + * getting the GdkScreen from xscreen (:. Dunno how to solve this + * (there is prolly some easy way I cant think of right now) + */ + 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_set_events(GTK_WIDGET(icon), GDK_ALL_EVENTS_MASK); + 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 (gtk_widget_get_screen (screen)); +#else + root_window = gdk_window_lookup (GDK_ROOT_WINDOW ()); +#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); +} diff --git a/src/plugins/trayicon/eggtrayicon.h b/src/plugins/trayicon/eggtrayicon.h new file mode 100644 index 000000000..c4396c512 --- /dev/null +++ b/src/plugins/trayicon/eggtrayicon.h @@ -0,0 +1,75 @@ +/* -*- 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 +#include +#include + +#define EGG_TYPE_TRAY_ICON (egg_tray_icon_get_type ()) +#define EGG_TRAY_ICON(obj) (GTK_CHECK_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon)) +#define EGG_TRAY_ICON_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) +#define EGG_IS_TRAY_ICON(obj) (GTK_CHECK_TYPE ((obj), EGG_TYPE_TRAY_ICON)) +#define EGG_IS_TRAY_ICON_CLASS(klass) (GTK_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; +}; + +GtkType 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); + + + +#endif /* __EGG_TRAY_ICON_H__ */ diff --git a/src/plugins/trayicon/gnome-mail.xpm b/src/plugins/trayicon/gnome-mail.xpm new file mode 100644 index 000000000..703b18299 --- /dev/null +++ b/src/plugins/trayicon/gnome-mail.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static char * mail_xpm[] = { +"28 16 7 1", +" c None", +". c #000000", +"+ c #FFFFFF", +"@ c #CCCCCC", +"# c #00FFFF", +"$ c #0000FF", +"% c #999999", +"............................", +".++++++++++++++++++++++++++.", +".+++++++++++++@@+++@@+###$+.", +".+%....%+++++@++@@@++@###$+.", +".++++++++++++@++@++@@+###$+.", +".+%....%++++++@@+@@++@$$$$+.", +".++++++++++++++++++++++++++.", +".++++++++..........++++++++.", +".++++++++++++++++++++++++++.", +".++++++++........++++++++++.", +".++++++++++++++++++++++++++.", +".++++++++...........+++++++.", +".++++++++++++++++++++++++++.", +".++++++++++++++++++++++++++.", +".++++++++++++++++++++++++++.", +"............................"}; diff --git a/src/plugins/trayicon/gnome-nomail.xpm b/src/plugins/trayicon/gnome-nomail.xpm new file mode 100644 index 000000000..15950fa73 --- /dev/null +++ b/src/plugins/trayicon/gnome-nomail.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * nomail_xpm[] = { +"28 16 4 1", +" c None", +". c #999999", +"+ c #FFFFFF", +"@ c #CCCCCC", +"............................", +".++++++++++++++++++++++++++.", +".+@@@@@@@@@@@@@@@@@@@@@@@.@.", +".+......@@@@@@@@@@@@@@@@@.+.", +".+@++++++@@@@@@@@@@@@@@@@.+.", +".+......@@@@@@@@@@@@@@....+.", +".+@++++++@@@@@@@@@@@@@@++++.", +".+@@@@@@@..........@@@@@@@@.", +".+@@@@@@@@++++++++++@@@@@@@.", +".+@@@@@@@........@@@@@@@@@@.", +".+@@@@@@@@++++++++@@@@@@@@@.", +".+@@@@@@@...........@@@@@@@.", +".+@@@@@@@@+++++++++++@@@@@@.", +".+@@@@@@@@@@@@@@@@@@@@@@@@@.", +".+@@@@@@@@@@@@@@@@@@@@@@@@@.", +"............................"}; diff --git a/src/plugins/trayicon/trayicon.c b/src/plugins/trayicon/trayicon.c new file mode 100644 index 000000000..9f2eebcff --- /dev/null +++ b/src/plugins/trayicon/trayicon.c @@ -0,0 +1,160 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Sylpheed-Claws Team + * + * This program 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include +#include + +#include "plugin.h" +#include "hooks.h" +#include "folder.h" +#include "mainwindow.h" + +#include "eggtrayicon.h" +#include "gnome-mail.xpm" +#include "gnome-nomail.xpm" + +static gint hook_id; + +static GdkPixmap *mail_pixmap; +static GdkPixmap *mail_bitmap; +static GdkPixmap *nomail_pixmap; +static GdkPixmap *nomail_bitmap; + +static EggTrayIcon *trayicon; +static GtkWidget *eventbox; +static GtkWidget *image; +static GtkTooltips *tooltips; + +typedef enum +{ + TRAYICON_NEW, + TRAYICON_UNREAD, + TRAYICON_UNREADMARKED, + TRAYICON_NOTHING, +} TrayIconType; + +static gboolean mainwin_hidden = FALSE; + +static gboolean click_cb(GtkWidget * widget, + GdkEventButton * event, gpointer user_data) +{ +/* + MainWindow *mainwin; + + mainwin = mainwindow_get_mainwindow(); + if (mainwin_hidden) { + gtk_widget_show(mainwin->window); + mainwin_hidden = FALSE; + } else { + gtk_widget_hide(mainwin->window); + mainwin_hidden = TRUE; + } +*/ + return TRUE; +} + +static void set_trayicon_pixmap(TrayIconType icontype) +{ + GdkPixmap *pixmap = NULL; + GdkBitmap *bitmap = NULL; + + switch(icontype) { + case TRAYICON_NEW: + pixmap = mail_pixmap; + bitmap = mail_bitmap; + break; + default: + pixmap = nomail_pixmap; + bitmap = nomail_bitmap; + break; + } + + gtk_pixmap_set(GTK_PIXMAP(image), pixmap, bitmap); +} + +static gboolean folder_item_update_hook(gpointer source, gpointer data) +{ + gint new, unread, unreadmarked, total; + gchar *buf; + + folder_count_total_msgs(&new, &unread, &unreadmarked, &total); + buf = g_strdup_printf("New %d, Unread: %d, Total: %d", new, unread, total); + + gtk_tooltips_set_tip(tooltips, eventbox, buf, ""); + g_free(buf); + + set_trayicon_pixmap(new > 0 ? TRAYICON_NEW : TRAYICON_NOTHING); + + return FALSE; +} + +int plugin_init(gchar **error) +{ + hook_id = hooks_register_hook (FOLDER_ITEM_UPDATE_HOOKLIST, folder_item_update_hook, NULL); + if (hook_id == -1) { + *error = g_strdup("Failed to register folder item update hook"); + return -1; + } + + trayicon = egg_tray_icon_new("Sylpheed-Claws"); + gtk_container_set_border_width(GTK_CONTAINER(trayicon), 0); + + nomail_pixmap = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(trayicon)->window, &nomail_bitmap, NULL, nomail_xpm); + mail_pixmap = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(trayicon)->window, &mail_bitmap, NULL, mail_xpm); + + eventbox = gtk_event_box_new(); + gtk_container_set_border_width(eventbox, 0); + gtk_container_add(GTK_CONTAINER(trayicon), GTK_WIDGET(eventbox)); + + image = gtk_pixmap_new (nomail_pixmap, nomail_bitmap); + gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(image)); + + gtk_signal_connect(GTK_OBJECT(eventbox), "button-press-event", GTK_SIGNAL_FUNC(click_cb), NULL); + + tooltips = gtk_tooltips_new(); + gtk_tooltips_set_delay(tooltips, 1000); + gtk_tooltips_enable(tooltips); + + gtk_widget_show_all(GTK_WIDGET(trayicon)); + + return 0; +} + +void plugin_done() +{ + gtk_widget_destroy(GTK_WIDGET(trayicon)); + hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_id); +} + +const gchar *plugin_name() +{ + return "Trayicon"; +} + +const gchar *plugin_desc() +{ + return ""; +} + +const gchar *plugin_type() +{ + return "GTK"; +}