changeset 14041:27182f83b79b

[gaim-migrate @ 16647] Statusbox comes in. It's now possible to change the account status. There's nothing yet for creating custom statuses. It's also possible now to delete accounts. committer: Tailor Script <tailor@pidgin.im>
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 05 Aug 2006 11:31:54 +0000
parents 80891029f5dd
children 143474e2b1cb
files console/gntaccount.c console/gntblist.c console/libgnt/gntbox.c console/libgnt/gntbox.h console/libgnt/gntcombobox.c console/libgnt/gntentry.c console/libgnt/test/tv.c
diffstat 7 files changed, 345 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/console/gntaccount.c	Sat Aug 05 09:19:14 2006 +0000
+++ b/console/gntaccount.c	Sat Aug 05 11:31:54 2006 +0000
@@ -49,6 +49,9 @@
 	GntWidget *remember;
 } AccountEditDialog;
 
+/* This is necessary to close an edit-dialog when an account is deleted */
+static GList *accountdialogs;
+
 static void
 account_add(GaimAccount *account)
 {
@@ -64,6 +67,7 @@
 static void
 edit_dialog_destroy(AccountEditDialog *dialog)
 {
+	accountdialogs = g_list_remove(accountdialogs, dialog);
 	g_list_free(dialog->prpl_entries);
 	g_list_free(dialog->split_entries);
 	g_free(dialog);
@@ -143,7 +147,6 @@
 		gaim_account_set_password(account, NULL);
 
 	/* Mail notification */
-	/* XXX: Only if the protocol has anything to do with emails */
 	gaim_account_set_check_mail(account,
 			gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->newmail)));
 
@@ -426,7 +429,19 @@
 	GList *list, *iter;
 	AccountEditDialog *dialog;
 
+	if (account)
+	{
+		GList *iter;
+		for (iter = accountdialogs; iter; iter = iter->next)
+		{
+			AccountEditDialog *dlg = iter->data;
+			if (dlg->account == account)
+				return;
+		}
+	}
+
 	dialog = g_new0(AccountEditDialog, 1);
+	accountdialogs = g_list_prepend(accountdialogs, dialog);
 
 	dialog->window = window = gnt_box_new(FALSE, TRUE);
 	dialog->account = account;
@@ -534,10 +549,38 @@
 }
 
 static void
+really_delete_account(GaimAccount *account)
+{
+	GList *iter;
+	for (iter = accountdialogs; iter; iter = iter->next)
+	{
+		AccountEditDialog *dlg = iter->data;
+		if (dlg->account == account)
+		{
+			gnt_widget_destroy(dlg->window);
+			break;
+		}
+	}
+	gaim_accounts_delete(account);
+}
+
+static void
 delete_account_cb(GntWidget *widget, GntTree *tree)
 {
-	/* XXX: After the request-api is complete */
-	/* Note: remove the modify-dialog for the account */
+	GaimAccount *account;
+	char *prompt;
+
+	account  = gnt_tree_get_selection_data(tree);
+	if (!account)
+		return;
+
+	prompt = g_strdup_printf(_("Are you sure you want to delete %s?"),
+			gaim_account_get_username(account));
+
+	gaim_request_close_with_handle(account); /* Close any other opened delete window */
+	gaim_request_action(account, _("Delete Account"), prompt, NULL, 0, account, 2,
+			_("Delete"), really_delete_account, _("Cancel"), NULL);
+	g_free(prompt);
 }
 
 static void
--- a/console/gntblist.c	Sat Aug 05 09:19:14 2006 +0000
+++ b/console/gntblist.c	Sat Aug 05 11:31:54 2006 +0000
@@ -1,19 +1,25 @@
 #include <account.h>
 #include <blist.h>
 #include <request.h>
+#include <savedstatuses.h>
 #include <server.h>
 #include <signal.h>
+#include <status.h>
 #include <util.h>
 
 #include "gntgaim.h"
 #include "gntbox.h"
+#include "gntcombobox.h"
+#include "gntentry.h"
 #include "gntlabel.h"
+#include "gntline.h"
 #include "gnttree.h"
 
 #include "gntblist.h"
 #include <string.h>
 
 #define PREF_ROOT "/gaim/gnt/blist"
+#define TYPING_TIMEOUT 4000
 
 typedef struct
 {
@@ -25,8 +31,29 @@
 
 	GntWidget *context;
 	GaimBlistNode *cnode;
+
+	/* XXX: I am KISSing */
+	GntWidget *status;          /* Dropdown with the statuses  */
+	GntWidget *statustext;      /* Status message */
+	int typing;
 } GGBlist;
 
