diff finch/gntconv.c @ 17319:8a7238fb7905

explicit merge of 'dc4f2ee34039521ae6a198fe7d62f4dca8a84589' and '9d8bc74c7c13474edcefafedd0af9020b27c9a2f'
author Jeffrey Connelly <jaconnel@calpoly.edu>
date Wed, 30 May 2007 02:56:25 +0000
parents 46f2f86e08e4
children 319bcb73eb4e
line wrap: on
line diff
--- a/finch/gntconv.c	Wed May 23 04:18:05 2007 +0000
+++ b/finch/gntconv.c	Wed May 30 02:56:25 2007 +0000
@@ -37,16 +37,29 @@
 #include "gntplugin.h"
 #include "gntprefs.h"
 #include "gntstatus.h"
+#include "gntpounce.h"
 
 #include "gnt.h"
 #include "gntbox.h"
 #include "gntentry.h"
+#include "gntlabel.h"
+#include "gntmenu.h"
+#include "gntmenuitem.h"
+#include "gntmenuitemcheck.h"
 #include "gnttextview.h"
+#include "gnttree.h"
+#include "gntutils.h"
+#include "gntwindow.h"
 
 #define PREF_ROOT	"/finch/conversations"
+#define PREF_CHAT   PREF_ROOT "/chats"
+#define PREF_USERLIST PREF_CHAT "/userlist"
 
 #include "config.h"
 
+static void finch_write_common(PurpleConversation *conv, const char *who,
+		const char *message, PurpleMessageFlags flags, time_t mtime);
+
 static void
 send_typing_notification(GntWidget *w, FinchConv *ggconv)
 {
@@ -258,6 +271,13 @@
 	g_free(title);
 }
 
+static void
+chat_left_cb(PurpleConversation *conv, gpointer null)
+{
+	finch_write_common(conv, NULL, _("You have left this chat."),
+			PURPLE_MESSAGE_SYSTEM, time(NULL));
+}
+
 static gpointer
 finch_conv_get_handle()
 {
@@ -266,6 +286,165 @@
 }
 
 static void
