grim/guifications1

5cd3ff5f7164
Parents
Children db564d18d780
hopefully i can test on windows again...
  • +56 -0
    gf_utils.h
  • +626 -0
    guifications.c
  • --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/gf_utils.h Sat Dec 27 14:14:38 2003 +0000
    @@ -0,0 +1,56 @@
    +/*
    + Guifications - The notification plugin to end all notification plugins!
    + Copyright (C) 2003 Gary Kramlich
    +
    + 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 GF_UTILS_H
    +#define GF_UTILS_H
    +
    +#include <glib.h>
    +#include <pango/pango.h>
    +#include <gtk/gtk.h>
    +
    +#include "guifications.h"
    +
    +/***********************************************************************
    + structs
    +***********************************************************************/
    +typedef struct _dimensions {
    + guint height;
    + guint width;
    +} dimensions;
    +/***********************************************************************
    + Prototypes
    +***********************************************************************/
    +void gf_resize(GtkWidget *window, GtkWidget *box);
    +gint get_window_x(GtkWidget *window);
    +gint get_window_y(GtkWidget *window);
    +gint get_window_width(GtkWidget *box);
    +gint get_window_height(GtkWidget *box);
    +void get_image_size(dimensions *image_size);
    +void get_zoom_size(dimensions image_size, dimensions *zoom_size);
    +void clip_layout(PangoLayout **layout, gboolean use_color, const gchar *text_x, const gchar *text_color);
    +void set_color(PangoLayout **layout, gboolean custom_color, const gchar *pref);
    +gchar *set_text_color(const gchar *text, const gchar *pref);
    +gchar *g_strrndup(const gchar *src, gint n);
    +gint get_icon_size();
    +gboolean is_even(gint number);
    +void get_text_pos(gint *x, gint *y, gboolean line, PangoLayout *layout);
    +void get_icon_pos(gint *x, gint *y);
    +GdkPixbuf *get_proto_icon(GaimBuddy *buddy);
    +/**********************************************************************/
    +
    +#endif
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/guifications.c Sat Dec 27 14:14:38 2003 +0000
    @@ -0,0 +1,626 @@
    +/*
    + Guifications - The notification plugin to end all notification plugins!
    + Copyright (C) 2003 Gary Kramlich
    +
    + 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 <glib.h>
    +#include <gtk/gtk.h>
    +#include <gdk/gdk.h>
    +#include <gdk-pixbuf/gdk-pixbuf.h>
    +#include <pango/pango.h>
    +
    +#include "internal.h"
    +#include "gaim.h"
    +#include "plugin.h"
    +#include "gtkplugin.h"
    +#include "gtklog.h"
    +#include "gtkpounce.h"
    +#include "gtkutils.h"
    +#include "prefs.h"
    +#include "account.h"
    +#include "conversation.h"
    +#include "blist.h"
    +#include "debug.h"
    +#include "server.h"
    +#include "stock.h"
    +#include "multi.h"
    +#include "util.h"
    +#include "notify.h"
    +
    +#include "guifications.h"
    +#include "gf_conf.h"
    +#include "gf_prefs.h"
    +#include "gf_utils.h"
    +#include "gf_pngs.h"
    +
    +/**********************************************************************
    + Globals
    +***********************************************************************/
    +GSList *connecting;
    +GtkWidget *gf_window = NULL;
    +GtkWidget *gf_box = NULL;
    +/***********************************************************************
    + Static prototypes
    +***********************************************************************/
    +static gboolean gf_load(GaimPlugin *plugin);
    +static gboolean gf_unload(GaimPlugin *plugin);
    +static void init_plugin(GaimPlugin *plugin);
    +
    +static void gf_account(GaimAccount *account, void *data);
    +static gint gf_rem_account(gpointer account);
    +static void gf_event(GaimBuddy *buddy, gpointer data);
    +static void make_gf_window();
    +static gint rem_guification(gpointer pguification);
    +static void destroy_guification(GtkWidget *guification);
    +
    +/* helpers */
    +static void gf_show_conv(GaimBuddy *buddy);
    +
    +/* Call backs */
    +static void gf_clicked(GtkWidget *widget, GdkEventButton *event_button, gpointer data);
    +static void gf_menu_info_cb(GtkWidget *widget, GaimBuddy *buddy);
    +static void gf_menu_im_cb(GtkWidget *widget, GaimBuddy *buddy);
    +static void gf_menu_pounce_cb(GtkWidget *widget, GaimBuddy *buddy);
    +static void gf_menu_log_cb(GtkWidget *widget, GaimBuddy *buddy);
    +static void gf_proto_menu_cb(GtkMenuItem *item, GaimBuddy *buddy);
    +static void gf_menu_destroy(GtkWidget *widget, gpointer data);
    +
    +static void gf_signed_on(GaimConnection *gc, void *data);
    +static void gf_release_check();
    +static void gf_release_check_cb(void *userdata, const char *data, size_t len);
    +/***********************************************************************
    + Plugin registration
    +***********************************************************************/
    +static void
    +init_plugin(GaimPlugin *plugin) {
    + gf_add_prefs();
    +}
    +/**********************************************************************/
    +static GaimGtkPluginUiInfo ui_info = { get_config_frame };
    +/**********************************************************************/
    +static GaimPluginInfo gf_info = {
    + 2, /* api version */
    + GAIM_PLUGIN_STANDARD, /* type */
    + GAIM_GTK_PLUGIN_TYPE, /* ui requirement */
    + 0, /* flags */
    + NULL, /* dependencies */
    + GAIM_PRIORITY_DEFAULT, /* priority */
    +
    + my_id, /* id */
    + my_name, /* name */
    + my_version, /* version */
    + my_summary, /* summary */
    + my_description, /* description */
    +
    + my_authors, /* author */
    + my_homepage, /* homepage */
    +
    + gf_load, /* load */
    + gf_unload, /* unload */
    + NULL, /* destroy */
    +
    + &ui_info, /* ui info */
    + NULL /* extra info */
    +};
    +/**********************************************************************/
    +GAIM_INIT_PLUGIN(my_name, init_plugin, gf_info)
    +/**********************************************************************/
    +static gboolean
    +gf_load(GaimPlugin *plugin) {
    + void *blist = gaim_blist_get_handle();
    +
    + gaim_signal_connect(gaim_accounts_get_handle(), "account-connecting", plugin, GAIM_CALLBACK(gf_account), NULL);
    + gaim_signal_connect(gaim_connections_get_handle(), "signed-on", plugin, GAIM_CALLBACK(gf_signed_on), NULL);
    +
    + gaim_signal_connect(blist, "buddy-signed-on", plugin, GAIM_CALLBACK(gf_event), (gpointer)event_signon);
    + gaim_signal_connect(blist, "buddy-signed-off", plugin, GAIM_CALLBACK(gf_event), (gpointer)event_signoff);
    + gaim_signal_connect(blist, "buddy-away", plugin, GAIM_CALLBACK(gf_event), (gpointer)event_away);
    + gaim_signal_connect(blist, "buddy-back", plugin, GAIM_CALLBACK(gf_event), (gpointer)event_back);
    + gaim_signal_connect(blist, "buddy-idle", plugin, GAIM_CALLBACK(gf_event), (gpointer)event_idle);
    + gaim_signal_connect(blist, "buddy-unidle", plugin, GAIM_CALLBACK(gf_event), (gpointer)event_unidle);
    +
    + if (gaim_connections_get_all())
    + gf_release_check();
    +
    + return TRUE;
    +}
    +/**********************************************************************/
    +static gboolean
    +gf_unload(GaimPlugin *plugin) {
    + g_slist_free(connecting);
    +
    + return TRUE;
    +}
    +/**********************************************************************/
    +static void
    +gf_event(GaimBuddy *buddy, gpointer data) {
    + gf_guification_event gf_new_event = (gf_guification_event)data;
    + GtkWidget *new_guification, *new_guification_box;
    + GdkPixbuf *pb_guification;
    + GdkPixmap *pm_guification;
    + gboolean vertical;
    + guint timeout_id = 0;
    + dimensions img_size, zoom;
    +
    + if (g_slist_find(connecting, buddy->account) != NULL)
    + return;
    +
    + /* snakez */
    + if (!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOWAWAY))
    + if (buddy->account->gc->away_state != NULL)
    + return;
    +
    + switch (gf_new_event) {
    + case event_signon:
    + if (!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_SIGN_ON))
    + return;
    + break;
    + case event_signoff:
    + if (!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_SIGN_OFF))
    + return;
    + break;
    + case event_away:
    + if (!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_AWAY))
    + return;
    + break;
    + case event_back:
    + if (!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_BACK))
    + return;
    + break;
    + case event_idle:
    + if (!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_IDLE))
    + return;
    + break;
    + case event_unidle:
    + if (!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_UNIDLE))
    + return;
    + break;
    + }
    +
    + if (gf_window == NULL)
    + make_gf_window();
    +
    + get_image_size(&img_size);
    + pm_guification = make_guification(buddy, gf_new_event);
    +
    + get_zoom_size(img_size, &zoom);
    +
    + pb_guification = gdk_pixbuf_get_from_drawable(NULL, pm_guification, NULL, 0, 0, 0, 0, -1, -1);
    + g_object_unref(pm_guification);
    +
    + if (zoom.width != img_size.width || zoom.height != img_size.height)
    + pb_guification = gdk_pixbuf_scale_simple(pb_guification, zoom.width, zoom.height, GDK_INTERP_BILINEAR);
    +
    + new_guification = gtk_image_new_from_pixbuf(pb_guification);
    + g_object_unref(pb_guification);
    +
    + gtk_widget_set_size_request(new_guification, zoom.width, zoom.height);
    +
    + new_guification_box = gtk_event_box_new();
    + gtk_widget_set_events(new_guification_box, GDK_BUTTON_PRESS);
    + g_object_set_data(G_OBJECT(new_guification_box), "buddy", buddy);
    + gtk_container_add(GTK_CONTAINER(new_guification_box), new_guification);
    +
    + vertical = gaim_prefs_get_bool(GF_PREF_BEHAVIOR_WINDOW_VERTICAL);
    +
    + switch (gaim_prefs_get_int(GF_PREF_BEHAVIOR_WINDOW_POS)) {
    + case gfpos_nw:
    + gtk_box_pack_start(GTK_BOX(gf_box), new_guification_box, FALSE, FALSE, 0);
    + break;
    + case gfpos_ne:
    + if (vertical)
    + gtk_box_pack_start(GTK_BOX(gf_box), new_guification_box, FALSE, FALSE, 0);
    + else
    + gtk_box_pack_end(GTK_BOX(gf_box), new_guification_box, FALSE, FALSE, 0);
    + break;
    + case gfpos_sw:
    + if (vertical)
    + gtk_box_pack_end(GTK_BOX(gf_box), new_guification_box, FALSE, FALSE, 0);
    + else
    + gtk_box_pack_start(GTK_BOX(gf_box), new_guification_box, FALSE, FALSE, 0);
    + break;
    + case gfpos_se:
    + gtk_box_pack_end(GTK_BOX(gf_box), new_guification_box, FALSE, FALSE, 0);
    + break;
    + }
    +
    + gtk_widget_show(new_guification);
    + gtk_widget_show(new_guification_box);
    +
    + gf_resize(gf_window, gf_box);
    +
    + g_signal_connect(G_OBJECT(new_guification_box), "button-press-event", G_CALLBACK(gf_clicked), new_guification_box);
    + timeout_id = gtk_timeout_add(gaim_prefs_get_int(GF_PREF_BEHAVIOR_TIMEOUT) * 1000, rem_guification, new_guification_box);
    + g_object_set_data(G_OBJECT(new_guification_box), "timeout", GUINT_TO_POINTER(timeout_id));
    +
    + if (!GTK_WIDGET_VISIBLE(gf_window))
    + gtk_widget_show_all(gf_window);
    +}
    +/**********************************************************************/
    +static gint
    +rem_guification(gpointer pguification) {
    + GtkWidget *guification = (GtkWidget*)pguification;
    +
    + if (guification != NULL)
    + destroy_guification(guification);
    +
    + return FALSE;
    +}
    +/**********************************************************************/
    +static void
    +destroy_guification(GtkWidget *guification) {
    + GList *gf_list = NULL;
    +
    + if (guification != NULL) {
    + gtk_widget_destroy(guification);
    + guification = NULL;
    +
    + if (gf_box != NULL) {
    + gf_list = gtk_container_get_children(GTK_CONTAINER(gf_box));
    +
    + if (gf_list == NULL) {
    + gtk_widget_destroy(gf_box);
    + gf_box = NULL;
    + gtk_widget_destroy(gf_window);
    + gf_window = NULL;
    + }
    + else
    + gf_resize(gf_window, gf_box);
    + g_list_free(gf_list);
    + }
    + }
    +}
    +/**********************************************************************/
    +static void
    +make_gf_window() {
    + dimensions def_size;
    +
    + gf_window = gtk_window_new(GTK_WINDOW_POPUP);
    +
    + get_image_size(&def_size);
    + gtk_window_set_default_size(GTK_WINDOW(gf_window), def_size.width, def_size.height);
    +
    + gtk_widget_realize(gf_window);
    +
    + if (gaim_prefs_get_bool(GF_PREF_BEHAVIOR_WINDOW_VERTICAL))
    + gf_box = gtk_vbox_new(FALSE, 0);
    + else
    + gf_box = gtk_hbox_new(FALSE, 0);
    +
    + gtk_container_add(GTK_CONTAINER(gf_window), gf_box);
    + gtk_widget_show(gf_box);
    +}
    +/**********************************************************************/
    +GdkPixmap *
    +make_guification(GaimBuddy *buddy, gf_guification_event event) {
    + GdkPixbuf *background = NULL, *protocol = NULL;
    + GdkPixmap *memmap;
    + PangoContext *context;
    + PangoFontDescription *font_desc = NULL;
    + PangoLayout *layout_name, *layout_status;
    + gchar *name = "", *message = "";
    + gint x, y;
    + gint px, py;
    +
    + if (gaim_prefs_get_bool(GF_PREF_APPEARANCE_IMAGE))
    + background = gdk_pixbuf_new_from_file(gaim_prefs_get_string(GF_PREF_APPEARANCE_IMAGE_FILE), NULL);
    + else
    + background = get_def_background_image();
    +
    + gdk_pixbuf_render_pixmap_and_mask(background, &memmap, NULL, 100);
    + g_object_unref(background);
    +
    + if (gaim_prefs_get_int(GF_PREF_APPEARANCE_PROT_POS) != ipos_none) {
    + get_icon_pos(&px, &py);
    + protocol = get_proto_icon(buddy);
    + gdk_draw_pixbuf(GDK_DRAWABLE(memmap), gf_window->style->bg_gc[GTK_STATE_NORMAL], protocol, 0, 0, px, py, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0);
    + g_object_unref(protocol);
    + }
    +
    + if (gaim_prefs_get_bool(GF_PREF_BEHAVIOR_ALIAS))
    + name = g_strdup(gaim_get_buddy_alias(buddy));
    + else
    + name = g_strdup(buddy->name);
    +
    + /* get pango context and setup layouts */
    + context = gtk_widget_get_pango_context(gf_window);
    +
    + layout_name = pango_layout_new(context);
    + pango_layout_set_width(layout_name, -1);
    +
    + layout_status = pango_layout_new(context);
    + pango_layout_set_width(layout_status, -1);
    +
    +
    + if (gaim_prefs_get_bool(GF_PREF_APPEARANCE_FONT)) {
    + font_desc = pango_font_description_from_string(gaim_prefs_get_string(GF_PREF_APPEARANCE_FONT_FACE));
    + pango_layout_set_font_description(layout_name, font_desc);
    + pango_layout_set_font_description(layout_status, font_desc);
    + pango_font_description_free(font_desc);
    + }
    +
    + switch (event) {
    + case event_signon: message = g_strdup(gaim_prefs_get_string(GF_PREF_MESSAGES_SIGN_ON)); break;
    + case event_signoff: message = g_strdup(gaim_prefs_get_string(GF_PREF_MESSAGES_SIGN_OFF)); break;
    + case event_away: message = g_strdup(gaim_prefs_get_string(GF_PREF_MESSAGES_AWAY)); break;
    + case event_back: message = g_strdup(gaim_prefs_get_string(GF_PREF_MESSAGES_BACK)); break;
    + case event_idle: message = g_strdup(gaim_prefs_get_string(GF_PREF_MESSAGES_IDLE)); break;
    + case event_unidle: message = g_strdup(gaim_prefs_get_string(GF_PREF_MESSAGES_UNIDLE)); break;
    + }
    +
    + pango_layout_set_text(layout_name, name, -1);
    + pango_layout_set_text(layout_status, message, -1);
    +
    + g_free(name);
    + g_free(message);
    +
    + clip_layout(&layout_name, gaim_prefs_get_bool(GF_PREF_APPEARANCE_COLOR), GF_PREF_APPEARANCE_TEXT_X, GF_PREF_COLORS_FOREGROUND);
    + clip_layout(&layout_status, gaim_prefs_get_bool(GF_PREF_APPEARANCE_COLOR), GF_PREF_APPEARANCE_TEXT_X, GF_PREF_COLORS_FOREGROUND);
    +
    + /* draw buddy name */
    + get_text_pos(&x, &y, TRUE, layout_name);
    + gdk_draw_layout(GDK_DRAWABLE(memmap), gf_window->style->bg_gc[GTK_STATE_NORMAL], x, y, layout_name);
    + g_object_unref(layout_name);
    +
    + /* draw status */
    + get_text_pos(&x, &y, FALSE, layout_status);
    + gdk_draw_layout(GDK_DRAWABLE(memmap), gf_window->style->bg_gc[GTK_STATE_NORMAL], x, y, layout_status);
    + g_object_unref(layout_status);
    +
    + return memmap;
    +}
    +/**********************************************************************/
    +static void
    +gf_account(GaimAccount *account, void *data) {
    + if (g_slist_find(connecting, account) == NULL) {
    + connecting = g_slist_append(connecting, account);
    + gtk_timeout_add(gaim_prefs_get_int(GF_PREF_ADVANCED_DELAY), gf_rem_account, account);
    + }
    +}
    +/**********************************************************************/
    +static gint
    +gf_rem_account(void *paccount) {
    + GaimAccount *account = (GaimAccount*) paccount;
    +
    + if (account == NULL || account->gc == NULL) {
    + connecting = g_slist_remove(connecting, account);
    + return FALSE;
    + }
    +
    + if (account->gc->state == GAIM_CONNECTING)
    + return TRUE;
    +
    + connecting = g_slist_remove(connecting, account);
    + return FALSE;
    +}
    +/**********************************************************************/
    +static void
    +gf_clicked(GtkWidget *widget, GdkEventButton *event_button, gpointer data) {
    + GtkWidget *guification = (GtkWidget*)data, *menu, *item;
    + GList *list;
    + guint timeout_id = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(guification), "timeout"));
    + GaimBuddy *buddy = g_object_get_data(G_OBJECT(guification), "buddy");
    +
    + gboolean destroy = FALSE;
    + GaimPluginProtocolInfo *prplinfo = NULL;
    + gf_mouse action;
    +
    + switch (event_button->button) {
    + case 1: action = gaim_prefs_get_int(GF_PREF_BEHAVIOR_MOUSE1); break;
    + case 2: action = gaim_prefs_get_int(GF_PREF_BEHAVIOR_MOUSE2); break;
    + case 3: action = gaim_prefs_get_int(GF_PREF_BEHAVIOR_MOUSE3); break;
    + default: action = mouse_destroy; break;
    + }
    +
    + switch (action) {
    + case mouse_destroy:
    + destroy = TRUE;
    + break;
    + case mouse_info:
    + serv_get_info(buddy->account->gc, buddy->name);
    + destroy = TRUE;
    + break;
    + case mouse_conversation:
    + gf_show_conv(buddy);
    + destroy = TRUE;
    + break;
    + case mouse_log:
    + gaim_gtk_log_show(buddy->name, buddy->account);
    + destroy = TRUE;
    + break;
    + case mouse_context:
    + prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(gaim_find_prpl(gaim_account_get_protocol(buddy->account)));
    +
    + menu = gtk_menu_new();
    +
    + if (prplinfo && prplinfo->get_info)
    + gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gf_menu_info_cb), buddy, 0, 0, NULL);
    +
    + gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gf_menu_im_cb), buddy, 0, 0, NULL);
    + gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, G_CALLBACK(gf_menu_pounce_cb), buddy, 0, 0, NULL);
    + gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gf_menu_log_cb), buddy, 0, 0, NULL);
    +
    + if (prplinfo && prplinfo->buddy_menu) {
    + list = prplinfo->buddy_menu(buddy->account->gc, buddy->name);
    + while (list) {
    + struct proto_buddy_menu *pbm = list->data;
    +
    + item = gtk_menu_item_new_with_mnemonic(pbm->label);
    +
    + g_object_set_data(G_OBJECT(item), "gaimcallback", pbm);
    + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gf_proto_menu_cb), buddy);
    +
    + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    +
    + list = list->next;
    + }
    + }
    +
    + g_source_remove(timeout_id);
    + g_signal_connect(G_OBJECT(menu), "hide", G_CALLBACK(gf_menu_destroy), guification);
    +
    + gtk_widget_show_all(menu);
    + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
    +
    + destroy = FALSE;
    +
    + break;
    + case mouse_count:
    + default:
    + destroy = TRUE;
    + break;
    + }
    +
    + if (destroy)
    + if (g_source_remove(timeout_id)) /* destroy if timer was found, else let timer destroy */
    + destroy_guification(guification);
    +}
    +/**********************************************************************/
    +void
    +gf_destroy_all() {
    + GtkWidget *child = NULL;
    + GList *children = NULL;
    + guint timeout_id = 0;
    +
    + if (gf_box != NULL) {
    + children = gtk_container_get_children(GTK_CONTAINER(gf_box));
    +
    + while (children) {
    + child = children->data;
    + children = children->next;
    +
    + timeout_id = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(child), "timeout"));
    +
    + g_source_remove(timeout_id);
    + destroy_guification(child);
    + }
    +
    + g_list_free(children);
    + }
    +}
    +/**********************************************************************/
    +static void
    +gf_show_conv(GaimBuddy *buddy) {
    + GaimConversation *conv;
    + GaimConvWindow *win;
    +
    + conv = gaim_conversation_new(GAIM_CONV_IM, buddy->account, buddy->name);
    + win = gaim_conversation_get_window(conv);
    +
    + gaim_conv_window_raise(win);
    + gaim_conv_window_switch_conversation(gaim_conversation_get_window(conv), gaim_conversation_get_index(conv));
    +
    + if (GAIM_IS_GTK_WINDOW(win))
    + gtk_window_present(GTK_WINDOW(GAIM_GTK_WINDOW(win)->window));
    +}
    +/**********************************************************************/
    +static void
    +gf_menu_info_cb(GtkWidget *widget, GaimBuddy *buddy) {
    + serv_get_info(buddy->account->gc, buddy->name);
    +}
    +/**********************************************************************/
    +static void
    +gf_menu_im_cb(GtkWidget *widget, GaimBuddy *buddy) {
    + gf_show_conv(buddy);
    +}
    +/**********************************************************************/
    +static void
    +gf_menu_pounce_cb(GtkWidget *widget, GaimBuddy *buddy) {
    + gaim_gtkpounce_dialog_show(buddy->account, buddy->name, NULL);
    +}
    +/**********************************************************************/
    +static void
    +gf_menu_log_cb(GtkWidget *widget, GaimBuddy *buddy) {
    + gaim_gtk_log_show(buddy->name, buddy->account);
    +}
    +/**********************************************************************/
    +static void
    +gf_proto_menu_cb(GtkMenuItem *item, GaimBuddy *buddy) {
    + struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback");
    +
    + if (pbm->callback)
    + pbm->callback(pbm->gc, buddy->name);
    +}
    +/**********************************************************************/
    +static void
    +gf_menu_destroy(GtkWidget *widget, gpointer data) {
    + GtkWidget *guification = (GtkWidget*)data;
    + gint timeout = (gaim_prefs_get_int(GF_PREF_BEHAVIOR_TIMEOUT) * 1000) / 2;
    + guint timeout_id;
    +
    + timeout_id = gtk_timeout_add(timeout, rem_guification, guification);
    + g_object_set_data(G_OBJECT(guification), "timeout", GUINT_TO_POINTER(timeout_id));
    +}
    +/**********************************************************************/
    +static void
    +gf_signed_on(GaimConnection *gc, void *data) {
    + gf_release_check();
    +}
    +/**********************************************************************/
    +static void
    +gf_release_check() {
    + int last_checked = gaim_prefs_get_int(GF_PREF_ADVANCED_RELEASE_CHECK);
    + gchar *url;
    +
    + if (gaim_prefs_get_bool(GF_PREF_ADVANCED_RELEASE)) {
    + if (!last_checked || time(NULL) - last_checked > 86400 /* one day */) {
    + url = g_strdup_printf("http://guifications.sourceforge.net/version.php?version=%s", my_version);
    + gaim_url_fetch(url, TRUE, NULL, FALSE, gf_release_check_cb, NULL);
    + gaim_prefs_set_int(GF_PREF_ADVANCED_RELEASE_CHECK, time(NULL));
    + g_free(url);
    + }
    + }
    +}
    +/**********************************************************************/
    +static void
    +gf_release_check_cb(void *userdata, const char *data, size_t len) {
    + const gchar *changelog = data;
    + GString *notification;
    + gchar *cur_ver, *formatted;
    + gint i = 0;
    +
    + /* I wasn't trying to rip Nathan's code.. but I couldn't find a
    + better way to do it... */
    +
    + if (!changelog || !len)
    + return;
    +
    + while (changelog[i] && changelog[i] != '\n')
    + i++;
    +
    + cur_ver = g_strndup(changelog, i);
    + changelog += i;
    +
    + while (*changelog == '\n')
    + changelog++;
    +
    + notification = g_string_new("");
    + g_string_append_printf(notification, "There is a new version of Guifications (%s) available, you are running version %s.<hr>", cur_ver, my_version);
    +
    + if (*changelog) {
    + formatted = gaim_strdup_withhtml(changelog);
    + g_string_append_printf(notification, "<b>ChangeLog</b><br>%s<br>", formatted);
    + g_free(formatted);
    + }
    +
    + g_string_append_printf(notification, "You can download version %s from <a href=\"http://guifications.sourceforge.net/\">http://guifications.sourceforge.net</a>.", cur_ver);
    +
    + gaim_notify_formatted(NULL, "New version of Guifications available", "New version of Guifications available", NULL, notification->str, NULL, NULL);
    +
    + g_string_free(notification, TRUE);
    + g_free(cur_ver);
    +}
    +/**********************************************************************/