changeset 10178:96a850ab30c8

[gaim-migrate @ 11293] Added a status selector widget and stuck it at the bottom of the blist. Despite conversations regarding this, nobody has done it yet, so here's a basic one that people can build off of. It'll at the very least allow people to figure out what needs fixing with prpls and status (and a lot of fixes are needed!) committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Sun, 14 Nov 2004 20:15:09 +0000
parents 82c1322c4b56
children 97ee3bf7bcf7
files src/Makefile.am src/gtkblist.c src/gtkstatusselector.c src/gtkstatusselector.h
diffstat 4 files changed, 535 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile.am	Sun Nov 14 20:14:00 2004 +0000
+++ b/src/Makefile.am	Sun Nov 14 20:15:09 2004 +0000
@@ -163,6 +163,7 @@
 	gtkroomlist.c \
 	gtksound.c \
 	gtksourceiter.c \
+	gtkstatusselector.c \
 	gtkutils.c \
 	idle.c \
 	main.c \
@@ -198,6 +199,7 @@
 	gtkroomlist.h \
 	gtksound.h \
 	gtksourceiter.h \
+	gtkstatusselector.h \
 	gtkutils.h \
 	internal.h \
 	stock.h
--- a/src/gtkblist.c	Sun Nov 14 20:14:00 2004 +0000
+++ b/src/gtkblist.c	Sun Nov 14 20:15:09 2004 +0000
@@ -49,6 +49,7 @@
 #include "gtkprivacy.h"
 #include "gtkroomlist.h"
 #include "gtksound.h"
+#include "gtkstatusselector.h"
 #include "gtkutils.h"
 
 #include "gaim.h"
@@ -3090,7 +3091,9 @@
 	void *handle;
 	GtkCellRenderer *rend;
 	GtkTreeViewColumn *column;
+	GtkWidget *menu;
 	GtkWidget *sw;
+	GtkWidget *selector;
 	GtkAccelGroup *accel_group;
 	GtkTreeSelection *selection;
 	GtkTargetEntry dte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW},
@@ -3113,6 +3116,7 @@
 	gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List"));
 
 	gtkblist->vbox = gtk_vbox_new(FALSE, 0);
+	gtk_widget_show(gtkblist->vbox);
 	gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox);
 
 	g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL);
@@ -3133,7 +3137,9 @@
 	gaim_gtk_load_accels();
 	g_signal_connect(G_OBJECT(accel_group), "accel-changed",
 														G_CALLBACK(gaim_gtk_save_accels_cb), NULL);
-	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(gtkblist->ift, "<GaimMain>"), FALSE, FALSE, 0);
+	menu = gtk_item_factory_get_widget(gtkblist->ift, "<GaimMain>");
+	gtk_widget_show(menu);
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), menu, FALSE, FALSE, 0);
 
 	awaymenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Away"));
 
@@ -3152,6 +3158,7 @@
 	gaim_gtk_blist_update_plugin_actions();
 	/****************************** GtkTreeView **********************************/
 	sw = gtk_scrolled_window_new(NULL,NULL);
+	gtk_widget_show(sw);
 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
@@ -3160,6 +3167,7 @@
 			G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER);
 
 	gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel));
+	gtk_widget_show(gtkblist->treeview);
 	gtk_widget_set_name(gtkblist->treeview, "gaim_gtkblist_treeview");
 
 	/* Set up selection stuff */
@@ -3233,6 +3241,10 @@
 	gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview);
 	gaim_gtk_blist_update_columns();
 
+	selector = gaim_gtk_status_selector_new();
+	gtk_widget_show(selector);
+	gtk_box_pack_start(GTK_BOX(gtkblist->vbox), selector, FALSE, TRUE, 0);
+
 	/* set the Show Offline Buddies option. must be done
 	 * after the treeview or faceprint gets mad. -Robot101
 	 */
@@ -3248,7 +3260,7 @@
 	/* OK... let's show this bad boy. */
 	gaim_gtk_blist_refresh(list);
 	gaim_gtk_blist_restore_position();