+clear_scrollback_cb(GntMenuItem *item, gpointer ggconv)
+{
+	FinchConv *ggc = ggconv;
+	gnt_text_view_clear(GNT_TEXT_VIEW(ggc->tv));
+}
+
+static void
+send_file_cb(GntMenuItem *item, gpointer ggconv)
+{
+	FinchConv *ggc = ggconv;
+	serv_send_file(purple_conversation_get_gc(ggc->active_conv),
+			purple_conversation_get_name(ggc->active_conv), NULL);
+}
+
+static void
+add_pounce_cb(GntMenuItem *item, gpointer ggconv)
+{
+	FinchConv *ggc = ggconv;
+	finch_pounce_editor_show(
+			purple_conversation_get_account(ggc->active_conv),
+			purple_conversation_get_name(ggc->active_conv), NULL);
+}
+
+static void
+get_info_cb(GntMenuItem *item, gpointer ggconv)
+{
+	FinchConv *ggc = ggconv;
+	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
+	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+	purple_notify_userinfo(ggc->active_conv->account->gc, purple_conversation_get_name(ggc->active_conv), info, NULL, NULL);
+	purple_notify_user_info_destroy(info);
+
+	serv_get_info(purple_conversation_get_gc(ggc->active_conv),
+			purple_conversation_get_name(ggc->active_conv));
+}
+
+static void
+toggle_timestamps_cb(GntMenuItem *item, gpointer ggconv)
+{
+	purple_prefs_set_bool(PREF_ROOT "/timestamps",
+		!purple_prefs_get_bool(PREF_ROOT "/timestamps"));
+}
+
+static void
+send_to_cb(GntMenuItem *m, gpointer n)
+{
+	PurpleAccount *account = g_object_get_data(G_OBJECT(m), "purple_account");
+	gchar *buddy = g_object_get_data(G_OBJECT(m), "purple_buddy_name");
+	PurpleConversation *conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, buddy);
+	finch_conversation_set_active(conv);
+}
+
+static void
+generate_send_to_menu(FinchConv *ggc)
+{
+	GntWidget *sub, *menu = ggc->menu;
+	GntMenuItem *item;
+	GSList *buds;
+	GList *list = NULL;
+
+	buds = purple_find_buddies(ggc->active_conv->account, ggc->active_conv->name);
+	if (!buds)
+		return;
+
+	item = gnt_menuitem_new(_("Send To"));
+	gnt_menu_add_item(GNT_MENU(menu), item);
+	sub = gnt_menu_new(GNT_MENU_POPUP);
+	gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+
+	for (; buds; buds = buds->next) {
+		PurpleBlistNode *node = (PurpleBlistNode *)purple_buddy_get_contact((PurpleBuddy *)buds->data);
+		for (node = node->child; node != NULL; node = node->next) {
+			PurpleBuddy *buddy = (PurpleBuddy *)node;
+			PurpleAccount *account = purple_buddy_get_account(buddy);
+			if (purple_account_is_connected(account)) {
+				/* Use the PurplePresence to get unique buddies. */
+				PurplePresence *presence = purple_buddy_get_presence(buddy);
+				if (!g_list_find(list, presence))
+					list = g_list_prepend(list, presence);
+			}
+		}
+	}
+	for (list = g_list_last(list); list != NULL; list = list->prev) {
+		PurplePresence *pre = list->data;
+		PurpleBuddy *buddy = purple_presence_get_buddy(pre);
+		PurpleAccount *account = purple_buddy_get_account(buddy);
+		gchar *name = g_strdup(purple_buddy_get_name(buddy));
+		gchar *text = g_strdup_printf("%s (%s)", purple_buddy_get_name(buddy), purple_account_get_username(account));
+		item = gnt_menuitem_new(text);
+		g_free(text);
+		gnt_menu_add_item(GNT_MENU(sub), item);
+		gnt_menuitem_set_callback(item, send_to_cb, NULL);
+		g_object_set_data(G_OBJECT(item), "purple_account", account);
+		g_object_set_data_full(G_OBJECT(item), "purple_buddy_name", name, g_free);
+	}
+	g_list_free(list);
+	g_slist_free(buds);
+}
+
+static void
+gg_create_menu(FinchConv *ggc)
+{
+	GntWidget *menu, *sub;
+	GntMenuItem *item;
+
+	ggc->menu = menu = gnt_menu_new(GNT_MENU_TOPLEVEL);
+	gnt_window_set_menu(GNT_WINDOW(ggc->window), GNT_MENU(menu));
+
+	item = gnt_menuitem_new(_("Conversation"));
+	gnt_menu_add_item(GNT_MENU(menu), item);
+
+	sub = gnt_menu_new(GNT_MENU_POPUP);
+	gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+
+	item = gnt_menuitem_new(_("Clear Scrollback"));
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(item, clear_scrollback_cb, ggc);
+
+	item = gnt_menuitem_check_new(_("Show Timestamps"));
+	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
+		purple_prefs_get_bool(PREF_ROOT "/timestamps"));
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(item, toggle_timestamps_cb, ggc);
+
+	if (purple_conversation_get_type(ggc->active_conv) == PURPLE_CONV_TYPE_IM) {
+		PurpleAccount *account = purple_conversation_get_account(ggc->active_conv);
+		PurplePluginProtocolInfo *pinfo = account->gc ? PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl) : NULL;
+
+		if (pinfo && pinfo->get_info) {
+			item = gnt_menuitem_new(_("Get Info"));
+			gnt_menu_add_item(GNT_MENU(sub), item);
+			gnt_menuitem_set_callback(item, get_info_cb, ggc);
+		}
+
+		item = gnt_menuitem_new(_("Add Buddy Pounce..."));
+		gnt_menu_add_item(GNT_MENU(sub), item);
+		gnt_menuitem_set_callback(item, add_pounce_cb, ggc);
+
+		if (pinfo && pinfo->send_file &&
+				(!pinfo->can_receive_file ||
+				 	pinfo->can_receive_file(account->gc, purple_conversation_get_name(ggc->active_conv)))) {
+			item = gnt_menuitem_new(_("Send File"));
+			gnt_menu_add_item(GNT_MENU(sub), item);
+			gnt_menuitem_set_callback(item, send_file_cb, ggc);
+		}
+
+		generate_send_to_menu(ggc);
+	}
+}
+
+static void
+create_conv_from_userlist(GntWidget *widget, FinchConv *fc)
+{
+	PurpleAccount *account = purple_conversation_get_account(fc->active_conv);
+	char *name = gnt_tree_get_selection_data(GNT_TREE(widget));
+	purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
+}
+
+static void
 finch_create_conversation(PurpleConversation *conv)
 {
 	FinchConv *ggc = conv->ui_data;
@@ -296,18 +475,38 @@
 	type = purple_conversation_get_type(conv);
 	title = get_conversation_title(conv, account);
 
-	ggc->window = gnt_box_new(FALSE, TRUE);
+	ggc->window = gnt_vwindow_new(FALSE);
 	gnt_box_set_title(GNT_BOX(ggc->window), title);
 	gnt_box_set_toplevel(GNT_BOX(ggc->window), TRUE);
 	gnt_box_set_pad(GNT_BOX(ggc->window), 0);
 	gnt_widget_set_name(ggc->window, "conversation-window");
 
+	gg_create_menu(ggc);
+
 	ggc->tv = gnt_text_view_new();
-	gnt_box_add_widget(GNT_BOX(ggc->window), ggc->tv);
 	gnt_widget_set_name(ggc->tv, "conversation-window-textview");
 	gnt_widget_set_size(ggc->tv, purple_prefs_get_int(PREF_ROOT "/size/width"),
 			purple_prefs_get_int(PREF_ROOT "/size/height"));
 
+	if (type == PURPLE_CONV_TYPE_CHAT) {
+		GntWidget *hbox, *tree;
+		FinchConvChat *fc = ggc->u.chat = g_new0(FinchConvChat, 1);
+		hbox = gnt_hbox_new(FALSE);
+		gnt_box_set_pad(GNT_BOX(hbox), 0);
+		tree = fc->userlist = gnt_tree_new_with_columns(2);
+		gnt_tree_set_col_width(GNT_TREE(tree), 0, 1);   /* The flag column */
+		gnt_tree_set_compare_func(GNT_TREE(tree), (GCompareFunc)g_utf8_collate);
+		gnt_tree_set_hash_fns(GNT_TREE(tree), g_str_hash, g_str_equal, g_free);
+		GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER);
+		gnt_box_add_widget(GNT_BOX(hbox), ggc->tv);
+		gnt_box_add_widget(GNT_BOX(hbox), tree);
+		gnt_box_add_widget(GNT_BOX(ggc->window), hbox);
+		g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(create_conv_from_userlist), ggc);
+		gnt_widget_set_visible(tree, purple_prefs_get_bool(PREF_USERLIST));
+	} else {
+		gnt_box_add_widget(GNT_BOX(ggc->window), ggc->tv);
+	}
+
 	ggc->info = gnt_vbox_new(FALSE);
 	gnt_box_add_widget(GNT_BOX(ggc->window), ggc->info);
 
