pidgin/gtkblist.c

branch
soc.2008.themes
changeset 24376
5c51bb7ab077
parent 24192
f1ca2fb37c50
parent 23973
189295d2ee8e
child 24377
8d0bfda8402d
--- a/pidgin/gtkblist.c	Thu Aug 28 09:20:22 2008 +0000
+++ b/pidgin/gtkblist.c	Thu Aug 28 13:00:23 2008 +0000
@@ -38,6 +38,8 @@
 #include "request.h"
 #include "signals.h"
 #include "pidginstock.h"
+#include "theme-loader.h"
+#include "theme-manager.h"
 #include "util.h"
 
 #include "gtkaccount.h"
@@ -58,6 +60,8 @@
 #include "gtkstatusbox.h"
 #include "gtkscrollbook.h"
 #include "gtksmiley.h"
+#include "gtkblist-loader.h"
+#include "gtkblist-theme.h"
 #include "gtkutils.h"
 #include "pidgin/minidialog.h"
 #include "pidgin/pidgintooltip.h"
@@ -121,6 +125,9 @@
 	 *  is showing; @c NULL otherwise.
 	 */
 	PidginMiniDialog *signed_on_elsewhere;
+
+	PidginBlistTheme *current_theme;
+
 } PidginBuddyListPrivate;
 
 #define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \
@@ -1790,7 +1797,8 @@
 	return handled;
 }
 
-static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data)
+static gboolean 
+gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer user_data)
 {
 	GtkTreePath *path;
 	PurpleBlistNode *node;
@@ -3776,19 +3784,22 @@
 	return ret;
 }
 
