changeset 26664:5d71de58452c

Make it possible to load non-status icons using a PidginStockIconTheme. For now, just chat-room emblems can be set, but adding other icons should be trivial. WISHLIST (Crazy/Driveby Patch Writers step forward please!): * Move the theme-editor plugin to a subdirectory under plugins/. * Make the 'Save' buttons work, possibly ask for some details when saving (e.g. Author's name etc.)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Mon, 20 Apr 2009 03:20:15 +0000
parents 7519de6ea00f
children df0915055560
files pidgin/pidginstock.c pidgin/pidginstock.h pidgin/plugins/themeedit-icon.c pidgin/plugins/themeedit.c
diffstat 4 files changed, 219 insertions(+), 93 deletions(-) [+]
line wrap: on
line diff
--- a/pidgin/pidginstock.c	Sun Apr 19 19:45:53 2009 +0000
+++ b/pidgin/pidginstock.c	Mon Apr 20 03:20:15 2009 +0000
@@ -320,7 +320,7 @@
 }
 
 static gchar *
-find_icon_file(PidginStatusIconTheme *theme, const gchar *size, SizedStockIcon sized_icon, gboolean rtl)
+find_icon_file(PidginIconTheme *theme, const gchar *size, SizedStockIcon sized_icon, gboolean rtl)
 {
 	const gchar *file, *dir;
 	gchar *file_full = NULL;
@@ -352,7 +352,7 @@
 }
 
 static void
-add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginStatusIconTheme *theme,
+add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginIconTheme *theme,
 		const char *size, SizedStockIcon sized_icon, gboolean translucent)
 {
 	char *filename;
@@ -447,9 +447,9 @@
 			translucent = gtk_icon_set_new();
 
 #define ADD_SIZED_ICON(name, size) if (sized_status_icons[i].name) { \
-					add_sized_icon(normal, name, theme, size, sized_status_icons[i], FALSE); \
+					add_sized_icon(normal, name, PIDGIN_ICON_THEME(theme), size, sized_status_icons[i], FALSE); \
 					if (sized_status_icons[i].translucent_name) \
-						add_sized_icon(translucent, name, theme, size, sized_status_icons[i], TRUE); \
+						add_sized_icon(translucent, name, PIDGIN_ICON_THEME(theme), size, sized_status_icons[i], TRUE); \
 				   }
 		ADD_SIZED_ICON(microscopic, "11");
 		ADD_SIZED_ICON(extra_small, "16");
@@ -474,49 +474,41 @@
 }
 
 void
-pidgin_stock_init(void)
+pidgin_stock_load_stock_icon_theme(PidginStockIconTheme *theme)
 {
 	GtkIconFactory *icon_factory;
-	size_t i;
+	gint i;
 	GtkWidget *win;
-	PidginIconThemeLoader *loader;
-	const gchar *path = NULL;
-
-	if (stock_initted)
-		return;
 
-	stock_initted = TRUE;
+	if (theme != NULL) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/stock/icon-theme",
+				        purple_theme_get_name(PURPLE_THEME(theme)));
+		purple_prefs_set_path(PIDGIN_PREFS_ROOT "/stock/icon-theme-dir",
+				      purple_theme_get_dir(PURPLE_THEME(theme)));
+	}
+	else {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/stock/icon-theme", "");
+		purple_prefs_set_path(PIDGIN_PREFS_ROOT "/stock/icon-theme-dir", "");
+	}
 
-	/* Setup the status icon theme */
-	loader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "status-icon", NULL);
-	purple_theme_manager_register_type(PURPLE_THEME_LOADER(loader));
-	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/status/icon-theme", "");
-	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", "");
-
-	/* Setup the icon factory. */
 	icon_factory = gtk_icon_factory_new();
 
 	gtk_icon_factory_add_default(icon_factory);
 
-	/* Er, yeah, a hack, but it works. :) */
 	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 	gtk_widget_realize(win);
 
 	/* All non-sized icons */