@@ -331,13 +530,10 @@
 
 	if (type == PURPLE_CONV_TYPE_IM) {
 		g_signal_connect(G_OBJECT(ggc->entry), "text_changed", G_CALLBACK(send_typing_notification), ggc);
-		purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", finch_conv_get_handle(),
-						PURPLE_CALLBACK(update_buddy_typing), NULL);
-		purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped", finch_conv_get_handle(),
-						PURPLE_CALLBACK(update_buddy_typing), NULL);
 	}
 
 	g_free(title);
+	gnt_box_give_focus_to_child(GNT_BOX(ggc->window), ggc->entry);
 }
 
 static void
@@ -350,6 +546,7 @@
 		ggc->active_conv = ggc->list->data;
 	
 	if (ggc->list == NULL) {
+		g_free(ggc->u.chat);
 		gnt_widget_destroy(ggc->window);
 		g_free(ggc);
 	}
@@ -380,8 +577,7 @@
 	gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), "\n", GNT_TEXT_FLAG_NORMAL);
 
 	/* Unnecessary to print the timestamp for delayed message */
-	if (!(flags & PURPLE_MESSAGE_DELAYED) &&
-			purple_prefs_get_bool("/finch/conversations/timestamps"))
+	if (purple_prefs_get_bool("/finch/conversations/timestamps"))
 		gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv),
 					purple_utf8_strftime("(%H:%M:%S) ", localtime(&mtime)), GNT_TEXT_FLAG_DIM);
 