-gchar *pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased)
-{
-	const char *name;
-	char *esc, *text = NULL;
+gchar *
+pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased)
+{
+	const char *name, *name_color, *name_font, *status_color, *status_font;
+	char *text = NULL;
 	PurplePlugin *prpl;
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleContact *contact;
 	PurplePresence *presence;
 	struct _pidgin_blist_node *gtkcontactnode = NULL;
-	char *idletime = NULL, *statustext = NULL;
-	time_t t;
+	char *idletime = NULL, *statustext = NULL, *nametext = NULL;
 	PurpleConversation *conv = find_conversation_with_buddy(b);
 	gboolean hidden_conv = FALSE;
+	gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
+	FontColorPair *pair;
+	PidginBlistTheme *theme;
 
 	if (conv != NULL) {
 		PidginBlistNode *ui = b->node.ui_data;
@@ -3806,174 +3817,164 @@
 	if(contact)
 		gtkcontactnode = ((PurpleBlistNode*)contact)->ui_data;
 
-	if(gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias)
+	/* Name */
+	if (gtkcontactnode && !gtkcontactnode->contact_expanded && contact->alias)
 		name = contact->alias;
 	else
 		name = purple_buddy_get_alias(b);
 	
-	esc = g_markup_escape_text(name, strlen(name));
+	nametext = g_markup_escape_text(name, strlen(name));
 
 	presence = purple_buddy_get_presence(b);
 
-	if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons") && aliased)
-	{
-		if (!selected && purple_presence_is_idle(presence))
-		{
-			text = g_strdup_printf("<span color='%s'>%s</span>",
-					       dim_grey(), esc);
-			g_free(esc);
-			if (hidden_conv) {
-				char *tmp = text;
-				text = g_strdup_printf("<b>%s</b>", text);
+	/* Name is all that is needed */
+	if (aliased && biglist) {
+
+		/* Status Info */
+		prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
+	
+		if (prpl != NULL)
+			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	
+		if (prpl_info && prpl_info->status_text && b->account->gc) {
+			char *tmp = prpl_info->status_text(b);
+			const char *end;
+	
+			if(tmp && !g_utf8_validate(tmp, -1, &end)) {
+				char *new = g_strndup(tmp,
+						g_utf8_pointer_to_offset(tmp, end));
+				g_free(tmp);
+				tmp = new;
+			}
+			/* add ... to messages that are too long, GTK 2.6+ does it automatically */
+#if !GTK_CHECK_VERSION(2,6,0)
+			if(tmp) {
+				char buf[32];
+				char *c = tmp;
+				int length = 0, vis=0;
+				gboolean inside = FALSE;
+				g_strdelimit(tmp, "\n", ' ');
+				purple_str_strip_char(tmp, '\r');
+	
+				while(*c && vis < 20) {
+					if(*c == '&')
+						inside = TRUE;
+					else if(*c == ';')
+						inside = FALSE;
+					if(!inside)
+						vis++;
+					c = g_utf8_next_char(c); /* this is fun */
+				}
+	
+				length = c - tmp;
+
+				if(vis == 20)
+					g_snprintf(buf, sizeof(buf), "%%.%ds...", length);
+				else
+					g_snprintf(buf, sizeof(buf), "%%s ");
+	
+				statustext = g_strdup_printf(buf, tmp);purple_presence_is_idle(presence)
+	
 				g_free(tmp);
 			}
-			return text;
-		}
-		else if (hidden_conv)
-		{
-			char *tmp = esc;
-			esc = g_strdup_printf("<b>%s</b>", esc);
-			g_free(tmp);
-		}
-		return esc;
-	}
-
-	prpl = purple_find_prpl(purple_account_get_protocol_id(b->account));
-
-	if (prpl != NULL)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info && prpl_info->status_text && b->account->gc) {
-		char *tmp = prpl_info->status_text(b);
-		const char *end;
-
-		if(tmp && !g_utf8_validate(tmp, -1, &end)) {
-			char *new = g_strndup(tmp,
-					g_utf8_pointer_to_offset(tmp, end));
-			g_free(tmp);
-			tmp = new;
+#else	
+			if(tmp) {
+				g_strdelimit(tmp, "\n", ' ');
+				purple_str_strip_char(tmp, '\r');
+			}
+			statustext = tmp;
+#endif	
 		}
-
-#if !GTK_CHECK_VERSION(2,6,0)
-		if(tmp) {
-			char buf[32];
-			char *c = tmp;
-			int length = 0, vis=0;
-			gboolean inside = FALSE;
-			g_strdelimit(tmp, "\n", ' ');
-			purple_str_strip_char(tmp, '\r');
-
-			while(*c && vis < 20) {
-				if(*c == '&')
-					inside = TRUE;
-				else if(*c == ';')
-					inside = FALSE;
-				if(!inside)
-					vis++;
-				c = g_utf8_next_char(c); /* this is fun */
-			}
-
-			length = c - tmp;
-
-			if(vis == 20)
-				g_snprintf(buf, sizeof(buf), "%%.%ds...", length);
-			else
-				g_snprintf(buf, sizeof(buf), "%%s ");
-
-			statustext = g_strdup_printf(buf, tmp);
-
-			g_free(tmp);
-		}
-#else
-		if(tmp) {
-			g_strdelimit(tmp, "\n", ' ');
-			purple_str_strip_char(tmp, '\r');
-		}
-		statustext = tmp;
-#endif
-	}
-
-	if(!purple_presence_is_online(presence) && !statustext)
-		statustext = g_strdup(_("Offline"));
-	else if (!statustext)
-		text = g_strdup(esc);
-
-	if (purple_presence_is_idle(presence)) {
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) {
+	
+		if(!purple_presence_is_online(presence) && !statustext)
+				statustext = g_strdup(_("Offline"));
+		
+		/* Idle Text */ 
+		if (purple_presence_is_idle(presence) && purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time")) {
 			time_t idle_secs = purple_presence_get_idle_time(presence);
 
 			if (idle_secs > 0) {
 				int iday, ihrs, imin;
+				time_t t;
 
 				time(&t);
 				iday = (t - idle_secs) / (24 * 60 * 60);
 				ihrs = ((t - idle_secs) / 60 / 60) % 24;
 				imin = ((t - idle_secs) / 60) % 60;
-
-                if (iday)
+	
+               			if (iday)
 					idletime = g_strdup_printf(_("Idle %dd %dh %02dm"), iday, ihrs, imin);
 				else if (ihrs)
 					idletime = g_strdup_printf(_("Idle %dh %02dm"), ihrs, imin);
 				else
 					idletime = g_strdup_printf(_("Idle %dm"), imin);
-			}
-			else
-				idletime = g_strdup(_("Idle"));
-
-			if (!selected) {
-				g_free(text);
-				text = g_strdup_printf("<span color='%s'>%s</span>\n"
-					"<span color='%s' size='smaller'>%s%s%s</span>",
-					dim_grey(), esc, dim_grey(),
-					idletime != NULL ? idletime : "",
-					(idletime != NULL && statustext != NULL) ? " - " : "",
-					statustext != NULL ? statustext : "");
-			}
-		}
-		else if (!selected && !statustext) {/* We handle selected text later */
-			g_free(text);
-			text = g_strdup_printf("<span color='%s'>%s</span>", dim_grey(), esc);
-		} else if (!selected && !text) {
-			g_free(text);
-			text = g_strdup_printf("<span color='%s'>%s</span>\n"
-				"<span color='%s' size='smaller'>%s</span>",
-				dim_grey(), esc, dim_grey(),
-				statustext != NULL ? statustext : "");
+
+			} else idletime = g_strdup(_("Idle"));
 		}
-	} else if (!PURPLE_BUDDY_IS_ONLINE(b)) {
-		if (!selected && !statustext) {/* We handle selected text later */
-			g_free(text);
-			text = g_strdup_printf("<span color='%s'>%s</span>", dim_grey(), esc);
-		} else if (!selected && !text)
-			text = g_strdup_printf("<span color='%s'>%s</span>\n"
-				"<span color='%s' size='smaller'>%s</span>",
-				dim_grey(), esc, dim_grey(),
-				statustext != NULL ? statustext : "");
-
-	}
-	/* Not idle and not selected */
-	else if (!selected && !text)
-	{
-		text = g_strdup_printf("%s\n"
-			"<span color='%s' size='smaller'>%s</span>",
-			esc, dim_grey(),
-			statustext != NULL ? statustext :  "");
-	}
-
-	/* It is selected. */
-	if ((selected && !text) || (selected && idletime)) {
-		g_free(text);
-		text = g_strdup_printf("%s\n"
-			"<span size='smaller'>%s%s%s</span>",
-			esc,
-			idletime != NULL ? idletime : "",
-			(idletime != NULL && statustext != NULL) ? " - " : "",
-			statustext != NULL ? statustext :  "");
-	}
-
-	g_free(idletime);
-	g_free(statustext);
-	g_free(esc);
+	}
+
+	/* choose the colors of the text */
+	theme = pidgin_blist_get_theme();
+
+	if (theme == NULL) {
+		status_color = name_color = "dim grey";
+		status_font = name_font = "";
+
+	} else  if (purple_presence_is_idle(presence)) {
+		pair = pidgin_blist_theme_get_idle_text_info(theme);
+		status_color = name_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey";
+		status_font = name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+	} else if (!purple_presence_is_online(presence)) {
+		pair = pidgin_blist_theme_get_offline_text_info(theme);
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black";
+		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+		pair = pidgin_blist_theme_get_status_text_info(theme);
+		status_color = (pair != NULL && pair->color != NULL) ? g_strdup(pair->color) : "dim grey";
+		status_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+	} else if (purple_presence_is_available(presence)) {
+		pair = pidgin_blist_theme_get_online_text_info(theme);
+		name_color = (pair != NULL && pair->color != NULL) ? g_strdup(pair->color) : "black";
+		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+		pair = pidgin_blist_theme_get_status_text_info(theme);
+		status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey";
+		status_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+	} else {
+		pair = pidgin_blist_theme_get_away_text_info(theme);
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black";
+		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+
+		pair = pidgin_blist_theme_get_status_text_info(theme);
+		status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey";
+		status_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
+	}
+
+	if (aliased && selected) {
+		name_color = "black";
+		status_color = "black";
+	}
+
+	/* Put it all together */
+	if (aliased && biglist && (statustext || idletime)) {
+		/* using <span size='smaller'> breaks the status, so it must be seperated into <small><span>*/
+		text = g_strdup_printf("<span font_desc='%s' foreground='%s'>%s</span>\n"
+				 	"<small><span font_desc='%s' foreground='%s'>%s%s%s</span></small>", 
+					name_font, name_color, nametext, status_font, status_color, 
+					idletime != NULL ? idletime : "",
+				        (idletime != NULL && statustext != NULL) ? " - " : "",
+				        statustext != NULL ? statustext : ""); 
+
+	} else text = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>", name_font, name_color, nametext); 
+
+	g_free(nametext);
+	if (statustext)
+		g_free(statustext);
+	if (idletime)
+		g_free(idletime);
 
 	if (hidden_conv) {
 		char *tmp = text;
@@ -5225,11 +5226,144 @@
 }
 #endif
 
+/* builds the blist layout according to to the current theme */
+static void
+pidgin_blist_build_layout(PurpleBuddyList *list)
+{
+	GtkTreeViewColumn *column;
+	PidginBlistLayout *layout;
+	PidginBlistTheme *theme;
+	GtkCellRenderer *rend;
+	gint i, status_icon = 0, text = 1, emblem = 2, protocol_icon = 3, buddy_icon = 4;
+	
+
+	column = gtkblist->text_column;
+
+	if ((theme = pidgin_blist_get_theme()) != NULL && (layout = pidgin_blist_theme_get_layout(theme)) != NULL) {
+		status_icon = layout->status_icon ;				
+		text = layout->text;
+		emblem = layout->emblem;
+		protocol_icon = layout->protocol_icon;
+		buddy_icon = layout->buddy_icon;
+	}
+
+	gtk_tree_view_column_clear(column);
+
+	/* group */
+	rend = pidgin_cell_renderer_expander_new();
+	gtk_tree_view_column_pack_start(column, rend, FALSE);
+	gtk_tree_view_column_set_attributes(column, rend,
+					    "visible", GROUP_EXPANDER_VISIBLE_COLUMN,
+					    "expander-visible", GROUP_EXPANDER_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+					    "sensitive", GROUP_EXPANDER_COLUMN,
+					    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+					    NULL);
+
+	/* contact */
+	rend = pidgin_cell_renderer_expander_new();
+	gtk_tree_view_column_pack_start(column, rend, FALSE);
+	gtk_tree_view_column_set_attributes(column, rend,
+					    "visible", CONTACT_EXPANDER_VISIBLE_COLUMN,
+					    "expander-visible", CONTACT_EXPANDER_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+					    "sensitive", CONTACT_EXPANDER_COLUMN,
+					    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif  
+					    NULL);
+
+	for (i = 0; i < 5; i++) {
+
+		if (status_icon == i) {		
+			/* status icons */
+			rend = gtk_cell_renderer_pixbuf_new();
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend,
+							    "pixbuf", STATUS_ICON_COLUMN,
+							    "visible", STATUS_ICON_VISIBLE_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif	
+							    NULL);
+			g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
+
+		} else if (text == i) {
+			/* name */
+			gtkblist->text_rend = rend = gtk_cell_renderer_text_new();
+			gtk_tree_view_column_pack_start(column, rend, TRUE);
+			gtk_tree_view_column_set_attributes(column, rend,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							    "markup", NAME_COLUMN,
+							    NULL);
+#if GTK_CHECK_VERSION(2,6,0)
+			g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL);
+			g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list);
+#endif
+			g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list);
+			g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
+#if GTK_CHECK_VERSION(2,6,0)
+			g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+#endif
+
+			/* idle */
+			rend = gtk_cell_renderer_text_new();
+			g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend,
+							    "markup", IDLE_COLUMN,
+							    "visible", IDLE_VISIBLE_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							    NULL);
+		} else if (emblem == i) {
+			/* emblem */
+			rend = gtk_cell_renderer_pixbuf_new();
+			g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL);
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+									  "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+									  "visible", EMBLEM_VISIBLE_COLUMN, NULL);
+
+		} else if (protocol_icon == i) {
+			/* protocol icon */
+			rend = gtk_cell_renderer_pixbuf_new();
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend,
+							   "pixbuf", PROTOCOL_ICON_COLUMN,
+							   "visible", PROTOCOL_ICON_VISIBLE_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							   "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							  NULL);
+			g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
+
+		} else if (buddy_icon == i) {
+			/* buddy icon */
+			rend = gtk_cell_renderer_pixbuf_new();
+			g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
+			gtk_tree_view_column_pack_start(column, rend, FALSE);
+			gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN,
+#if GTK_CHECK_VERSION(2,6,0)
+							    "cell-background-gdk", BGCOLOR_COLUMN,
+#endif
+							    "visible", BUDDY_ICON_VISIBLE_COLUMN,
+							    NULL);
+		}
+
+	}/* end for loop */
+
+}
+
 static void pidgin_blist_show(PurpleBuddyList *list)
 {
 	PidginBuddyListPrivate *priv;
 	void *handle;
-	GtkCellRenderer *rend;
 	GtkTreeViewColumn *column;
 	GtkWidget *menu;
 	GtkWidget *ebox;
@@ -5255,6 +5389,8 @@
 	gtkblist = PIDGIN_BLIST(list);
 	priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
 
+	priv->current_theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), "blist"));
+
 	gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
 	gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000);
 