-	for (i = 0; i < G_N_ELEMENTS(stock_icons); i++)
-	{
+	for (i = 0; i < G_N_ELEMENTS(stock_icons); i++) {
 		GtkIconSource *source;
 		GtkIconSet *iconset;
 		gchar *filename;
 
-		if (stock_icons[i].dir == NULL)
-		{
+		if (stock_icons[i].dir == NULL) {
 			/* GTK+ Stock icon */
 			iconset = gtk_style_lookup_icon_set(gtk_widget_get_style(win),
 					stock_icons[i].filename);
-		}
-		else
-		{
+		} else {
 			filename = find_file(stock_icons[i].dir, stock_icons[i].filename);
 
 			if (filename == NULL)
@@ -540,21 +532,13 @@
 		gtk_icon_set_unref(iconset);
 	}
 
-	/* register custom icon sizes */
-	microscopic =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11);
-	extra_small =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16);
-	small =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22);
-	medium =       gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MEDIUM, 32, 32);
-	large =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_LARGE, 48, 48);
-	huge =         gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_HUGE, 64, 64);
-
 	/* All non-status sized icons */
 	for (i = 0; i < G_N_ELEMENTS(sized_stock_icons); i++)
 	{
 		GtkIconSet *iconset = gtk_icon_set_new();
 
 #define ADD_SIZED_ICON(name, size) if (sized_stock_icons[i].name) \
-					add_sized_icon(iconset, name, NULL, size, sized_stock_icons[i], FALSE);
+					add_sized_icon(iconset, name, PIDGIN_ICON_THEME(theme), size, sized_stock_icons[i], FALSE);
 		ADD_SIZED_ICON(microscopic, "11");
 		ADD_SIZED_ICON(extra_small, "16");
 		ADD_SIZED_ICON(small, "22");
@@ -569,6 +553,39 @@
 
 	gtk_widget_destroy(win);
 	g_object_unref(G_OBJECT(icon_factory));
+}
+
+void
+pidgin_stock_init(void)
+{
+	PidginIconThemeLoader *loader, *stockloader;
+	const gchar *path = NULL;
+
+	if (stock_initted)
+		return;
+
+	stock_initted = TRUE;
+
+	/* Setup the status icon theme */
+	loader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "status-icon", NULL);
+	purple_theme_manager_register_type(PURPLE_THEME_LOADER(loader));
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/status/icon-theme", "");
+	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", "");
+
+	stockloader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "stock-icon", NULL);
+	purple_theme_manager_register_type(PURPLE_THEME_LOADER(stockloader));
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/stock/icon-theme", "");
+	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/stock/icon-theme-dir", "");
+
+	/* register custom icon sizes */
+	microscopic =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11);
+	extra_small =  gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16);
+	small =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22);
+	medium =       gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MEDIUM, 32, 32);
+	large =        gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_LARGE, 48, 48);
+	huge =         gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_HUGE, 64, 64);
+
+	pidgin_stock_load_stock_icon_theme(NULL);
 
 	/* Pre-load Status icon theme - this avoids a bug with displaying the correct icon in the tray, theme is destroyed after*/
 	if (purple_prefs_get_string(PIDGIN_PREFS_ROOT "/icon/status/theme") &&
@@ -583,3 +600,31 @@
 	/* Register the stock items. */
 	gtk_stock_add_static(stock_items, G_N_ELEMENTS(stock_items));
 }