+typedef enum
+{
+	STATUS_PRIMITIVE = 0,
+	STATUS_SAVED
+} StatusType;
+
+typedef struct
+{
+	StatusType type;
+	union
+	{
+		GaimStatusPrimitive prim;
+		GaimSavedStatus *saved;
+	} u;
+} StatusBoxItem;
+
 GGBlist *ggblist;
 
 static void add_buddy(GaimBuddy *buddy, GGBlist *ggblist);
@@ -34,6 +61,7 @@
 static void add_chat(GaimChat *chat, GGBlist *ggblist);
 static void add_node(GaimBlistNode *node, GGBlist *ggblist);
 static void draw_tooltip(GGBlist *ggblist);
+static void remove_typing_cb(gpointer null);
 static void remove_peripherals(GGBlist *ggblist);
 static const char * get_display_name(GaimBlistNode *node);
 
@@ -113,7 +141,7 @@
 {
 }
 
-static GaimBlistUiOps blist_ui_ops = 
+static GaimBlistUiOps blist_ui_ops =
 {
 	new_list,
 	new_node,
@@ -202,6 +230,8 @@
 	GaimBlistNode *node = (GaimBlistNode *)chat;
 	if (node->ui_data)
 		return;
+	if (!gaim_account_is_connected(chat->account))
+		return;
 
 	group = gaim_chat_get_group(chat);
 	add_node((GaimBlistNode*)group, ggblist);
@@ -290,7 +320,7 @@
 	GList *list;
 	if (action == NULL)
 		return;
-	
+
 	gnt_tree_add_row_after(tree, action,
 			gnt_tree_create_row(tree, action->label), parent, NULL);
 	for (list = action->children; list; list = list->next)
@@ -367,7 +397,7 @@
 	add_custom_action(tree, _("View Log"),
 			GAIM_CALLBACK(gg_blist_view_log_cb)), buddy);
 #endif
-	
+
 	/* Protocol actions */
 	append_proto_menu(tree,
 			gaim_account_get_connection(gaim_buddy_get_account(buddy)),
@@ -436,9 +466,9 @@
 		name = ((GaimGroup*)node)->name;
 	else
 		g_return_if_reached();
-	
+
 	prompt = g_strdup_printf(_("Please enter the new name for %s"), name);
-	
+
 	gaim_request_input(node, _("Rename"), prompt, _("Enter empty string to reset the name."),
 			name, FALSE, FALSE, NULL, _("Rename"), G_CALLBACK(rename_blist_node),
 			_("Cancel"), NULL, node);
@@ -682,7 +712,7 @@
 		ret = gnt_widget_key_pressed(ggblist->context, text);
 		stop = TRUE;
 	}
-	
+
 	if (text[0] == 27)
 	{
 		if (strcmp(text + 1, GNT_KEY_POPUP) == 0)
@@ -760,7 +790,8 @@
 		node->ui_data = NULL;
 		node = gaim_blist_node_next(node, TRUE);
 	}
-		
+
+	remove_typing_cb(NULL);
 	remove_peripherals(ggblist);
 	g_free(ggblist);
 	ggblist = NULL;
@@ -777,10 +808,55 @@
 	while (node)
 	{
 		node_update(list, node);
-		node = gaim_blist_node_next(node, TRUE);
+		node = gaim_blist_node_next(node, FALSE);
 	}
 }
 
+static void
+destroy_status_list(GList *list)
+{
+	g_list_foreach(list, (GFunc)g_free, NULL);
+	g_list_free(list);
+}
+
+static void
+populate_status_dropdown()
+{
+	int i;
+	GList *iter;
+	GList *items = NULL;
+
+	/* First the primitives */
+	GaimStatusPrimitive prims[] = {GAIM_STATUS_AVAILABLE, GAIM_STATUS_AWAY,
+			GAIM_STATUS_INVISIBLE, GAIM_STATUS_OFFLINE, GAIM_STATUS_UNSET};
+
+	for (i = 0; prims[i] != GAIM_STATUS_UNSET; i++)
+	{
+		StatusBoxItem *item = g_new0(StatusBoxItem, 1);
+		item->type = STATUS_PRIMITIVE;
+		item->u.prim = prims[i];
+		items = g_list_prepend(items, item);
+		gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
+				gaim_primitive_get_name_from_type(prims[i]));
+	}
+
+	/* Now the popular statuses */
+	for (iter = gaim_savedstatuses_get_popular(6); iter; iter = iter->next)
+	{
+		StatusBoxItem *item = g_new0(StatusBoxItem, 1);
+		item->type = STATUS_SAVED;
+		item->u.saved = iter->data;
+		items = g_list_prepend(items, item);
+		gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
+				gaim_savedstatus_get_title(iter->data));
+	}
+
+	/* The keys for the combobox are created here, and never used
+	 * anywhere else. So make sure the keys are freed when the widget
+	 * is destroyed. */
+	g_object_set_data_full(G_OBJECT(ggblist->status), "list of statuses", items, (GDestroyNotify)destroy_status_list);
+}
+
 void gg_blist_init()
 {
 	gaim_prefs_add_none(PREF_ROOT);
@@ -796,6 +872,120 @@
 	return;
 }
 
