pidgin/purple-plugin-pack

Add the turtles target to keep the foot clan at bay
default tip
13 months ago, Gary Kramlich
63ad7e4f10b4
Add the turtles target to keep the foot clan at bay

Testing Done:
Ran `ninja turtles` and verified it worked correctly.

Reviewed at https://reviews.imfreedom.org/r/2409/
/*
* 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)