# HG changeset patch # User Sean Egan # Date 1160090640 0 # Node ID 2c1781ea074ca88c1e36cf9891f39dd1e20f2fd0 # Parent fa285d018c715955e3e831c7676cd9114d490a0a [gaim-migrate @ 17433] Depluginize the docklet. committer: Tailor Script diff -r fa285d018c71 -r 2c1781ea074c gtk/Makefile.am --- a/gtk/Makefile.am Wed Oct 04 20:31:44 2006 +0000 +++ b/gtk/Makefile.am Thu Oct 05 23:24:00 2006 +0000 @@ -59,6 +59,7 @@ bin_PROGRAMS = gaim gaim_SOURCES = \ + eggtrayicon.c \ gaimcombobox.c \ gaimstock.c \ gtkaccount.c \ @@ -73,6 +74,8 @@ gtkdebug.c \ gtkdialogs.c \ gtkdnd-hints.c \ + gtkdocklet.c \ + gtkdocklet-x11.c \ gtkeventloop.c \ gtkexpander.c \ gtkft.c \ @@ -100,6 +103,7 @@ gtkwhiteboard.c gaim_headers = \ + eggtrayicon.h \ gtkaccount.h \ gtkblist.h \ gtkcelllayout.h \ @@ -116,6 +120,7 @@ gtkdebug.h \ gtkdialogs.h \ gtkdnd-hints.h \ + gtkdocklet.h \ gtkeventloop.h \ gtkexpander.h \ gtkft.h \ diff -r fa285d018c71 -r 2c1781ea074c gtk/docklet-x11.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/docklet-x11.c Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,305 @@ +/* + * System tray icon (aka docklet) plugin for Gaim + * + * Copyright (C) 2002-3 Robert McQueen + * Copyright (C) 2003 Herman Bloggs + * Inspired by a similar plugin by: + * John (J5) Palmieri + * + * 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 "internal.h" +#include "gtkgaim.h" +#include "debug.h" +#include "gaimstock.h" + +#include "gaim.h" +#include "gtkdialogs.h" + +#include "eggtrayicon.h" +#include "docklet.h" + +#define EMBED_TIMEOUT 5000 + +/* globals */ +static EggTrayIcon *docklet = NULL; +static GtkWidget *image = NULL; +static GtkTooltips *tooltips = NULL; +static GdkPixbuf *blank_icon = NULL; +static int embed_timeout = 0; + +/* protos */ +static void docklet_x11_create(void); + +static gboolean +docklet_x11_create_cb() +{ + docklet_x11_create(); + + return FALSE; /* for when we're called by the glib idle handler */ +} + +static void +docklet_x11_embedded_cb(GtkWidget *widget, void *data) +{ + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "embedded\n"); + + g_source_remove(embed_timeout); + embed_timeout = 0; + docklet_embedded(); +} + +static void +docklet_x11_destroyed_cb(GtkWidget *widget, void *data) +{ + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "destroyed\n"); + + docklet_remove(); + + g_object_unref(G_OBJECT(docklet)); + docklet = NULL; + + g_idle_add(docklet_x11_create_cb, &handle); +} + +static void +docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data) +{ + if (event->type != GDK_BUTTON_PRESS) + return; + + docklet_clicked(event->button); +} + +static void +docklet_x11_update_icon(DockletStatus icon) +{ + const gchar *icon_name = NULL; + + g_return_if_fail(image != NULL); + + switch (icon) { + case DOCKLET_STATUS_OFFLINE: + icon_name = GAIM_STOCK_ICON_OFFLINE; + break; + case DOCKLET_STATUS_CONNECTING: + icon_name = GAIM_STOCK_ICON_CONNECT; + break; + case DOCKLET_STATUS_ONLINE: + icon_name = GAIM_STOCK_ICON_ONLINE; + break; + case DOCKLET_STATUS_ONLINE_PENDING: + icon_name = GAIM_STOCK_ICON_ONLINE_MSG; + break; + case DOCKLET_STATUS_AWAY: + icon_name = GAIM_STOCK_ICON_AWAY; + break; + case DOCKLET_STATUS_AWAY_PENDING: + icon_name = GAIM_STOCK_ICON_AWAY_MSG; + break; + } + + if(icon_name) + gtk_image_set_from_stock(GTK_IMAGE(image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); + +#if 0 + GdkPixbuf *p; + GdkBitmap *mask = NULL; + + p = gtk_widget_render_icon(GTK_WIDGET(image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + + if (p && (gdk_pixbuf_get_colorspace(p) == GDK_COLORSPACE_RGB) && (gdk_pixbuf_get_bits_per_sample(p) == 8) + && (gdk_pixbuf_get_has_alpha(p)) && (gdk_pixbuf_get_n_channels(p) == 4)) { + int len = gdk_pixbuf_get_width(p) * gdk_pixbuf_get_height(p); + guchar *data = gdk_pixbuf_get_pixels(p); + guchar *bitmap = g_malloc((len / 8) + 1); + int i; + + for (i = 0; i < len; i++) + if (data[i*4 + 3] > 55) + bitmap[i/8] |= 1 << i % 8; + else + bitmap[i/8] &= ~(1 << i % 8); + + mask = gdk_bitmap_create_from_data(GDK_DRAWABLE(GTK_WIDGET(image)->window), bitmap, gdk_pixbuf_get_width(p), gdk_pixbuf_get_height(p)); + g_free(bitmap); + } + + if (mask) + gdk_window_shape_combine_mask(image->window, mask, 0, 0); + + g_object_unref(G_OBJECT(p)); +#endif +} + +static void +docklet_x11_blank_icon() +{ + if (!blank_icon) { + gint width, height; + + gtk_icon_size_lookup(GTK_ICON_SIZE_LARGE_TOOLBAR, &width, &height); + blank_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); + gdk_pixbuf_fill(blank_icon, 0); + } + + gtk_image_set_from_pixbuf(GTK_IMAGE(image), blank_icon); +} + +static void +docklet_x11_set_tooltip(gchar *tooltip) +{ + if (!tooltips) + tooltips = gtk_tooltips_new(); + + /* image->parent is a GtkEventBox */ + if (tooltip) { + gtk_tooltips_enable(tooltips); + gtk_tooltips_set_tip(tooltips, image->parent, tooltip, NULL); + } else { + gtk_tooltips_set_tip(tooltips, image->parent, "", NULL); + gtk_tooltips_disable(tooltips); + } +} + +#if GTK_CHECK_VERSION(2,2,0) +static void +docklet_x11_position_menu(GtkMenu *menu, int *x, int *y, gboolean *push_in, + gpointer user_data) +{ + GtkWidget *widget = GTK_WIDGET(docklet); + GtkRequisition req; + gint menu_xpos, menu_ypos; + + gtk_widget_size_request(GTK_WIDGET(menu), &req); + gdk_window_get_origin(widget->window, &menu_xpos, &menu_ypos); + + menu_xpos += widget->allocation.x; + menu_ypos += widget->allocation.y; + + if (menu_ypos > gdk_screen_get_height(gtk_widget_get_screen(widget)) / 2) + menu_ypos -= req.height; + else + menu_ypos += widget->allocation.height; + + *x = menu_xpos; + *y = menu_ypos; + + *push_in = TRUE; +} +#endif + +static void +docklet_x11_destroy() +{ + g_return_if_fail(docklet != NULL); + + docklet_remove(); + + g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_x11_destroyed_cb), NULL); + gtk_widget_destroy(GTK_WIDGET(docklet)); + + g_object_unref(G_OBJECT(docklet)); + docklet = NULL; + + if (blank_icon) + g_object_unref(G_OBJECT(blank_icon)); + blank_icon = NULL; + + image = NULL; + + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "destroyed\n"); +} + +static gboolean +docklet_x11_embed_timeout_cb() +{ + /* The docklet was not embedded within the timeout. + * Remove it as a visibility manager, but leave the plugin + * loaded so that it can embed automatically if/when a notification + * area becomes available. + */ + gaim_debug_info("tray icon", "failed to embed within timeout\n"); + docklet_remove(); + + return FALSE; +} + +static void +docklet_x11_create() +{ + GtkWidget *box; + + if (docklet) { + /* if this is being called when a tray icon exists, it's because + something messed up. try destroying it before we proceed, + although docklet_refcount may be all hosed. hopefully won't happen. */ + gaim_debug(GAIM_DEBUG_WARNING, "tray icon", "trying to create icon but it already exists?\n"); + docklet_x11_destroy(); + } + + docklet = egg_tray_icon_new("Gaim"); + box = gtk_event_box_new(); + image = gtk_image_new(); + + g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_x11_embedded_cb), NULL); + g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_x11_destroyed_cb), NULL); + g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_x11_clicked_cb), NULL); + + gtk_container_add(GTK_CONTAINER(box), image); + gtk_container_add(GTK_CONTAINER(docklet), box); + + if (!gtk_check_version(2,4,0)) + g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL); + + gtk_widget_show_all(GTK_WIDGET(docklet)); + + /* ref the docklet before we bandy it about the place */ + g_object_ref(G_OBJECT(docklet)); + + /* This is a hack to avoid a race condition between the docklet getting + * embedded in the notification area and the gtkblist restoring its + * previous visibility state. If the docklet does not get embedded within + * the timeout, it will be removed as a visibility manager until it does + * get embedded. Ideally, we would only call docklet_embedded() when the + * icon was actually embedded. + */ + docklet_embedded(); + embed_timeout = g_timeout_add(EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL); + + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "created\n"); +} + +static struct docklet_ui_ops ui_ops = +{ + docklet_x11_create, + docklet_x11_destroy, + docklet_x11_update_icon, + docklet_x11_blank_icon, + docklet_x11_set_tooltip, +#if GTK_CHECK_VERSION(2,2,0) + docklet_x11_position_menu +#else + NULL +#endif +}; + +void +docklet_ui_init() +{ + docklet_set_ui_ops(&ui_ops); +} diff -r fa285d018c71 -r 2c1781ea074c gtk/docklet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/docklet.h Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,61 @@ +/* + * System tray icon (aka docklet) plugin for Gaim + * + * Copyright (C) 2002-3 Robert McQueen + * Copyright (C) 2003 Herman Bloggs + * Inspired by a similar plugin by: + * John (J5) Palmieri + * + * 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. + */ + +#ifndef _DOCKLET_H_ +#define _DOCKLET_H_ + +typedef enum +{ + DOCKLET_STATUS_OFFLINE, + DOCKLET_STATUS_ONLINE, + DOCKLET_STATUS_ONLINE_PENDING, + DOCKLET_STATUS_AWAY, + DOCKLET_STATUS_AWAY_PENDING, + DOCKLET_STATUS_CONNECTING +} DockletStatus; + +struct docklet_ui_ops +{ + void (*create)(void); + void (*destroy)(void); + void (*update_icon)(DockletStatus); + void (*blank_icon)(void); + void (*set_tooltip)(gchar *); + GtkMenuPositionFunc position_menu; +}; + +/* useful for setting idle callbacks that will be cleaned up */ +extern GaimPlugin *handle; + +/* functions in docklet.c */ +void docklet_clicked(int); +void docklet_embedded(void); +void docklet_remove(void); +void docklet_set_ui_ops(struct docklet_ui_ops *); +void docklet_unload(void); + +/* function in docklet-{x11,win32}.c */ +void docklet_ui_init(void); + +#endif /* _DOCKLET_H_ */ diff -r fa285d018c71 -r 2c1781ea074c gtk/eggtrayicon.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/eggtrayicon.c Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,572 @@ +/* -*- 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" + +#include +#include + +#define _(x) x +#define N_(x) x + +#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_realize (GtkWidget *widget); +static void egg_tray_icon_unrealize (GtkWidget *widget); + +static void egg_tray_icon_add (GtkContainer *container, + GtkWidget *widget); + +static void egg_tray_icon_update_manager_window (EggTrayIcon *icon, + gboolean dock_if_realized); +static void egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon); + +GType +egg_tray_icon_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + our_type = g_type_from_name("EggTrayIcon"); + + 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, + NULL /* value_table */ + }; + + our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0); + } + else if (parent_class == NULL) + { + /* we're reheating the old class from a previous instance - engage ugly hack =( */ + egg_tray_icon_class_init((EggTrayIconClass *)g_type_class_peek(our_type)); + } + } + + 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)); +} + +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; + } +} + +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_return_if_fail(icon->manager_window != None); + +#if GTK_CHECK_VERSION(2,1,0) + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); +#else + xdisplay = gdk_display; +#endif + + 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; +} + +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 GTK_CHECK_VERSION(2,1,0) + 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 GTK_CHECK_VERSION(2,1,0) + root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); +#else + root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ()); +#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_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 GTK_CHECK_VERSION(2,1,0) + 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, + gboolean dock_if_realized) +{ + Display *xdisplay; + + if (icon->manager_window != None) + return; + +#if GTK_CHECK_VERSION(2,1,0) + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); +#else + xdisplay = gdk_display; +#endif + + 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; + +#if GTK_CHECK_VERSION(2,1,0) + 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); + + if (dock_if_realized && GTK_WIDGET_REALIZED (icon)) + egg_tray_icon_send_dock_request (icon); + + egg_tray_icon_get_orientation_property (icon); + } +} + +static void +egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon) +{ + GdkWindow *gdkwin; + + g_return_if_fail (icon->manager_window != None); + +#if GTK_CHECK_VERSION(2,1,0) + 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); + + icon->manager_window = None; + + egg_tray_icon_update_manager_window (icon, TRUE); +} + +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_realize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + gint 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); + +#if GTK_CHECK_VERSION(2,1,0) + screen = gdk_screen_get_number (gtk_widget_get_screen (widget)); + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)); +#else + screen = XScreenNumberOfScreen (DefaultScreenOfDisplay (gdk_display)); + xdisplay = gdk_display; +#endif + + /* Now see if there's a manager window around */ + g_snprintf (buffer, sizeof (buffer), + "_NET_SYSTEM_TRAY_S%d", + 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); + +#if GTK_CHECK_VERSION(2,1,0) + root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); +#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); +} + +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); +} + +#if GTK_CHECK_VERSION(2,1,0) +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); +} +#endif + +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); + + 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 GTK_CHECK_VERSION(2,1,0) + 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); +} + +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 -r fa285d018c71 -r 2c1781ea074c gtk/eggtrayicon.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/eggtrayicon.h Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,80 @@ +/* -*- 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 + +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; + Atom orientation_atom; + Window manager_window; + + GtkOrientation orientation; +}; + +struct _EggTrayIconClass +{ + GtkPlugClass parent_class; +}; + +GType egg_tray_icon_get_type (void); + +#if GTK_CHECK_VERSION(2,1,0) +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); + +GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon); + +G_END_DECLS + +#endif /* __EGG_TRAY_ICON_H__ */ diff -r fa285d018c71 -r 2c1781ea074c gtk/gtkdocklet-win32.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/gtkdocklet-win32.c Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,263 @@ +/* + * System tray icon (aka docklet) plugin for Gaim + * + * Copyright (C) 2002-3 Robert McQueen + * Copyright (C) 2003 Herman Bloggs + * Inspired by a similar plugin by: + * John (J5) Palmieri + * + * 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 "internal.h" +#include "gtkblist.h" +#include "gtkprefs.h" +#include "debug.h" + +#include "gaim.h" +#include "gtkdialogs.h" + +#include "resource.h" +#include "MinimizeToTray.h" +#include "gtkwin32dep.h" +#include "docklet.h" + +/* + * DEFINES, MACROS & DATA TYPES + */ +#define WM_TRAYMESSAGE WM_USER /* User defined WM Message */ + +/* + * LOCALS + */ +static HWND systray_hwnd=0; +static HICON sysicon_disconn=0; +static HICON sysicon_conn=0; +static HICON sysicon_away=0; +static HICON sysicon_pend=0; +static HICON sysicon_awypend=0; +static HICON sysicon_blank=0; +static NOTIFYICONDATA wgaim_nid; + + +static LRESULT CALLBACK systray_mainmsg_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + static UINT taskbarRestartMsg; /* static here means value is kept across multiple calls to this func */ + + switch(msg) { + case WM_CREATE: + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "WM_CREATE\n"); + taskbarRestartMsg = RegisterWindowMessage("TaskbarCreated"); + break; + + case WM_TIMER: + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "WM_TIMER\n"); + break; + + case WM_DESTROY: + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "WM_DESTROY\n"); + break; + + case WM_TRAYMESSAGE: + { + int type = 0; + + /* We'll use Double Click - Single click over on linux */ + if( lparam == WM_LBUTTONDBLCLK ) + type = 1; + else if( lparam == WM_MBUTTONUP ) + type = 2; + else if( lparam == WM_RBUTTONUP ) + type = 3; + else + break; + + docklet_clicked(type); + break; + } + default: + if (msg == taskbarRestartMsg) { + /* explorer crashed and left us hanging... + This will put the systray icon back in it's place, when it restarts */ + Shell_NotifyIcon(NIM_ADD,&wgaim_nid); + } + break; + }/* end switch */ + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +/* Create hidden window to process systray messages */ +static HWND systray_create_hiddenwin() { + WNDCLASSEX wcex; + TCHAR wname[32]; + + strcpy(wname, "GaimWin"); + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = 0; + wcex.lpfnWndProc = (WNDPROC)systray_mainmsg_handler; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = gtkwgaim_hinstance(); + wcex.hIcon = NULL; + wcex.hCursor = NULL, + wcex.hbrBackground = NULL; + wcex.lpszMenuName = NULL; + wcex.lpszClassName = wname; + wcex.hIconSm = NULL; + + RegisterClassEx(&wcex); + + /* Create the window */ + return (CreateWindow(wname, "", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, gtkwgaim_hinstance(), 0)); +} + +static void systray_init_icon(HWND hWnd, HICON icon) { + ZeroMemory(&wgaim_nid,sizeof(wgaim_nid)); + wgaim_nid.cbSize=sizeof(NOTIFYICONDATA); + wgaim_nid.hWnd=hWnd; + wgaim_nid.uID=0; + wgaim_nid.uFlags=NIF_ICON | NIF_MESSAGE | NIF_TIP; + wgaim_nid.uCallbackMessage=WM_TRAYMESSAGE; + wgaim_nid.hIcon=icon; + lstrcpy(wgaim_nid.szTip, "Gaim"); + Shell_NotifyIcon(NIM_ADD,&wgaim_nid); + docklet_embedded(); +} + +static void systray_change_icon(HICON icon) { + wgaim_nid.hIcon = icon; + Shell_NotifyIcon(NIM_MODIFY,&wgaim_nid); +} + +static void systray_remove_nid(void) { + Shell_NotifyIcon(NIM_DELETE,&wgaim_nid); +} + +static void wgaim_tray_update_icon(DockletStatus icon) { + switch (icon) { + case DOCKLET_STATUS_OFFLINE: + systray_change_icon(sysicon_disconn); + break; + case DOCKLET_STATUS_CONNECTING: + break; + case DOCKLET_STATUS_ONLINE: + systray_change_icon(sysicon_conn); + break; + case DOCKLET_STATUS_ONLINE_PENDING: + systray_change_icon(sysicon_pend); + break; + case DOCKLET_STATUS_AWAY: + systray_change_icon(sysicon_away); + break; + case DOCKLET_STATUS_AWAY_PENDING: + systray_change_icon(sysicon_awypend); + break; + } +} + +static void wgaim_tray_blank_icon() { + systray_change_icon(sysicon_blank); +} + +static void wgaim_tray_set_tooltip(gchar *tooltip) { + if (tooltip) { + char *locenc = NULL; + locenc = g_locale_from_utf8(tooltip, -1, NULL, NULL, NULL); + lstrcpyn(wgaim_nid.szTip, locenc, sizeof(wgaim_nid.szTip)/sizeof(TCHAR)); + g_free(locenc); + } else { + lstrcpy(wgaim_nid.szTip, "Gaim"); + } + Shell_NotifyIcon(NIM_MODIFY, &wgaim_nid); +} + +void wgaim_tray_minimize(GaimGtkBuddyList *gtkblist) { + MinimizeWndToTray(GDK_WINDOW_HWND(gtkblist->window->window)); +} + +void wgaim_tray_maximize(GaimGtkBuddyList *gtkblist) { + RestoreWndFromTray(GDK_WINDOW_HWND(gtkblist->window->window)); +} + + +static void wgaim_tray_create() { + OSVERSIONINFO osinfo; + /* dummy window to process systray messages */ + systray_hwnd = systray_create_hiddenwin(); + + osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osinfo); + + /* Load icons, and init systray notify icon + * NOTE: Windows < XP only supports displaying 4-bit images in the Systray, + * 2K and ME will use the highest color depth that the desktop will support, + * but will scale it back to 4-bits for display. + * That is why we use custom 4-bit icons for pre XP Windowses */ + if ((osinfo.dwMajorVersion == 5 && osinfo.dwMinorVersion > 0) || + (osinfo.dwMajorVersion >= 6)) + { + sysicon_disconn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_OFFLINE_TRAY_ICON), IMAGE_ICON, 16, 16, 0); + sysicon_conn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_TRAY_ICON), IMAGE_ICON, 16, 16, 0); + sysicon_away = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAY_TRAY_ICON), IMAGE_ICON, 16, 16, 0); + sysicon_pend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_PEND_TRAY_ICON), IMAGE_ICON, 16, 16, 0); + sysicon_awypend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAYPEND_TRAY_ICON), IMAGE_ICON, 16, 16, 0); + } else { + sysicon_disconn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_OFFLINE_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); + sysicon_conn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); + sysicon_away = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAY_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); + sysicon_pend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_PEND_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); + sysicon_awypend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAYPEND_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); + } + sysicon_blank = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_BLANK_TRAY_ICON), IMAGE_ICON, 16, 16, 0); + + /* Create icon in systray */ + systray_init_icon(systray_hwnd, sysicon_disconn); + + gaim_signal_connect(gaim_gtk_blist_get_handle(), "gtkblist-hiding", + &handle, GAIM_CALLBACK(wgaim_tray_minimize), NULL); + gaim_signal_connect(gaim_gtk_blist_get_handle(), "gtkblist-unhiding", + &handle, GAIM_CALLBACK(wgaim_tray_maximize), NULL); + + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "created\n"); +} + +static void wgaim_tray_destroy() { + gaim_signals_disconnect_by_handle(&handle); + systray_remove_nid(); + DestroyWindow(systray_hwnd); + docklet_remove(); +} + +static struct docklet_ui_ops wgaim_tray_ops = +{ + wgaim_tray_create, + wgaim_tray_destroy, + wgaim_tray_update_icon, + wgaim_tray_blank_icon, + wgaim_tray_set_tooltip, + NULL +}; + +/* Used by docklet's plugin load func */ +void docklet_ui_init() { + docklet_set_ui_ops(&wgaim_tray_ops); +} diff -r fa285d018c71 -r 2c1781ea074c gtk/gtkdocklet-x11.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/gtkdocklet-x11.c Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,305 @@ +/* + * System tray icon (aka docklet) plugin for Gaim + * + * Copyright (C) 2002-3 Robert McQueen + * Copyright (C) 2003 Herman Bloggs + * Inspired by a similar plugin by: + * John (J5) Palmieri + * + * 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 "internal.h" +#include "gtkgaim.h" +#include "debug.h" +#include "gaimstock.h" + +#include "gaim.h" +#include "gtkdialogs.h" + +#include "eggtrayicon.h" +#include "gtkdocklet.h" + +#define EMBED_TIMEOUT 5000 + +/* globals */ +static EggTrayIcon *docklet = NULL; +static GtkWidget *image = NULL; +static GtkTooltips *tooltips = NULL; +static GdkPixbuf *blank_icon = NULL; +static int embed_timeout = 0; + +/* protos */ +static void docklet_x11_create(void); + +static gboolean +docklet_x11_create_cb() +{ + docklet_x11_create(); + + return FALSE; /* for when we're called by the glib idle handler */ +} + +static void +docklet_x11_embedded_cb(GtkWidget *widget, void *data) +{ + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "embedded\n"); + + g_source_remove(embed_timeout); + embed_timeout = 0; + gaim_gtk_docklet_embedded(); +} + +static void +docklet_x11_destroyed_cb(GtkWidget *widget, void *data) +{ + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "destroyed\n"); + + gaim_gtk_docklet_remove(); + + g_object_unref(G_OBJECT(docklet)); + docklet = NULL; + + g_idle_add(docklet_x11_create_cb, NULL); +} + +static void +docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data) +{ + if (event->type != GDK_BUTTON_PRESS) + return; + + gaim_gtk_docklet_clicked(event->button); +} + +static void +docklet_x11_update_icon(DockletStatus icon) +{ + const gchar *icon_name = NULL; + + g_return_if_fail(image != NULL); + + switch (icon) { + case DOCKLET_STATUS_OFFLINE: + icon_name = GAIM_STOCK_ICON_OFFLINE; + break; + case DOCKLET_STATUS_CONNECTING: + icon_name = GAIM_STOCK_ICON_CONNECT; + break; + case DOCKLET_STATUS_ONLINE: + icon_name = GAIM_STOCK_ICON_ONLINE; + break; + case DOCKLET_STATUS_ONLINE_PENDING: + icon_name = GAIM_STOCK_ICON_ONLINE_MSG; + break; + case DOCKLET_STATUS_AWAY: + icon_name = GAIM_STOCK_ICON_AWAY; + break; + case DOCKLET_STATUS_AWAY_PENDING: + icon_name = GAIM_STOCK_ICON_AWAY_MSG; + break; + } + + if(icon_name) + gtk_image_set_from_stock(GTK_IMAGE(image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); + +#if 0 + GdkPixbuf *p; + GdkBitmap *mask = NULL; + + p = gtk_widget_render_icon(GTK_WIDGET(image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + + if (p && (gdk_pixbuf_get_colorspace(p) == GDK_COLORSPACE_RGB) && (gdk_pixbuf_get_bits_per_sample(p) == 8) + && (gdk_pixbuf_get_has_alpha(p)) && (gdk_pixbuf_get_n_channels(p) == 4)) { + int len = gdk_pixbuf_get_width(p) * gdk_pixbuf_get_height(p); + guchar *data = gdk_pixbuf_get_pixels(p); + guchar *bitmap = g_malloc((len / 8) + 1); + int i; + + for (i = 0; i < len; i++) + if (data[i*4 + 3] > 55) + bitmap[i/8] |= 1 << i % 8; + else + bitmap[i/8] &= ~(1 << i % 8); + + mask = gdk_bitmap_create_from_data(GDK_DRAWABLE(GTK_WIDGET(image)->window), bitmap, gdk_pixbuf_get_width(p), gdk_pixbuf_get_height(p)); + g_free(bitmap); + } + + if (mask) + gdk_window_shape_combine_mask(image->window, mask, 0, 0); + + g_object_unref(G_OBJECT(p)); +#endif +} + +static void +docklet_x11_blank_icon() +{ + if (!blank_icon) { + gint width, height; + + gtk_icon_size_lookup(GTK_ICON_SIZE_LARGE_TOOLBAR, &width, &height); + blank_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); + gdk_pixbuf_fill(blank_icon, 0); + } + + gtk_image_set_from_pixbuf(GTK_IMAGE(image), blank_icon); +} + +static void +docklet_x11_set_tooltip(gchar *tooltip) +{ + if (!tooltips) + tooltips = gtk_tooltips_new(); + + /* image->parent is a GtkEventBox */ + if (tooltip) { + gtk_tooltips_enable(tooltips); + gtk_tooltips_set_tip(tooltips, image->parent, tooltip, NULL); + } else { + gtk_tooltips_set_tip(tooltips, image->parent, "", NULL); + gtk_tooltips_disable(tooltips); + } +} + +#if GTK_CHECK_VERSION(2,2,0) +static void +docklet_x11_position_menu(GtkMenu *menu, int *x, int *y, gboolean *push_in, + gpointer user_data) +{ + GtkWidget *widget = GTK_WIDGET(docklet); + GtkRequisition req; + gint menu_xpos, menu_ypos; + + gtk_widget_size_request(GTK_WIDGET(menu), &req); + gdk_window_get_origin(widget->window, &menu_xpos, &menu_ypos); + + menu_xpos += widget->allocation.x; + menu_ypos += widget->allocation.y; + + if (menu_ypos > gdk_screen_get_height(gtk_widget_get_screen(widget)) / 2) + menu_ypos -= req.height; + else + menu_ypos += widget->allocation.height; + + *x = menu_xpos; + *y = menu_ypos; + + *push_in = TRUE; +} +#endif + +static void +docklet_x11_destroy() +{ + g_return_if_fail(docklet != NULL); + + gaim_gtk_docklet_remove(); + + g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_x11_destroyed_cb), NULL); + gtk_widget_destroy(GTK_WIDGET(docklet)); + + g_object_unref(G_OBJECT(docklet)); + docklet = NULL; + + if (blank_icon) + g_object_unref(G_OBJECT(blank_icon)); + blank_icon = NULL; + + image = NULL; + + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "destroyed\n"); +} + +static gboolean +docklet_x11_embed_timeout_cb() +{ + /* The docklet was not embedded within the timeout. + * Remove it as a visibility manager, but leave the plugin + * loaded so that it can embed automatically if/when a notification + * area becomes available. + */ + gaim_debug_info("tray icon", "failed to embed within timeout\n"); + gaim_gtk_docklet_remove(); + + return FALSE; +} + +static void +docklet_x11_create() +{ + GtkWidget *box; + + if (docklet) { + /* if this is being called when a tray icon exists, it's because + something messed up. try destroying it before we proceed, + although docklet_refcount may be all hosed. hopefully won't happen. */ + gaim_debug(GAIM_DEBUG_WARNING, "tray icon", "trying to create icon but it already exists?\n"); + docklet_x11_destroy(); + } + + docklet = egg_tray_icon_new("Gaim"); + box = gtk_event_box_new(); + image = gtk_image_new(); + + g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_x11_embedded_cb), NULL); + g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_x11_destroyed_cb), NULL); + g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_x11_clicked_cb), NULL); + + gtk_container_add(GTK_CONTAINER(box), image); + gtk_container_add(GTK_CONTAINER(docklet), box); + + if (!gtk_check_version(2,4,0)) + g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL); + + gtk_widget_show_all(GTK_WIDGET(docklet)); + + /* ref the docklet before we bandy it about the place */ + g_object_ref(G_OBJECT(docklet)); + + /* This is a hack to avoid a race condition between the docklet getting + * embedded in the notification area and the gtkblist restoring its + * previous visibility state. If the docklet does not get embedded within + * the timeout, it will be removed as a visibility manager until it does + * get embedded. Ideally, we would only call docklet_embedded() when the + * icon was actually embedded. + */ + gaim_gtk_docklet_embedded(); + embed_timeout = g_timeout_add(EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL); + + gaim_debug(GAIM_DEBUG_INFO, "tray icon", "created\n"); +} + +static struct docklet_ui_ops ui_ops = +{ + docklet_x11_create, + docklet_x11_destroy, + docklet_x11_update_icon, + docklet_x11_blank_icon, + docklet_x11_set_tooltip, +#if GTK_CHECK_VERSION(2,2,0) + docklet_x11_position_menu +#else + NULL +#endif +}; + +void +docklet_ui_init() +{ + gaim_gtk_docklet_set_ui_ops(&ui_ops); +} diff -r fa285d018c71 -r 2c1781ea074c gtk/gtkdocklet.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/gtkdocklet.c Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,694 @@ +/* + * System tray icon (aka docklet) plugin for Gaim + * + * Copyright (C) 2002-3 Robert McQueen + * Copyright (C) 2003 Herman Bloggs + * Inspired by a similar plugin by: + * John (J5) Palmieri + * + * 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 "internal.h" +#include "gtkgaim.h" + +#include "core.h" +#include "conversation.h" +#include "debug.h" +#include "prefs.h" +#include "signals.h" +#include "sound.h" +#include "version.h" + +#include "gtkaccount.h" +#include "gtkblist.h" +#include "gtkconv.h" +#include "gtkft.h" +#include "gtkplugin.h" +#include "gtkprefs.h" +#include "gtksavedstatuses.h" +#include "gtksound.h" +#include "gtkutils.h" +#include "gaimstock.h" +#include "gtkdocklet.h" + +#include "gaim.h" +#include "gtkdialogs.h" + +#ifndef DOCKLET_TOOLTIP_LINE_LIMIT +#define DOCKLET_TOOLTIP_LINE_LIMIT 5 +#endif + +/* globals */ +static struct docklet_ui_ops *ui_ops = NULL; +static DockletStatus status = DOCKLET_STATUS_OFFLINE; +static gboolean enable_join_chat = FALSE; +static guint docklet_blinking_timer = 0; +static gboolean visibility_manager = FALSE; + +/************************************************************************** + * docklet status and utility functions + **************************************************************************/ +static gboolean +docklet_blink_icon() +{ + static gboolean blinked = FALSE; + gboolean ret = FALSE; /* by default, don't keep blinking */ + + blinked = !blinked; + + switch (status) { + case DOCKLET_STATUS_ONLINE_PENDING: + case DOCKLET_STATUS_AWAY_PENDING: + if (blinked) { + if (ui_ops && ui_ops->blank_icon) + ui_ops->blank_icon(); + } else { + if (ui_ops && ui_ops->update_icon) + ui_ops->update_icon(status); + } + ret = TRUE; /* keep blinking */ + break; + default: + docklet_blinking_timer = 0; + blinked = FALSE; + break; + } + + return ret; +} + +static GList * +get_pending_list(guint max) +{ + const char *im = gaim_prefs_get_string("/plugins/gtk/docklet/blink_im"); + const char *chat = gaim_prefs_get_string("/plugins/gtk/docklet/blink_chat"); + GList *l_im = NULL; + GList *l_chat = NULL; + + if (im != NULL && strcmp(im, "always") == 0) { + l_im = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_IM, + GAIM_UNSEEN_TEXT, + FALSE, max); + } else if (im != NULL && strcmp(im, "hidden") == 0) { + l_im = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_IM, + GAIM_UNSEEN_TEXT, + TRUE, max); + } + + if (chat != NULL && strcmp(chat, "always") == 0) { + l_chat = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_CHAT, + GAIM_UNSEEN_TEXT, + FALSE, max); + } else if (chat != NULL && strcmp(chat, "nick") == 0) { + l_chat = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_CHAT, + GAIM_UNSEEN_NICK, + FALSE, max); + } + + if (l_im != NULL && l_chat != NULL) + return g_list_concat(l_im, l_chat); + else if (l_im != NULL) + return l_im; + else + return l_chat; +} + +static gboolean +docklet_update_status() +{ + GList *convs; + GList *l; + int count; + DockletStatus newstatus = DOCKLET_STATUS_OFFLINE; + gboolean pending = FALSE; + + /* determine if any ims have unseen messages */ + convs = get_pending_list(DOCKLET_TOOLTIP_LINE_LIMIT); + + if (convs != NULL) { + pending = TRUE; + + /* set tooltip if messages are pending */ + if (ui_ops->set_tooltip) { + GString *tooltip_text = g_string_new(""); + for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) { + if (GAIM_IS_GTK_CONVERSATION(l->data)) { + GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION((GaimConversation *)l->data); + if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) + g_string_append(tooltip_text, _("Right-click for more unread messages...\n")); + else + g_string_append_printf(tooltip_text, + ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count), + gtkconv->unseen_count, + gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); + } + } + + /* get rid of the last newline */ + if (tooltip_text->len > 0) + tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1); + + ui_ops->set_tooltip(tooltip_text->str); + + g_string_free(tooltip_text, TRUE); + } + + g_list_free(convs); + + } else if (ui_ops->set_tooltip) { + ui_ops->set_tooltip(NULL); + } + + /* iterate through all accounts and determine which + * status to show in the tray icon based on the following + * ranks (highest encountered rank will be used): + * + * 1) OFFLINE + * 2) ONLINE + * 3) ONLINE_PENDING + * 4) AWAY + * 5) AWAY_PENDING + * 6) CONNECTING + */ + for(l = gaim_accounts_get_all(); l != NULL; l = l->next) { + DockletStatus tmpstatus = DOCKLET_STATUS_OFFLINE; + + GaimAccount *account = (GaimAccount*)l->data; + GaimStatus *account_status; + + if (!gaim_account_get_enabled(account, GAIM_GTK_UI)) + continue; + + if (gaim_account_is_disconnected(account)) + continue; + + account_status = gaim_account_get_active_status(account); + + if (gaim_account_is_connecting(account)) { + tmpstatus = DOCKLET_STATUS_CONNECTING; + } else if (gaim_status_is_online(account_status)) { + if (!gaim_status_is_available(account_status)) { + if (pending) + tmpstatus = DOCKLET_STATUS_AWAY_PENDING; + else + tmpstatus = DOCKLET_STATUS_AWAY; + } + else { + if (pending) + tmpstatus = DOCKLET_STATUS_ONLINE_PENDING; + else + tmpstatus = DOCKLET_STATUS_ONLINE; + } + } + + if (tmpstatus > newstatus) + newstatus = tmpstatus; + } + + /* update the icon if we changed status */ + if (status != newstatus) { + status = newstatus; + + if (ui_ops && ui_ops->update_icon) + ui_ops->update_icon(status); + + /* and schedule the blinker function if messages are pending */ + if ((status == DOCKLET_STATUS_ONLINE_PENDING + || status == DOCKLET_STATUS_AWAY_PENDING) + && docklet_blinking_timer == 0) { + docklet_blinking_timer = g_timeout_add(500, docklet_blink_icon, NULL); + } + } + + return FALSE; /* for when we're called by the glib idle handler */ +} + +static gboolean +online_account_supports_chat() +{ + GList *c = NULL; + c = gaim_connections_get_all(); + + while(c != NULL) { + GaimConnection *gc = c->data; + GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + if (prpl_info != NULL && prpl_info->chat_info != NULL) + return TRUE; + c = c->next; + } + + return FALSE; +} + +/************************************************************************** + * callbacks and signal handlers + **************************************************************************/ +static void +gaim_quit_cb() +{ + /* TODO: confirm quit while pending */ +} + +static void +docklet_update_status_cb(void *data) +{ + docklet_update_status(); +} + +static void +docklet_prefs_cb(const char *name, GaimPrefType type, + gconstpointer val, gpointer data) +{ + docklet_update_status(); +} + +static void +docklet_conv_updated_cb(GaimConversation *conv, GaimConvUpdateType type) +{ + if (type == GAIM_CONV_UPDATE_UNSEEN) + docklet_update_status(); +} + +static void +docklet_signed_on_cb(GaimConnection *gc) +{ + if (!enable_join_chat) { + if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) + enable_join_chat = TRUE; + } + docklet_update_status(); +} + +static void +docklet_signed_off_cb(GaimConnection *gc) +{ + if (enable_join_chat) { + if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) + enable_join_chat = online_account_supports_chat(); + } + docklet_update_status(); +} + +/************************************************************************** + * docklet pop-up menu + **************************************************************************/ +static void +docklet_toggle_mute(GtkWidget *toggle, void *data) +{ + gaim_prefs_set_bool("/gaim/gtk/sound/mute", GTK_CHECK_MENU_ITEM(toggle)->active); +} + +static void +docklet_toggle_blist(GtkWidget *toggle, void *data) +{ + gaim_blist_set_visible(GTK_CHECK_MENU_ITEM(toggle)->active); +} + +#ifdef _WIN32 +/* This is a workaround for a bug in windows GTK+. Clicking outside of the + menu does not get rid of it, so instead we get rid of it as soon as the + pointer leaves the menu. */ +static gboolean +hide_docklet_menu(gpointer data) +{ + if (data != NULL) { + gtk_menu_popdown(GTK_MENU(data)); + } + return FALSE; +} + +static gboolean +docklet_menu_leave_enter(GtkWidget *menu, GdkEventCrossing *event, void *data) +{ + static guint hide_docklet_timer = 0; + if (event->type == GDK_LEAVE_NOTIFY && event->detail == GDK_NOTIFY_ANCESTOR) { + gaim_debug(GAIM_DEBUG_INFO, "docklet", "menu leave-notify-event\n"); + /* Add some slop so that the menu doesn't annoyingly disappear when mousing around */ + if (hide_docklet_timer == 0) { + hide_docklet_timer = gaim_timeout_add(500, + hide_docklet_menu, menu); + } + } else if (event->type == GDK_ENTER_NOTIFY && event->detail == GDK_NOTIFY_ANCESTOR) { + gaim_debug(GAIM_DEBUG_INFO, "docklet", "menu enter-notify-event\n"); + if (hide_docklet_timer != 0) { + /* Cancel the hiding if we reenter */ + + gaim_timeout_remove(hide_docklet_timer); + hide_docklet_timer = 0; + } + } + return FALSE; +} +#endif + +static void +show_custom_status_editor_cb(GtkMenuItem *menuitem, gpointer user_data) +{ + GaimSavedStatus *saved_status; + saved_status = gaim_savedstatus_get_current(); + gaim_gtk_status_editor_show(FALSE, + gaim_savedstatus_is_transient(saved_status) ? saved_status : NULL); +} + +static void +activate_status_primitive_cb(GtkMenuItem *menuitem, gpointer user_data) +{ + GaimStatusPrimitive primitive; + GaimSavedStatus *saved_status; + + primitive = GPOINTER_TO_INT(user_data); + + /* Try to lookup an already existing transient saved status */ + saved_status = gaim_savedstatus_find_transient_by_type_and_message(primitive, NULL); + + /* Create a new transient saved status if we weren't able to find one */ + if (saved_status == NULL) + saved_status = gaim_savedstatus_new(NULL, primitive); + + /* Set the status for each account */ + gaim_savedstatus_activate(saved_status); +} + +static void +activate_saved_status_cb(GtkMenuItem *menuitem, gpointer user_data) +{ + time_t creation_time; + GaimSavedStatus *saved_status; + + creation_time = GPOINTER_TO_INT(user_data); + saved_status = gaim_savedstatus_find_by_creation_time(creation_time); + if (saved_status != NULL) + gaim_savedstatus_activate(saved_status); +} + +static GtkWidget * +new_menu_item_with_gaim_icon(GtkWidget *menu, const char *str, GaimStatusPrimitive primitive, GtkSignalFunc sf, gpointer data, guint accel_key, guint accel_mods, char *mod) +{ + GtkWidget *menuitem; + GdkPixbuf *pixbuf; + GtkWidget *image; + + menuitem = gtk_image_menu_item_new_with_mnemonic(str); + + if (menu) + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + if (sf) + g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); + + pixbuf = gaim_gtk_create_gaim_icon_with_status(primitive, 0.5); + image = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); + + gtk_widget_show_all(menuitem); + + return menuitem; +} + +static GtkWidget * +docklet_status_submenu() +{ + GtkWidget *submenu, *menuitem; + GList *popular_statuses, *cur; + + submenu = gtk_menu_new(); + menuitem = gtk_menu_item_new_with_label(_("Change Status")); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + + new_menu_item_with_gaim_icon(submenu, _("Available"), + GAIM_STATUS_AVAILABLE, G_CALLBACK(activate_status_primitive_cb), + GINT_TO_POINTER(GAIM_STATUS_AVAILABLE), 0, 0, NULL); + + new_menu_item_with_gaim_icon(submenu, _("Away"), + GAIM_STATUS_AWAY, G_CALLBACK(activate_status_primitive_cb), + GINT_TO_POINTER(GAIM_STATUS_AWAY), 0, 0, NULL); + + new_menu_item_with_gaim_icon(submenu, _("Invisible"), + GAIM_STATUS_INVISIBLE, G_CALLBACK(activate_status_primitive_cb), + GINT_TO_POINTER(GAIM_STATUS_INVISIBLE), 0, 0, NULL); + + new_menu_item_with_gaim_icon(submenu, _("Offline"), + GAIM_STATUS_OFFLINE, G_CALLBACK(activate_status_primitive_cb), + GINT_TO_POINTER(GAIM_STATUS_OFFLINE), 0, 0, NULL); + + popular_statuses = gaim_savedstatuses_get_popular(6); + if (popular_statuses != NULL) + gaim_separator(submenu); + for (cur = popular_statuses; cur != NULL; cur = cur->next) + { + GaimSavedStatus *saved_status = cur->data; + time_t creation_time = gaim_savedstatus_get_creation_time(saved_status); + new_menu_item_with_gaim_icon(submenu, + gaim_savedstatus_get_title(saved_status), + gaim_savedstatus_get_type(saved_status), G_CALLBACK(activate_saved_status_cb), + GINT_TO_POINTER(creation_time), 0, 0, NULL); + } + g_list_free(popular_statuses); + + gaim_separator(submenu); + + new_menu_item_with_gaim_icon(submenu, _("New..."), GAIM_STATUS_AVAILABLE, G_CALLBACK(show_custom_status_editor_cb), NULL, 0, 0, NULL); + new_menu_item_with_gaim_icon(submenu, _("Saved..."), GAIM_STATUS_AVAILABLE, G_CALLBACK(gaim_gtk_status_window_show), NULL, 0, 0, NULL); + + return menuitem; +} + +static void +docklet_menu() { + static GtkWidget *menu = NULL; + GtkWidget *menuitem; + + if (menu) { + gtk_widget_destroy(menu); + } + + menu = gtk_menu_new(); + + menuitem = gtk_check_menu_item_new_with_label(_("Show Buddy List")); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gaim_prefs_get_bool("/gaim/gtk/blist/list_visible")); + g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_blist), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + menuitem = gtk_menu_item_new_with_label(_("Unread Messages")); + + if (status == DOCKLET_STATUS_ONLINE_PENDING || status == DOCKLET_STATUS_AWAY_PENDING) { + GtkWidget *submenu = gtk_menu_new(); + GList *l = get_pending_list(0); + if (l == NULL) { + gtk_widget_set_sensitive(menuitem, FALSE); + gaim_debug_warning("docklet", + "status indicates messages pending, but no conversations with unseen messages were found."); + } else { + gaim_gtk_conversations_fill_menu(submenu, l); + g_list_free(l); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + } + } else { + gtk_widget_set_sensitive(menuitem, FALSE); + } + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + gaim_separator(menu); + + menuitem = gaim_new_item_from_stock(menu, _("New Message..."), GAIM_STOCK_IM, G_CALLBACK(gaim_gtkdialogs_im), NULL, 0, 0, NULL); + if (status == DOCKLET_STATUS_OFFLINE) + gtk_widget_set_sensitive(menuitem, FALSE); + + menuitem = docklet_status_submenu(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + gaim_separator(menu); + + gaim_new_item_from_stock(menu, _("Accounts"), GAIM_STOCK_ACCOUNTS, G_CALLBACK(gaim_gtk_accounts_window_show), NULL, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("Plugins"), GAIM_STOCK_PLUGIN, G_CALLBACK(gaim_gtk_plugin_dialog_show), NULL, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("Preferences"), GTK_STOCK_PREFERENCES, G_CALLBACK(gaim_gtk_prefs_show), NULL, 0, 0, NULL); + + gaim_separator(menu); + + menuitem = gtk_check_menu_item_new_with_label(_("Mute Sounds")); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gaim_prefs_get_bool("/gaim/gtk/sound/mute")); + if (!strcmp(gaim_prefs_get_string("/gaim/gtk/sound/method"), "none")) + gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE); + g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_mute), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + + gaim_separator(menu); + + /* TODO: need a submenu to change status, this needs to "link" + * to the status in the buddy list gtkstatusbox + */ + + gaim_new_item_from_stock(menu, _("Quit"), GTK_STOCK_QUIT, G_CALLBACK(gaim_core_quit), NULL, 0, 0, NULL); + +#ifdef _WIN32 + g_signal_connect(menu, "leave-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL); + g_signal_connect(menu, "enter-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL); +#endif + gtk_widget_show_all(menu); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, + ui_ops->position_menu, + NULL, 0, gtk_get_current_event_time()); +} + +/************************************************************************** + * public api for ui_ops + **************************************************************************/ +void +gaim_gtk_docklet_clicked(int button_type) +{ + switch (button_type) { + case 1: + if (status == DOCKLET_STATUS_ONLINE_PENDING || status == DOCKLET_STATUS_AWAY_PENDING) { + GList *l = get_pending_list(1); + if (l != NULL) { + gaim_gtkconv_present_conversation((GaimConversation *)l->data); + g_list_free(l); + } + } else { + gaim_gtk_blist_toggle_visibility(); + } + break; + case 3: + docklet_menu(); + break; + } +} + +void +gaim_gtk_docklet_embedded() +{ + if (!visibility_manager) { + gaim_gtk_blist_visibility_manager_add(); + visibility_manager = TRUE; + } + docklet_update_status(); + if (ui_ops && ui_ops->update_icon) + ui_ops->update_icon(status); +} + +void +gaim_gtk_docklet_remove() +{ + if (visibility_manager) { + gaim_gtk_blist_visibility_manager_remove(); + visibility_manager = FALSE; + } +} + +void +gaim_gtk_docklet_set_ui_ops(struct docklet_ui_ops *ops) +{ + ui_ops = ops; +} + +void* +gaim_gtk_docklet_get_handle() +{ + static int i; + return &i; +} + +void +gaim_gtk_docklet_init() +{ + void *conn_handle = gaim_connections_get_handle(); + void *conv_handle = gaim_conversations_get_handle(); + void *accounts_handle = gaim_accounts_get_handle(); + void *core_handle = gaim_get_core(); + void *docklet_handle = gaim_gtk_docklet_get_handle(); + + gaim_debug(GAIM_DEBUG_INFO, "docklet", "plugin loaded\n"); + + gaim_prefs_add_none("/plugins/gtk/docklet"); + gaim_prefs_add_string("/plugins/gtk/docklet/blink_im", "hidden"); + gaim_prefs_add_string("/plugins/gtk/docklet/blink_chat", "never"); + + docklet_ui_init(); + if (ui_ops && ui_ops->create) + ui_ops->create(); + gaim_signal_connect(conn_handle, "signed-on", + docklet_handle, GAIM_CALLBACK(docklet_signed_on_cb), NULL); + gaim_signal_connect(conn_handle, "signed-off", + docklet_handle, GAIM_CALLBACK(docklet_signed_off_cb), NULL); + gaim_signal_connect(accounts_handle, "account-status-changed", + docklet_handle, GAIM_CALLBACK(docklet_update_status_cb), NULL); + gaim_signal_connect(conv_handle, "received-im-msg", + docklet_handle, GAIM_CALLBACK(docklet_update_status_cb), NULL); + gaim_signal_connect(conv_handle, "conversation-created", + docklet_handle, GAIM_CALLBACK(docklet_update_status_cb), NULL); + gaim_signal_connect(conv_handle, "deleting-conversation", + docklet_handle, GAIM_CALLBACK(docklet_update_status_cb), NULL); + gaim_signal_connect(conv_handle, "conversation-updated", + docklet_handle, GAIM_CALLBACK(docklet_conv_updated_cb), NULL); + + gaim_signal_connect(core_handle, "quitting", + NULL, GAIM_CALLBACK(gaim_quit_cb), NULL); + +/* gaim_prefs_connect_callback(plugin, "/plugins/gtk/docklet/blink_im", + docklet_prefs_cb, NULL); + gaim_prefs_connect_callback(plugin, "/plugins/gtk/docklet/blink_chat", + docklet_prefs_cb, NULL); +*/ + enable_join_chat = online_account_supports_chat(); +} + +void +gaim_gtk_docklet_uninit() +{ + if (ui_ops && ui_ops->destroy) + ui_ops->destroy(); +} + +static GtkWidget * +plugin_config_frame(GaimPlugin *plugin) +{ + GtkWidget *frame; + GtkWidget *vbox; + GtkSizeGroup *sg; + GtkWidget *dd; + + frame = gtk_vbox_new(FALSE, 18); + gtk_container_set_border_width(GTK_CONTAINER(frame), 12); + + vbox = gaim_gtk_make_frame(frame, _("Blink tray icon for unread...")); + sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + dd = gaim_gtk_prefs_dropdown(vbox, _("_Instant Messages:"), + GAIM_PREF_STRING, "/plugins/gtk/docklet/blink_im", + _("Never"), "never", + _("In hidden conversations"), "hidden", + _("Always"), "always", + NULL); + gtk_size_group_add_widget(sg, dd); + + dd = gaim_gtk_prefs_dropdown(vbox, _("C_hat Messages:"), + GAIM_PREF_STRING, "/plugins/gtk/docklet/blink_chat", + _("Never"), "never", + _("When my nick is said"), "nick", + _("Always"), "always", + NULL); + gtk_size_group_add_widget(sg, dd); + + gtk_widget_show_all(frame); + return frame; +} + +static GaimGtkPluginUiInfo ui_info = +{ + plugin_config_frame, + 0 /* page_num (Reserved) */ +}; diff -r fa285d018c71 -r 2c1781ea074c gtk/gtkdocklet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/gtkdocklet.h Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,62 @@ +/* + * System tray icon (aka docklet) plugin for Gaim + * + * Copyright (C) 2002-3 Robert McQueen + * Copyright (C) 2003 Herman Bloggs + * Inspired by a similar plugin by: + * John (J5) Palmieri + * + * 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. + */ + +#ifndef _GTKDOCKLET_H_ +#define _GTKDOCKLET_H_ + +typedef enum +{ + DOCKLET_STATUS_OFFLINE, + DOCKLET_STATUS_ONLINE, + DOCKLET_STATUS_ONLINE_PENDING, + DOCKLET_STATUS_AWAY, + DOCKLET_STATUS_AWAY_PENDING, + DOCKLET_STATUS_CONNECTING +} DockletStatus; + +struct docklet_ui_ops +{ + void (*create)(void); + void (*destroy)(void); + void (*update_icon)(DockletStatus); + void (*blank_icon)(void); + void (*set_tooltip)(gchar *); + GtkMenuPositionFunc position_menu; +}; + + +/* functions in docklet.c */ +void gaim_gtk_docklet_clicked(int); +void gaim_gtk_docklet_embedded(void); +void gaim_gtk_docklet_remove(void); +void gaim_gtk_docklet_set_ui_ops(struct docklet_ui_ops *); +void gaim_gtk_docklet_unload(void); +void gaim_gtk_docklet_init(); +void gaim_gtk_docklet_uninit(); +void*gaim_gtk_docklet_get_handle(); + +/* function in docklet-{x11,win32}.c */ +void docklet_ui_init(void); + +#endif /* _GTKDOCKLET_H_ */ diff -r fa285d018c71 -r 2c1781ea074c gtk/gtkmain.c --- a/gtk/gtkmain.c Wed Oct 04 20:31:44 2006 +0000 +++ b/gtk/gtkmain.c Thu Oct 05 23:24:00 2006 +0000 @@ -46,6 +46,7 @@ #include "gtkconv.h" #include "gtkdebug.h" #include "gtkdialogs.h" +#include "gtkdocklet.h" #include "gtkeventloop.h" #include "gtkft.h" #include "gtkidle.h" @@ -310,6 +311,7 @@ gaim_gtk_conversations_uninit(); gaim_gtk_status_uninit(); gaim_gtk_blist_uninit(); + gaim_gtk_docklet_uninit(); gaim_gtk_connection_uninit(); gaim_gtk_account_uninit(); gaim_gtk_xfers_uninit(); @@ -719,6 +721,7 @@ /* load plugins we had when we quit */ gaim_plugins_load_saved("/gaim/gtk/plugins/loaded"); + gaim_gtk_docklet_init(); /* TODO: Move pounces loading into gaim_pounces_init() */ gaim_pounces_load(); diff -r fa285d018c71 -r 2c1781ea074c gtk/plugins/docklet/Makefile.am --- a/gtk/plugins/docklet/Makefile.am Wed Oct 04 20:31:44 2006 +0000 +++ b/gtk/plugins/docklet/Makefile.am Thu Oct 05 23:24:00 2006 +0000 @@ -1,8 +1,4 @@ -EXTRA_DIST = \ - Makefile.mingw \ - docklet-win32.c \ - MinimizeToTray.c \ - MinimizeToTray.h +EXTRA_DIST = Makefile.mingw plugindir = $(libdir)/gaim @@ -12,12 +8,7 @@ plugin_LTLIBRARIES = docklet.la -docklet_la_SOURCES = \ - eggtrayicon.h \ - eggtrayicon.c \ - docklet.h \ - docklet.c \ - docklet-x11.c +docklet_la_SOURCES = docklet.c docklet_la_LIBADD = $(GTK_LIBS) diff -r fa285d018c71 -r 2c1781ea074c gtk/plugins/docklet/docklet-win32.c --- a/gtk/plugins/docklet/docklet-win32.c Wed Oct 04 20:31:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,263 +0,0 @@ -/* - * System tray icon (aka docklet) plugin for Gaim - * - * Copyright (C) 2002-3 Robert McQueen - * Copyright (C) 2003 Herman Bloggs - * Inspired by a similar plugin by: - * John (J5) Palmieri - * - * 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 "internal.h" -#include "gtkblist.h" -#include "gtkprefs.h" -#include "debug.h" - -#include "gaim.h" -#include "gtkdialogs.h" - -#include "resource.h" -#include "MinimizeToTray.h" -#include "gtkwin32dep.h" -#include "docklet.h" - -/* - * DEFINES, MACROS & DATA TYPES - */ -#define WM_TRAYMESSAGE WM_USER /* User defined WM Message */ - -/* - * LOCALS - */ -static HWND systray_hwnd=0; -static HICON sysicon_disconn=0; -static HICON sysicon_conn=0; -static HICON sysicon_away=0; -static HICON sysicon_pend=0; -static HICON sysicon_awypend=0; -static HICON sysicon_blank=0; -static NOTIFYICONDATA wgaim_nid; - - -static LRESULT CALLBACK systray_mainmsg_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - static UINT taskbarRestartMsg; /* static here means value is kept across multiple calls to this func */ - - switch(msg) { - case WM_CREATE: - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "WM_CREATE\n"); - taskbarRestartMsg = RegisterWindowMessage("TaskbarCreated"); - break; - - case WM_TIMER: - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "WM_TIMER\n"); - break; - - case WM_DESTROY: - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "WM_DESTROY\n"); - break; - - case WM_TRAYMESSAGE: - { - int type = 0; - - /* We'll use Double Click - Single click over on linux */ - if( lparam == WM_LBUTTONDBLCLK ) - type = 1; - else if( lparam == WM_MBUTTONUP ) - type = 2; - else if( lparam == WM_RBUTTONUP ) - type = 3; - else - break; - - docklet_clicked(type); - break; - } - default: - if (msg == taskbarRestartMsg) { - /* explorer crashed and left us hanging... - This will put the systray icon back in it's place, when it restarts */ - Shell_NotifyIcon(NIM_ADD,&wgaim_nid); - } - break; - }/* end switch */ - - return DefWindowProc(hwnd, msg, wparam, lparam); -} - -/* Create hidden window to process systray messages */ -static HWND systray_create_hiddenwin() { - WNDCLASSEX wcex; - TCHAR wname[32]; - - strcpy(wname, "GaimWin"); - - wcex.cbSize = sizeof(WNDCLASSEX); - - wcex.style = 0; - wcex.lpfnWndProc = (WNDPROC)systray_mainmsg_handler; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = gtkwgaim_hinstance(); - wcex.hIcon = NULL; - wcex.hCursor = NULL, - wcex.hbrBackground = NULL; - wcex.lpszMenuName = NULL; - wcex.lpszClassName = wname; - wcex.hIconSm = NULL; - - RegisterClassEx(&wcex); - - /* Create the window */ - return (CreateWindow(wname, "", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, gtkwgaim_hinstance(), 0)); -} - -static void systray_init_icon(HWND hWnd, HICON icon) { - ZeroMemory(&wgaim_nid,sizeof(wgaim_nid)); - wgaim_nid.cbSize=sizeof(NOTIFYICONDATA); - wgaim_nid.hWnd=hWnd; - wgaim_nid.uID=0; - wgaim_nid.uFlags=NIF_ICON | NIF_MESSAGE | NIF_TIP; - wgaim_nid.uCallbackMessage=WM_TRAYMESSAGE; - wgaim_nid.hIcon=icon; - lstrcpy(wgaim_nid.szTip, "Gaim"); - Shell_NotifyIcon(NIM_ADD,&wgaim_nid); - docklet_embedded(); -} - -static void systray_change_icon(HICON icon) { - wgaim_nid.hIcon = icon; - Shell_NotifyIcon(NIM_MODIFY,&wgaim_nid); -} - -static void systray_remove_nid(void) { - Shell_NotifyIcon(NIM_DELETE,&wgaim_nid); -} - -static void wgaim_tray_update_icon(DockletStatus icon) { - switch (icon) { - case DOCKLET_STATUS_OFFLINE: - systray_change_icon(sysicon_disconn); - break; - case DOCKLET_STATUS_CONNECTING: - break; - case DOCKLET_STATUS_ONLINE: - systray_change_icon(sysicon_conn); - break; - case DOCKLET_STATUS_ONLINE_PENDING: - systray_change_icon(sysicon_pend); - break; - case DOCKLET_STATUS_AWAY: - systray_change_icon(sysicon_away); - break; - case DOCKLET_STATUS_AWAY_PENDING: - systray_change_icon(sysicon_awypend); - break; - } -} - -static void wgaim_tray_blank_icon() { - systray_change_icon(sysicon_blank); -} - -static void wgaim_tray_set_tooltip(gchar *tooltip) { - if (tooltip) { - char *locenc = NULL; - locenc = g_locale_from_utf8(tooltip, -1, NULL, NULL, NULL); - lstrcpyn(wgaim_nid.szTip, locenc, sizeof(wgaim_nid.szTip)/sizeof(TCHAR)); - g_free(locenc); - } else { - lstrcpy(wgaim_nid.szTip, "Gaim"); - } - Shell_NotifyIcon(NIM_MODIFY, &wgaim_nid); -} - -void wgaim_tray_minimize(GaimGtkBuddyList *gtkblist) { - MinimizeWndToTray(GDK_WINDOW_HWND(gtkblist->window->window)); -} - -void wgaim_tray_maximize(GaimGtkBuddyList *gtkblist) { - RestoreWndFromTray(GDK_WINDOW_HWND(gtkblist->window->window)); -} - - -static void wgaim_tray_create() { - OSVERSIONINFO osinfo; - /* dummy window to process systray messages */ - systray_hwnd = systray_create_hiddenwin(); - - osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osinfo); - - /* Load icons, and init systray notify icon - * NOTE: Windows < XP only supports displaying 4-bit images in the Systray, - * 2K and ME will use the highest color depth that the desktop will support, - * but will scale it back to 4-bits for display. - * That is why we use custom 4-bit icons for pre XP Windowses */ - if ((osinfo.dwMajorVersion == 5 && osinfo.dwMinorVersion > 0) || - (osinfo.dwMajorVersion >= 6)) - { - sysicon_disconn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_OFFLINE_TRAY_ICON), IMAGE_ICON, 16, 16, 0); - sysicon_conn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_TRAY_ICON), IMAGE_ICON, 16, 16, 0); - sysicon_away = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAY_TRAY_ICON), IMAGE_ICON, 16, 16, 0); - sysicon_pend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_PEND_TRAY_ICON), IMAGE_ICON, 16, 16, 0); - sysicon_awypend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAYPEND_TRAY_ICON), IMAGE_ICON, 16, 16, 0); - } else { - sysicon_disconn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_OFFLINE_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); - sysicon_conn = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); - sysicon_away = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAY_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); - sysicon_pend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_PEND_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); - sysicon_awypend = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_AWAYPEND_TRAY_ICON_4BIT), IMAGE_ICON, 16, 16, 0); - } - sysicon_blank = (HICON)LoadImage(gtkwgaim_hinstance(), MAKEINTRESOURCE(GAIM_BLANK_TRAY_ICON), IMAGE_ICON, 16, 16, 0); - - /* Create icon in systray */ - systray_init_icon(systray_hwnd, sysicon_disconn); - - gaim_signal_connect(gaim_gtk_blist_get_handle(), "gtkblist-hiding", - &handle, GAIM_CALLBACK(wgaim_tray_minimize), NULL); - gaim_signal_connect(gaim_gtk_blist_get_handle(), "gtkblist-unhiding", - &handle, GAIM_CALLBACK(wgaim_tray_maximize), NULL); - - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "created\n"); -} - -static void wgaim_tray_destroy() { - gaim_signals_disconnect_by_handle(&handle); - systray_remove_nid(); - DestroyWindow(systray_hwnd); - docklet_remove(); -} - -static struct docklet_ui_ops wgaim_tray_ops = -{ - wgaim_tray_create, - wgaim_tray_destroy, - wgaim_tray_update_icon, - wgaim_tray_blank_icon, - wgaim_tray_set_tooltip, - NULL -}; - -/* Used by docklet's plugin load func */ -void docklet_ui_init() { - docklet_set_ui_ops(&wgaim_tray_ops); -} diff -r fa285d018c71 -r 2c1781ea074c gtk/plugins/docklet/docklet-x11.c --- a/gtk/plugins/docklet/docklet-x11.c Wed Oct 04 20:31:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,305 +0,0 @@ -/* - * System tray icon (aka docklet) plugin for Gaim - * - * Copyright (C) 2002-3 Robert McQueen - * Copyright (C) 2003 Herman Bloggs - * Inspired by a similar plugin by: - * John (J5) Palmieri - * - * 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 "internal.h" -#include "gtkgaim.h" -#include "debug.h" -#include "gaimstock.h" - -#include "gaim.h" -#include "gtkdialogs.h" - -#include "eggtrayicon.h" -#include "docklet.h" - -#define EMBED_TIMEOUT 5000 - -/* globals */ -static EggTrayIcon *docklet = NULL; -static GtkWidget *image = NULL; -static GtkTooltips *tooltips = NULL; -static GdkPixbuf *blank_icon = NULL; -static int embed_timeout = 0; - -/* protos */ -static void docklet_x11_create(void); - -static gboolean -docklet_x11_create_cb() -{ - docklet_x11_create(); - - return FALSE; /* for when we're called by the glib idle handler */ -} - -static void -docklet_x11_embedded_cb(GtkWidget *widget, void *data) -{ - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "embedded\n"); - - g_source_remove(embed_timeout); - embed_timeout = 0; - docklet_embedded(); -} - -static void -docklet_x11_destroyed_cb(GtkWidget *widget, void *data) -{ - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "destroyed\n"); - - docklet_remove(); - - g_object_unref(G_OBJECT(docklet)); - docklet = NULL; - - g_idle_add(docklet_x11_create_cb, &handle); -} - -static void -docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data) -{ - if (event->type != GDK_BUTTON_PRESS) - return; - - docklet_clicked(event->button); -} - -static void -docklet_x11_update_icon(DockletStatus icon) -{ - const gchar *icon_name = NULL; - - g_return_if_fail(image != NULL); - - switch (icon) { - case DOCKLET_STATUS_OFFLINE: - icon_name = GAIM_STOCK_ICON_OFFLINE; - break; - case DOCKLET_STATUS_CONNECTING: - icon_name = GAIM_STOCK_ICON_CONNECT; - break; - case DOCKLET_STATUS_ONLINE: - icon_name = GAIM_STOCK_ICON_ONLINE; - break; - case DOCKLET_STATUS_ONLINE_PENDING: - icon_name = GAIM_STOCK_ICON_ONLINE_MSG; - break; - case DOCKLET_STATUS_AWAY: - icon_name = GAIM_STOCK_ICON_AWAY; - break; - case DOCKLET_STATUS_AWAY_PENDING: - icon_name = GAIM_STOCK_ICON_AWAY_MSG; - break; - } - - if(icon_name) - gtk_image_set_from_stock(GTK_IMAGE(image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR); - -#if 0 - GdkPixbuf *p; - GdkBitmap *mask = NULL; - - p = gtk_widget_render_icon(GTK_WIDGET(image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); - - if (p && (gdk_pixbuf_get_colorspace(p) == GDK_COLORSPACE_RGB) && (gdk_pixbuf_get_bits_per_sample(p) == 8) - && (gdk_pixbuf_get_has_alpha(p)) && (gdk_pixbuf_get_n_channels(p) == 4)) { - int len = gdk_pixbuf_get_width(p) * gdk_pixbuf_get_height(p); - guchar *data = gdk_pixbuf_get_pixels(p); - guchar *bitmap = g_malloc((len / 8) + 1); - int i; - - for (i = 0; i < len; i++) - if (data[i*4 + 3] > 55) - bitmap[i/8] |= 1 << i % 8; - else - bitmap[i/8] &= ~(1 << i % 8); - - mask = gdk_bitmap_create_from_data(GDK_DRAWABLE(GTK_WIDGET(image)->window), bitmap, gdk_pixbuf_get_width(p), gdk_pixbuf_get_height(p)); - g_free(bitmap); - } - - if (mask) - gdk_window_shape_combine_mask(image->window, mask, 0, 0); - - g_object_unref(G_OBJECT(p)); -#endif -} - -static void -docklet_x11_blank_icon() -{ - if (!blank_icon) { - gint width, height; - - gtk_icon_size_lookup(GTK_ICON_SIZE_LARGE_TOOLBAR, &width, &height); - blank_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); - gdk_pixbuf_fill(blank_icon, 0); - } - - gtk_image_set_from_pixbuf(GTK_IMAGE(image), blank_icon); -} - -static void -docklet_x11_set_tooltip(gchar *tooltip) -{ - if (!tooltips) - tooltips = gtk_tooltips_new(); - - /* image->parent is a GtkEventBox */ - if (tooltip) { - gtk_tooltips_enable(tooltips); - gtk_tooltips_set_tip(tooltips, image->parent, tooltip, NULL); - } else { - gtk_tooltips_set_tip(tooltips, image->parent, "", NULL); - gtk_tooltips_disable(tooltips); - } -} - -#if GTK_CHECK_VERSION(2,2,0) -static void -docklet_x11_position_menu(GtkMenu *menu, int *x, int *y, gboolean *push_in, - gpointer user_data) -{ - GtkWidget *widget = GTK_WIDGET(docklet); - GtkRequisition req; - gint menu_xpos, menu_ypos; - - gtk_widget_size_request(GTK_WIDGET(menu), &req); - gdk_window_get_origin(widget->window, &menu_xpos, &menu_ypos); - - menu_xpos += widget->allocation.x; - menu_ypos += widget->allocation.y; - - if (menu_ypos > gdk_screen_get_height(gtk_widget_get_screen(widget)) / 2) - menu_ypos -= req.height; - else - menu_ypos += widget->allocation.height; - - *x = menu_xpos; - *y = menu_ypos; - - *push_in = TRUE; -} -#endif - -static void -docklet_x11_destroy() -{ - g_return_if_fail(docklet != NULL); - - docklet_remove(); - - g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_x11_destroyed_cb), NULL); - gtk_widget_destroy(GTK_WIDGET(docklet)); - - g_object_unref(G_OBJECT(docklet)); - docklet = NULL; - - if (blank_icon) - g_object_unref(G_OBJECT(blank_icon)); - blank_icon = NULL; - - image = NULL; - - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "destroyed\n"); -} - -static gboolean -docklet_x11_embed_timeout_cb() -{ - /* The docklet was not embedded within the timeout. - * Remove it as a visibility manager, but leave the plugin - * loaded so that it can embed automatically if/when a notification - * area becomes available. - */ - gaim_debug_info("tray icon", "failed to embed within timeout\n"); - docklet_remove(); - - return FALSE; -} - -static void -docklet_x11_create() -{ - GtkWidget *box; - - if (docklet) { - /* if this is being called when a tray icon exists, it's because - something messed up. try destroying it before we proceed, - although docklet_refcount may be all hosed. hopefully won't happen. */ - gaim_debug(GAIM_DEBUG_WARNING, "tray icon", "trying to create icon but it already exists?\n"); - docklet_x11_destroy(); - } - - docklet = egg_tray_icon_new("Gaim"); - box = gtk_event_box_new(); - image = gtk_image_new(); - - g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_x11_embedded_cb), NULL); - g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_x11_destroyed_cb), NULL); - g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_x11_clicked_cb), NULL); - - gtk_container_add(GTK_CONTAINER(box), image); - gtk_container_add(GTK_CONTAINER(docklet), box); - - if (!gtk_check_version(2,4,0)) - g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL); - - gtk_widget_show_all(GTK_WIDGET(docklet)); - - /* ref the docklet before we bandy it about the place */ - g_object_ref(G_OBJECT(docklet)); - - /* This is a hack to avoid a race condition between the docklet getting - * embedded in the notification area and the gtkblist restoring its - * previous visibility state. If the docklet does not get embedded within - * the timeout, it will be removed as a visibility manager until it does - * get embedded. Ideally, we would only call docklet_embedded() when the - * icon was actually embedded. - */ - docklet_embedded(); - embed_timeout = g_timeout_add(EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL); - - gaim_debug(GAIM_DEBUG_INFO, "tray icon", "created\n"); -} - -static struct docklet_ui_ops ui_ops = -{ - docklet_x11_create, - docklet_x11_destroy, - docklet_x11_update_icon, - docklet_x11_blank_icon, - docklet_x11_set_tooltip, -#if GTK_CHECK_VERSION(2,2,0) - docklet_x11_position_menu -#else - NULL -#endif -}; - -void -docklet_ui_init() -{ - docklet_set_ui_ops(&ui_ops); -} diff -r fa285d018c71 -r 2c1781ea074c gtk/plugins/docklet/docklet.c --- a/gtk/plugins/docklet/docklet.c Wed Oct 04 20:31:44 2006 +0000 +++ b/gtk/plugins/docklet/docklet.c Thu Oct 05 23:24:00 2006 +0000 @@ -23,730 +23,9 @@ */ #include "internal.h" #include "gtkgaim.h" - +#include "gtkplugin.h" #include "core.h" -#include "conversation.h" -#include "debug.h" -#include "prefs.h" -#include "signals.h" -#include "sound.h" #include "version.h" -#include "gtkaccount.h" -#include "gtkblist.h" -#include "gtkconv.h" -#include "gtkft.h" -#include "gtkplugin.h" -#include "gtkprefs.h" -#include "gtksavedstatuses.h" -#include "gtksound.h" -#include "gtkutils.h" -#include "gaimstock.h" -#include "docklet.h" - -#include "gaim.h" -#include "gtkdialogs.h" - -#define DOCKLET_PLUGIN_ID "gtk-docklet" - -#ifndef DOCKLET_TOOLTIP_LINE_LIMIT -#define DOCKLET_TOOLTIP_LINE_LIMIT 5 -#endif - -/* globals */ -GaimPlugin *handle = NULL; -static struct docklet_ui_ops *ui_ops = NULL; -static DockletStatus status = DOCKLET_STATUS_OFFLINE; -static gboolean enable_join_chat = FALSE; -static guint docklet_blinking_timer = 0; -static gboolean visibility_manager = FALSE; - -/************************************************************************** - * docklet status and utility functions - **************************************************************************/ -static gboolean -docklet_blink_icon() -{ - static gboolean blinked = FALSE; - gboolean ret = FALSE; /* by default, don't keep blinking */ - - blinked = !blinked; - - switch (status) { - case DOCKLET_STATUS_ONLINE_PENDING: - case DOCKLET_STATUS_AWAY_PENDING: - if (blinked) { - if (ui_ops && ui_ops->blank_icon) - ui_ops->blank_icon(); - } else { - if (ui_ops && ui_ops->update_icon) - ui_ops->update_icon(status); - } - ret = TRUE; /* keep blinking */ - break; - default: - docklet_blinking_timer = 0; - blinked = FALSE; - break; - } - - return ret; -} - -static GList * -get_pending_list(guint max) -{ - const char *im = gaim_prefs_get_string("/plugins/gtk/docklet/blink_im"); - const char *chat = gaim_prefs_get_string("/plugins/gtk/docklet/blink_chat"); - GList *l_im = NULL; - GList *l_chat = NULL; - - if (im != NULL && strcmp(im, "always") == 0) { - l_im = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_IM, - GAIM_UNSEEN_TEXT, - FALSE, max); - } else if (im != NULL && strcmp(im, "hidden") == 0) { - l_im = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_IM, - GAIM_UNSEEN_TEXT, - TRUE, max); - } - - if (chat != NULL && strcmp(chat, "always") == 0) { - l_chat = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_CHAT, - GAIM_UNSEEN_TEXT, - FALSE, max); - } else if (chat != NULL && strcmp(chat, "nick") == 0) { - l_chat = gaim_gtk_conversations_find_unseen_list(GAIM_CONV_TYPE_CHAT, - GAIM_UNSEEN_NICK, - FALSE, max); - } - - if (l_im != NULL && l_chat != NULL) - return g_list_concat(l_im, l_chat); - else if (l_im != NULL) - return l_im; - else - return l_chat; -} - -static gboolean -docklet_update_status() -{ - GList *convs; - GList *l; - int count; - DockletStatus newstatus = DOCKLET_STATUS_OFFLINE; - gboolean pending = FALSE; - - /* determine if any ims have unseen messages */ - convs = get_pending_list(DOCKLET_TOOLTIP_LINE_LIMIT); - - if (convs != NULL) { - pending = TRUE; - - /* set tooltip if messages are pending */ - if (ui_ops->set_tooltip) { - GString *tooltip_text = g_string_new(""); - for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) { - if (GAIM_IS_GTK_CONVERSATION(l->data)) { - GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION((GaimConversation *)l->data); - if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) - g_string_append(tooltip_text, _("Right-click for more unread messages...\n")); - else - g_string_append_printf(tooltip_text, - ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count), - gtkconv->unseen_count, - gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); - } - } - - /* get rid of the last newline */ - if (tooltip_text->len > 0) - tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1); - - ui_ops->set_tooltip(tooltip_text->str); - - g_string_free(tooltip_text, TRUE); - } - - g_list_free(convs); - - } else if (ui_ops->set_tooltip) { - ui_ops->set_tooltip(NULL); - } - - /* iterate through all accounts and determine which - * status to show in the tray icon based on the following - * ranks (highest encountered rank will be used): - * - * 1) OFFLINE - * 2) ONLINE - * 3) ONLINE_PENDING - * 4) AWAY - * 5) AWAY_PENDING - * 6) CONNECTING - */ - for(l = gaim_accounts_get_all(); l != NULL; l = l->next) { - DockletStatus tmpstatus = DOCKLET_STATUS_OFFLINE; - - GaimAccount *account = (GaimAccount*)l->data; - GaimStatus *account_status; - - if (!gaim_account_get_enabled(account, GAIM_GTK_UI)) - continue; - - if (gaim_account_is_disconnected(account)) - continue; - - account_status = gaim_account_get_active_status(account); - - if (gaim_account_is_connecting(account)) { - tmpstatus = DOCKLET_STATUS_CONNECTING; - } else if (gaim_status_is_online(account_status)) { - if (!gaim_status_is_available(account_status)) { - if (pending) - tmpstatus = DOCKLET_STATUS_AWAY_PENDING; - else - tmpstatus = DOCKLET_STATUS_AWAY; - } - else { - if (pending) - tmpstatus = DOCKLET_STATUS_ONLINE_PENDING; - else - tmpstatus = DOCKLET_STATUS_ONLINE; - } - } - - if (tmpstatus > newstatus) - newstatus = tmpstatus; - } - - /* update the icon if we changed status */ - if (status != newstatus) { - status = newstatus; - - if (ui_ops && ui_ops->update_icon) - ui_ops->update_icon(status); - - /* and schedule the blinker function if messages are pending */ - if ((status == DOCKLET_STATUS_ONLINE_PENDING - || status == DOCKLET_STATUS_AWAY_PENDING) - && docklet_blinking_timer == 0) { - docklet_blinking_timer = g_timeout_add(500, docklet_blink_icon, &handle); - } - } - - return FALSE; /* for when we're called by the glib idle handler */ -} - -static gboolean -online_account_supports_chat() -{ - GList *c = NULL; - c = gaim_connections_get_all(); - - while(c != NULL) { - GaimConnection *gc = c->data; - GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); - if (prpl_info != NULL && prpl_info->chat_info != NULL) - return TRUE; - c = c->next; - } - - return FALSE; -} - -/************************************************************************** - * callbacks and signal handlers - **************************************************************************/ -static void -gaim_quit_cb() -{ - /* TODO: confirm quit while pending */ -} - -static void -docklet_update_status_cb(void *data) -{ - docklet_update_status(); -} - -static void -docklet_prefs_cb(const char *name, GaimPrefType type, - gconstpointer val, gpointer data) -{ - docklet_update_status(); -} - -static void -docklet_conv_updated_cb(GaimConversation *conv, GaimConvUpdateType type) -{ - if (type == GAIM_CONV_UPDATE_UNSEEN) - docklet_update_status(); -} - -static void -docklet_signed_on_cb(GaimConnection *gc) -{ - if (!enable_join_chat) { - if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) - enable_join_chat = TRUE; - } - docklet_update_status(); -} - -static void -docklet_signed_off_cb(GaimConnection *gc) -{ - if (enable_join_chat) { - if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL) - enable_join_chat = online_account_supports_chat(); - } - docklet_update_status(); -} - -/************************************************************************** - * docklet pop-up menu - **************************************************************************/ -static void -docklet_toggle_mute(GtkWidget *toggle, void *data) -{ - gaim_prefs_set_bool("/gaim/gtk/sound/mute", GTK_CHECK_MENU_ITEM(toggle)->active); -} - -static void -docklet_toggle_blist(GtkWidget *toggle, void *data) -{ - gaim_blist_set_visible(GTK_CHECK_MENU_ITEM(toggle)->active); -} - -#ifdef _WIN32 -/* This is a workaround for a bug in windows GTK+. Clicking outside of the - menu does not get rid of it, so instead we get rid of it as soon as the - pointer leaves the menu. */ -static gboolean -hide_docklet_menu(gpointer data) -{ - if (data != NULL) { - gtk_menu_popdown(GTK_MENU(data)); - } - return FALSE; -} - -static gboolean -docklet_menu_leave_enter(GtkWidget *menu, GdkEventCrossing *event, void *data) -{ - static guint hide_docklet_timer = 0; - if (event->type == GDK_LEAVE_NOTIFY && event->detail == GDK_NOTIFY_ANCESTOR) { - gaim_debug(GAIM_DEBUG_INFO, "docklet", "menu leave-notify-event\n"); - /* Add some slop so that the menu doesn't annoyingly disappear when mousing around */ - if (hide_docklet_timer == 0) { - hide_docklet_timer = gaim_timeout_add(500, - hide_docklet_menu, menu); - } - } else if (event->type == GDK_ENTER_NOTIFY && event->detail == GDK_NOTIFY_ANCESTOR) { - gaim_debug(GAIM_DEBUG_INFO, "docklet", "menu enter-notify-event\n"); - if (hide_docklet_timer != 0) { - /* Cancel the hiding if we reenter */ - - gaim_timeout_remove(hide_docklet_timer); - hide_docklet_timer = 0; - } - } - return FALSE; -} -#endif - -static void -show_custom_status_editor_cb(GtkMenuItem *menuitem, gpointer user_data) -{ - GaimSavedStatus *saved_status; - saved_status = gaim_savedstatus_get_current(); - gaim_gtk_status_editor_show(FALSE, - gaim_savedstatus_is_transient(saved_status) ? saved_status : NULL); -} - -static void -activate_status_primitive_cb(GtkMenuItem *menuitem, gpointer user_data) -{ - GaimStatusPrimitive primitive; - GaimSavedStatus *saved_status; - - primitive = GPOINTER_TO_INT(user_data); - - /* Try to lookup an already existing transient saved status */ - saved_status = gaim_savedstatus_find_transient_by_type_and_message(primitive, NULL); - - /* Create a new transient saved status if we weren't able to find one */ - if (saved_status == NULL) - saved_status = gaim_savedstatus_new(NULL, primitive); - - /* Set the status for each account */ - gaim_savedstatus_activate(saved_status); -} - -static void -activate_saved_status_cb(GtkMenuItem *menuitem, gpointer user_data) -{ - time_t creation_time; - GaimSavedStatus *saved_status; - creation_time = GPOINTER_TO_INT(user_data); - saved_status = gaim_savedstatus_find_by_creation_time(creation_time); - if (saved_status != NULL) - gaim_savedstatus_activate(saved_status); -} -static GtkWidget * -new_menu_item_with_gaim_icon(GtkWidget *menu, const char *str, GaimStatusPrimitive primitive, GtkSignalFunc sf, gpointer data, guint accel_key, guint accel_mods, char *mod) -{ - GtkWidget *menuitem; - GdkPixbuf *pixbuf; - GtkWidget *image; - - menuitem = gtk_image_menu_item_new_with_mnemonic(str); - - if (menu) - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - if (sf) - g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); - - pixbuf = gaim_gtk_create_gaim_icon_with_status(primitive, 0.5); - image = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); - - gtk_widget_show_all(menuitem); - - return menuitem; -} - -static GtkWidget * -docklet_status_submenu() -{ - GtkWidget *submenu, *menuitem; - GList *popular_statuses, *cur; - - submenu = gtk_menu_new(); - menuitem = gtk_menu_item_new_with_label(_("Change Status")); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - - new_menu_item_with_gaim_icon(submenu, _("Available"), - GAIM_STATUS_AVAILABLE, G_CALLBACK(activate_status_primitive_cb), - GINT_TO_POINTER(GAIM_STATUS_AVAILABLE), 0, 0, NULL); - - new_menu_item_with_gaim_icon(submenu, _("Away"), - GAIM_STATUS_AWAY, G_CALLBACK(activate_status_primitive_cb), - GINT_TO_POINTER(GAIM_STATUS_AWAY), 0, 0, NULL); - - new_menu_item_with_gaim_icon(submenu, _("Invisible"), - GAIM_STATUS_INVISIBLE, G_CALLBACK(activate_status_primitive_cb), - GINT_TO_POINTER(GAIM_STATUS_INVISIBLE), 0, 0, NULL); - - new_menu_item_with_gaim_icon(submenu, _("Offline"), - GAIM_STATUS_OFFLINE, G_CALLBACK(activate_status_primitive_cb), - GINT_TO_POINTER(GAIM_STATUS_OFFLINE), 0, 0, NULL); - - popular_statuses = gaim_savedstatuses_get_popular(6); - if (popular_statuses != NULL) - gaim_separator(submenu); - for (cur = popular_statuses; cur != NULL; cur = cur->next) - { - GaimSavedStatus *saved_status = cur->data; - time_t creation_time = gaim_savedstatus_get_creation_time(saved_status); - new_menu_item_with_gaim_icon(submenu, - gaim_savedstatus_get_title(saved_status), - gaim_savedstatus_get_type(saved_status), G_CALLBACK(activate_saved_status_cb), - GINT_TO_POINTER(creation_time), 0, 0, NULL); - } - g_list_free(popular_statuses); - - gaim_separator(submenu); - - new_menu_item_with_gaim_icon(submenu, _("New..."), GAIM_STATUS_AVAILABLE, G_CALLBACK(show_custom_status_editor_cb), NULL, 0, 0, NULL); - new_menu_item_with_gaim_icon(submenu, _("Saved..."), GAIM_STATUS_AVAILABLE, G_CALLBACK(gaim_gtk_status_window_show), NULL, 0, 0, NULL); - - return menuitem; -} - -static void -docklet_menu() { - static GtkWidget *menu = NULL; - GtkWidget *menuitem; - - if (menu) { - gtk_widget_destroy(menu); - } - - menu = gtk_menu_new(); - - menuitem = gtk_check_menu_item_new_with_label(_("Show Buddy List")); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gaim_prefs_get_bool("/gaim/gtk/blist/list_visible")); - g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_blist), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - menuitem = gtk_menu_item_new_with_label(_("Unread Messages")); - - if (status == DOCKLET_STATUS_ONLINE_PENDING || status == DOCKLET_STATUS_AWAY_PENDING) { - GtkWidget *submenu = gtk_menu_new(); - GList *l = get_pending_list(0); - if (l == NULL) { - gtk_widget_set_sensitive(menuitem, FALSE); - gaim_debug_warning("docklet", - "status indicates messages pending, but no conversations with unseen messages were found."); - } else { - gaim_gtk_conversations_fill_menu(submenu, l); - g_list_free(l); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - } - } else { - gtk_widget_set_sensitive(menuitem, FALSE); - } - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - gaim_separator(menu); - - menuitem = gaim_new_item_from_stock(menu, _("New Message..."), GAIM_STOCK_IM, G_CALLBACK(gaim_gtkdialogs_im), NULL, 0, 0, NULL); - if (status == DOCKLET_STATUS_OFFLINE) - gtk_widget_set_sensitive(menuitem, FALSE); - - menuitem = docklet_status_submenu(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - gaim_separator(menu); - - gaim_new_item_from_stock(menu, _("Accounts"), GAIM_STOCK_ACCOUNTS, G_CALLBACK(gaim_gtk_accounts_window_show), NULL, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("Plugins"), GAIM_STOCK_PLUGIN, G_CALLBACK(gaim_gtk_plugin_dialog_show), NULL, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("Preferences"), GTK_STOCK_PREFERENCES, G_CALLBACK(gaim_gtk_prefs_show), NULL, 0, 0, NULL); - - gaim_separator(menu); - - menuitem = gtk_check_menu_item_new_with_label(_("Mute Sounds")); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gaim_prefs_get_bool("/gaim/gtk/sound/mute")); - if (!strcmp(gaim_prefs_get_string("/gaim/gtk/sound/method"), "none")) - gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE); - g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_mute), NULL); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - - gaim_separator(menu); - - /* TODO: need a submenu to change status, this needs to "link" - * to the status in the buddy list gtkstatusbox - */ - - gaim_new_item_from_stock(menu, _("Quit"), GTK_STOCK_QUIT, G_CALLBACK(gaim_core_quit), NULL, 0, 0, NULL); - -#ifdef _WIN32 - g_signal_connect(menu, "leave-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL); - g_signal_connect(menu, "enter-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL); -#endif - gtk_widget_show_all(menu); - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, - ui_ops->position_menu, - NULL, 0, gtk_get_current_event_time()); -} - -/************************************************************************** - * public api for ui_ops - **************************************************************************/ -void -docklet_clicked(int button_type) -{ - switch (button_type) { - case 1: - if (status == DOCKLET_STATUS_ONLINE_PENDING || status == DOCKLET_STATUS_AWAY_PENDING) { - GList *l = get_pending_list(1); - if (l != NULL) { - gaim_gtkconv_present_conversation((GaimConversation *)l->data); - g_list_free(l); - } - } else { - gaim_gtk_blist_toggle_visibility(); - } - break; - case 3: - docklet_menu(); - break; - } -} - -void -docklet_embedded() -{ - if (!visibility_manager) { - gaim_gtk_blist_visibility_manager_add(); - visibility_manager = TRUE; - } - docklet_update_status(); - if (ui_ops && ui_ops->update_icon) - ui_ops->update_icon(status); -} - -void -docklet_remove() -{ - if (visibility_manager) { - gaim_gtk_blist_visibility_manager_remove(); - visibility_manager = FALSE; - } -} - -void -docklet_unload() -{ - gaim_plugin_unload(handle); -} - -void -docklet_set_ui_ops(struct docklet_ui_ops *ops) -{ - ui_ops = ops; -} - -/************************************************************************** - * plugin glue - **************************************************************************/ -static gboolean -plugin_load(GaimPlugin *plugin) -{ - void *conn_handle = gaim_connections_get_handle(); - void *conv_handle = gaim_conversations_get_handle(); - void *accounts_handle = gaim_accounts_get_handle(); - void *core_handle = gaim_get_core(); - - gaim_debug(GAIM_DEBUG_INFO, "docklet", "plugin loaded\n"); - - handle = plugin; - - docklet_ui_init(); - if (ui_ops && ui_ops->create) - ui_ops->create(); - gaim_signal_connect(conn_handle, "signed-on", - plugin, GAIM_CALLBACK(docklet_signed_on_cb), NULL); - gaim_signal_connect(conn_handle, "signed-off", - plugin, GAIM_CALLBACK(docklet_signed_off_cb), NULL); - gaim_signal_connect(accounts_handle, "account-status-changed", - plugin, GAIM_CALLBACK(docklet_update_status_cb), NULL); - gaim_signal_connect(conv_handle, "received-im-msg", - plugin, GAIM_CALLBACK(docklet_update_status_cb), NULL); - gaim_signal_connect(conv_handle, "conversation-created", - plugin, GAIM_CALLBACK(docklet_update_status_cb), NULL); - gaim_signal_connect(conv_handle, "deleting-conversation", - plugin, GAIM_CALLBACK(docklet_update_status_cb), NULL); - gaim_signal_connect(conv_handle, "conversation-updated", - plugin, GAIM_CALLBACK(docklet_conv_updated_cb), NULL); - - gaim_signal_connect(core_handle, "quitting", - plugin, GAIM_CALLBACK(gaim_quit_cb), NULL); - - gaim_prefs_connect_callback(plugin, "/plugins/gtk/docklet/blink_im", - docklet_prefs_cb, NULL); - gaim_prefs_connect_callback(plugin, "/plugins/gtk/docklet/blink_chat", - docklet_prefs_cb, NULL); - - enable_join_chat = online_account_supports_chat(); - - return TRUE; -} - -static gboolean -plugin_unload(GaimPlugin *plugin) -{ - if (ui_ops && ui_ops->destroy) - ui_ops->destroy(); - - /* remove callbacks */ - gaim_signals_disconnect_by_handle(handle); - gaim_prefs_disconnect_by_handle(handle); - - gaim_debug(GAIM_DEBUG_INFO, "docklet", "plugin unloaded\n"); - - return TRUE; -} - -static GtkWidget * -plugin_config_frame(GaimPlugin *plugin) -{ - GtkWidget *frame; - GtkWidget *vbox; - GtkSizeGroup *sg; - GtkWidget *dd; - - frame = gtk_vbox_new(FALSE, 18); - gtk_container_set_border_width(GTK_CONTAINER(frame), 12); - - vbox = gaim_gtk_make_frame(frame, _("Blink tray icon for unread...")); - sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - - dd = gaim_gtk_prefs_dropdown(vbox, _("_Instant Messages:"), - GAIM_PREF_STRING, "/plugins/gtk/docklet/blink_im", - _("Never"), "never", - _("In hidden conversations"), "hidden", - _("Always"), "always", - NULL); - gtk_size_group_add_widget(sg, dd); - - dd = gaim_gtk_prefs_dropdown(vbox, _("C_hat Messages:"), - GAIM_PREF_STRING, "/plugins/gtk/docklet/blink_chat", - _("Never"), "never", - _("When my nick is said"), "nick", - _("Always"), "always", - NULL); - gtk_size_group_add_widget(sg, dd); - - gtk_widget_show_all(frame); - return frame; -} - -static GaimGtkPluginUiInfo ui_info = -{ - plugin_config_frame, - 0 /* page_num (Reserved) */ -}; - -static GaimPluginInfo info = -{ - GAIM_PLUGIN_MAGIC, - GAIM_MAJOR_VERSION, - GAIM_MINOR_VERSION, - GAIM_PLUGIN_STANDARD, /**< type */ - GAIM_GTK_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - GAIM_PRIORITY_DEFAULT, /**< priority */ - - DOCKLET_PLUGIN_ID, /**< id */ - N_("System Tray Icon"), /**< name */ - VERSION, /**< version */ - /** summary */ - N_("Displays an icon for Gaim in the system tray."), - /** description */ - N_("Displays a system tray icon (in GNOME, KDE, or Windows for example) " - "to show the current status of Gaim, allow fast access to commonly " - "used functions, and to toggle display of the buddy list. " - "Also provides options to blink for unread messages."), - /** author */ - "Robert McQueen " - "\n\t\t\tCasey Harkins ", - GAIM_WEBSITE, /**< homepage */ - - plugin_load, /**< load */ - plugin_unload, /**< unload */ - NULL, /**< destroy */ - - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, - NULL -}; - -static void -plugin_init(GaimPlugin *plugin) -{ - gaim_prefs_add_none("/plugins/gtk/docklet"); - gaim_prefs_add_string("/plugins/gtk/docklet/blink_im", "hidden"); - gaim_prefs_add_string("/plugins/gtk/docklet/blink_chat", "never"); -} - -GAIM_INIT_PLUGIN(docklet, plugin_init, info) diff -r fa285d018c71 -r 2c1781ea074c gtk/plugins/docklet/docklet.h --- a/gtk/plugins/docklet/docklet.h Wed Oct 04 20:31:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * System tray icon (aka docklet) plugin for Gaim - * - * Copyright (C) 2002-3 Robert McQueen - * Copyright (C) 2003 Herman Bloggs - * Inspired by a similar plugin by: - * John (J5) Palmieri - * - * 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. - */ - -#ifndef _DOCKLET_H_ -#define _DOCKLET_H_ - -typedef enum -{ - DOCKLET_STATUS_OFFLINE, - DOCKLET_STATUS_ONLINE, - DOCKLET_STATUS_ONLINE_PENDING, - DOCKLET_STATUS_AWAY, - DOCKLET_STATUS_AWAY_PENDING, - DOCKLET_STATUS_CONNECTING -} DockletStatus; - -struct docklet_ui_ops -{ - void (*create)(void); - void (*destroy)(void); - void (*update_icon)(DockletStatus); - void (*blank_icon)(void); - void (*set_tooltip)(gchar *); - GtkMenuPositionFunc position_menu; -}; - -/* useful for setting idle callbacks that will be cleaned up */ -extern GaimPlugin *handle; - -/* functions in docklet.c */ -void docklet_clicked(int); -void docklet_embedded(void); -void docklet_remove(void); -void docklet_set_ui_ops(struct docklet_ui_ops *); -void docklet_unload(void); - -/* function in docklet-{x11,win32}.c */ -void docklet_ui_init(void); - -#endif /* _DOCKLET_H_ */ diff -r fa285d018c71 -r 2c1781ea074c gtk/plugins/docklet/eggtrayicon.c --- a/gtk/plugins/docklet/eggtrayicon.c Wed Oct 04 20:31:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,572 +0,0 @@ -/* -*- 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" - -#include -#include - -#define _(x) x -#define N_(x) x - -#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_realize (GtkWidget *widget); -static void egg_tray_icon_unrealize (GtkWidget *widget); - -static void egg_tray_icon_add (GtkContainer *container, - GtkWidget *widget); - -static void egg_tray_icon_update_manager_window (EggTrayIcon *icon, - gboolean dock_if_realized); -static void egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon); - -GType -egg_tray_icon_get_type (void) -{ - static GType our_type = 0; - - if (our_type == 0) - { - our_type = g_type_from_name("EggTrayIcon"); - - 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, - NULL /* value_table */ - }; - - our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0); - } - else if (parent_class == NULL) - { - /* we're reheating the old class from a previous instance - engage ugly hack =( */ - egg_tray_icon_class_init((EggTrayIconClass *)g_type_class_peek(our_type)); - } - } - - 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)); -} - -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; - } -} - -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_return_if_fail(icon->manager_window != None); - -#if GTK_CHECK_VERSION(2,1,0) - xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); -#else - xdisplay = gdk_display; -#endif - - 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; -} - -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 GTK_CHECK_VERSION(2,1,0) - 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 GTK_CHECK_VERSION(2,1,0) - root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); -#else - root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ()); -#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_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 GTK_CHECK_VERSION(2,1,0) - 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, - gboolean dock_if_realized) -{ - Display *xdisplay; - - if (icon->manager_window != None) - return; - -#if GTK_CHECK_VERSION(2,1,0) - xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); -#else - xdisplay = gdk_display; -#endif - - 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; - -#if GTK_CHECK_VERSION(2,1,0) - 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); - - if (dock_if_realized && GTK_WIDGET_REALIZED (icon)) - egg_tray_icon_send_dock_request (icon); - - egg_tray_icon_get_orientation_property (icon); - } -} - -static void -egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon) -{ - GdkWindow *gdkwin; - - g_return_if_fail (icon->manager_window != None); - -#if GTK_CHECK_VERSION(2,1,0) - 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); - - icon->manager_window = None; - - egg_tray_icon_update_manager_window (icon, TRUE); -} - -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_realize (GtkWidget *widget) -{ - EggTrayIcon *icon = EGG_TRAY_ICON (widget); - gint 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); - -#if GTK_CHECK_VERSION(2,1,0) - screen = gdk_screen_get_number (gtk_widget_get_screen (widget)); - xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)); -#else - screen = XScreenNumberOfScreen (DefaultScreenOfDisplay (gdk_display)); - xdisplay = gdk_display; -#endif - - /* Now see if there's a manager window around */ - g_snprintf (buffer, sizeof (buffer), - "_NET_SYSTEM_TRAY_S%d", - 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); - -#if GTK_CHECK_VERSION(2,1,0) - root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); -#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); -} - -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); -} - -#if GTK_CHECK_VERSION(2,1,0) -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); -} -#endif - -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); - - 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 GTK_CHECK_VERSION(2,1,0) - 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); -} - -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 -r fa285d018c71 -r 2c1781ea074c gtk/plugins/docklet/eggtrayicon.h --- a/gtk/plugins/docklet/eggtrayicon.h Wed Oct 04 20:31:44 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 -#include -#include - -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; - Atom orientation_atom; - Window manager_window; - - GtkOrientation orientation; -}; - -struct _EggTrayIconClass -{ - GtkPlugClass parent_class; -}; - -GType egg_tray_icon_get_type (void); - -#if GTK_CHECK_VERSION(2,1,0) -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); - -GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon); - -G_END_DECLS - -#endif /* __EGG_TRAY_ICON_H__ */ diff -r fa285d018c71 -r 2c1781ea074c gtk/win32/MinimizeToTray.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/win32/MinimizeToTray.c Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,119 @@ +/* MinimizeToTray + * + * A couple of routines to show how to make it produce a custom caption + * animation to make it look like we are minimizing to and maximizing + * from the system tray + * + * These routines are public domain, but it would be nice if you dropped + * me a line if you use them! + * + * 1.0 29.06.2000 Initial version + * 1.1 01.07.2000 The window retains it's place in the Z-order of windows + * when minimized/hidden. This means that when restored/shown, it doesn't + * always appear as the foreground window unless we call SetForegroundWindow + * + * Copyright 2000 Matthew Ellis + */ +#define _WIN32_WINNT 0x0500 +#include +#include "MinimizeToTray.h" + +#define DEFAULT_RECT_WIDTH 150 +#define DEFAULT_RECT_HEIGHT 30 + +static void GetTrayWndRect(LPRECT lpTrayRect) { + APPBARDATA appBarData; + HWND hShellTrayWnd = FindWindowEx(NULL, NULL, TEXT("Shell_TrayWnd"), + NULL); + + if(hShellTrayWnd) { + HWND hTrayNotifyWnd = FindWindowEx(hShellTrayWnd, NULL, + TEXT("TrayNotifyWnd"), NULL); + + if(hTrayNotifyWnd) { + GetWindowRect(hTrayNotifyWnd,lpTrayRect); + return; + } + } + + appBarData.cbSize = sizeof(appBarData); + if(SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData)) { + switch(appBarData.uEdge) { + case ABE_LEFT: + case ABE_RIGHT: + lpTrayRect->top = appBarData.rc.bottom - 100; + lpTrayRect->bottom = appBarData.rc.bottom - 16; + lpTrayRect->left = appBarData.rc.left; + lpTrayRect->right = appBarData.rc.right; + break; + case ABE_TOP: + case ABE_BOTTOM: + lpTrayRect->top = appBarData.rc.top; + lpTrayRect->bottom = appBarData.rc.bottom; + lpTrayRect->left = appBarData.rc.right - 100; + lpTrayRect->right = appBarData.rc.right - 16; + break; + } + return; + } + + hShellTrayWnd = FindWindowEx(NULL, NULL, TEXT("Shell_TrayWnd"), NULL); + if(hShellTrayWnd) { + GetWindowRect(hShellTrayWnd, lpTrayRect); + if(lpTrayRect->right-lpTrayRect->left > DEFAULT_RECT_WIDTH) + lpTrayRect->left = lpTrayRect->right - DEFAULT_RECT_WIDTH; + if(lpTrayRect->bottom-lpTrayRect->top > DEFAULT_RECT_HEIGHT) + lpTrayRect->top=lpTrayRect->bottom - DEFAULT_RECT_HEIGHT; + + return; + } + + SystemParametersInfo(SPI_GETWORKAREA, 0, lpTrayRect, 0); + lpTrayRect->left = lpTrayRect->right - DEFAULT_RECT_WIDTH; + lpTrayRect->top = lpTrayRect->bottom - DEFAULT_RECT_HEIGHT; +} + +static BOOL GetDoAnimateMinimize(void) { + ANIMATIONINFO ai; + + ai.cbSize = sizeof(ai); + SystemParametersInfo(SPI_GETANIMATION, sizeof(ai), &ai, 0); + + return ai.iMinAnimate ? TRUE : FALSE; +} + +void MinimizeWndToTray(HWND hWnd) { + + if(!IsWindowVisible(hWnd)) + return; + + if(GetDoAnimateMinimize()) { + RECT rcFrom, rcTo; + + GetWindowRect(hWnd, &rcFrom); + GetTrayWndRect(&rcTo); + + DrawAnimatedRects(hWnd, IDANI_CAPTION, &rcFrom, &rcTo); + } + + ShowWindow(hWnd, SW_HIDE); +} + +void RestoreWndFromTray(HWND hWnd) { + + if(IsWindowVisible(hWnd)) + return; + + if(GetDoAnimateMinimize()) { + RECT rcFrom, rcTo; + GetTrayWndRect(&rcFrom); + GetWindowRect(hWnd, &rcTo); + + DrawAnimatedRects(hWnd, IDANI_CAPTION, &rcFrom, &rcTo); + } + + ShowWindow(hWnd, SW_SHOW); + SetActiveWindow(hWnd); + SetForegroundWindow(hWnd); +} + diff -r fa285d018c71 -r 2c1781ea074c gtk/win32/MinimizeToTray.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/win32/MinimizeToTray.h Thu Oct 05 23:24:00 2006 +0000 @@ -0,0 +1,7 @@ +#ifndef _MINIMIZE_TO_TRAY_H_ +#define _MINIMIZE_TO_TRAY_H_ + +void MinimizeWndToTray(HWND hWnd); +void RestoreWndFromTray(HWND hWnd); + +#endif /* _MINIMIZE_TO_TRAY_H_ */ diff -r fa285d018c71 -r 2c1781ea074c libgaim/util.c --- a/libgaim/util.c Wed Oct 04 20:31:44 2006 +0000 +++ b/libgaim/util.c Thu Oct 05 23:24:00 2006 +0000 @@ -2577,7 +2577,7 @@ #endif } -gboolean +inline gboolean gaim_running_osx(void) { #if defined(__APPLE__)