pidgin/purple-plugin-pack

4b64e3618c52
ChangeLog the removal of the irssi easter egg
/*
* Nicksaid - Record when someone said your nick in a chat.
* Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
*
* 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-nicksaid"
#define PLUGIN_STATIC_NAME "nicksaid"
#define PLUGIN_AUTHOR "Sadrul H Chowdhury <sadrul@users.sourceforge.net>"
#define PREF_PREFIX "/plugins/gtk/" PLUGIN_ID
#define PREF_HLWORDS PREF_PREFIX "/hlwords"
#define PREF_CHARS PREF_PREFIX "/chars"
#define PREF_TIMESTAMP PREF_PREFIX "/timestamp"
#define PREF_DATESTAMP PREF_PREFIX "/datestamp"
#define PREF_SHOWWHO PREF_PREFIX "/showwho"
#define PREF_SHOWALL PREF_PREFIX "/showall"
/* System headers */
#include <string.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>
/* libpurple headers */
#include <notify.h>
/* Pidgin headers */
#include <gtkconv.h>
#include <gtkimhtml.h>
#include <gtkmenutray.h>
#include <gtkplugin.h>
#include <gtkutils.h>
#include <pidginstock.h>
#define DELIMS " .,;|<>?/\\`~!@#$%^&*()_-+={}[]:'\""
static GList *hlwords = NULL; /* Words to highlight on */
#if 0
static void
go_next(GtkWidget *w, PidginConversation *gtkconv)
{
}
static void
go_previous(GtkWidget *w, PidginConversation *gtkconv)
{
}
#endif
typedef struct _NickSaid NickSaid;
struct _NickSaid
{
int offset;
char *who;
char *what;
};
/* <lift src="pidgin/src/util.c"> ??? */
static const gchar *
ns_time(void)
{
static gchar buf[80];
time_t tme;
time(&tme);
strftime(buf, sizeof(buf), "%H:%M:%S", localtime(&tme));
return buf;
}
static const gchar *
ns_date(void)
{
static gchar buf[80];
time_t tme;
time(&tme);
strftime(buf, sizeof(buf), "%Y-%m-%d", localtime(&tme));
return buf;
}
static const gchar *
ns_date_full(void)
{
gchar *buf;
time_t tme;
time(&tme);
buf = ctime(&tme);
buf[strlen(buf) - 1] = '\0';
return buf;
}
/* <lift/> */
struct _callbackdata
{
GtkTextView *view;
GtkTextIter iter;
};
static gboolean draw_line(GtkWidget *widget, GdkEventExpose *event, struct _callbackdata *data);
static gboolean
remove_line(struct _callbackdata *data)
{
if (g_signal_handlers_disconnect_matched(G_OBJECT(data->view), G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, data))
{
g_object_set_data(G_OBJECT(data->view), "nicksaid:mark", GINT_TO_POINTER(0));
gtk_widget_queue_draw(GTK_WIDGET(data->view));
g_free(data);
}
return FALSE;
}
static gboolean
draw_line(GtkWidget *widget, GdkEventExpose *event, struct _callbackdata *data)
{
GtkTextIter iter;
GdkRectangle rect, visible_rect;
int top, bottom, pad;
GdkGC *gc;
GtkTextView *view;
GdkColor green = {0, 0, 0xffff, 0};
view = data->view;
iter = data->iter;
gtk_text_view_get_iter_location(view, &iter, &rect);
pad = (gtk_text_view_get_pixels_below_lines(view) +
gtk_text_view_get_pixels_above_lines(view)) / 2;
top = rect.y - pad;
bottom = rect.y + rect.height + pad;
gtk_text_view_buffer_to_window_coords(view, GTK_TEXT_WINDOW_TEXT,
0, top, 0, &top);
gtk_text_view_buffer_to_window_coords(view, GTK_TEXT_WINDOW_TEXT,
0, bottom, 0, &bottom);
gtk_text_view_get_visible_rect(view, &visible_rect);
gc = gdk_gc_new(GDK_DRAWABLE(event->window));
gdk_gc_set_rgb_fg_color(gc, &green);
gdk_draw_line(event->window, gc, 0, top, visible_rect.width, top);
gdk_draw_line(event->window, gc, 0, bottom, visible_rect.width, bottom);
gdk_gc_unref(gc);
if (!g_object_get_data(G_OBJECT(view), "nicksaid:mark"))
{
g_timeout_add(2500, (GSourceFunc)remove_line, data);
g_object_set_data(G_OBJECT(view), "nicksaid:mark", GINT_TO_POINTER(1));
}
return FALSE;
}
static void
go_selected(GtkWidget *w, PidginConversation *gtkconv)
{
GtkTextIter iter;
int offset;
struct _callbackdata *data;
offset = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), "nicksaid:offset")) + 1;
gtk_text_buffer_get_iter_at_offset(GTK_IMHTML(gtkconv->imhtml)->text_buffer, &iter, offset);
gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(gtkconv->imhtml), &iter, 0, TRUE, 0, 0);
data = g_new0(struct _callbackdata, 1);
data->view = GTK_TEXT_VIEW(gtkconv->imhtml);
data->iter = iter;
g_signal_connect(G_OBJECT(gtkconv->imhtml), "expose_event", G_CALLBACK(draw_line), data);
gtk_widget_queue_draw(gtkconv->imhtml);
}
static void
clear_list(GtkWidget *w, PidginConversation *gtkconv)
{
GList *ll, *list;
ll = list = g_object_get_data(G_OBJECT(gtkconv->imhtml), "nicksaid:list");
while (list)
{
NickSaid *said = list->data;
g_free(said->who);
g_free(said->what);
g_free(said);
list = list->next;
}
g_list_free(ll);
g_object_set_data(G_OBJECT(gtkconv->imhtml), "nicksaid:list", NULL);
}
static void
show_all(GtkWidget *w, PidginConversation *gtkconv)
{
GList *list = g_object_get_data(G_OBJECT(gtkconv->imhtml), "nicksaid:list");
GString *str = g_string_new(NULL);
while (list)
{
NickSaid *said = list->data;
g_string_append_printf(str, "%s<br/>\n", said->what);
list = list->next;
}
purple_notify_formatted(gtkconv, _("Nicksaid"), _("List of highlighted messages:"),
NULL, str->str, NULL, NULL);
g_string_free(str, TRUE);
}
static gboolean
generate_popup(GtkWidget *w, GdkEventButton *event, PidginWindow *win)
{
GtkWidget *menu, *item;
PurpleConversation *conv;
PidginConversation *gtkconv;
GList *list;
conv = pidgin_conv_window_get_active_conversation(win);
if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
return FALSE;
menu = gtk_menu_new();
gtkconv = PIDGIN_CONVERSATION(conv);
list = g_object_get_data(G_OBJECT(gtkconv->imhtml), "nicksaid:list");
if (!list)
{
item = gtk_menu_item_new_with_label(_("None"));
gtk_widget_set_sensitive(item, FALSE);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
gtk_widget_show(item);
}
else
{
#if 0
item = gtk_menu_item_new_with_label(_("Next"));
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(go_next), gtkconv);
item = gtk_menu_item_new_with_label(_("Previous"));
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(go_previous), gtkconv);
pidgin_separator(menu);
#endif
while (list)
{
NickSaid *said = list->data;
item = gtk_menu_item_new_with_label(said->who);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
gtk_widget_show(item);
g_object_set_data(G_OBJECT(item), "nicksaid:offset",
GINT_TO_POINTER(said->offset));
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(go_selected), gtkconv);
/* TODO:
* If the line to scrollback to is greater /pidgin/gtk/conversations/scrollback_lines,
* desensitise the widget so it at least displays.. or something like that. you get the drift. */
list = list->next;
}
pidgin_separator(menu);
item = gtk_menu_item_new_with_label(_("Clear History"));
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(clear_list), gtkconv);
item = gtk_menu_item_new_with_label(_("Show All"));
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
if (purple_prefs_get_bool(PREF_SHOWALL))
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(show_all), gtkconv);
else
gtk_widget_set_sensitive(item, FALSE);
}
gtk_widget_show_all(menu);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button,
event->time);
return TRUE;
}
static gboolean
displaying_msg_cb(PurpleAccount *account, const char *name, char **buffer,
PurpleConversation *conv, PurpleMessageFlags flags, gpointer null)
{
PidginConversation *gtkconv;
GtkWidget *imhtml;
GtkTextIter iter;
GList *list;
int pos;
char *tmp, *who, *prefix = NULL;
NickSaid *said;
gboolean timestamp = purple_prefs_get_bool(PREF_TIMESTAMP);
gboolean datestamp = purple_prefs_get_bool(PREF_DATESTAMP);
gboolean showwho = purple_prefs_get_bool(PREF_SHOWWHO);
if (!(flags & PURPLE_MESSAGE_NICK) || !PIDGIN_IS_PIDGIN_CONVERSATION(conv) ||
purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
return FALSE;
gtkconv = PIDGIN_CONVERSATION(conv);
imhtml = gtkconv->imhtml;
gtk_text_buffer_get_end_iter(GTK_IMHTML(imhtml)->text_buffer, &iter);
pos = gtk_text_iter_get_offset(&iter);
list = g_object_get_data(G_OBJECT(imhtml), "nicksaid:list");
tmp = purple_markup_strip_html(*buffer);
who = g_strndup(tmp, purple_prefs_get_int(PREF_CHARS));
g_free(tmp);
if (!g_utf8_validate(who, -1, (const char **)&tmp))
*tmp = '\0';
if (showwho) {
tmp = who;
who = g_strdup_printf("%s: %s", name, tmp);
g_free(tmp);
}
if (datestamp && timestamp) {
prefix = g_strdup_printf("(%s) ", ns_date_full());
} else if (datestamp && !timestamp) {
prefix = g_strdup_printf("(%s) ", ns_date());
} else if (!datestamp && timestamp) {
prefix = g_strdup_printf("(%s) ", ns_time());
}
said = g_new0(NickSaid, 1);
said->offset = pos;
if (prefix != NULL) {
said->who = g_strdup_printf("%s%s", prefix, who);
g_free(who);
} else {
said->who = who;
}
if (purple_prefs_get_bool(PREF_SHOWALL))
{
said->what = g_strdup_printf("%s<b>%s: </b>%s",
prefix ? prefix : "", name, *buffer);
}
g_free(prefix);
list = g_list_prepend(list, said);
g_object_set_data(G_OBJECT(imhtml), "nicksaid:list", list);
return FALSE;
}
static GtkWidget *
get_tray_icon_for_window(PidginWindow *win)
{
GtkWidget *w = g_object_get_data(G_OBJECT(win->window), "nicksaid:trayicon");
if (w == NULL)
{
w = gtk_event_box_new();
#if GTK_CHECK_VERSION(2,4,0)
gtk_event_box_set_visible_window(GTK_EVENT_BOX(w), TRUE);
#endif
gtk_container_add(GTK_CONTAINER(w),
gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, GTK_ICON_SIZE_MENU));
pidgin_menu_tray_append(PIDGIN_MENU_TRAY(win->menu.tray), w, "Nicksaid");
gtk_widget_show_all(w);
g_object_set_data(G_OBJECT(win->window), "nicksaid:trayicon", w);
g_signal_connect(G_OBJECT(w), "button_press_event", G_CALLBACK(generate_popup), win);
}
return w;
}
static void
update_menu_tray(PurpleConversation *conv)
{
PidginConversation *gtkconv;
PidginWindow *win;
GtkWidget *icon;
if (!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
return;
gtkconv = PIDGIN_CONVERSATION(conv);
win = gtkconv->win;
if (!win)
return;
icon = get_tray_icon_for_window(win);
if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT)
gtk_widget_hide_all(icon);
else
gtk_widget_show_all(icon);
}
static void
deleting_conversation_cb(PurpleConversation *conv, gpointer null)
{
if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_CHAT ||
!PIDGIN_IS_PIDGIN_CONVERSATION(conv))
return;
clear_list(NULL, PIDGIN_CONVERSATION(conv));
}
static gboolean
rcvd_msg_cb(PurpleAccount *account, const char **who, const char **message,
PurpleConversation *conv, PurpleMessageFlags *flags)
{
char *msg, *delims;
GList *iter;
if (*flags & PURPLE_MESSAGE_NICK)
return FALSE;
delims = g_strdup(DELIMS);
g_strdelimit(delims, purple_prefs_get_string(PREF_HLWORDS), ' ');
msg = g_strdup(*message);
g_strdelimit(msg, delims, ' ');
g_free(delims);
for (iter = hlwords; iter; iter = iter->next)
{
char *s = g_strstr_len(msg, -1, iter->data);
if (s && (s == msg || *(s - 1) == ' '))
{
char *e = s + strlen(iter->data);
if (*e == ' ' || *e == '\0')
{
*flags |= PURPLE_MESSAGE_NICK;
break;
}
}
}
g_free(msg);
return FALSE;
}
static void
destroy_list()
{
while (hlwords)
{
g_free(hlwords->data);
hlwords = g_list_delete_link(hlwords, hlwords);
}
}
static void
construct_list()
{
char *string;
const char *s, *e;
destroy_list();
string = g_strdup(purple_prefs_get_string(PREF_HLWORDS));
s = string;
e = NULL;
if (s == NULL)
return;
while (*s)
{
while (*s == ' ' || *s == '\t')
s++;
e = s + 1;
while (*e != ' ' && *e != '\t' && *e != '\0')
e++;
hlwords = g_list_prepend(hlwords, g_strndup(s, e-s));
s = e;
}
g_free(string);
}
static void
unload_cleanup_gtkconv(PidginConversation *gtkconv, gpointer null)
{
clear_list(NULL, gtkconv);
}
static void
unload_cleanup_win(PidginWindow *win, gpointer null)
{
GtkWidget *w = get_tray_icon_for_window(win);
g_object_set_data(G_OBJECT(win->window), "nicksaid:trayicon", NULL);
gtk_widget_destroy(w);
g_list_foreach(pidgin_conv_window_get_gtkconvs(win), (GFunc)unload_cleanup_gtkconv, NULL);
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
construct_list();
purple_signal_connect(purple_conversations_get_handle(), "receiving-chat-msg",
plugin, PURPLE_CALLBACK(rcvd_msg_cb), NULL);
purple_signal_connect(pidgin_conversations_get_handle(), "displaying-chat-msg",
plugin, PURPLE_CALLBACK(displaying_msg_cb), NULL);
purple_signal_connect(pidgin_conversations_get_handle(), "conversation-switched",
plugin, PURPLE_CALLBACK(update_menu_tray), NULL);
purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation",
plugin, PURPLE_CALLBACK(deleting_conversation_cb), NULL);
purple_prefs_connect_callback(plugin, PREF_HLWORDS,
(PurplePrefCallback)construct_list, NULL);
purple_conversation_foreach(update_menu_tray);
return TRUE;
}
static gboolean
plugin_unload(PurplePlugin *plugin)
{
destroy_list();
g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)unload_cleanup_win, NULL);
purple_prefs_disconnect_by_handle(plugin);
return TRUE;
}
static PurplePluginPrefFrame *
get_plugin_pref_frame(PurplePlugin *plugin)
{
PurplePluginPrefFrame *frame;
PurplePluginPref *pref;
frame = purple_plugin_pref_frame_new();
pref = purple_plugin_pref_new_with_label(_("Highlight"));
purple_plugin_pref_frame_add(frame, pref);
pref = purple_plugin_pref_new_with_name_and_label(PREF_HLWORDS,
_("_Words to highlight on\n(separate the words with a blank space)"));
purple_plugin_pref_frame_add(frame, pref);
pref = purple_plugin_pref_new_with_label(_("Number of displayed characters"));
purple_plugin_pref_frame_add(frame, pref);
pref = purple_plugin_pref_new_with_name_and_label(PREF_CHARS,
_("_Set the number of characters displayed\nin the nicksaid menu"));
purple_plugin_pref_set_bounds(pref, 10, 40);
purple_plugin_pref_frame_add(frame, pref);
pref = purple_plugin_pref_new_with_name_and_label(PREF_SHOWWHO,
_("Display who said your name in the nicksaid menu"));
purple_plugin_pref_frame_add(frame, pref);
pref = purple_plugin_pref_new_with_name_and_label(PREF_TIMESTAMP,
_("Display _timestamps in the nicksaid menu"));
purple_plugin_pref_frame_add(frame, pref);
pref = purple_plugin_pref_new_with_name_and_label(PREF_DATESTAMP,
_("_Display _datestamps in the nicksaid menu"));
purple_plugin_pref_frame_add(frame, pref);
pref = purple_plugin_pref_new_with_name_and_label(PREF_SHOWALL,
_("Allow displaying in a separate dialog"));
purple_plugin_pref_frame_add(frame, pref);
return frame;
}
static PurplePluginUiInfo prefs_info = {
get_plugin_pref_frame,
0,
NULL,
NULL,
NULL,
NULL,
NULL
};
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 */
&prefs_info, /* 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 = _("Nicksaid");
info.summary = _("Record when someone said your nick in a chat.");
info.description = _("Record when someone said your nick in a chat.");
purple_prefs_add_none(PREF_PREFIX);
purple_prefs_add_string(PREF_HLWORDS, "");
purple_prefs_add_int(PREF_CHARS, 15);
purple_prefs_add_bool(PREF_TIMESTAMP, TRUE);
purple_prefs_add_bool(PREF_DATESTAMP, FALSE);
purple_prefs_add_bool(PREF_SHOWWHO, TRUE);
purple_prefs_add_bool(PREF_SHOWALL, FALSE);
}
PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)