@@ -5287,8 +5423,8 @@
 	gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu),
 								  blist_menu, NULL);
 	pidgin_load_accels();
-	g_signal_connect(G_OBJECT(accel_group), "accel-changed",
-														G_CALLBACK(pidgin_save_accels_cb), NULL);
+	g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(pidgin_save_accels_cb), NULL);
+
 	menu = gtk_item_factory_get_widget(gtkblist->ift, "<PurpleMain>");
 	gtkblist->menutray = pidgin_menu_tray_new();
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtkblist->menutray);
@@ -5438,105 +5574,16 @@
 
 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE);
 
+	/* expander columns */
 	column = gtk_tree_view_column_new();
 	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
 	gtk_tree_view_column_set_visible(column, FALSE);
 	gtk_tree_view_set_expander_column(GTK_TREE_VIEW(gtkblist->treeview), column);
 
-	gtkblist->text_column = column = gtk_tree_view_column_new ();
-	rend = pidgin_cell_renderer_expander_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "visible", GROUP_EXPANDER_VISIBLE_COLUMN,
-					    "expander-visible", GROUP_EXPANDER_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "sensitive", GROUP_EXPANDER_COLUMN,
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    NULL);
-
-	rend = pidgin_cell_renderer_expander_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "expander-visible", CONTACT_EXPANDER_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "sensitive", CONTACT_EXPANDER_COLUMN,
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    "visible", CONTACT_EXPANDER_VISIBLE_COLUMN,
-					    NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "pixbuf", STATUS_ICON_COLUMN,
-					    "visible", STATUS_ICON_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    NULL);
-	g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
-
-	gtkblist->text_rend = rend = gtk_cell_renderer_text_new();
-	gtk_tree_view_column_pack_start (column, rend, TRUE);
-	gtk_tree_view_column_set_attributes(column, rend,
-#if GTK_CHECK_VERSION(2,6,0)
-					    	    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-										"markup", NAME_COLUMN,
-										NULL);
-#if GTK_CHECK_VERSION(2,6,0)
-	g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL);
-	g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list);
-#endif
-	g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list);
-	g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
-#if GTK_CHECK_VERSION(2,6,0)
-	g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
-	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column);
-
-	rend = gtk_cell_renderer_text_new();
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					    "markup", IDLE_COLUMN,
-					    "visible", IDLE_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-							  "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-							  "visible", EMBLEM_VISIBLE_COLUMN, NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend,
-					   "pixbuf", PROTOCOL_ICON_COLUMN,
-					   "visible", PROTOCOL_ICON_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					   "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					  NULL);
-	g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
-
-	rend = gtk_cell_renderer_pixbuf_new();
-	g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
-	gtk_tree_view_column_pack_start(column, rend, FALSE);
-	gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
-					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
-					    "visible", BUDDY_ICON_VISIBLE_COLUMN,
-					    NULL);
-
+	/* everything else column */
+	gtkblist->text_column = gtk_tree_view_column_new ();
+	gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->text_column);
+	pidgin_blist_build_layout(list);
 
 	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL);
 	g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL);