+static void
+remove_typing_cb(gpointer null)
+{
+	GaimSavedStatus *current;
+	const char *message, *newmessage;
+	GaimStatusPrimitive prim, newprim;
+	StatusBoxItem *item;
+
+	current = gaim_savedstatus_get_current();
+	message = gaim_savedstatus_get_message(current);
+	prim = gaim_savedstatus_get_type(current);
+
+	newmessage = gnt_entry_get_text(GNT_ENTRY(ggblist->statustext));
+	item = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist->status));
+	g_return_if_fail(item->type == STATUS_PRIMITIVE);
+	newprim = item->u.prim;
+
+	if (newprim != prim || ((message && !newmessage) ||
+				(!message && newmessage) ||
+				(message && newmessage && g_utf8_collate(message, newmessage) != 0)))
+	{
+		GaimSavedStatus *status = gaim_savedstatus_find_transient_by_type_and_message(newprim, newmessage);
+									/* Holy Crap! That's a LAWNG function name */
+		if (status == NULL)
+		{
+			status = gaim_savedstatus_new(NULL, newprim);
+			gaim_savedstatus_set_message(status, newmessage);
+		}
+
+		gaim_savedstatus_activate(status);
+	}
+
+	gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
+	g_source_remove(ggblist->typing);
+	ggblist->typing = 0;
+}
+
+static void
+status_selection_changed(GntComboBox *box, StatusBoxItem *old, StatusBoxItem *now, gpointer null)
+{
+	gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), NULL);
+	if (now->type == STATUS_SAVED)
+	{
+		/* Set the status immediately */
+		gaim_savedstatus_activate(now->u.saved);
+	}
+	else if (now->type == STATUS_PRIMITIVE)
+	{
+		/* Move the focus to the entry box */
+		gnt_box_move_focus(GNT_BOX(ggblist->window), 1);
+		ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL);
+	}
+	else
+		g_return_if_reached();
+}
+
+static gboolean
+status_text_changed(GntEntry *entry, const char *text, gpointer null)
+{
+	if (text[0] == 27 && ggblist->typing == 0)
+		return FALSE;
+
+	g_source_remove(ggblist->typing);
+	ggblist->typing = 0;
+
+	if (text[0] == '\r' && text[1] == 0)
+	{
+		/* Set the status only after you press 'Enter' */
+		remove_typing_cb(NULL);
+		return TRUE;
+	}
+
+	ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL);
+	return FALSE;
+}
+
+static void
+savedstatus_changed(GaimSavedStatus *now, GaimSavedStatus *old)
+{
+	/* Block the signals we don't want to emit */
+	GList *list;
+	GaimStatusPrimitive prim;
+	const char *message;
+
+	if (!ggblist)
+		return;
+
+	g_signal_handlers_block_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
+			0, 0, NULL, status_selection_changed, NULL);
+	g_signal_handlers_block_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
+			0, 0, NULL, status_text_changed, NULL);
+
+	prim = gaim_savedstatus_get_type(now);
+	message = gaim_savedstatus_get_message(now);
+
+	list = g_object_get_data(G_OBJECT(ggblist->status), "list of statuses");
+	for (; list; list = list->next)
+	{
+		StatusBoxItem *item = list->data;
+		if (item->type == STATUS_PRIMITIVE && item->u.prim == prim)
+		{
+			gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist->status), item);
+			gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), message);
+			gnt_widget_draw(ggblist->status);
+			break;
+		}
+	}
+
+	g_signal_handlers_unblock_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
+			0, 0, NULL, status_selection_changed, NULL);
+	g_signal_handlers_unblock_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
+			0, 0, NULL, status_text_changed, NULL);
+}
+
 void gg_blist_show()
 {
 	if (ggblist)
@@ -805,7 +995,7 @@
 
 	gaim_get_blist()->ui_data = ggblist;
 
-	ggblist->window = gnt_box_new(FALSE, FALSE);
+	ggblist->window = gnt_vbox_new(FALSE);
 	gnt_widget_set_name(ggblist->window, "buddylist");
 	gnt_box_set_toplevel(GNT_BOX(ggblist->window), TRUE);
 	gnt_box_set_title(GNT_BOX(ggblist->window), _("Buddy List"));
@@ -820,13 +1010,23 @@
 			gaim_prefs_get_int(PREF_ROOT "/position/y"));
 
 	gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