+
+static void
+pidgin_stock_icon_theme_class_init(PidginStockIconThemeClass *klass)
+{
+}
+
+GType
+pidgin_stock_icon_theme_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof (PidginStockIconThemeClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_stock_icon_theme_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (PidginStockIconTheme),
+			0, /* n_preallocs */
+			NULL,
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PIDGIN_TYPE_ICON_THEME,
+				"PidginStockIconTheme", &info, 0);
+	}
+	return type;
+}
--- a/pidgin/pidginstock.h	Sun Apr 19 19:45:53 2009 +0000
+++ b/pidgin/pidginstock.h	Mon Apr 20 03:20:15 2009 +0000
@@ -185,15 +185,54 @@
 #define PIDGIN_ICON_SIZE_TANGO_HUGE           "pidgin-icon-size-tango-huge"
 
 /**
+ * extends PidginIconTheme (gtkicon-theme.h)
+ * A pidgin stock icon theme.
+ * This object represents a Pidgin stock icon theme.
+ *
+ * PidginStockIconTheme is a PidginIconTheme Object.
+ */
+typedef struct _PidginStockIconTheme        PidginStockIconTheme;
+typedef struct _PidginStockIconThemeClass   PidginStockIconThemeClass;
+
+#define PIDGIN_TYPE_STOCK_ICON_THEME            (pidgin_stock_icon_theme_get_type ())
+#define PIDGIN_STOCK_ICON_THEME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_STOCK_ICON_THEME, PidginStockIconTheme))
+#define PIDGIN_STOCK_ICON_THEME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_STOCK_ICON_THEME, PidginStockIconThemeClass))
+#define PIDGIN_IS_STOCK_ICON_THEME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_STOCK_ICON_THEME))
+#define PIDGIN_IS_STOCK_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_STOCK_ICON_THEME))
+#define PIDGIN_STOCK_ICON_THEME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_STOCK_ICON_THEME, PidginStockIconThemeClass))
+
+struct _PidginStockIconTheme
+{
+	PidginIconTheme parent;
+};
+
+struct _PidginStockIconThemeClass
+{
+	PidginIconThemeClass parent_class;
+};
+
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_stock_icon_theme_get_type(void);
+
+/**
  * Loades all of the icons from the status icon theme into Pidgin stock
  *
  * @param theme		the theme to load, or null to load all the default icons
  */
 void pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme);
 
+
+void pidgin_stock_load_stock_icon_theme(PidginStockIconTheme *theme);
+
 /**
  * Sets up the purple stock repository.
  */
 void pidgin_stock_init(void);
 
+G_END_DECLS
 #endif /* _PIDGIN_STOCK_H_ */
--- a/pidgin/plugins/themeedit-icon.c	Sun Apr 19 19:45:53 2009 +0000
+++ b/pidgin/plugins/themeedit-icon.c	Mon Apr 20 03:20:15 2009 +0000
@@ -43,7 +43,7 @@
 	PIDGIN_ICON_SIZE_TANGO_HUGE, /* We don't have huge status icons, it seems! */
 };
 