@@ -480,6 +676,20 @@
 	finch_write_common(conv, name, message, flags, mtime);
 }
 
+static const char *
+chat_flag_text(PurpleConvChatBuddyFlags flags)
+{
+	if (flags & PURPLE_CBFLAGS_FOUNDER)
+		return "~";
+	if (flags & PURPLE_CBFLAGS_OP)
+		return "@";
+	if (flags & PURPLE_CBFLAGS_HALFOP)
+		return "%";
+	if (flags & PURPLE_CBFLAGS_VOICE)
+		return "+";
+	return " ";
+}
+
 static void
 finch_chat_add_users(PurpleConversation *conv, GList *users, gboolean new_arrivals)
 {
@@ -510,8 +720,11 @@
 	for (; users; users = users->next)
 	{
 		PurpleConvChatBuddy *cbuddy = users->data;
+		GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
 		gnt_entry_add_suggest(entry, cbuddy->name);
 		gnt_entry_add_suggest(entry, cbuddy->alias);
+		gnt_tree_add_row_after(tree, g_strdup(cbuddy->name),
+				gnt_tree_create_row(tree, chat_flag_text(cbuddy->flags), cbuddy->alias), NULL, NULL);
 	}
 }
 
@@ -521,42 +734,60 @@
 	/* Update the name for string completion */
 	FinchConv *ggc = conv->ui_data;
 	GntEntry *entry = GNT_ENTRY(ggc->entry);
+	GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
+	PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), new_n);
+
 	gnt_entry_remove_suggest(entry, old);
+	gnt_tree_remove(tree, (gpointer)old);
+
 	gnt_entry_add_suggest(entry, new_n);
 	gnt_entry_add_suggest(entry, new_a);
+	gnt_tree_add_row_after(tree, g_strdup(new_n),
+			gnt_tree_create_row(tree, chat_flag_text(cb->flags), new_a), NULL, NULL);
 }
 
 static void
-finch_chat_remove_user(PurpleConversation *conv, GList *list)
+finch_chat_remove_users(PurpleConversation *conv, GList *list)
 {
 	/* Remove the name from string completion */
 	FinchConv *ggc = conv->ui_data;
 	GntEntry *entry = GNT_ENTRY(ggc->entry);
-	for (; list; list = list->next)
+	for (; list; list = list->next) {
+		GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
 		gnt_entry_remove_suggest(entry, list->data);
+		gnt_tree_remove(tree, list->data);
+	}
 }
 
 static void
 finch_chat_update_user(PurpleConversation *conv, const char *user)
 {
+	PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), user);
+	FinchConv *ggc = conv->ui_data;
+	gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist), (gpointer)user, 0, chat_flag_text(cb->flags));
 }
 
 static PurpleConversationUiOps conv_ui_ops = 
 {
-	.create_conversation = finch_create_conversation,
-	.destroy_conversation = finch_destroy_conversation,
-	.write_chat = finch_write_chat,
-	.write_im = finch_write_im,
-	.write_conv = finch_write_conv,
-	.chat_add_users = finch_chat_add_users,
-	.chat_rename_user = finch_chat_rename_user,
-	.chat_remove_users = finch_chat_remove_user,
-	.chat_update_user = finch_chat_update_user,
-	.present = NULL,
-	.has_focus = NULL,
-	.custom_smiley_add = NULL,
-	.custom_smiley_write = NULL,
-	.custom_smiley_close = NULL
+	finch_create_conversation,
+	finch_destroy_conversation,
+	finch_write_chat,
+	finch_write_im,
+	finch_write_conv,
+	finch_chat_add_users,
+	finch_chat_rename_user,
+	finch_chat_remove_users,
+	finch_chat_update_user,
+	NULL, /* present */
+	NULL, /* has_focus */
+	NULL, /* custom_smiley_add */
+	NULL, /* custom_smiley_write */
+	NULL, /* custom_smiley_close */
+	NULL, /* send_confirm */
+	NULL,
+	NULL,
+	NULL,
+	NULL
 };
 
 PurpleConversationUiOps *finch_conv_get_ui_ops()