@@ -5963,13 +6010,21 @@
 		GtkTreeIter iter;
 		GtkTreePath *path;
 		gboolean expanded;
-		GdkColor bgcolor;
+		GdkColor *bgcolor = NULL;
 		GdkPixbuf *avatar = NULL;
+		PidginBlistTheme *theme = NULL;
 
 		if(!insert_node(list, gnode, &iter))
 			return;
 
-		bgcolor = gtkblist->treeview->style->bg[GTK_STATE_ACTIVE];
+		if ((theme = pidgin_blist_get_theme()) == NULL)
+			bgcolor = NULL;
+		else if (purple_blist_node_get_bool(gnode, "collapsed") || count <= 0)
+			bgcolor = pidgin_blist_theme_get_collapsed_background_color(theme);
+		else bgcolor = pidgin_blist_theme_get_expanded_background_color(theme);
+
+		if (bgcolor == NULL)
+			bgcolor = &(gtkblist->treeview->style->bg[GTK_STATE_ACTIVE]);
 
 		path = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter);
 		expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(gtkblist->treeview), path);
@@ -5987,7 +6042,7 @@
 				   STATUS_ICON_COLUMN, NULL,
 				   NAME_COLUMN, title,
 				   NODE_COLUMN, gnode,
-	/*			   BGCOLOR_COLUMN, &bgcolor,     */
+				   BGCOLOR_COLUMN, bgcolor,
 				   GROUP_EXPANDER_COLUMN, TRUE,
 				   GROUP_EXPANDER_VISIBLE_COLUMN, TRUE,
 				   CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE,