+
+	gnt_box_add_widget(GNT_BOX(ggblist->window), gnt_hline_new());
+
+	ggblist->status = gnt_combo_box_new();
+	gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->status);
+	ggblist->statustext = gnt_entry_new(NULL);
+	gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->statustext);
+
+	populate_status_dropdown();
+
 	gnt_widget_show(ggblist->window);
 
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-status-changed", gg_blist_get_handle(),
 				GAIM_CALLBACK(buddy_status_changed), ggblist);
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-idle-changed", gg_blist_get_handle(),
 				GAIM_CALLBACK(buddy_idle_changed), ggblist);
-	
+
 #if 0
 	gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gg_blist_get_handle(),
 				GAIM_CALLBACK(buddy_signed_on), ggblist);
@@ -854,7 +1054,17 @@
 	g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL);
 	g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL);
 
+	/* Status signals */
+	gaim_signal_connect(gaim_savedstatuses_get_handle(), "savedstatus-changed", gg_blist_get_handle(),
+				GAIM_CALLBACK(savedstatus_changed), NULL);
+	g_signal_connect(G_OBJECT(ggblist->status), "selection_changed",
+				G_CALLBACK(status_selection_changed), NULL);
+	g_signal_connect(G_OBJECT(ggblist->statustext), "key_pressed",
+				G_CALLBACK(status_text_changed), NULL);
+
 	populate_buddylist();
+
+	savedstatus_changed(gaim_savedstatus_get_current(), NULL);
 }
 
 void gg_blist_uninit()
--- a/console/libgnt/gntbox.c	Sat Aug 05 09:19:14 2006 +0000
+++ b/console/libgnt/gntbox.c	Sat Aug 05 11:31:54 2006 +0000
@@ -245,6 +245,28 @@
 	} while (box->active != last);
 }
 
+static void
+find_prev_focus(GntBox *box)
+{
+	gpointer last = box->active;
+
+	if (!box->focus)
+		return;
+
+	do
+	{
+		GList *iter = g_list_find(box->focus, box->active);
+		if (!iter)
+			box->active = box->focus->data;
+		else if (!iter->prev)
+			box->active = g_list_last(box->focus)->data;
+		else
+			box->active = iter->prev->data;
+		if (!GNT_WIDGET_IS_FLAG_SET(box->active, GNT_WIDGET_INVISIBLE))
+			break;
+	} while (box->active != last);
+}
+
 static gboolean
 gnt_box_key_pressed(GntWidget *widget, const char *text)
 {
@@ -263,15 +285,7 @@
 	{
 		if (strcmp(text+1, GNT_KEY_LEFT) == 0)
 		{
-			GList *iter = g_list_find(box->focus, box->active);
-			if ((!iter || !iter->prev) && box->focus)
-			{
-				box->active = box->focus->data;
-			}
-			else
-			{
-				box->active = iter->prev->data;
-			}
+			find_prev_focus(box);
 		}
 		else if (strcmp(text+1, GNT_KEY_RIGHT) == 0)
 		{
@@ -689,3 +703,46 @@
 	box->fill = fill;
 }
 
+void gnt_box_move_focus(GntBox *box, int dir)
+{
+	GntWidget *now;
+
+	if (box->active == NULL)
+	{
+		find_focusable_widget(box);
+		return;
+	}
+
+	now = box->active;
+
+	if (dir == 1)
+		find_next_focus(box);
+	else if (dir == -1)
+		find_prev_focus(box);
+
+	if (now && now != box->active)
+	{
+		gnt_widget_set_focus(now, FALSE);
+		gnt_widget_set_focus(box->active, TRUE);
+	}
+
+	if (GNT_WIDGET(box)->window)
+		gnt_widget_draw(GNT_WIDGET(box));
+}
+
+void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget)
+{
+	GList *find = g_list_find(box->focus, widget);
+	gpointer now = box->active;
+	if (find)
+		box->active = widget;
+	if (now && now != box->active)
+	{
+		gnt_widget_set_focus(now, FALSE);
+		gnt_widget_set_focus(box->active, TRUE);
+	}
+
+	if (GNT_WIDGET(box)->window)
+		gnt_widget_draw(GNT_WIDGET(box));
+}
+
--- a/console/libgnt/gntbox.h	Sat Aug 05 09:19:14 2006 +0000
+++ b/console/libgnt/gntbox.h	Sat Aug 05 11:31:54 2006 +0000
@@ -88,6 +88,10 @@
 
 void gnt_box_set_fill(GntBox *box, gboolean fill);
 
+void gnt_box_move_focus(GntBox *box, int dir);  /* +1 to move forward, -1 for backward */
+
+void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget);
+
 G_END_DECLS
 
 #endif /* GNT_BOX_H */
