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
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
26 #include "theme-manager.h"
29 #include "gtkblist-theme.h"
31 #include "gtkplugin.h"
33 #include "pidginstock.h"
34 #include "themeedit-icon.h"
38 FLAG_SIZE_MICROSOPIC
= 0,
39 FLAG_SIZE_EXTRA_SMALL
,
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
{
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")},
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")},
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")},
91 {PIDGIN_STOCK_DIALOG_COOL
, N_("What kind of dialog is this?")},
96 const struct options
*options
;
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
)},
105 static PidginStatusIconTheme
*
106 create_icon_theme(GtkWidget
*window
)
109 const char *dirname
= g_get_tmp_dir();
110 PidginStatusIconTheme
*theme
;
117 theme
= g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME
, "type", "status-icon",
119 "directory", dirname
,
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");
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
++) {
133 GtkIconSize iconsize
;
137 GError
*error
= NULL
;
139 if (!(sections
[s
].flags
& (1 << j
)))
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
);
147 name
= g_build_filename(dirname
, size
, NULL
);
148 purple_build_dir(name
, S_IRUSR
| S_IWUSR
| S_IXUSR
);
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
);
156 g_object_unref(G_OBJECT(scale
));
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
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
);
189 save_icon_theme(GtkWidget
*w
, GtkWidget
*window
)
192 gtk_widget_destroy(window
);
197 close_icon_theme(GtkWidget
*w
, GtkWidget
*window
)
199 gtk_widget_destroy(window
);
203 stock_icon_selected(const char *filename
, gpointer image
)
205 GError
*error
= NULL
;
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");
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
++) {
229 GtkIconSize iconsize
;
230 GtkWidget
*prev
= g_object_get_data(G_OBJECT(image
), stocksizes
[i
]);
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
);
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
);
257 void pidgin_icon_theme_edit(PurplePluginAction
*unused
)
260 GtkWidget
*box
, *vbox
;
262 GtkSizeGroup
*sizegroup
;
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
++) {
303 if (!(sections
[s
].flags
& (1 << j
)))
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
);
318 pidgin_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_SAVE
, G_CALLBACK(save_icon_theme
), dialog
);
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
);