@@ -6010,6 +6065,9 @@
 	char *mark, *esc;
 	PurpleBlistNode *selected_node = NULL;
 	GtkTreeIter iter;
+	FontColorPair *pair;
+	gchar *text_color, *text_font;
+	PidginBlistTheme *theme;
 
 	group = (PurpleGroup*)gnode;
 
@@ -6025,8 +6083,20 @@
 		           purple_blist_get_group_size(group, FALSE));
 	}
 
+	theme = pidgin_blist_get_theme();
+	if (theme == NULL)
+		pair = NULL;
+	else if 
+		(expanded) pair = pidgin_blist_theme_get_expanded_text_info(theme);
+	else pair = pidgin_blist_theme_get_collapsed_text_info(theme);
+
+	
+	text_color = (selected || pair == NULL || pair->color == NULL) ? "black" : pair->color;
+	text_font = (pair == NULL || pair->font == NULL) ? "" : pair->font;
+
 	esc = g_markup_escape_text(group->name, -1);
-	mark = g_strdup_printf("<span weight='bold'>%s</span>%s", esc ? esc : "", group_count);
+	mark = g_strdup_printf("<span foreground='%s' font_desc='%s'><b>%s</b>%s</span>",
+							text_color, text_font, esc ? esc : "", group_count);
 
 	g_free(esc);
 	return mark;