--- a/console/libgnt/gntcombobox.c	Sat Aug 05 09:19:14 2006 +0000
+++ b/console/libgnt/gntcombobox.c	Sat Aug 05 11:31:54 2006 +0000
@@ -23,11 +23,11 @@
 		/* XXX: make sure the key actually does exist */
 		gpointer old = box->selected;
 		box->selected = key;
-		g_signal_emit(box, signals[SIG_SELECTION_CHANGED], 0, old, key);
 		if (GNT_WIDGET(box)->window)
 			gnt_widget_draw(GNT_WIDGET(box));
 		if (box->dropdown)
 			gnt_tree_set_selected(GNT_TREE(box->dropdown), key);
+		g_signal_emit(box, signals[SIG_SELECTION_CHANGED], 0, old, key);
 	}
 }
 
@@ -65,7 +65,7 @@
 	}
 
 	mvwprintw(widget->window, 1, 1, text);
-	whline(widget->window, '\0' | COLOR_PAIR(type), widget->priv.width - 4 - len);
+	whline(widget->window, ' ' | COLOR_PAIR(type), widget->priv.width - 4 - len);
 	mvwaddch(widget->window, 1, widget->priv.width - 3, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL));
 	mvwaddch(widget->window, 1, widget->priv.width - 2, ACS_DARROW | COLOR_PAIR(GNT_COLOR_NORMAL));
 
--- a/console/libgnt/gntentry.c	Sat Aug 05 09:19:14 2006 +0000
+++ b/console/libgnt/gntentry.c	Sat Aug 05 11:31:54 2006 +0000
@@ -16,8 +16,9 @@
 {
 	GntEntry *entry = GNT_ENTRY(widget);
 	int stop;
+	gboolean focus;
 
-	if (gnt_widget_has_focus(widget))
+	if ((focus = gnt_widget_has_focus(widget)))
 		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TEXT_NORMAL));
 	else
 		wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D));
@@ -34,8 +35,9 @@
 	if (stop < widget->priv.width)
 		mvwhline(widget->window, 0, stop, ENTRY_CHAR, widget->priv.width - stop);
 
-	mvwchgat(widget->window, 0, g_utf8_pointer_to_offset(entry->scroll, entry->cursor), 1,
-			A_REVERSE, COLOR_PAIR(GNT_COLOR_TEXT_NORMAL), NULL);
+	if (focus)
+		mvwchgat(widget->window, 0, g_utf8_pointer_to_offset(entry->scroll, entry->cursor),
+				1, A_REVERSE, COLOR_PAIR(GNT_COLOR_TEXT_NORMAL), NULL);
 
 	DEBUG;
 }
--- a/console/libgnt/test/tv.c	Sat Aug 05 09:19:14 2006 +0000
+++ b/console/libgnt/test/tv.c	Sat Aug 05 11:31:54 2006 +0000
@@ -16,7 +16,8 @@
 				GNT_TEXT_FLAG_HIGHLIGHT);
 		gnt_text_view_next_line(GNT_TEXT_VIEW(view));
 		gnt_entry_clear(GNT_ENTRY(w));
-		gnt_text_view_scroll(GNT_TEXT_VIEW(view), 0);
+		if (gnt_text_view_get_lines_below(GNT_TEXT_VIEW(view)) <= 1)
+			gnt_text_view_scroll(GNT_TEXT_VIEW(view), 0);
 
 		return TRUE;
 	}