@@ -604,7 +835,7 @@
 	PurpleCmdStatus status;
 
 	if (!g_ascii_strcasecmp(args[0], "version")) {
-		tmp = g_strdup_printf("me is using %s.", VERSION);
+		tmp = g_strdup_printf("me is using Finch v%s.", VERSION);
 		markup = g_markup_escape_text(tmp, -1);
 
 		status = purple_cmd_do_command(conv, tmp, markup, error);
@@ -677,6 +908,23 @@
 	return PURPLE_CMD_STATUS_OK;
 }
 
+static PurpleCmdRet
+users_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
+{
+	FinchConv *fc = conv->ui_data;
+	FinchConvChat *ch;
+	if (!fc)
+		return PURPLE_CMD_STATUS_FAILED;
+
+	ch = fc->u.chat;
+	gnt_widget_set_visible(ch->userlist,
+			(GNT_WIDGET_IS_FLAG_SET(ch->userlist, GNT_WIDGET_INVISIBLE)));
+	gnt_box_readjust(GNT_BOX(fc->window));
+	gnt_box_give_focus_to_child(GNT_BOX(fc->window), fc->entry);
+	purple_prefs_set_bool(PREF_USERLIST, !(GNT_WIDGET_IS_FLAG_SET(ch->userlist, GNT_WIDGET_INVISIBLE)));
+	return PURPLE_CMD_STATUS_OK;
+}
+
 void finch_conversation_init()
 {
 	purple_prefs_add_none(PREF_ROOT);
@@ -686,6 +934,8 @@
 	purple_prefs_add_none(PREF_ROOT "/position");
 	purple_prefs_add_int(PREF_ROOT "/position/x", 0);
 	purple_prefs_add_int(PREF_ROOT "/position/y", 0);
+	purple_prefs_add_none(PREF_CHAT);
+	purple_prefs_add_bool(PREF_USERLIST, FALSE);
 
 	/* Xerox the commands */
 	purple_cmd_register("say", "S", PURPLE_CMD_P_DEFAULT,
@@ -703,6 +953,9 @@
 	purple_cmd_register("help", "w", PURPLE_CMD_P_DEFAULT,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL,
 	                  help_command_cb, _("help &lt;command&gt;:  Help on a specific command."), NULL);
+	purple_cmd_register("users", "", PURPLE_CMD_P_DEFAULT,
+	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, NULL,
+	                  users_command_cb, _("users:  Show the list of users in the chat."), NULL);
 
 	/* Now some commands to bring up some other windows */
 	purple_cmd_register("plugins", "", PURPLE_CMD_P_DEFAULT,
@@ -723,10 +976,18 @@
 	purple_cmd_register("status", "", PURPLE_CMD_P_DEFAULT,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
 	                  cmd_show_window, _("statuses: Show the savedstatuses window."), finch_savedstatus_show_all);
+
+	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", finch_conv_get_handle(),
+					PURPLE_CALLBACK(update_buddy_typing), NULL);
+	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped", finch_conv_get_handle(),
+					PURPLE_CALLBACK(update_buddy_typing), NULL);
+	purple_signal_connect(purple_conversations_get_handle(), "chat-left", finch_conv_get_handle(),
+					PURPLE_CALLBACK(chat_left_cb), NULL);
 }
 
 void finch_conversation_uninit()
 {
+	purple_signals_disconnect_by_handle(finch_conv_get_handle());
 }
 
 void finch_conversation_set_active(PurpleConversation *conv)