-static const struct {
+static const struct options {
 	const char *stockid;
 	const char *text;
 } statuses[] = {
@@ -52,50 +52,69 @@
 	{PIDGIN_STOCK_STATUS_BUSY, N_("Busy")},
 	{PIDGIN_STOCK_STATUS_OFFLINE, N_("Offline")},
 	{NULL, NULL}
+}, chatemblems[] = {
+	{PIDGIN_STOCK_STATUS_IGNORED, N_("Ignored")},
+	{PIDGIN_STOCK_STATUS_FOUNDER, N_("Founder")},
+	{PIDGIN_STOCK_STATUS_OPERATOR, N_("Operator")},
+	{PIDGIN_STOCK_STATUS_HALFOP, N_("Half Operator")},
+	{PIDGIN_STOCK_STATUS_VOICE, N_("Voice")},
+	{NULL, NULL}
+};
+
+static const struct {
+	const char *heading;
+	const struct options *options;
+} sections[] = {
+	{N_("Status Icons"), statuses},
+	{N_("Chatroom Embems"), chatemblems},
+	{NULL, NULL}
 };
 
 static PidginStatusIconTheme *
 create_icon_theme(GtkWidget *window)
 {
-	int i, j;
+	int s, i, j;
 	char *dirname = "/tmp";   /* FIXME */
 	PidginStatusIconTheme *theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, "type", "status-icon",
 				"author", getlogin(),
 				"directory", dirname,
 				NULL);
 
-	for (i = 0; statuses[i].stockid; i++) {
-		GtkWidget *image = g_object_get_data(G_OBJECT(window), statuses[i].stockid);
-		GdkPixbuf *pixbuf = g_object_get_data(G_OBJECT(image), "pixbuf");
-		if (!pixbuf)
-			continue;
-		pidgin_icon_theme_set_icon(PIDGIN_ICON_THEME(theme), statuses[i].stockid,
-				statuses[i].stockid);
-		for (j = 0; stocksizes[j]; j++) {
-			int width, height;
-			GtkIconSize iconsize;
-			char size[8];
-			char *name;
-			GdkPixbuf *scale;
-			GError *error = NULL;
+	for (s = 0; sections[s].heading; s++) {
+		GtkWidget *vbox = g_object_get_data(G_OBJECT(window), sections[s].heading);
+		for (i = 0; sections[s].options[i].stockid; i++) {
+			GtkWidget *image = g_object_get_data(G_OBJECT(vbox), sections[s].options[i].stockid);
+			GdkPixbuf *pixbuf = g_object_get_data(G_OBJECT(image), "pixbuf");
+			if (!pixbuf)
+				continue;
+			pidgin_icon_theme_set_icon(PIDGIN_ICON_THEME(theme), sections[s].options[i].stockid,
+					sections[s].options[i].stockid);
+			for (j = 0; stocksizes[j]; j++) {
+				int width, height;
+				GtkIconSize iconsize;
+				char size[8];
+				char *name;
+				GdkPixbuf *scale;
+				GError *error = NULL;
 
-			iconsize = gtk_icon_size_from_name(stocksizes[j]);
-			gtk_icon_size_lookup(iconsize, &width, &height);
-			g_snprintf(size, sizeof(size), "%d", width);
+				iconsize = gtk_icon_size_from_name(stocksizes[j]);
+				gtk_icon_size_lookup(iconsize, &width, &height);
+				g_snprintf(size, sizeof(size), "%d", width);
 
-			if (i == 0) {
-				name = g_build_filename(dirname, size, NULL);
-				purple_build_dir(name, S_IRUSR | S_IWUSR | S_IXUSR);
+				if (i == 0) {
+					name = g_build_filename(dirname, size, NULL);
+					purple_build_dir(name, S_IRUSR | S_IWUSR | S_IXUSR);
+					g_free(name);
+				}
+
+				name = g_build_filename(dirname, size, sections[s].options[i].stockid, NULL);
+				scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
+				gdk_pixbuf_save(scale, name, "png", &error, "compression", "9", NULL);
 				g_free(name);
+				g_object_unref(G_OBJECT(scale));
+				if (error)
+					g_error_free(error);
 			}
-
-			name = g_build_filename(dirname, size, statuses[i].stockid, NULL);
-			scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
-			gdk_pixbuf_save(scale, name, "png", &error, "compression", "9", NULL);
-			g_free(name);
-			g_object_unref(G_OBJECT(scale));
-			if (error)
-				g_error_free(error);
 		}
 	}
 	return theme;
@@ -104,8 +123,21 @@
 static void
 use_icon_theme(GtkWidget *w, GtkWidget *window)
 {
+	/* I don't quite understand the icon-theme stuff. For example, I don't
+	 * know why PidginIconTheme needs to be abstract, or how PidginStatusIconTheme
+	 * would be different from other PidginIconTheme's (e.g. PidginStockIconTheme)
+	 * etc., but anyway, this works for now.
+	 *
+	 * Here's an interesting note: A PidginStatusIconTheme can be used for both
+	 * stock and status icons. Like I said, I don't quite know how they could be
+	 * different. So I am going to just keep it as it is, for now anyway, until I
+	 * have the time to dig through this, or someone explains this stuff to me
+	 * clearly.
+	 *		-- Sad
+	 */
 	PidginStatusIconTheme *theme = create_icon_theme(window);
 	pidgin_stock_load_status_icon_theme(PIDGIN_STATUS_ICON_THEME(theme));
+	pidgin_stock_load_stock_icon_theme((PidginStockIconTheme *)theme);
 	pidgin_blist_refresh(purple_get_blist());
 	g_object_unref(theme);
 }
@@ -123,8 +155,12 @@
 	GError *error = NULL;
 	GdkPixbuf *scale;
 	int i;
-	GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
+	GdkPixbuf *pixbuf;
 
+	if (!filename)
+		return;
+
+	pixbuf = gdk_pixbuf_new_from_file(filename, &error);
 	if (error || !pixbuf) {
 		purple_debug_error("theme-editor-icon", "Unable to load icon file '%s' (%s)\n",
 				filename, error ? error->message : "Reason unknown");
@@ -171,41 +207,47 @@
 	GtkWidget *dialog;
 	GtkWidget *box, *vbox;
 	GtkSizeGroup *sizegroup;
-	int i, j;
+	int s, i, j;
 	dialog = pidgin_create_dialog(_("Pidgin Icon Theme Editor"), 0, "theme-editor-icon", FALSE);
 	box = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog), FALSE, PIDGIN_HIG_BOX_SPACE);
 
-	vbox = pidgin_make_frame(box, _("Status Icons"));
 	sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
-	for (i = 0; statuses[i].stockid; i++) {
-		const char *id = statuses[i].stockid;
-		const char *text = _(statuses[i].text);
+	for (s = 0; sections[s].heading; s++) {
+		const char *heading = sections[s].heading;
+
+		vbox = pidgin_make_frame(box, heading);
+		g_object_set_data(G_OBJECT(dialog), heading, vbox);
 
-		GtkWidget *hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
-		GtkWidget *label = gtk_label_new(text);
-		GtkWidget *image = gtk_image_new_from_stock(id,
-				gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
-		GtkWidget *ebox = gtk_event_box_new();
-		gtk_container_add(GTK_CONTAINER(ebox), image);
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+		for (i = 0; sections[s].options[i].stockid; i++) {
+			const char *id = sections[s].options[i].stockid;
+			const char *text = _(sections[s].options[i].text);
+
+			GtkWidget *hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+			GtkWidget *label = gtk_label_new(text);
+			GtkWidget *image = gtk_image_new_from_stock(id,
+					gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+			GtkWidget *ebox = gtk_event_box_new();
+			gtk_container_add(GTK_CONTAINER(ebox), image);
+			gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
-		g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(change_stock_image), image);
-		g_object_set_data(G_OBJECT(image), "property-name", (gpointer)id);
+			g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(change_stock_image), image);
+			g_object_set_data(G_OBJECT(image), "property-name", (gpointer)id);
 
-		gtk_size_group_add_widget(sizegroup, label);
-		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-		gtk_box_pack_start(GTK_BOX(hbox), ebox, FALSE, FALSE, 0);
+			gtk_size_group_add_widget(sizegroup, label);
+			gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+			gtk_box_pack_start(GTK_BOX(hbox), ebox, FALSE, FALSE, 0);
 
-		for (j = 0; stocksizes[j]; j++) {
-			GtkWidget *sh = gtk_image_new_from_stock(id, gtk_icon_size_from_name(stocksizes[j]));
-			gtk_box_pack_start(GTK_BOX(hbox), sh, FALSE, FALSE, 0);
-			g_object_set_data(G_OBJECT(image), stocksizes[j], sh);
+			for (j = 0; stocksizes[j]; j++) {
+				GtkWidget *sh = gtk_image_new_from_stock(id, gtk_icon_size_from_name(stocksizes[j]));
+				gtk_box_pack_start(GTK_BOX(hbox), sh, FALSE, FALSE, 0);
+				g_object_set_data(G_OBJECT(image), stocksizes[j], sh);
+			}
+
+			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+			g_object_set_data(G_OBJECT(vbox), id, image);
 		}
-
-		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-
-		g_object_set_data(G_OBJECT(dialog), id, image);
 	}
 
 	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), TRUE);
--- a/pidgin/plugins/themeedit.c	Sun Apr 19 19:45:53 2009 +0000
+++ b/pidgin/plugins/themeedit.c	Mon Apr 20 03:20:15 2009 +0000
@@ -299,7 +299,7 @@
 
 	act = purple_plugin_action_new(_("Edit Buddylist Theme"), pidgin_blist_theme_edit);
 	l = g_list_append(l, act);
-	act = purple_plugin_action_new(_("Edit Status Icon Theme"), pidgin_icon_theme_edit);
+	act = purple_plugin_action_new(_("Edit Icon Theme"), pidgin_icon_theme_edit);
 	l = g_list_append(l, act);
 
 	return l;