@@ -6034,14 +6104,15 @@
 
 static void buddy_node(PurpleBuddy *buddy, GtkTreeIter *iter, PurpleBlistNode *node)
 {
-	PurplePresence *presence;
+	PurplePresence *presence = purple_buddy_get_presence(buddy);
 	GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
+	GdkColor *color = NULL;
 	char *mark;
 	char *idle = NULL;
 	gboolean expanded = ((struct _pidgin_blist_node *)(node->parent->ui_data))->contact_expanded;
 	gboolean selected = (gtkblist->selected_node == node);
 	gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
-	presence = purple_buddy_get_presence(buddy);
+	PidginBlistTheme *theme;	
 
 	if (editing_blist)
 		return;
@@ -6065,35 +6136,38 @@
 	emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy);
 	mark = pidgin_blist_get_name_markup(buddy, selected, TRUE);
 
+	theme = pidgin_blist_get_theme();
+
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time") &&
-		purple_presence_is_idle(presence) &&
-		!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"))
+		purple_presence_is_idle(presence) && !biglist)
 	{
 		time_t idle_secs = purple_presence_get_idle_time(presence);
 
 		if (idle_secs > 0)
 		{
+			FontColorPair *pair = NULL;
+			const gchar *textcolor;
 			time_t t;
 			int ihrs, imin;
 			time(&t);
+
 			ihrs = (t - idle_secs) / 3600;
 			imin = ((t - idle_secs) / 60) % 60;
-			idle = g_strdup_printf("%d:%02d", ihrs, imin);
-		}
-	}
-
-	if (purple_presence_is_idle(presence))
-	{
-		if (idle && !selected) {
-			char *i2 = g_strdup_printf("<span color='%s'>%s</span>",
-						   dim_grey(), idle);
-			g_free(idle);
-			idle = i2;
+
+			if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL && pair->color != NULL)
+				textcolor = pair->color;
+			else textcolor = "black";
+
+			idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>", textcolor, 
+					      (pair == NULL || pair->font == NULL) ? "" : pair->font, ihrs, imin);
 		}
 	}
 
 	prpl_icon = pidgin_create_prpl_icon(buddy->account, PIDGIN_PRPL_ICON_SMALL);
 
