grim/purple-plugin-pack
--leaks
org.guifications.plugins
2009-04-15, darkrain42
* Purple-XChat - Use XChat-like chats * Copyright (C) 2005-2008 * 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., 51 Franklin Street, Fifth Floor, Boston, MA /* If you can't figure out what this line is for, DON'T TOUCH IT. */ #include "../common/pp_internal.h" #define PLUGIN_ID "gtk-plugin_pack-xchat-chats" #define PLUGIN_NAME "xchat-chats" #define PLUGIN_AUTHOR "Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>" #define PREFS_PREFIX "/plugins/gtk/" PLUGIN_ID #define PREFS_DATE_FORMAT PREFS_PREFIX "/date_format" #include <conversation.h> static PurpleConversationUiOps *uiops = NULL; static void (*default_write_conv)(PurpleConversation *conv, const char *name, const char *alias, const char *message, PurpleMessageFlags flags, time_t mtime); static void (*default_create_conversation)(PurpleConversation *conv); static void (*default_destroy_conversation)(PurpleConversation *conv); static GtkWidget* hack_and_get_widget(PidginConversation *gtkconv); void palette_alloc (GtkWidget * widget); typedef struct _PurpleXChat PurpleXChat; static GHashTable *xchats = NULL; /* Hashtable of xchats */ static GdkColor colors[][2] = { { {0, 0x4c4c, 0x4c4c, 0x4c4c}, {0, 0x4c4c, 0x4c4c, 0x4c4c} }, /* Message sent */ { {0, 0x35c2, 0x35c2, 0xb332}, {0, 0, 0, 0} }, /* Message received */ { {0, 0xd9d9, 0xa6a6, 0x4141}, {0, 0xd9d9, 0xa6a6, 0x4141} }, /* System message */ { {0, 0xc7c7, 0x3232, 0x3232}, {0, 0xc7c7, 0x3232, 0x3232} }, /* Highlight message */ { {0, 0xc7c7, 0x3232, 0x3232}, {0, 0x4c4c, 0x4c4c, 0x4c4c} }, /* Error message */ {0, 0xcccc, 0xcccc, 0xcccc}, /* 16 white */ {0, 0x0000, 0x0000, 0x0000}, /* 17 black */ {0, 0x35c2, 0x35c2, 0xb332}, /* 18 blue */ {0, 0x2a3d, 0x8ccc, 0x2a3d}, /* 19 green */ {0, 0xc7c7, 0x3232, 0x3232}, /* 21 light red */ {0, 0x8000, 0x2666, 0x7fff}, /* 22 purple */ {0, 0xd999, 0xa6d3, 0x4147}, /* 24 yellow */ {0, 0x3d70, 0xcccc, 0x3d70}, /* 25 green */ {0, 0x199a, 0x5555, 0x5555}, /* 26 aqua */ {0, 0x2eef, 0x8ccc, 0x74df}, /* 27 light aqua */ {0, 0x451e, 0x451e, 0xe666}, /* 28 blue */ {0, 0x4c4c, 0x4c4c, 0x4c4c}, /* 30 grey */ {0, 0x9595, 0x9595, 0x9595}, /* 31 light grey */ {0, 0xcccc, 0xcccc, 0xcccc}, /* 16 white */ {0, 0x0000, 0x0000, 0x0000}, /* 17 black */ {0, 0x35c2, 0x35c2, 0xb332}, /* 18 blue */ {0, 0x2a3d, 0x8ccc, 0x2a3d}, /* 19 green */ {0, 0xc7c7, 0x3232, 0x3232}, /* 21 light red */ {0, 0x8000, 0x2666, 0x7fff}, /* 22 purple */ {0, 0xd999, 0xa6d3, 0x4147}, /* 24 yellow */ {0, 0x3d70, 0xcccc, 0x3d70}, /* 25 green */ {0, 0x199a, 0x5555, 0x5555}, /* 26 aqua */ {0, 0x2eef, 0x8ccc, 0x74df}, /* 27 light aqua */ {0, 0x451e, 0x451e, 0xe666}, /* 28 blue */ {0, 0x4c4c, 0x4c4c, 0x4c4c}, /* 30 grey */ {0, 0x9595, 0x9595, 0x9595}, /* 31 light grey */ {0, 0xffff, 0xffff, 0xffff}, /* 32 marktext Fore (white) */ {0, 0x3535, 0x6e6e, 0xc1c1}, /* 33 marktext Back (blue) */ {0, 0x0000, 0x0000, 0x0000}, /* 34 foreground (black) */ {0, 0xf0f0, 0xf0f0, 0xf0f0}, /* 35 background (white) */ {0, 0xcccc, 0x1010, 0x1010}, /* 36 marker line (red) */ {0, 0x9999, 0x0000, 0x0000}, /* 37 tab New Data (dark red) */ {0, 0x0000, 0x0000, 0xffff}, /* 38 tab Nick Mentioned (blue) */ {0, 0xffff, 0x0000, 0x0000}, /* 39 tab New Message (red) */ {0, 0x9595, 0x9595, 0x9595}, /* 40 away user (grey) */ /* check if a word is clickable */ mg_word_check (GtkWidget * xtext, char *word, int len) session *sess = current_sess; ret = url_check_word (word, len); /* common/url.c */ if (( (word[0]=='@' || word[0]=='+') && userlist_find (sess, word+1)) || userlist_find (sess, word)) if (sess->type == SESS_DIALOG) return (purple_version_check(2, 4, 0) == NULL); static GtkWidget *get_xtext(PurpleConversation *conv) if ((gx = g_hash_table_lookup(xchats, conv)) == NULL) PidginConversation *gtkconv; GtkWidget *imhtml = NULL; gtkconv = PIDGIN_CONVERSATION(conv); imhtml = gtkconv->imhtml; style = gtk_widget_get_style(imhtml); palette_alloc(pidgin_conv_get_window(gtkconv)->window); gx = g_new0(PurpleXChat, 1); xtext = gtk_xtext_new(colors, TRUE); /* TODO: Figure out a way to set the colors correctly */ gtk_xtext_set_foreground_color(GTK_XTEXT(xtext), &style->text[0]); gtk_xtext_set_background_color(GTK_XTEXT(xtext), &style->base[0]); gtk_xtext_set_indent(GTK_XTEXT(xtext), TRUE); gtk_xtext_set_max_indent(GTK_XTEXT(xtext), 200); gx->imhtml = hack_and_get_widget(gtkconv); if (!gtk_xtext_set_font(GTK_XTEXT(xtext), pango_font_description_to_string(style->font_desc))) g_hash_table_insert(xchats, conv, gx); palette_alloc (GtkWidget * widget) static int done_alloc = FALSE; if (!done_alloc) /* don't do it again */ cmap = gtk_widget_get_colormap (widget); for (i = G_N_ELEMENTS(colors)-1; i >= 0; i--) gdk_colormap_alloc_color (cmap, &colors[i][0], FALSE, TRUE); gdk_colormap_alloc_color (cmap, &colors[i][1], FALSE, TRUE); static void purple_xchat_write_conv(PurpleConversation *conv, const char *name, const char *alias, const char *message, PurpleMessageFlags flags, time_t mtime) PurpleConversationType type; PurpleXChatMessage col = 0; /* Do the usual stuff first. */ default_write_conv(conv, name, alias, message, flags, mtime); type = purple_conversation_get_type(conv); if (type != PURPLE_CONV_TYPE_CHAT) /* If it's IM, we have nothing to do. */ /* So it's a chat. Let's play. */ if (name == NULL || !strcmp(name, purple_conversation_get_name(conv))) msg = purple_markup_strip_html(message); if (msg && msg[0] == '/' && msg[1] == 'm' && msg[2] == 'e' && msg[3] == ' ') msg = g_strdup_printf("%s%s", name, tmp+3); if (flags & PURPLE_MESSAGE_SEND) else if (flags & PURPLE_MESSAGE_RECV) if (flags & PURPLE_MESSAGE_NICK) else if (flags & PURPLE_MESSAGE_ERROR) else if ((flags & PURPLE_MESSAGE_SYSTEM) || (flags & PURPLE_MESSAGE_NO_LOG)) gtk_xtext_append_indent(GTK_XTEXT(xtext)->buffer, (guchar*)name, strlen(name), colors[col][0].pixel, (guchar*)msg, strlen(msg), colors[col][1].pixel); name = G_OBJECT_TYPE_NAME(x); \ hack_and_get_widget(PidginConversation *gtkconv) GtkWidget *tab_cont, *vbox, *hpaned, *frame; /* If you think this is ugly, you are right. */ tab_cont = gtkconv->tab_cont; list = gtk_container_get_children(GTK_CONTAINER(tab_cont)); if (!is_2_4_0_or_above()) { GtkWidget *pane = list->data; vbox = GTK_PANED(pane)->child1; list = GTK_BOX(vbox)->children; if (GTK_IS_PANED(((GtkBoxChild*)list->data)->widget)) hpaned = ((GtkBoxChild*)list->data)->widget; frame = GTK_PANED(hpaned)->child1; purple_conversation_use_xtext(PurpleConversation *conv) PidginConversation *gtkconv; GtkWidget *parent, *box, *wid, *frame, *xtext; if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT) gtkconv = PIDGIN_CONVERSATION(conv); frame = hack_and_get_widget(gtkconv); gtk_widget_hide_all(frame); box = gtk_hbox_new(FALSE, 0); GTK_PANED(parent)->child1 = NULL; gtk_paned_pack1(GTK_PANED(parent), box, TRUE, TRUE); gtk_box_pack_start(GTK_BOX(box), xtext, TRUE, TRUE, 0); wid = gtk_vscrollbar_new(GTK_XTEXT(xtext)->adj); gtk_box_pack_start(GTK_BOX(box), wid, FALSE, FALSE, 0); GTK_WIDGET_UNSET_FLAGS(wid, GTK_CAN_FOCUS); gtk_widget_show_all(box); gtk_widget_realize(xtext); purple_xchat_create_conv(PurpleConversation *conv) default_create_conversation(conv); purple_conversation_use_xtext(conv); purple_xchat_destroy_conv(PurpleConversation *conv) default_destroy_conversation(conv); gx = g_hash_table_lookup(xchats, conv); g_hash_table_remove(xchats, conv); workaround_for_hidden_convs(PidginConversation *gtkconv) PurpleConversation *conv = gtkconv->active_conv; if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT || g_hash_table_lookup(xchats, conv)) purple_conversation_use_xtext(conv); plugin_load(PurplePlugin *plugin) uiops = pidgin_conversations_get_conv_ui_ops(); /* Use the oh-so-useful uiops. Signals? bleh. */ default_write_conv = uiops->write_conv; uiops->write_conv = purple_xchat_write_conv; default_create_conversation = uiops->create_conversation; uiops->create_conversation = purple_xchat_create_conv; default_destroy_conversation = uiops->destroy_conversation; uiops->destroy_conversation = purple_xchat_destroy_conv; xchats = g_hash_table_new(g_direct_hash, g_direct_equal); list = purple_get_chats(); /* TODO: We can get the message history of the conversation and populate * the next xtext with that data. * Note: purple_conversation_get_message_history purple_conversation_use_xtext(list->data); purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", plugin, G_CALLBACK(workaround_for_hidden_convs), NULL); static void remove_xtext(PurpleConversation *conv, PurpleXChat *gx, gpointer null) GtkWidget *frame, *parent; frame = gx->xtext->parent; GTK_PANED(parent)->child1 = NULL; gx->imhtml->parent = NULL; gtk_paned_add1(GTK_PANED(parent), gx->imhtml); g_object_unref(gx->imhtml); gtk_widget_destroy(frame); gtk_widget_show_all(gx->imhtml); plugin_unload(PurplePlugin *plugin) /* Restore the default ui-ops */ uiops->write_conv = default_write_conv; uiops->create_conversation = default_create_conversation; uiops->destroy_conversation = default_destroy_conversation; /* Clear up everything */ g_hash_table_foreach(xchats, (GHFunc)remove_xtext, NULL); g_hash_table_destroy(xchats); static PurplePluginInfo info = PURPLE_PLUGIN_MAGIC, /* Magic */ PURPLE_MAJOR_VERSION, /* Purple Major Version */ PURPLE_MINOR_VERSION, /* Purple Minor Version */ PURPLE_PLUGIN_STANDARD, /* plugin type */ PIDGIN_PLUGIN_TYPE, /* ui requirement */ PURPLE_PRIORITY_DEFAULT, /* priority */ PLUGIN_ID, /* plugin id */ PP_VERSION, /* version */ PLUGIN_AUTHOR, /* author */ PP_WEBSITE, /* website */ plugin_unload, /* unload */ init_plugin(PurplePlugin *plugin) { bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); info.name = _("XChat Chats"); info.summary = _("XChat-like chats with Pidgin"); info.description = _("You can chat in Pidgin using XChat's indented view."); purple_prefs_add_none(PREFS_PREFIX); purple_prefs_add_string(PREFS_DATE_FORMAT, "[%H:%M]"); PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)