Replace strcmp() with purple_strequal()
[pidgin-git.git] / pidgin / plugins / themeedit-icon.c
blob252423f7d4523fcab994eed750aa9a3d145c95c2
1 /* Pidgin
3 * Pidgin is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 #include "internal.h"
22 #include "pidgin.h"
23 #include "debug.h"
24 #include "version.h"
26 #include "theme-manager.h"
28 #include "gtkblist.h"
29 #include "gtkblist-theme.h"
30 #include "gtkutils.h"
31 #include "gtkplugin.h"
33 #include "pidginstock.h"
34 #include "themeedit-icon.h"
36 typedef enum
38 FLAG_SIZE_MICROSOPIC = 0,
39 FLAG_SIZE_EXTRA_SMALL,
40 FLAG_SIZE_SMALL,
41 FLAG_SIZE_MEDIUM,
42 FLAG_SIZE_LARGE,
43 FLAG_SIZE_HUGE,
44 FLAG_SIZE_NONE,
45 } SectionFlags;
47 #define SECTION_FLAGS_ALL (0x3f)
49 static const char *stocksizes [] = {
50 [FLAG_SIZE_MICROSOPIC] = PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC,
51 [FLAG_SIZE_EXTRA_SMALL] = PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL,
52 [FLAG_SIZE_SMALL] = PIDGIN_ICON_SIZE_TANGO_SMALL,
53 [FLAG_SIZE_MEDIUM] = PIDGIN_ICON_SIZE_TANGO_MEDIUM,
54 [FLAG_SIZE_LARGE] = PIDGIN_ICON_SIZE_TANGO_LARGE,
55 [FLAG_SIZE_HUGE] = PIDGIN_ICON_SIZE_TANGO_HUGE,
56 [FLAG_SIZE_NONE] = NULL,
59 static const struct options {
60 const char *stockid;
61 const char *text;
62 } statuses[] = {
63 {PIDGIN_STOCK_STATUS_AVAILABLE, N_("Available")},
64 {PIDGIN_STOCK_STATUS_AWAY, N_("Away")},
65 {PIDGIN_STOCK_STATUS_XA, N_("Extended Away")},
66 {PIDGIN_STOCK_STATUS_BUSY, N_("Busy")},
67 {PIDGIN_STOCK_STATUS_OFFLINE, N_("Offline")},
68 {PIDGIN_STOCK_STATUS_LOGIN, N_("Just logged in")},
69 {PIDGIN_STOCK_STATUS_LOGOUT, N_("Just logged out")},
70 {PIDGIN_STOCK_STATUS_PERSON, N_("Icon for Contact/\nIcon for Unknown person")},
71 {PIDGIN_STOCK_STATUS_CHAT, N_("Icon for Chat")},
72 {NULL, NULL}
73 }, chatemblems[] = {
74 {PIDGIN_STOCK_STATUS_IGNORED, N_("Ignored")},
75 {PIDGIN_STOCK_STATUS_FOUNDER, N_("Founder")},
76 /* A user in a chat room who has special privileges. */
77 {PIDGIN_STOCK_STATUS_OPERATOR, N_("Operator")},
78 /* A half operator is someone who has a subset of the privileges
79 that an operator has. */
80 {PIDGIN_STOCK_STATUS_HALFOP, N_("Half Operator")},
81 {PIDGIN_STOCK_STATUS_VOICE, N_("Voice")},
82 {NULL, NULL}
83 }, dialogicons[] = {
84 {PIDGIN_STOCK_DIALOG_AUTH, N_("Authorization dialog")},
85 {PIDGIN_STOCK_DIALOG_ERROR, N_("Error dialog")},
86 {PIDGIN_STOCK_DIALOG_INFO, N_("Information dialog")},
87 {PIDGIN_STOCK_DIALOG_MAIL, N_("Mail dialog")},
88 {PIDGIN_STOCK_DIALOG_QUESTION, N_("Question dialog")},
89 {PIDGIN_STOCK_DIALOG_WARNING, N_("Warning dialog")},
90 {NULL, NULL},
91 {PIDGIN_STOCK_DIALOG_COOL, N_("What kind of dialog is this?")},
94 static const struct {
95 const char *heading;
96 const struct options *options;
97 SectionFlags flags;
98 } sections[] = {
99 {N_("Status Icons"), statuses, SECTION_FLAGS_ALL ^ (1 << FLAG_SIZE_HUGE)},
100 {N_("Chatroom Emblems"), chatemblems, FLAG_SIZE_SMALL},
101 {N_("Dialog Icons"), dialogicons, (1 << FLAG_SIZE_EXTRA_SMALL) | (1 << FLAG_SIZE_HUGE)},
102 {NULL, NULL, 0}
105 static PidginStatusIconTheme *
106 create_icon_theme(GtkWidget *window)
108 int s, i, j;
109 const char *dirname = g_get_tmp_dir();
110 PidginStatusIconTheme *theme;
111 const char *author;
112 #ifndef _WIN32
113 author = getlogin();
114 #else
115 author = "user";
116 #endif
117 theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, "type", "status-icon",
118 "author", author,
119 "directory", dirname,
120 NULL);
122 for (s = 0; sections[s].heading; s++) {
123 GtkWidget *vbox = g_object_get_data(G_OBJECT(window), sections[s].heading);
124 for (i = 0; sections[s].options[i].stockid; i++) {
125 GtkWidget *image = g_object_get_data(G_OBJECT(vbox), sections[s].options[i].stockid);
126 GdkPixbuf *pixbuf = g_object_get_data(G_OBJECT(image), "pixbuf");
127 if (!pixbuf)
128 continue;
129 pidgin_icon_theme_set_icon(PIDGIN_ICON_THEME(theme), sections[s].options[i].stockid,
130 sections[s].options[i].stockid);
131 for (j = 0; stocksizes[j]; j++) {
132 int width, height;
133 GtkIconSize iconsize;
134 char size[8];
135 char *name;
136 GdkPixbuf *scale;
137 GError *error = NULL;
139 if (!(sections[s].flags & (1 << j)))
140 continue;
142 iconsize = gtk_icon_size_from_name(stocksizes[j]);
143 gtk_icon_size_lookup(iconsize, &width, &height);
144 g_snprintf(size, sizeof(size), "%d", width);
146 if (i == 0) {
147 name = g_build_filename(dirname, size, NULL);
148 purple_build_dir(name, S_IRUSR | S_IWUSR | S_IXUSR);
149 g_free(name);
152 name = g_build_filename(dirname, size, sections[s].options[i].stockid, NULL);
153 scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
154 gdk_pixbuf_save(scale, name, "png", &error, "compression", "9", NULL);
155 g_free(name);
156 g_object_unref(G_OBJECT(scale));
157 if (error)
158 g_error_free(error);
162 return theme;
165 static void
166 use_icon_theme(GtkWidget *w, GtkWidget *window)
168 /* I don't quite understand the icon-theme stuff. For example, I don't
169 * know why PidginIconTheme needs to be abstract, or how PidginStatusIconTheme
170 * would be different from other PidginIconTheme's (e.g. PidginStockIconTheme)
171 * etc., but anyway, this works for now.
173 * Here's an interesting note: A PidginStatusIconTheme can be used for both
174 * stock and status icons. Like I said, I don't quite know how they could be
175 * different. So I am going to just keep it as it is, for now anyway, until I
176 * have the time to dig through this, or someone explains this stuff to me
177 * clearly.
178 * -- Sad
180 PidginStatusIconTheme *theme = create_icon_theme(window);
181 pidgin_stock_load_status_icon_theme(PIDGIN_STATUS_ICON_THEME(theme));
182 pidgin_stock_load_stock_icon_theme((PidginStockIconTheme *)theme);
183 pidgin_blist_refresh(purple_get_blist());
184 g_object_unref(theme);
187 #ifdef NOT_SADRUL
188 static void
189 save_icon_theme(GtkWidget *w, GtkWidget *window)
191 /* TODO: SAVE! */
192 gtk_widget_destroy(window);
194 #endif
196 static void
197 close_icon_theme(GtkWidget *w, GtkWidget *window)
199 gtk_widget_destroy(window);
202 static void
203 stock_icon_selected(const char *filename, gpointer image)
205 GError *error = NULL;
206 GdkPixbuf *scale;
207 int i;
208 GdkPixbuf *pixbuf;
210 if (!filename)
211 return;
213 pixbuf = gdk_pixbuf_new_from_file(filename, &error);
214 if (error || !pixbuf) {
215 purple_debug_error("theme-editor-icon", "Unable to load icon file '%s' (%s)\n",
216 filename, error ? error->message : "Reason unknown");
217 if (error)
218 g_error_free(error);
219 return;
222 scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR);
223 gtk_image_set_from_pixbuf(GTK_IMAGE(image), scale);
224 g_object_unref(G_OBJECT(scale));
226 /* Update the size previews */
227 for (i = 0; stocksizes[i]; i++) {
228 int width, height;
229 GtkIconSize iconsize;
230 GtkWidget *prev = g_object_get_data(G_OBJECT(image), stocksizes[i]);
231 if (!prev)
232 continue;
233 iconsize = gtk_icon_size_from_name(stocksizes[i]);
234 gtk_icon_size_lookup(iconsize, &width, &height);
235 scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
236 gtk_image_set_from_pixbuf(GTK_IMAGE(prev), scale);
237 g_object_unref(G_OBJECT(scale));
240 /* Save the original pixbuf so we can use it for resizing later */
241 g_object_set_data_full(G_OBJECT(image), "pixbuf", pixbuf,
242 (GDestroyNotify)g_object_unref);
245 static gboolean
246 change_stock_image(GtkWidget *widget, GdkEventButton *event, GtkWidget *image)
248 GtkWidget *win = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
249 stock_icon_selected, image);
250 gtk_window_set_title(GTK_WINDOW(win),
251 g_object_get_data(G_OBJECT(image), "localized-name"));
252 gtk_widget_show_all(win);
254 return TRUE;
257 void pidgin_icon_theme_edit(PurplePluginAction *unused)
259 GtkWidget *dialog;
260 GtkWidget *box, *vbox;
261 GtkWidget *notebook;
262 GtkSizeGroup *sizegroup;
263 int s, i, j;
264 dialog = pidgin_create_dialog(_("Pidgin Icon Theme Editor"), 0, "theme-editor-icon", FALSE);
265 box = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog), FALSE, PIDGIN_HIG_BOX_SPACE);
267 notebook = gtk_notebook_new();
268 gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
269 sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
271 for (s = 0; sections[s].heading; s++) {
272 const char *heading = sections[s].heading;
274 box = gtk_vbox_new(FALSE, 0);
275 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box, gtk_label_new(heading));
277 vbox = pidgin_make_frame(box, heading);
278 g_object_set_data(G_OBJECT(dialog), heading, vbox);
280 for (i = 0; sections[s].options[i].stockid; i++) {
281 const char *id = sections[s].options[i].stockid;
282 const char *text = _(sections[s].options[i].text);
284 GtkWidget *hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
285 GtkWidget *label = gtk_label_new(text);
286 GtkWidget *image = gtk_image_new_from_stock(id,
287 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
288 GtkWidget *ebox = gtk_event_box_new();
289 gtk_container_add(GTK_CONTAINER(ebox), image);
290 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
292 g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(change_stock_image), image);
293 g_object_set_data(G_OBJECT(image), "property-name", (gpointer)id);
294 g_object_set_data(G_OBJECT(image), "localized-name", (gpointer)text);
296 gtk_size_group_add_widget(sizegroup, label);
297 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
298 gtk_box_pack_start(GTK_BOX(hbox), ebox, FALSE, FALSE, 0);
300 for (j = 0; stocksizes[j]; j++) {
301 GtkWidget *sh;
303 if (!(sections[s].flags & (1 << j)))
304 continue;
306 sh = gtk_image_new_from_stock(id, gtk_icon_size_from_name(stocksizes[j]));
307 gtk_box_pack_start(GTK_BOX(hbox), sh, FALSE, FALSE, 0);
308 g_object_set_data(G_OBJECT(image), stocksizes[j], sh);
311 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
313 g_object_set_data(G_OBJECT(vbox), id, image);
317 #ifdef NOT_SADRUL
318 pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, G_CALLBACK(save_icon_theme), dialog);
319 #endif
320 pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_APPLY, G_CALLBACK(use_icon_theme), dialog);
321 pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, G_CALLBACK(close_icon_theme), dialog);
322 gtk_widget_show_all(dialog);
323 g_object_unref(sizegroup);