+	if (theme != NULL)
+		color = pidgin_blist_theme_get_contact_color(theme);
+
 	gtk_tree_store_set(gtkblist->treemodel, iter,
 			   STATUS_ICON_COLUMN, status,
 			   STATUS_ICON_VISIBLE_COLUMN, TRUE,
@@ -6106,7 +6180,7 @@
 			   EMBLEM_VISIBLE_COLUMN, (emblem != NULL),
 			   PROTOCOL_ICON_COLUMN, prpl_icon,
 			   PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
-			   BGCOLOR_COLUMN, NULL,
+			   BGCOLOR_COLUMN, color,
 			   CONTACT_EXPANDER_COLUMN, NULL,
 			   CONTACT_EXPANDER_VISIBLE_COLUMN, expanded,
 			   GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
@@ -6164,19 +6238,37 @@
 
 		if(gtknode->contact_expanded) {
 			GdkPixbuf *status;
-			char *mark;
+			gchar *mark;
+			GdkColor *color = NULL;
+			PidginBlistTheme *theme = pidgin_blist_get_theme();
+			gboolean selected = (gtkblist->selected_node == cnode);
+			
+			mark = g_markup_escape_text(purple_contact_get_alias(contact), -1);
+
+			if (theme != NULL) {
+				FontColorPair *pair = pidgin_blist_theme_get_contact_text_info(theme);
+				color = pidgin_blist_theme_get_contact_color(theme);
+
+				if (pair != NULL) {
+					gchar *temp = g_strdup_printf("<span foreground='%s' font_desc='%s'>%s</span>",
+								     (selected || pair->color == NULL) ? "black" : pair->color,
+								     (pair->font == NULL) ? "" : pair->font, mark);
+				
+					g_free(mark);
+					mark = temp;
+				}
+			}
 
 			status = pidgin_blist_get_status_icon(cnode,
 					 biglist? PIDGIN_STATUS_ICON_LARGE : PIDGIN_STATUS_ICON_SMALL);
-
-			mark = g_markup_escape_text(purple_contact_get_alias(contact), -1);
+			
 			gtk_tree_store_set(gtkblist->treemodel, &iter,
 					   STATUS_ICON_COLUMN, status,
 					   STATUS_ICON_VISIBLE_COLUMN, TRUE,
 					   NAME_COLUMN, mark,
 					   IDLE_COLUMN, NULL,
 					   IDLE_VISIBLE_COLUMN, FALSE,
-					   BGCOLOR_COLUMN, NULL,
+					   BGCOLOR_COLUMN, color,
 					   BUDDY_ICON_COLUMN, NULL,
 					   CONTACT_EXPANDER_COLUMN, TRUE,
 					   CONTACT_EXPANDER_VISIBLE_COLUMN, TRUE,
@@ -6244,12 +6336,16 @@
 	if(purple_account_is_connected(chat->account)) {
 		GtkTreeIter iter;
 		GdkPixbuf *status, *avatar, *emblem, *prpl_icon;
-		char *mark;
+		gchar *mark, *color, *font, *tmp;
 		gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
 		gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons");
 		PidginBlistNode *ui;
 		PurpleConversation *conv;
 		gboolean hidden;
+		GdkColor *bgcolor = NULL;
+		FontColorPair *pair;
+		PidginBlistTheme *theme;
+		gboolean selected = (gtkblist->selected_node == node);
 
 		if (!insert_node(list, node, &iter))
 			return;
@@ -6269,14 +6365,29 @@
 		else
 			avatar = NULL;
 
-		mark = g_markup_escape_text(purple_chat_get_name(chat), -1);
-		if (hidden) {
-			char *bold = g_strdup_printf("<b>%s</b>", mark);
-			g_free(mark);
-			mark = bold;
-		}
+		mark = g_markup_escape_text(purple_chat_get_name(chat), -1);	
+	
+		theme = pidgin_blist_get_theme();
+
+		if (theme == NULL) 
+			pair = NULL;
+		else if (hidden) 
+			pair = pidgin_blist_theme_get_unread_message_text_info(theme);
+		else pair = pidgin_blist_theme_get_online_text_info(theme); 
+			
+		font = (pair == NULL || pair->font == NULL) ? "" : g_strdup(pair->font);
+		color = (selected || pair == NULL || pair->color == NULL) ? "black" : g_strdup(pair->color);
+
+		tmp = g_strdup_printf("<span font_desc='%s' color='%s' weight='%s'>%s</span>", 
+				      font, color, hidden ? "bold" : "normal", mark);
+
+		g_free(mark);
+		mark = tmp;
 
 		prpl_icon = pidgin_create_prpl_icon(chat->account, PIDGIN_PRPL_ICON_SMALL);
+		
+		if (theme != NULL)
+			bgcolor = pidgin_blist_theme_get_contact_color(theme);
 
 		gtk_tree_store_set(gtkblist->treemodel, &iter,
 				STATUS_ICON_COLUMN, status,
@@ -6288,6 +6399,7 @@
 				PROTOCOL_ICON_COLUMN, prpl_icon,
 				PROTOCOL_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"),
 				NAME_COLUMN, mark,
+				BGCOLOR_COLUMN, bgcolor,
 				GROUP_EXPANDER_VISIBLE_COLUMN, FALSE,
 				-1);
 
@@ -6300,6 +6412,7 @@
 			g_object_unref(avatar);
 		if(prpl_icon)
 			g_object_unref(prpl_icon);
+
 	} else {
 		pidgin_blist_hide_node(list, node, TRUE);
 	}
@@ -7159,6 +7272,33 @@
 			(GSourceFunc)buddy_signonoff_timeout_cb, buddy);
 }
 
+void 
+pidgin_blist_set_theme(PidginBlistTheme *theme)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	PurpleBuddyList *list = purple_get_blist();
+
+	if (theme != NULL)
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme", 
+				purple_theme_get_name(PURPLE_THEME(theme)));
+	else purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme", "");
+
+	priv->current_theme = theme;
+
+	pidgin_blist_build_layout(list);
+
+	pidgin_blist_refresh(list);
+}
+
+
+PidginBlistTheme *
+pidgin_blist_get_theme()
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);	
+	
+	return priv->current_theme;
+}
+
 void pidgin_blist_init(void)
 {
 	void *gtk_blist_handle = pidgin_blist_get_handle();
@@ -7184,6 +7324,9 @@
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/width", 250); /* Golden ratio, baby */
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/height", 405); /* Golden ratio, baby */
 	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/blist/tooltip_delay", 500);
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/blist/theme", "");
+
+	purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_BLIST_THEME_LOADER, "type", "blist", NULL));
 
 	/* Register our signals */
 	purple_signal_register(gtk_blist_handle, "gtkblist-hiding",

mercurial