Wed, 10 Dec 2008 04:08:40 -0500
Added support for 'or' dependencies
/* * 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 * 02111-1301, USA. */ /* 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" /* System headers */ #include <string.h> #include <gdk/gdk.h> #include <gtk/gtk.h> /* Purple headers */ #include <conversation.h> #include <util.h> /* Pidgin headers */ #include <gtkconv.h> #include <gtkplugin.h> #include "xtext.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; struct _PurpleXChat { GtkWidget *imhtml; GtkWidget *xtext; }; static GHashTable *xchats = NULL; /* Hashtable of xchats */ typedef enum { GX_SEND, GX_RECV, GX_SYSTEM, GX_HIGHLIGHT, GX_ERROR }PurpleXChatMessage; static GdkColor colors[][2] = { /* colors for xtext */ { {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 */ }; #if 0 {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) */ /* colors for GUI */ {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) */ }; #endif #if 0 /* check if a word is clickable */ static int mg_word_check (GtkWidget * xtext, char *word, int len) { session *sess = current_sess; int ret; ret = url_check_word (word, len); /* common/url.c */ if (ret == 0) { if (( (word[0]=='@' || word[0]=='+') && userlist_find (sess, word+1)) || userlist_find (sess, word)) return WORD_NICK; if (sess->type == SESS_DIALOG) return WORD_DIALOG; } return ret; } #endif static gboolean is_2_4_0_or_above(void) { return (purple_version_check(2, 4, 0) == NULL); } static GtkWidget *get_xtext(PurpleConversation *conv) { PurpleXChat *gx; if ((gx = g_hash_table_lookup(xchats, conv)) == NULL) { PidginConversation *gtkconv; GtkWidget *xtext; GtkWidget *imhtml = NULL; GtkStyle *style; gtkconv = PIDGIN_CONVERSATION(conv); if (!gtkconv) return NULL; 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->xtext = xtext; gx->imhtml = hack_and_get_widget(gtkconv); if (!gtk_xtext_set_font(GTK_XTEXT(xtext), pango_font_description_to_string(style->font_desc))) return NULL; g_hash_table_insert(xchats, conv, gx); } return gx->xtext; } void palette_alloc (GtkWidget * widget) { int i; static int done_alloc = FALSE; GdkColormap *cmap; if (!done_alloc) /* don't do it again */ { done_alloc = TRUE; 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; GtkWidget *xtext; char *msg; 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. */ return; } /* So it's a chat. Let's play. */ xtext = get_xtext(conv); if (name == NULL || !strcmp(name, purple_conversation_get_name(conv))) name = "*"; msg = purple_markup_strip_html(message); if (msg && msg[0] == '/' && msg[1] == 'm' && msg[2] == 'e' && msg[3] == ' ') { char *tmp = msg; msg = g_strdup_printf("%s%s", name, tmp+3); g_free(tmp); name = "*"; } if (flags & PURPLE_MESSAGE_SEND) col = GX_SEND; else if (flags & PURPLE_MESSAGE_RECV) { if (flags & PURPLE_MESSAGE_NICK) col = GX_HIGHLIGHT; else col = GX_RECV; } else if (flags & PURPLE_MESSAGE_ERROR) col = GX_ERROR; else if ((flags & PURPLE_MESSAGE_SYSTEM) || (flags & PURPLE_MESSAGE_NO_LOG)) col = GX_SYSTEM; gtk_xtext_append_indent(GTK_XTEXT(xtext)->buffer, (guchar*)name, strlen(name), colors[col][0].pixel, (guchar*)msg, strlen(msg), colors[col][1].pixel); g_free(msg); } #define DEBUG_INFO(x) \ name = G_OBJECT_TYPE_NAME(x); \ printf("%s\n", name) static GtkWidget* hack_and_get_widget(PidginConversation *gtkconv) { GtkWidget *tab_cont, *vbox, *hpaned, *frame; GList *list; const char *name; /* If you think this is ugly, you are right. */ tab_cont = gtkconv->tab_cont; DEBUG_INFO(tab_cont); list = gtk_container_get_children(GTK_CONTAINER(tab_cont)); if (!is_2_4_0_or_above()) { GtkWidget *pane = list->data; DEBUG_INFO(pane); vbox = GTK_PANED(pane)->child1; } else { vbox = list->data; } g_list_free(list); DEBUG_INFO(vbox); list = GTK_BOX(vbox)->children; while (list) { if (GTK_IS_PANED(((GtkBoxChild*)list->data)->widget)) break; list = list->next; } hpaned = ((GtkBoxChild*)list->data)->widget; DEBUG_INFO(hpaned); frame = GTK_PANED(hpaned)->child1; return frame; } static void purple_conversation_use_xtext(PurpleConversation *conv) { PidginConversation *gtkconv; GtkWidget *parent, *box, *wid, *frame, *xtext; if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT) return; gtkconv = PIDGIN_CONVERSATION(conv); if (!gtkconv) return; frame = hack_and_get_widget(gtkconv); parent = frame->parent; gtk_widget_hide_all(frame); g_object_ref(frame); box = gtk_hbox_new(FALSE, 0); xtext = get_xtext(conv); 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); } static void purple_xchat_create_conv(PurpleConversation *conv) { default_create_conversation(conv); purple_conversation_use_xtext(conv); } static void purple_xchat_destroy_conv(PurpleConversation *conv) { PurpleXChat *gx; default_destroy_conversation(conv); gx = g_hash_table_lookup(xchats, conv); if (gx) { g_free(gx); g_hash_table_remove(xchats, conv); } } #if 0 static void 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)) return; purple_conversation_use_xtext(conv); } #endif static gboolean plugin_load(PurplePlugin *plugin) { GList *list; uiops = pidgin_conversations_get_conv_ui_ops(); if (uiops == NULL) return FALSE; /* 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(); while (list) { /* 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); list = list->next; } #if 0 purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed", plugin, G_CALLBACK(workaround_for_hidden_convs), NULL); #endif return TRUE; } static void remove_xtext(PurpleConversation *conv, PurpleXChat *gx, gpointer null) { GtkWidget *frame, *parent; frame = gx->xtext->parent; parent = frame->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); } static gboolean 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); return TRUE; } 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 */ 0, /* flags */ NULL, /* dependencies */ PURPLE_PRIORITY_DEFAULT, /* priority */ PLUGIN_ID, /* plugin id */ NULL, /* name */ PP_VERSION, /* version */ NULL, /* summary */ NULL, /* description */ PLUGIN_AUTHOR, /* author */ PP_WEBSITE, /* website */ plugin_load, /* load */ plugin_unload, /* unload */ NULL, /* destroy */ NULL, /* ui_info */ NULL, /* extra_info */ NULL, /* prefs_info */ NULL, /* actions */ NULL, /* reserved 1 */ NULL, /* reserved 2 */ NULL, /* reserved 3 */ NULL /* reserved 4 */ }; static void init_plugin(PurplePlugin *plugin) { #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); #endif /* ENABLE_NLS */ 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)