-	gtk_widget_show_all(gtkblist->window);
+	gtk_widget_show(gtkblist->window);
 
 	/* start the refresh timer */
 	if (gaim_prefs_get_bool("/gaim/gtk/blist/show_idle_time") ||
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkstatusselector.c	Sun Nov 14 20:15:09 2004 +0000
@@ -0,0 +1,434 @@
+/**
+ * @file gtkstatusselector.c Status selector widget
+ * @ingroup gtkui
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "internal.h"
+#include "gtkgaim.h"
+#include "gtkimhtml.h"
+#include "gtkstatusselector.h"
+#include "gtkutils.h"
+
+#include "account.h"
+#include "debug.h"
+#include "prefs.h"
+
+struct _GaimGtkStatusSelectorPrivate
+{
+	GtkWidget *combo;
+	GtkWidget *entry;
+	GtkWidget *sw;
+
+#if GTK_CHECK_VERSION(2,4,0)
+	GtkListStore *model;
+#endif
+
+	GHashTable *icon_table;
+};
+
+#if GTK_CHECK_VERSION(2,4,0)
+enum
+{
+	COLUMN_STATUS_TYPE_ID,
+	COLUMN_ICON,
+	COLUMN_NAME,
+	NUM_COLUMNS
+};
+#endif /* GTK >= 2.4.0 */
+
+static void gaim_gtk_status_selector_class_init(GaimGtkStatusSelectorClass *klass);
+static void gaim_gtk_status_selector_init(GaimGtkStatusSelector *selector);
+static void gaim_gtk_status_selector_finalize(GObject *obj);
+static void gaim_gtk_status_selector_destroy(GtkObject *obj);
+static void status_switched_cb(GtkWidget *combo, GaimGtkStatusSelector *selector);
+static void signed_on_off_cb(GaimConnection *gc, GaimGtkStatusSelector *selector);
+static void rebuild_list(GaimGtkStatusSelector *selector);
+
+static GtkVBox *parent_class = NULL;
+
+GType
+gaim_gtk_status_selector_get_type(void)
+{
+	static GType type = 0;
+
+	if (!type)
+	{
+		static const GTypeInfo info =
+		{
+			sizeof(GaimGtkStatusSelectorClass),
+			NULL,
+			NULL,
+			(GClassInitFunc)gaim_gtk_status_selector_class_init,
+			NULL,
+			NULL,
+			sizeof(GaimGtkStatusSelector),
+			0,
+			(GInstanceInitFunc)gaim_gtk_status_selector_init
+		};
+
+		type = g_type_register_static(GTK_TYPE_VBOX,
+									  "GaimGtkStatusSelector", &info, 0);
+	}
+
+	return type;
+}
+
+static void
+gaim_gtk_status_selector_class_init(GaimGtkStatusSelectorClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	gobject_class = G_OBJECT_CLASS(klass);
+	object_class  = GTK_OBJECT_CLASS(klass);
+
+	gobject_class->finalize = gaim_gtk_status_selector_finalize;
+
+	object_class->destroy = gaim_gtk_status_selector_destroy;
+}
+
+static void
+gaim_gtk_status_selector_init(GaimGtkStatusSelector *selector)
+{
+	GtkWidget *combo;
+	GtkWidget *entry;
+	GtkWidget *sw;
+#if GTK_CHECK_VERSION(2,4,0)
+	GtkCellRenderer *renderer;
+#endif
+
+	selector->priv = g_new0(GaimGtkStatusSelectorPrivate, 1);
+
+	selector->priv->icon_table =
+		g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+	g_hash_table_insert(selector->priv->icon_table,
+						"online", "available.png");
+	g_hash_table_insert(selector->priv->icon_table,
+						"away", "away.png");
+
+#if GTK_CHECK_VERSION(2,4,0)
+	selector->priv->model = gtk_list_store_new(NUM_COLUMNS, G_TYPE_POINTER,
+											   GDK_TYPE_PIXBUF, G_TYPE_STRING);
+
+	combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(selector->priv->model));
+	selector->priv->combo = combo;
+
+	g_object_unref(G_OBJECT(selector->priv->model));
+
+	renderer = gtk_cell_renderer_pixbuf_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, FALSE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
+								   "pixbuf", COLUMN_ICON,
+								   NULL);
+
+	renderer = gtk_cell_renderer_text_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
+								   "text", COLUMN_NAME,
+								   NULL);
+
+	g_signal_connect(G_OBJECT(combo), "changed",
+					 G_CALLBACK(status_switched_cb), selector);
+#else /* GTK < 2.4.0 */
+
+	/* TODO */
+
+#endif /* GTK < 2.4.0 */
+
+	gtk_widget_show(combo);
+	gtk_box_pack_start(GTK_BOX(selector), combo, FALSE, FALSE, 0);
+
+	selector->priv->sw = sw = gtk_scrolled_window_new(NULL, NULL);
+	gtk_box_pack_start(GTK_BOX(selector), sw, TRUE, TRUE, 0);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+										GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+								   GTK_POLICY_NEVER,
+								   GTK_POLICY_AUTOMATIC);
+
+	selector->priv->entry = entry = gtk_imhtml_new(NULL, NULL);
+	gtk_widget_show(entry);
+	gtk_container_add(GTK_CONTAINER(sw), entry);
+	gtk_widget_set_name(entry, "gaim_gtk_status_selector_imhtml");
+	gtk_imhtml_set_editable(GTK_IMHTML(entry), TRUE);
+	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(entry), GTK_WRAP_WORD_CHAR);
+
+	if (gaim_prefs_get_bool("/gaim/gtk/conversations/spellcheck"))
+		gaim_gtk_setup_gtkspell(GTK_TEXT_VIEW(entry));
+
+	gaim_signal_connect(gaim_connections_get_handle(), "signed-on",
+						selector, GAIM_CALLBACK(signed_on_off_cb),
+						selector);
+	gaim_signal_connect(gaim_connections_get_handle(), "signed-off",
+						selector, GAIM_CALLBACK(signed_on_off_cb),
+						selector);
+
+	rebuild_list(selector);
+}
+
+static void
+gaim_gtk_status_selector_finalize(GObject *obj)
+{
+	GaimGtkStatusSelector *selector;
+
+	g_return_if_fail(obj != NULL);
+	g_return_if_fail(GAIM_GTK_IS_STATUS_SELECTOR(obj));
+
+	selector = GAIM_GTK_STATUS_SELECTOR(obj);
+
+	g_free(selector->priv);
+
+	if (G_OBJECT_CLASS(parent_class)->finalize)
+		G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+gaim_gtk_status_selector_destroy(GtkObject *obj)
+{
+	GaimGtkStatusSelector *selector;
+
+	g_return_if_fail(obj != NULL);
+	g_return_if_fail(GAIM_GTK_IS_STATUS_SELECTOR(obj));
+
+	selector = GAIM_GTK_STATUS_SELECTOR(obj);
+
+	if (selector->priv->icon_table != NULL)
+	{
+		g_hash_table_destroy(selector->priv->icon_table);
+		selector->priv->icon_table = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS(parent_class)->destroy)
+		GTK_OBJECT_CLASS(parent_class)->destroy(obj);
+}
+
+static void
+status_switched_cb(GtkWidget *combo, GaimGtkStatusSelector *selector)
+{
+	GtkTreeIter iter;
+
+	if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(selector->priv->combo),
+									  &iter))
+	{
+		const char *status_type_id;
+		char *text;
+		GList *l;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(selector->priv->model), &iter,
+						   COLUMN_NAME, &text,
+						   COLUMN_STATUS_TYPE_ID, &status_type_id,
+						   -1);
+
+		if (!strcmp(text, _("New status")))
+		{
+			/* TODO */
+		}
+		else
+		{
+			const char *message = "";
+			GtkTextBuffer *buffer;
+			gboolean allow_message = FALSE;
+
+			buffer =
+				gtk_text_view_get_buffer(GTK_TEXT_VIEW(selector->priv->entry));
+
+			gtk_text_buffer_set_text(buffer, message, -1);
+
+			for (l = gaim_connections_get_all(); l != NULL; l = l->next)
+			{
+				GaimConnection *gc = (GaimConnection *)l->data;
+				GaimAccount *account = gaim_connection_get_account(gc);
+				GaimStatusType *status_type;
+
+				status_type = gaim_account_get_status_type(account,
+														   status_type_id);
+
+				if (status_type == NULL)
+					continue;
+
+				if (gaim_status_type_get_attr(status_type, "message") != NULL)
+				{
+					gaim_account_set_status(account,
+											"away", TRUE,
+											"message", message,
+											NULL);
+
+					allow_message = TRUE;
+				}
+				else
+				{
+					gaim_account_set_status(gaim_connection_get_account(gc),
+											"away", TRUE,
+											NULL);
+				}
+			}
+
+			if (allow_message)
+				gtk_widget_show(selector->priv->sw);
+			else
+				gtk_widget_hide(selector->priv->sw);
+		}
+	}
+}
+
+static void
+signed_on_off_cb(GaimConnection *gc, GaimGtkStatusSelector *selector)
+{
+	rebuild_list(selector);
+}
+
+static GdkPixbuf *
+load_icon(const char *basename)
+{
+	char *filename;
+	GdkPixbuf *pixbuf, *scale = NULL;
+
+	if (!strcmp(basename, "available.png"))
+		basename = "online.png";
+	else if (!strcmp(basename, "hidden.png"))
+		basename = "invisible.png";
+
+	filename = g_build_filename(DATADIR, "pixmaps", "gaim", "icons",
+								basename, NULL);
+	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+	g_free(filename);
+
+	if (pixbuf != NULL)
+	{
+		scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16,
+										GDK_INTERP_BILINEAR);
+
+		g_object_unref(G_OBJECT(pixbuf));
+	}
+	else
+	{
+		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
+									"default", basename, NULL);
+		scale = gdk_pixbuf_new_from_file(filename, NULL);
+		g_free(filename);
+	}
+
+	return scale;
+}
+
+static void
+add_item(GaimGtkStatusSelector *selector, const char *status_type_id,
+		 const char *text, GdkPixbuf *pixbuf)
+{
+	GtkTreeIter iter;
+
+	gtk_list_store_append(selector->priv->model, &iter);
+	gtk_list_store_set(selector->priv->model, &iter,
+					   COLUMN_STATUS_TYPE_ID, status_type_id,
+					   COLUMN_ICON, pixbuf,
+					   COLUMN_NAME, text,
+					   -1);
+
+	if (pixbuf != NULL)
+		g_object_unref(G_OBJECT(pixbuf));
+}
+
+static void
+rebuild_list(GaimGtkStatusSelector *selector)
+{
+	gboolean single_prpl = TRUE;
+	GaimAccount *first_account = NULL;
+	const char *first_prpl_type = NULL;
+	GList *l;
+
+	g_return_if_fail(selector != NULL);
+	g_return_if_fail(GAIM_GTK_IS_STATUS_SELECTOR(selector));
+
+	gtk_list_store_clear(selector->priv->model);
+
+	/*
+	 * If the user only has one IM account or one type of IM account
+	 * connected, they'll see all their statuses. This is ideal for those
+	 * who use only one account, or one single protocol. Everyone else
+	 * gets Available and Away and a list of saved statuses.
+	 */
+	for (l = gaim_connections_get_all(); l != NULL && single_prpl; l = l->next)
+	{
+		GaimConnection *gc = (GaimConnection *)l->data;
+		GaimAccount *account = gaim_connection_get_account(gc);
+		GaimPluginProtocolInfo *prpl_info;
+		const char *basename;
+
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+		basename = prpl_info->list_icon(account, NULL);
+
+		if (first_prpl_type == NULL)
+		{
+			first_prpl_type = basename;
+			first_account = account;
+		}
+		else if (!strcmp(first_prpl_type, basename))
+			single_prpl = FALSE;
+	}
+
+	if (single_prpl)
+	{
+		const GList *l;
+
+		for (l = gaim_account_get_status_types(first_account);
+			 l != NULL;
+			 l = l->next)
+		{
+			GaimStatusType *status_type = (GaimStatusType *)l->data;
+			char filename[BUFSIZ];
+
+			if (!gaim_status_type_is_user_settable(status_type))
+				continue;
+
+			g_snprintf(filename, sizeof(filename), "%s.png",
+					   gaim_status_type_get_id(status_type));
+
+			add_item(selector,
+					 gaim_status_type_get_id(status_type),
+					 gaim_status_type_get_name(status_type),
+					 load_icon(filename));
+		}
+	}
+	else
+	{
+		add_item(selector, "available", _("Available"),
+				 load_icon("online.png"));
+		add_item(selector, "away", _("Away"), load_icon("away.png"));
+	}
+
+	add_item(selector, NULL, _("New Status"),
+			 gtk_widget_render_icon(GTK_WIDGET(selector), GTK_STOCK_NEW,
+									GTK_ICON_SIZE_MENU, NULL));
+}
+
+GtkWidget *
+gaim_gtk_status_selector_new(void)
+{
+	GaimGtkStatusSelector *selector;
+
+	selector = g_object_new(GAIM_GTK_TYPE_STATUS_SELECTOR, NULL);
+
+	return GTK_WIDGET(selector);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkstatusselector.h	Sun Nov 14 20:15:09 2004 +0000
@@ -0,0 +1,85 @@
+/**
+ * @file gtkstatusselector.h Status selector widget
+ * @ingroup gtkui
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef _GAIM_GTKSTATUSSELECTOR_H_
+#define _GAIM_GTKSTATUSSELECTOR_H_
+
+#include <gtk/gtkvbox.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GAIM_GTK_TYPE_STATUS_SELECTOR            (gaim_gtk_status_selector_get_type())
+#define GAIM_GTK_STATUS_SELECTOR(obj)            (GTK_CHECK_CAST((obj), GAIM_GTK_TYPE_STATUS_SELECTOR, GaimGtkStatusSelector))
+#define GAIM_GTK_STATUS_SELECTOR_CLASS(klass)    (GTK_CHECK_CLASS_CAST((klass), GAIM_GTK_TYPE_STATUS_SELECTOR, GaimGtkStatusSelectorClass))
+#define GAIM_GTK_IS_STATUS_SELECTOR(obj)         (GTK_CHECK_TYPE((obj), GAIM_GTK_TYPE_STATUS_SELECTOR))
+#define GAIM_GTK_IS_STATUS_SELECTOR_CLASS(klass) (GTK_CHECK_CLASS_TYPE((klass), GAIM_GTK_TYPE_STATUS_SELECTOR))
+
+typedef struct _GaimGtkStatusSelector        GaimGtkStatusSelector;
+typedef struct _GaimGtkStatusSelectorClass   GaimGtkStatusSelectorClass;
+typedef struct _GaimGtkStatusSelectorPrivate GaimGtkStatusSelectorPrivate;
+
+struct _GaimGtkStatusSelector
+{
+	GtkVBox parent_object;
+
+	GaimGtkStatusSelectorPrivate *priv;
+
+	void (*_gtk_reserved1)(void);
+	void (*_gtk_reserved2)(void);
+	void (*_gtk_reserved3)(void);
+	void (*_gtk_reserved4)(void);
+};
+
+struct _GaimGtkStatusSelectorClass
+{
+	GtkVBoxClass parent_class;
+
+	void (*_gtk_reserved1)(void);
+	void (*_gtk_reserved2)(void);
+	void (*_gtk_reserved3)(void);
+	void (*_gtk_reserved4)(void);
+};
+
+
+/**
+ * Returns the status selector widget's GType.
+ *
+ * @return GaimGtkStatusSelector's GType.
+ */
+GType gaim_gtk_status_selector_get_type(void);
+
+/**
+ * Creates a new status selector widget.
+ *
+ * @return The new status selector widget.
+ */
+GtkWidget *gaim_gtk_status_selector_new(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GAIM_GTKSTATUSSELECTOR_H_ */