0.8.11claws39
authorChristoph Hohmann <reboot@gmx.ch>
Sat, 22 Mar 2003 23:16:41 +0000 (23:16 +0000)
committerChristoph Hohmann <reboot@gmx.ch>
Sat, 22 Mar 2003 23:16:41 +0000 (23:16 +0000)
* 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.

21 files changed:
ChangeLog.claws
configure.ac
src/common/plugin.c
src/common/session.c
src/common/session.h
src/common/sylpheed.c
src/imap.c
src/main.c
src/plugins/Makefile.am
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
src/plugins/trayicon/.cvsignore [new file with mode: 0644]
src/plugins/trayicon/Makefile.am [new file with mode: 0644]
src/plugins/trayicon/eggtrayicon.c [new file with mode: 0644]
src/plugins/trayicon/eggtrayicon.h [new file with mode: 0644]
src/plugins/trayicon/gnome-mail.xpm [new file with mode: 0644]
src/plugins/trayicon/gnome-nomail.xpm [new file with mode: 0644]
src/plugins/trayicon/trayicon.c [new file with mode: 0644]

index f77c655..5199642 100644 (file)
@@ -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
index eee3eb2..041dbcd 100644 (file)
@@ -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
index adb484f..c8a3d15 100644 (file)
@@ -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()
index e314122..fefb9f1 100644 (file)
@@ -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;
index 0f58f2b..f846e5f 100644 (file)
@@ -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);
index 2587f00..da8b142 100644 (file)
@@ -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();
index c8dfd71..9454fd5 100644 (file)
@@ -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;
 
index 2cd5a01..af18903 100644 (file)
@@ -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();
index 0830d86..ab83c20 100644 (file)
@@ -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)
index caa134d..b59917e 100644 (file)
@@ -65,3 +65,8 @@ const gchar *plugin_desc()
               "\n\n"
               "It is not really usefull";
 }
+
+const gchar *plugin_type()
+{
+       return "Common";
+}
index 389d795..bd27297 100644 (file)
@@ -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";
+}
index 4f8836e..adaad1f 100644 (file)
@@ -147,3 +147,8 @@ const gchar *plugin_desc()
 {
        return "";
 }
+
+const gchar *plugin_type()
+{
+       return "GTK";
+}
index 99ed7cf..719ca7a 100644 (file)
@@ -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";
+}
index fe04f52..bab8dad 100644 (file)
@@ -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 (file)
index 0000000..2a6ab49
--- /dev/null
@@ -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 (file)
index 0000000..27bce04
--- /dev/null
@@ -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 (file)
index 0000000..6a0d8c2
--- /dev/null
@@ -0,0 +1,388 @@
+/* -*- 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_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 (file)
index 0000000..c4396c5
--- /dev/null
@@ -0,0 +1,75 @@
+/* -*- 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/gtk.h>
+#include <gtk/gtkplug.h>
+#include <gdk/gdkx.h>
+
+#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 (file)
index 0000000..703b182
--- /dev/null
@@ -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 (file)
index 0000000..15950fa
--- /dev/null
@@ -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 (file)
index 0000000..9f2eebc
--- /dev/null
@@ -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 <stdio.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#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";
+}