Make a frame for the combo box theme selectors and make the labels left-aligned
[pidgin-git.git] / pidgin / gtkprefs.c
blob6e5be4f616720695ac08a160c7a8da0180546074
1 /**
2 * @file gtkprefs.c GTK+ Preferences
3 * @ingroup pidgin
4 */
6 /* pidgin
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "internal.h"
28 #include "pidgin.h"
30 #include "debug.h"
31 #include "nat-pmp.h"
32 #include "notify.h"
33 #include "prefs.h"
34 #include "proxy.h"
35 #include "prpl.h"
36 #include "request.h"
37 #include "savedstatuses.h"
38 #include "sound.h"
39 #include "sound-theme.h"
40 #include "stun.h"
41 #include "theme-manager.h"
42 #include "upnp.h"
43 #include "util.h"
44 #include "network.h"
46 #include "gtkblist.h"
47 #include "gtkconv.h"
48 #include "gtkdebug.h"
49 #include "gtkdialogs.h"
50 #include "gtkimhtml.h"
51 #include "gtkimhtmltoolbar.h"
52 #include "gtkprefs.h"
53 #include "gtksavedstatuses.h"
54 #include "gtksound.h"
55 #include "gtkstatus-icon-theme.h"
56 #include "gtkthemes.h"
57 #include "gtkutils.h"
58 #include "pidginstock.h"
60 #define PROXYHOST 0
61 #define PROXYPORT 1
62 #define PROXYUSER 2
63 #define PROXYPASS 3
65 #define PREFS_OPTIMAL_ICON_SIZE 32
67 struct theme_info {
68 gchar *type;
69 gchar *extension;
70 gchar *original_name;
73 static int sound_row_sel = 0;
74 static GtkWidget *prefsnotebook;
76 static GtkWidget *sound_entry = NULL;
77 static GtkListStore *smiley_theme_store = NULL;
78 static GtkTreeSelection *smiley_theme_sel = NULL;
79 static GtkWidget *prefs_proxy_frame = NULL;
80 static GtkWidget *prefs_proxy_subframe = NULL;
82 static GtkWidget *prefs = NULL;
83 static GtkWidget *debugbutton = NULL;
84 static int notebook_page = 0;
85 static GtkTreeRowReference *previous_smiley_row = NULL;
87 static GtkListStore *prefs_sound_themes;
88 static GtkListStore *prefs_blist_themes;
89 static GtkListStore *prefs_status_icon_themes;
91 static GtkWidget *prefs_sound_themes_combo_box;
92 static GtkWidget *prefs_blist_themes_combo_box;
93 static GtkWidget *prefs_status_themes_combo_box;
95 static gboolean prefs_sound_themes_loading;
98 * PROTOTYPES
100 static void delete_prefs(GtkWidget *, void *);
102 static void
103 update_spin_value(GtkWidget *w, GtkWidget *spin)
105 const char *key = g_object_get_data(G_OBJECT(spin), "val");
106 int value;
108 value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
110 purple_prefs_set_int(key, value);
113 GtkWidget *
114 pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title,
115 const char *key, int min, int max, GtkSizeGroup *sg)
117 GtkWidget *spin;
118 GtkObject *adjust;
119 int val;
121 val = purple_prefs_get_int(key);
123 adjust = gtk_adjustment_new(val, min, max, 1, 1, 0);
124 spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
125 g_object_set_data(G_OBJECT(spin), "val", (char *)key);
126 if (max < 10000)
127 gtk_widget_set_size_request(spin, 50, -1);
128 else
129 gtk_widget_set_size_request(spin, 60, -1);
130 g_signal_connect(G_OBJECT(adjust), "value-changed",
131 G_CALLBACK(update_spin_value), GTK_WIDGET(spin));
132 gtk_widget_show(spin);
134 return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL);
137 static void
138 entry_set(GtkEntry *entry, gpointer data)
140 const char *key = (const char*)data;
142 purple_prefs_set_string(key, gtk_entry_get_text(entry));
145 GtkWidget *
146 pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title,
147 const char *key, GtkSizeGroup *sg)
149 GtkWidget *entry;
150 const gchar *value;
152 value = purple_prefs_get_string(key);
154 entry = gtk_entry_new();
155 gtk_entry_set_text(GTK_ENTRY(entry), value);
156 g_signal_connect(G_OBJECT(entry), "changed",
157 G_CALLBACK(entry_set), (char*)key);
158 gtk_widget_show(entry);
160 return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL);
163 GtkWidget *
164 pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title,
165 const char *key, GtkSizeGroup *sg)
167 GtkWidget *entry;
168 const gchar *value;
170 value = purple_prefs_get_string(key);
172 entry = gtk_entry_new();
173 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
174 gtk_entry_set_text(GTK_ENTRY(entry), value);
175 g_signal_connect(G_OBJECT(entry), "changed",
176 G_CALLBACK(entry_set), (char*)key);
177 gtk_widget_show(entry);
179 return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL);
183 static void
184 dropdown_set(GObject *w, const char *key)
186 const char *str_value;
187 int int_value;
188 PurplePrefType type;
190 type = GPOINTER_TO_INT(g_object_get_data(w, "type"));
192 if (type == PURPLE_PREF_INT) {
193 int_value = GPOINTER_TO_INT(g_object_get_data(w, "value"));
195 purple_prefs_set_int(key, int_value);
197 else if (type == PURPLE_PREF_STRING) {
198 str_value = (const char *)g_object_get_data(w, "value");
200 purple_prefs_set_string(key, str_value);
202 else if (type == PURPLE_PREF_BOOLEAN) {
203 purple_prefs_set_bool(key,
204 GPOINTER_TO_INT(g_object_get_data(w, "value")));
208 GtkWidget *
209 pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title,
210 PurplePrefType type, const char *key, GList *menuitems)
212 GtkWidget *dropdown, *opt, *menu;
213 GtkWidget *label = NULL;
214 gchar *text;
215 const char *stored_str = NULL;
216 int stored_int = 0;
217 int int_value = 0;
218 const char *str_value = NULL;
219 int o = 0;
221 g_return_val_if_fail(menuitems != NULL, NULL);
223 #if 0 /* GTK_CHECK_VERSION(2,4,0) */
224 if(type == PURPLE_PREF_INT)
225 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
226 else if(type == PURPLE_PREF_STRING)
227 model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
228 dropdown = gtk_combo_box_new_with_model(model);
229 #else
230 dropdown = gtk_option_menu_new();
231 menu = gtk_menu_new();
232 #endif
234 if (type == PURPLE_PREF_INT)
235 stored_int = purple_prefs_get_int(key);
236 else if (type == PURPLE_PREF_STRING)
237 stored_str = purple_prefs_get_string(key);
239 while (menuitems != NULL && (text = (char *) menuitems->data) != NULL) {
240 menuitems = g_list_next(menuitems);
241 g_return_val_if_fail(menuitems != NULL, NULL);
243 opt = gtk_menu_item_new_with_label(text);
245 g_object_set_data(G_OBJECT(opt), "type", GINT_TO_POINTER(type));
247 if (type == PURPLE_PREF_INT) {
248 int_value = GPOINTER_TO_INT(menuitems->data);
249 g_object_set_data(G_OBJECT(opt), "value",
250 GINT_TO_POINTER(int_value));
252 else if (type == PURPLE_PREF_STRING) {
253 str_value = (const char *)menuitems->data;
255 g_object_set_data(G_OBJECT(opt), "value", (char *)str_value);
257 else if (type == PURPLE_PREF_BOOLEAN) {
258 g_object_set_data(G_OBJECT(opt), "value",
259 menuitems->data);
262 g_signal_connect(G_OBJECT(opt), "activate",
263 G_CALLBACK(dropdown_set), (char *)key);
265 gtk_widget_show(opt);
266 gtk_menu_shell_append(GTK_MENU_SHELL(menu), opt);
268 if ((type == PURPLE_PREF_INT && stored_int == int_value) ||
269 (type == PURPLE_PREF_STRING && stored_str != NULL &&
270 !strcmp(stored_str, str_value)) ||
271 (type == PURPLE_PREF_BOOLEAN &&
272 (purple_prefs_get_bool(key) == GPOINTER_TO_INT(menuitems->data)))) {
274 gtk_menu_set_active(GTK_MENU(menu), o);
277 menuitems = g_list_next(menuitems);
279 o++;
282 gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu);
284 pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label);
286 return label;
289 GtkWidget *
290 pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type,
291 const char *key, ...)
293 va_list ap;
294 GList *menuitems = NULL;
295 GtkWidget *dropdown = NULL;
296 char *name;
297 int int_value;
298 const char *str_value;
300 g_return_val_if_fail(type == PURPLE_PREF_BOOLEAN || type == PURPLE_PREF_INT ||
301 type == PURPLE_PREF_STRING, NULL);
303 va_start(ap, key);
304 while ((name = va_arg(ap, char *)) != NULL) {
306 menuitems = g_list_prepend(menuitems, name);
308 if (type == PURPLE_PREF_INT || type == PURPLE_PREF_BOOLEAN) {
309 int_value = va_arg(ap, int);
310 menuitems = g_list_prepend(menuitems, GINT_TO_POINTER(int_value));
312 else {
313 str_value = va_arg(ap, const char *);
314 menuitems = g_list_prepend(menuitems, (char *)str_value);
317 va_end(ap);
319 g_return_val_if_fail(menuitems != NULL, NULL);
321 menuitems = g_list_reverse(menuitems);
323 dropdown = pidgin_prefs_dropdown_from_list(box, title, type, key,
324 menuitems);
326 g_list_free(menuitems);
328 return dropdown;
331 static void
332 delete_prefs(GtkWidget *asdf, void *gdsa)
334 /* Close any "select sound" request dialogs */
335 purple_request_close_with_handle(prefs);
337 /* Unregister callbacks. */
338 purple_prefs_disconnect_by_handle(prefs);
340 prefs = NULL;
341 sound_entry = NULL;
342 debugbutton = NULL;
343 notebook_page = 0;
344 smiley_theme_store = NULL;
345 if (previous_smiley_row)
346 gtk_tree_row_reference_free(previous_smiley_row);
347 previous_smiley_row = NULL;
351 static void
352 smiley_sel(GtkTreeSelection *sel, GtkTreeModel *model)
354 GtkTreeIter iter;
355 const char *themename;
356 char *description;
357 GValue val;
358 GtkTreePath *path, *oldpath;
359 struct smiley_theme *new_theme, *old_theme;
360 GtkWidget *remove_button = g_object_get_data(G_OBJECT(sel), "remove_button");
362 if (!gtk_tree_selection_get_selected(sel, &model, &iter)) {
363 gtk_widget_set_sensitive(remove_button, FALSE);
364 return;
367 old_theme = current_smiley_theme;
368 val.g_type = 0;
369 gtk_tree_model_get_value(model, &iter, 3, &val);
370 path = gtk_tree_model_get_path(model, &iter);
371 themename = g_value_get_string(&val);
372 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", themename);
374 gtk_widget_set_sensitive(remove_button, (strcmp(themename, "none") &&
375 strcmp(themename, _("Default"))));
376 g_value_unset (&val);
378 /* current_smiley_theme is set in callback for the above pref change */
379 new_theme = current_smiley_theme;
380 description = g_strdup_printf("<span size='larger' weight='bold'>%s</span> - %s\n"
381 "<span size='smaller' foreground='white'>%s</span>",
382 _(new_theme->name), _(new_theme->author), _(new_theme->desc));
383 gtk_list_store_set(smiley_theme_store, &iter, 1, description, -1);
384 g_free(description);
386 if (new_theme != old_theme && previous_smiley_row) {
387 oldpath = gtk_tree_row_reference_get_path(previous_smiley_row);
388 if (gtk_tree_model_get_iter(model, &iter, oldpath)) {
389 description = g_strdup_printf("<span size='larger' weight='bold'>%s</span> - %s\n"
390 "<span size='smaller' foreground='dim grey'>%s</span>",
391 _(old_theme->name), _(old_theme->author), _(old_theme->desc));
392 gtk_list_store_set(smiley_theme_store, &iter, 1,
393 description, -1);
394 g_free(description);
396 gtk_tree_path_free(oldpath);
398 if (previous_smiley_row)
399 gtk_tree_row_reference_free(previous_smiley_row);
400 previous_smiley_row = gtk_tree_row_reference_new(model, path);
401 gtk_tree_path_free(path);
404 static GtkTreeRowReference *
405 theme_refresh_theme_list(void)
407 GdkPixbuf *pixbuf;
408 GSList *themes;
409 GtkTreeIter iter;
410 GtkTreeRowReference *row_ref = NULL;
412 if (previous_smiley_row)
413 gtk_tree_row_reference_free(previous_smiley_row);
414 previous_smiley_row = NULL;
416 pidgin_themes_smiley_theme_probe();
418 if (!(themes = smiley_themes))
419 return NULL;
421 gtk_list_store_clear(smiley_theme_store);
423 while (themes) {
424 struct smiley_theme *theme = themes->data;
425 char *description = g_strdup_printf("<span size='larger' weight='bold'>%s</span> - %s\n"
426 "<span size='smaller' foreground='dim grey'>%s</span>",
427 _(theme->name), _(theme->author), _(theme->desc));
428 gtk_list_store_append (smiley_theme_store, &iter);
431 * LEAK - Gentoo memprof thinks pixbuf is leaking here... but it
432 * looks like it should be ok to me. Anyone know what's up? --Mark
434 pixbuf = (theme->icon ? gdk_pixbuf_new_from_file(theme->icon, NULL) : NULL);
436 gtk_list_store_set(smiley_theme_store, &iter,
437 0, pixbuf,
438 1, description,
439 2, theme->path,
440 3, theme->name,
441 -1);
443 if (pixbuf != NULL)
444 g_object_unref(G_OBJECT(pixbuf));
446 g_free(description);
447 themes = themes->next;
449 /* If this is the currently selected theme,
450 * we will need to select it. Grab the row reference. */
451 if (theme == current_smiley_theme) {
452 GtkTreePath *path = gtk_tree_model_get_path(
453 GTK_TREE_MODEL(smiley_theme_store), &iter);
454 row_ref = gtk_tree_row_reference_new(
455 GTK_TREE_MODEL(smiley_theme_store), path);
456 gtk_tree_path_free(path);
460 return row_ref;
463 static gchar *
464 get_theme_markup(const char *name, gboolean custom, const char *author,
465 const char *description)
468 return g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>",
469 name, custom ? " " : "", custom ? _("(Custom)") : "",
470 author != NULL ? " - " : "", author != NULL ? author : "",
471 description != NULL ? description : "");
474 /* Rebuild the markup for the sound theme selection for "(Custom)" themes */
475 static void
476 pref_sound_generate_markup(void)
478 gboolean print_custom, customized;
479 const gchar *author, *description, *current_theme;
480 gchar *name, *markup;
481 PurpleSoundTheme *theme;
482 GtkTreeIter iter;
484 customized = pidgin_sound_is_customized();
485 current_theme = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme");
487 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(prefs_sound_themes), &iter)) {
488 do {
489 gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &iter, 2, &name, -1);
491 print_custom = customized && name && g_str_equal(current_theme, name);
493 if (!name || *name == '\0') {
494 g_free(name);
495 name = g_strdup(_("Default"));
496 author = _("Penguin Pimps");
497 description = _("The default Pidgin sound theme");
498 } else {
499 theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(name, "sound"));
500 author = purple_theme_get_author(PURPLE_THEME(theme));
501 description = purple_theme_get_description(PURPLE_THEME(theme));
504 markup = get_theme_markup(name, print_custom, author, description);
506 gtk_list_store_set(prefs_sound_themes, &iter, 1, markup, -1);
508 g_free(name);
509 g_free(markup);
511 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(prefs_sound_themes), &iter));
515 /* adds the themes to the theme list from the manager so they can be displayed in prefs */
516 static void
517 prefs_themes_sort(PurpleTheme *theme)
519 GdkPixbuf *pixbuf = NULL;
520 GtkTreeIter iter;
521 gchar *image_full = NULL, *markup;
522 const gchar *name, *author, *description;
524 if (PURPLE_IS_SOUND_THEME(theme)){
526 image_full = purple_theme_get_image_full(theme);
527 if (image_full != NULL){
528 pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
529 g_free(image_full);
530 } else
531 pixbuf = NULL;
533 gtk_list_store_append(prefs_sound_themes, &iter);
534 gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, purple_theme_get_name(theme), -1);
536 if (pixbuf != NULL)
537 g_object_unref(G_OBJECT(pixbuf));
539 } else if (PIDGIN_IS_BLIST_THEME(theme) || PIDGIN_IS_STATUS_ICON_THEME(theme)){
540 GtkListStore *store;
542 if (PIDGIN_IS_BLIST_THEME(theme))
543 store = prefs_blist_themes;
544 else
545 store = prefs_status_icon_themes;
547 image_full = purple_theme_get_image_full(theme);
548 if (image_full != NULL){
549 pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
550 g_free(image_full);
551 } else
552 pixbuf = NULL;
554 name = purple_theme_get_name(theme);
555 author = purple_theme_get_author(theme);
556 description = purple_theme_get_description(theme);
558 markup = get_theme_markup(name, FALSE, author, description);
560 gtk_list_store_append(store, &iter);
561 gtk_list_store_set(store, &iter, 0, pixbuf, 1, markup, 2, name, -1);
563 g_free(markup);
564 if (pixbuf != NULL)
565 g_object_unref(G_OBJECT(pixbuf));
569 static void
570 prefs_set_active_theme_combo(GtkWidget *combo_box, GtkListStore *store, const gchar *current_theme)
572 GtkTreeIter iter;
573 gchar *theme = NULL;
574 gboolean unset = TRUE;
576 if (current_theme && *current_theme && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
577 do {
578 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1);
580 if (g_str_equal(current_theme, theme)) {
581 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
582 unset = FALSE;
585 g_free(theme);
586 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
589 if (unset)
590 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
593 static void
594 prefs_themes_refresh(void)
596 GdkPixbuf *pixbuf = NULL;
597 gchar *tmp;
598 GtkTreeIter iter;
600 prefs_sound_themes_loading = TRUE;
601 /* refresh the list of themes in the manager */
602 purple_theme_manager_refresh();
604 tmp = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
605 pixbuf = gdk_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
606 g_free(tmp);
608 /* sound themes */
609 gtk_list_store_clear(prefs_sound_themes);
610 gtk_list_store_append(prefs_sound_themes, &iter);
611 gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, "", -1);
613 /* blist themes */
614 gtk_list_store_clear(prefs_blist_themes);
615 gtk_list_store_append(prefs_blist_themes, &iter);
616 tmp = get_theme_markup(_("Default"), FALSE, _("Penguin Pimps"),
617 _("The default Pidgin buddy list theme"));
618 gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
619 g_free(tmp);
621 /* status icon themes */
622 gtk_list_store_clear(prefs_status_icon_themes);
623 gtk_list_store_append(prefs_status_icon_themes, &iter);
624 tmp = get_theme_markup(_("Default"), FALSE, _("Penguin Pimps"),
625 _("The default Pidgin status icon theme"));
626 gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
627 g_free(tmp);
628 if (pixbuf)
629 g_object_unref(G_OBJECT(pixbuf));
631 purple_theme_manager_for_each_theme(prefs_themes_sort);
632 pref_sound_generate_markup();
634 /* set active */
635 prefs_set_active_theme_combo(prefs_sound_themes_combo_box, prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
636 prefs_set_active_theme_combo(prefs_blist_themes_combo_box, prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"));
637 prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"));
638 prefs_sound_themes_loading = FALSE;
641 /* init all the theme variables so that the themes can be sorted later and used by pref pages */
642 static void
643 prefs_themes_init(void)
645 prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
647 prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
649 prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
652 static PurpleTheme *
653 prefs_theme_find_theme(const gchar *path, const gchar *type)
655 PurpleTheme *theme = purple_theme_manager_load_theme(path, type);
656 GDir *dir = g_dir_open(path, 0, NULL);
657 const gchar *next;
659 while (!PURPLE_IS_THEME(theme) && (next = g_dir_read_name(dir))) {
660 gchar *next_path = g_build_filename(path, next, NULL);
662 if (g_file_test(next_path, G_FILE_TEST_IS_DIR))
663 theme = prefs_theme_find_theme(next_path, type);
665 g_free(next_path);
668 g_dir_close(dir);
670 return theme;
673 /* Eww. Seriously ewww. But thanks, grim! This is taken from guifications2 */
674 static gboolean
675 purple_theme_file_copy(const gchar *source, const gchar *destination)
677 FILE *src, *dest;
678 gint chr = EOF;
680 if(!(src = g_fopen(source, "rb")))
681 return FALSE;
682 if(!(dest = g_fopen(destination, "wb"))) {
683 fclose(src);
684 return FALSE;
687 while((chr = fgetc(src)) != EOF) {
688 fputc(chr, dest);
691 fclose(dest);
692 fclose(src);
694 return TRUE;
697 static void
698 free_theme_info(struct theme_info *info)
700 if (info != NULL) {
701 g_free(info->type);
702 g_free(info->extension);
703 g_free(info->original_name);
704 g_free(info);
708 /* installs a theme, info is freed by function */
709 static void
710 theme_install_theme(char *path, struct theme_info *info)
712 #ifndef _WIN32
713 gchar *command;
714 #endif
715 gchar *destdir;
716 const char *tail;
717 GtkTreeRowReference *theme_rowref;
718 gboolean is_smiley_theme, is_archive;
719 PurpleTheme *theme = NULL;
721 if (info == NULL)
722 return;
724 /* check the extension */
725 tail = info->extension ? info->extension : strrchr(path, '.');
727 if (!tail) {
728 free_theme_info(info);
729 return;
732 is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz");
734 /* Just to be safe */
735 g_strchomp(path);
737 if ((is_smiley_theme = g_str_equal(info->type, "smiley")))
738 destdir = g_build_filename(purple_user_dir(), "smileys", NULL);
739 else
740 destdir = g_build_filename(purple_user_dir(), "themes", "temp", NULL);
742 /* We'll check this just to make sure. This also lets us do something different on
743 * other platforms, if need be */
744 if (is_archive) {
745 #ifndef _WIN32
746 gchar *path_escaped = g_shell_quote(path);
747 gchar *destdir_escaped = g_shell_quote(destdir);
749 if (!g_file_test(destdir, G_FILE_TEST_IS_DIR))
750 purple_build_dir(destdir, S_IRUSR | S_IWUSR | S_IXUSR);
752 command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped);
753 g_free(path_escaped);
754 g_free(destdir_escaped);
756 /* Fire! */
757 if (system(command)) {
758 purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL);
759 g_free(command);
760 g_free(destdir);
761 free_theme_info(info);
762 return;
764 #else
765 if (!winpidgin_gz_untar(path, destdir)) {
766 purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL);
767 g_free(destdir);
768 free_theme_info(info);
769 return;
771 #endif
774 if (is_smiley_theme) {
775 /* just extract the folder to the smiley directory */
776 theme_rowref = theme_refresh_theme_list();
778 if (theme_rowref != NULL) {
779 GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref);
781 if (tp)
782 gtk_tree_selection_select_path(smiley_theme_sel, tp);
784 gtk_tree_row_reference_free(theme_rowref);
787 } else if (is_archive) {
788 theme = prefs_theme_find_theme(destdir, info->type);
790 if (PURPLE_IS_THEME(theme)) {
791 /* create the location for the theme */
792 gchar *theme_dest = g_build_filename(purple_user_dir(), "themes",
793 purple_theme_get_name(theme),
794 "purple", info->type, NULL);
796 if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
797 purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
799 g_free(theme_dest);
800 theme_dest = g_build_filename(purple_user_dir(), "themes",
801 purple_theme_get_name(theme),
802 "purple", info->type, NULL);
804 /* move the entire directory to new location */
805 g_rename(purple_theme_get_dir(theme), theme_dest);
807 g_free(theme_dest);
808 g_remove(destdir);
809 g_object_unref(theme);
811 prefs_themes_refresh();
813 } else {
814 /* something was wrong with the theme archive */
815 g_unlink(destdir);
816 purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL);
819 } else { /* just a single file so copy it to a new temp directory and attempt to load it*/
820 gchar *temp_path, *temp_file;
822 temp_path = g_build_filename(purple_user_dir(), "themes", "temp", "sub_folder", NULL);
824 if (info->original_name != NULL) {
825 /* name was changed from the original (probably a dnd) change it back before loading */
826 temp_file = g_build_filename(temp_path, info->original_name, NULL);
828 } else {
829 gchar *source_name = g_path_get_basename(path);
830 temp_file = g_build_filename(temp_path, source_name, NULL);
831 g_free(source_name);
834 if (!g_file_test(temp_path, G_FILE_TEST_IS_DIR))
835 purple_build_dir(temp_path, S_IRUSR | S_IWUSR | S_IXUSR);
837 if (purple_theme_file_copy(path, temp_file)) {
838 /* find the theme, could be in subfolder */
839 theme = prefs_theme_find_theme(temp_path, info->type);
841 if (PURPLE_IS_THEME(theme)) {
842 gchar *theme_dest = g_build_filename(purple_user_dir(), "themes",
843 purple_theme_get_name(theme),
844 "purple", info->type, NULL);
846 if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
847 purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
849 g_rename(purple_theme_get_dir(theme), theme_dest);
851 g_free(theme_dest);
852 g_object_unref(theme);
854 prefs_themes_refresh();
855 } else {
856 g_remove(temp_path);
857 purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL);
859 } else {
860 purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL);
863 g_free(temp_file);
864 g_free(temp_path);
867 g_free(destdir);
868 free_theme_info(info);
871 static void
872 theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
873 const gchar *themedata, size_t len, const gchar *error_message)
875 FILE *f;
876 gchar *path;
877 size_t wc;
879 if ((error_message != NULL) || (len == 0)) {
880 free_theme_info(user_data);
881 return;
884 f = purple_mkstemp(&path, TRUE);
885 wc = fwrite(themedata, len, 1, f);
886 if (wc != 1) {
887 purple_debug_warning("theme_got_url", "Unable to write theme data.\n");
888 fclose(f);
889 g_unlink(path);
890 g_free(path);
891 free_theme_info(user_data);
892 return;
894 fclose(f);
896 theme_install_theme(path, user_data);
898 g_unlink(path);
899 g_free(path);
902 static void
903 theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
904 GtkSelectionData *sd, guint info, guint t, gpointer user_data)
906 gchar *name = g_strchomp((gchar *)sd->data);
908 if ((sd->length >= 0) && (sd->format == 8)) {
909 /* Well, it looks like the drag event was cool.
910 * Let's do something with it */
911 gchar *temp;
912 struct theme_info *info = g_new0(struct theme_info, 1);
913 info->type = g_strdup((gchar *)user_data);
914 info->extension = g_strdup(g_strrstr(name,"."));
915 temp = g_strrstr(name, "/");
916 info->original_name = temp ? g_strdup(++temp) : NULL;
918 if (!g_ascii_strncasecmp(name, "file://", 7)) {
919 GError *converr = NULL;
920 gchar *tmp;
921 /* It looks like we're dealing with a local file. Let's
922 * just untar it in the right place */
923 if(!(tmp = g_filename_from_uri(name, NULL, &converr))) {
924 purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n",
925 (converr ? converr->message :
926 "g_filename_from_uri error"));
927 free_theme_info(info);
928 return;
930 theme_install_theme(tmp, info);
931 g_free(tmp);
932 } else if (!g_ascii_strncasecmp(name, "http://", 7)) {
933 /* Oo, a web drag and drop. This is where things
934 * will start to get interesting */
935 purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, info);
936 } else if (!g_ascii_strncasecmp(name, "https://", 8)) {
937 /* purple_util_fetch_url() doesn't support HTTPS, but we want users
938 * to be able to drag and drop links from the SF trackers, so
939 * we'll try it as an HTTP URL. */
940 char *tmp = g_strdup(name + 1);
941 tmp[0] = 'h';
942 tmp[1] = 't';
943 tmp[2] = 't';
944 tmp[3] = 'p';
946 purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, info);
947 g_free(tmp);
948 } else
949 free_theme_info(info);
951 gtk_drag_finish(dc, TRUE, FALSE, t);
954 gtk_drag_finish(dc, FALSE, FALSE, t);
957 /* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
958 static GtkWidget *
959 prefs_build_theme_combo_box(GtkListStore *store, const char *current_theme, const char *type)
961 GtkCellRenderer *cell_rend;
962 GtkWidget *combo_box;
963 GtkTargetEntry te[3] = {
964 {"text/plain", 0, 0},
965 {"text/uri-list", 0, 1},
966 {"STRING", 0, 2}
969 g_return_val_if_fail(store != NULL && current_theme != NULL, NULL);
971 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
973 cell_rend = gtk_cell_renderer_pixbuf_new();
974 gtk_cell_renderer_set_fixed_size(cell_rend, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE);
975 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
976 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL);
978 cell_rend = gtk_cell_renderer_text_new();
979 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
980 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL);
981 /*#if GTK_CHECK_VERSION(2,6,0)
982 g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
983 #endif*/
985 gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te,
986 sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
988 g_signal_connect(G_OBJECT(combo_box), "drag_data_received", G_CALLBACK(theme_dnd_recv), (gpointer) type);
990 return combo_box;
993 /* sets the current sound theme */
994 static void
995 prefs_set_sound_theme_cb(GtkComboBox *combo_box, gpointer user_data)
997 gint i;
998 gchar *pref;
999 gchar *new_theme;
1000 GtkTreeIter new_iter;
1002 if(gtk_combo_box_get_active_iter(combo_box, &new_iter) && !prefs_sound_themes_loading) {
1004 gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1);
1006 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme);
1008 /* New theme removes all customization */
1009 for(i = 0; i < PURPLE_NUM_SOUNDS; i++){
1010 pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
1011 pidgin_sound_get_event_option(i));
1012 purple_prefs_set_path(pref, "");
1013 g_free(pref);
1016 /* gets rid of the "(Custom)" from the last selection */
1017 pref_sound_generate_markup();
1019 gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
1021 g_free(new_theme);
1026 /* Does same as normal sort, except "none" is sorted first */
1027 static gint pidgin_sort_smileys (GtkTreeModel *model,
1028 GtkTreeIter *a,
1029 GtkTreeIter *b,
1030 gpointer userdata)
1032 gint ret = 0;
1033 gchar *name1 = NULL, *name2 = NULL;
1035 gtk_tree_model_get(model, a, 3, &name1, -1);
1036 gtk_tree_model_get(model, b, 3, &name2, -1);
1038 if (name1 == NULL || name2 == NULL) {
1039 if (!(name1 == NULL && name2 == NULL))
1040 ret = (name1 == NULL) ? -1: 1;
1041 } else if (!g_ascii_strcasecmp(name1, "none")) {
1042 if (!g_utf8_collate(name1, name2))
1043 ret = 0;
1044 else
1045 /* Sort name1 first */
1046 ret = -1;
1047 } else if (!g_ascii_strcasecmp(name2, "none")) {
1048 /* Sort name2 first */
1049 ret = 1;
1050 } else {
1051 /* Neither string is "none", default to normal sort */
1052 ret = purple_utf8_strcasecmp(name1,name2);
1055 g_free(name1);
1056 g_free(name2);
1058 return ret;
1061 static void
1062 request_theme_file_name_cb(gpointer data, char *theme_file_name)
1064 struct theme_info *info = g_new0(struct theme_info, 1);
1065 info->type = g_strdup("smiley");
1067 theme_install_theme(theme_file_name, info);
1070 static void
1071 add_theme_button_clicked_cb(GtkWidget *widget, gpointer user_data)
1073 purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL);
1076 static void
1077 remove_theme_button_clicked_cb(GtkWidget *button, GtkTreeView *tv)
1079 char *theme_name = NULL, *theme_file = NULL;
1080 GtkTreeModel *tm;
1081 GtkTreeIter itr;
1082 GtkTreeRowReference *trr = NULL;
1084 if ((tm = gtk_tree_view_get_model(tv)) == NULL)
1085 return;
1086 if (!gtk_tree_selection_get_selected(smiley_theme_sel, NULL, &itr))
1087 return;
1088 gtk_tree_model_get(tm, &itr, 2, &theme_file, 3, &theme_name, -1);
1090 if (theme_file && theme_name && strcmp(theme_name, "none"))
1091 pidgin_themes_remove_smiley_theme(theme_file);
1093 if ((trr = theme_refresh_theme_list()) != NULL) {
1094 GtkTreePath *tp = gtk_tree_row_reference_get_path(trr);
1096 if (tp) {
1097 gtk_tree_selection_select_path(smiley_theme_sel, tp);
1098 gtk_tree_path_free(tp);
1100 gtk_tree_row_reference_free(trr);
1103 g_free(theme_file);
1104 g_free(theme_name);
1107 /* sets the current buddy list theme */
1108 static void
1109 prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data)
1111 PidginBlistTheme *theme = NULL;
1112 GtkTreeIter iter;
1113 gchar *name = NULL;
1115 if(gtk_combo_box_get_active_iter(combo_box, &iter)) {
1117 gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1);
1119 if(!name || !g_str_equal(name, ""))
1120 theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist"));
1122 g_free(name);
1124 pidgin_blist_set_theme(theme);
1128 /* sets the current icon theme */
1129 static void
1130 prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
1132 PidginStatusIconTheme *theme = NULL;
1133 GtkTreeIter iter;
1134 gchar *name = NULL;
1136 if(gtk_combo_box_get_active_iter(combo_box, &iter)) {
1138 gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1);
1140 if(!name || !g_str_equal(name, ""))
1141 theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon"));
1143 g_free(name);
1145 pidgin_stock_load_status_icon_theme(theme);
1146 pidgin_blist_refresh(purple_get_blist());
1150 static GtkWidget *
1151 theme_page(void)
1153 GtkWidget *add_button, *remove_button, *hbox_buttons, *themesel_hbox;
1154 GtkWidget *vbox, *alignment, *ret, *sw, *view, *label;
1155 GtkCellRenderer *rend;
1156 GtkTreeViewColumn *col;
1157 GtkTreeSelection *sel;
1158 GtkTreeRowReference *rowref;
1159 GtkTargetEntry te[3] = {
1160 {"text/plain", 0, 0},
1161 {"text/uri-list", 0, 1},
1162 {"STRING", 0, 2}
1164 GtkSizeGroup *label_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1165 GtkSizeGroup *combo_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1167 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
1168 gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
1170 vbox = pidgin_make_frame(ret, _("Theme Selections"));
1172 /* Buddy List Themes */
1173 themesel_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
1175 label = gtk_label_new(_("Buddy List Theme:"));
1176 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1177 gtk_size_group_add_widget(label_sg, label);
1178 gtk_box_pack_start(GTK_BOX(themesel_hbox), label, FALSE, FALSE, 0);
1180 prefs_blist_themes_combo_box = prefs_build_theme_combo_box(prefs_blist_themes,
1181 purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"),
1182 "blist");
1183 g_signal_connect(G_OBJECT(prefs_blist_themes_combo_box), "changed",
1184 (GCallback)prefs_set_blist_theme_cb, NULL);
1185 gtk_size_group_add_widget(combo_sg, prefs_blist_themes_combo_box);
1186 gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_blist_themes_combo_box, FALSE, FALSE, 0);
1188 gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
1190 /* Status Icon Themes */
1191 themesel_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
1193 label = gtk_label_new(_("Status Icon Theme:"));
1194 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1195 gtk_size_group_add_widget(label_sg, label);
1196 gtk_box_pack_start(GTK_BOX(themesel_hbox), label, FALSE, FALSE, 0);
1198 prefs_status_themes_combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes,
1199 purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"),
1200 "icon");
1201 g_signal_connect(G_OBJECT(prefs_status_themes_combo_box), "changed",
1202 (GCallback)prefs_set_status_icon_theme_cb, NULL);
1203 gtk_size_group_add_widget(combo_sg, prefs_status_themes_combo_box);
1204 gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_status_themes_combo_box, FALSE, FALSE, 0);
1206 gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
1208 /* Sound Themes */
1209 themesel_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
1211 label = gtk_label_new(_("Sound Theme:"));
1212 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1213 gtk_size_group_add_widget(label_sg, label);
1214 gtk_box_pack_start(GTK_BOX(themesel_hbox), label, FALSE, FALSE, 0);
1216 prefs_sound_themes_combo_box = prefs_build_theme_combo_box(prefs_sound_themes,
1217 purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"),
1218 "sound");
1219 g_signal_connect(G_OBJECT(prefs_sound_themes_combo_box), "changed",
1220 (GCallback)prefs_set_sound_theme_cb, NULL);
1221 gtk_size_group_add_widget(combo_sg, prefs_sound_themes_combo_box);
1222 gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_sound_themes_combo_box, FALSE, FALSE, 0);
1224 gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
1226 /* Smiley Themes */
1227 label = gtk_label_new(_("Select a smiley theme that you would like to use "
1228 "from the list below. New themes can be installed "
1229 "by dragging and dropping them onto the theme list."));
1231 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1232 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1233 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
1235 gtk_box_pack_start(GTK_BOX(ret), label, FALSE, FALSE, 0);
1236 gtk_widget_show(label);
1238 sw = gtk_scrolled_window_new(NULL,NULL);
1239 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1240 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
1242 gtk_box_pack_start(GTK_BOX(ret), sw, TRUE, TRUE, 0);
1243 smiley_theme_store = gtk_list_store_new (4, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
1245 rowref = theme_refresh_theme_list();
1247 view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(smiley_theme_store));
1249 gtk_drag_dest_set(view, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te,
1250 sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
1252 g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), "smiley");
1254 rend = gtk_cell_renderer_pixbuf_new();
1255 smiley_theme_sel = sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1257 /* Custom sort so "none" theme is at top of list */
1258 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(smiley_theme_store),
1259 3, pidgin_sort_smileys, NULL, NULL);
1261 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(smiley_theme_store),
1262 3, GTK_SORT_ASCENDING);
1264 col = gtk_tree_view_column_new_with_attributes (_("Icon"),
1265 rend,
1266 "pixbuf", 0,
1267 NULL);
1268 gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);
1270 rend = gtk_cell_renderer_text_new();
1271 col = gtk_tree_view_column_new_with_attributes (_("Description"),
1272 rend,
1273 "markup", 1,
1274 NULL);
1275 gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);
1276 g_object_unref(G_OBJECT(smiley_theme_store));
1277 gtk_container_add(GTK_CONTAINER(sw), view);
1279 g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(smiley_sel), NULL);
1281 alignment = gtk_alignment_new(1.0, 0.5, 0.0, 1.0);
1282 gtk_widget_show(alignment);
1283 gtk_box_pack_start(GTK_BOX(ret), alignment, FALSE, TRUE, 0);
1285 hbox_buttons = gtk_hbox_new(TRUE, PIDGIN_HIG_CAT_SPACE);
1286 gtk_widget_show(hbox_buttons);
1287 gtk_container_add(GTK_CONTAINER(alignment), hbox_buttons);
1289 add_button = gtk_button_new_from_stock(GTK_STOCK_ADD);
1290 gtk_widget_show(add_button);
1291 gtk_box_pack_start(GTK_BOX(hbox_buttons), add_button, FALSE, TRUE, 0);
1292 g_signal_connect(G_OBJECT(add_button), "clicked", (GCallback)add_theme_button_clicked_cb, view);
1294 remove_button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
1295 gtk_widget_show(remove_button);
1296 gtk_box_pack_start(GTK_BOX(hbox_buttons), remove_button, FALSE, TRUE, 0);
1297 g_signal_connect(G_OBJECT(remove_button), "clicked", (GCallback)remove_theme_button_clicked_cb, view);
1298 g_object_set_data(G_OBJECT(sel), "remove_button", remove_button);
1300 if (rowref) {
1301 GtkTreePath *path = gtk_tree_row_reference_get_path(rowref);
1302 gtk_tree_row_reference_free(rowref);
1303 gtk_tree_selection_select_path(sel, path);
1304 gtk_tree_path_free(path);
1307 gtk_widget_show_all(ret);
1309 pidgin_set_accessible_label (view, label);
1311 return ret;
1314 static void
1315 formatting_toggle_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *toolbar)
1317 gboolean bold, italic, uline;
1319 gtk_imhtml_get_current_format(GTK_IMHTML(imhtml),
1320 &bold, &italic, &uline);
1322 if (buttons & GTK_IMHTML_BOLD)
1323 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", bold);
1324 if (buttons & GTK_IMHTML_ITALIC)
1325 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", italic);
1326 if (buttons & GTK_IMHTML_UNDERLINE)
1327 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", uline);
1329 if (buttons & GTK_IMHTML_GROW || buttons & GTK_IMHTML_SHRINK)
1330 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/font_size",
1331 gtk_imhtml_get_current_fontsize(GTK_IMHTML(imhtml)));
1332 if (buttons & GTK_IMHTML_FACE) {
1333 char *face = gtk_imhtml_get_current_fontface(GTK_IMHTML(imhtml));
1334 if (!face)
1335 face = g_strdup("");
1337 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/font_face", face);
1338 g_free(face);
1341 if (buttons & GTK_IMHTML_FORECOLOR) {
1342 char *color = gtk_imhtml_get_current_forecolor(GTK_IMHTML(imhtml));
1343 if (!color)
1344 color = g_strdup("");
1346 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor", color);
1347 g_free(color);
1350 if (buttons & GTK_IMHTML_BACKCOLOR) {
1351 char *color;
1352 GObject *object;
1354 color = gtk_imhtml_get_current_backcolor(GTK_IMHTML(imhtml));
1355 if (!color)
1356 color = g_strdup("");
1358 /* Block the signal to prevent a loop. */
1359 object = g_object_ref(G_OBJECT(imhtml));
1360 g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
1361 NULL, toolbar);
1362 /* Clear the backcolor. */
1363 gtk_imhtml_toggle_backcolor(GTK_IMHTML(imhtml), "");
1364 /* Unblock the signal. */
1365 g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
1366 NULL, toolbar);
1367 g_object_unref(object);
1369 /* This will fire a toggle signal and get saved below. */
1370 gtk_imhtml_toggle_background(GTK_IMHTML(imhtml), color);
1372 g_free(color);
1375 if (buttons & GTK_IMHTML_BACKGROUND) {
1376 char *color = gtk_imhtml_get_current_background(GTK_IMHTML(imhtml));
1377 if (!color)
1378 color = g_strdup("");
1380 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor", color);
1381 g_free(color);
1385 static void
1386 formatting_clear_cb(GtkIMHtml *imhtml, void *data)
1388 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", FALSE);
1389 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", FALSE);
1390 purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", FALSE);
1392 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/conversations/font_size", 3);
1394 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/font_face", "");
1395 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor", "");
1396 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor", "");
1399 static void
1400 conversation_usetabs_cb(const char *name, PurplePrefType type,
1401 gconstpointer value, gpointer data)
1403 gboolean usetabs = GPOINTER_TO_INT(value);
1405 if (usetabs)
1406 gtk_widget_set_sensitive(GTK_WIDGET(data), TRUE);
1407 else
1408 gtk_widget_set_sensitive(GTK_WIDGET(data), FALSE);
1412 #define CONVERSATION_CLOSE_ACCEL_PATH "<main>/Conversation/Close"
1414 /* Filled in in keyboard_shortcuts(). */
1415 static GtkAccelKey ctrl_w = { 0, 0, 0 };
1416 static GtkAccelKey escape = { 0, 0, 0 };
1418 static guint escape_closes_conversation_cb_id = 0;
1420 static gboolean
1421 accel_is_escape(GtkAccelKey *k)
1423 return (k->accel_key == escape.accel_key
1424 && k->accel_mods == escape.accel_mods);
1427 /* Update the tickybox in Preferences when the keybinding for Conversation ->
1428 * Close is changed via Gtk.
1430 static void
1431 conversation_close_accel_changed_cb (GtkAccelMap *object,
1432 gchar *accel_path,
1433 guint accel_key,
1434 GdkModifierType accel_mods,
1435 gpointer checkbox_)
1437 GtkToggleButton *checkbox = GTK_TOGGLE_BUTTON(checkbox_);
1438 GtkAccelKey new = { accel_key, accel_mods, 0 };
1440 g_signal_handler_block(checkbox, escape_closes_conversation_cb_id);
1441 gtk_toggle_button_set_active(checkbox, accel_is_escape(&new));
1442 g_signal_handler_unblock(checkbox, escape_closes_conversation_cb_id);
1446 static void
1447 escape_closes_conversation_cb(GtkWidget *w,
1448 gpointer unused)
1450 gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
1451 gboolean changed;
1452 GtkAccelKey *new_key = active ? &escape : &ctrl_w;
1454 changed = gtk_accel_map_change_entry(CONVERSATION_CLOSE_ACCEL_PATH,
1455 new_key->accel_key, new_key->accel_mods, TRUE);
1457 /* If another path is already bound to the new accelerator,
1458 * _change_entry tries to delete that binding (because it was passed
1459 * replace=TRUE). If that other path is locked, then _change_entry
1460 * will fail. We don't ever lock any accelerator paths, so this case
1461 * should never arise.
1463 if(!changed)
1464 purple_debug_warning("gtkprefs", "Escape accel failed to change\n");
1468 /* Creates preferences for keyboard shortcuts that it's hard to change with the
1469 * standard Gtk accelerator-changing mechanism.
1471 static void
1472 keyboard_shortcuts(GtkWidget *page)
1474 GtkWidget *vbox = pidgin_make_frame(page, _("Keyboard Shortcuts"));
1475 GtkWidget *checkbox;
1476 GtkAccelKey current = { 0, 0, 0 };
1477 GtkAccelMap *map = gtk_accel_map_get();
1479 /* Maybe it would be better just to hardcode the values?
1480 * -- resiak, 2007-04-30
1482 if (ctrl_w.accel_key == 0)
1484 gtk_accelerator_parse ("<Control>w", &(ctrl_w.accel_key),
1485 &(ctrl_w.accel_mods));
1486 g_assert(ctrl_w.accel_key != 0);
1488 gtk_accelerator_parse ("Escape", &(escape.accel_key),
1489 &(escape.accel_mods));
1490 g_assert(escape.accel_key != 0);
1493 checkbox = gtk_check_button_new_with_mnemonic(
1494 _("Cl_ose conversations with the Escape key"));
1495 gtk_accel_map_lookup_entry(CONVERSATION_CLOSE_ACCEL_PATH, &current);
1496 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
1497 accel_is_escape(&current));
1499 escape_closes_conversation_cb_id = g_signal_connect(checkbox,
1500 "clicked", G_CALLBACK(escape_closes_conversation_cb), NULL);
1502 g_signal_connect_object(map, "changed::" CONVERSATION_CLOSE_ACCEL_PATH,
1503 G_CALLBACK(conversation_close_accel_changed_cb), checkbox, (GConnectFlags)0);
1505 gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
1508 static GtkWidget *
1509 interface_page(void)
1511 GtkWidget *ret;
1512 GtkWidget *vbox;
1513 GtkWidget *vbox2;
1514 GtkWidget *label;
1515 GtkSizeGroup *sg;
1516 GList *names = NULL;
1518 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
1519 gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
1521 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1523 /* System Tray */
1524 vbox = pidgin_make_frame(ret, _("System Tray Icon"));
1525 label = pidgin_prefs_dropdown(vbox, _("_Show system tray icon:"), PURPLE_PREF_STRING,
1526 PIDGIN_PREFS_ROOT "/docklet/show",
1527 _("Always"), "always",
1528 _("On unread messages"), "pending",
1529 _("Never"), "never",
1530 NULL);
1531 gtk_size_group_add_widget(sg, label);
1532 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1534 vbox = pidgin_make_frame(ret, _("Conversation Window"));
1535 label = pidgin_prefs_dropdown(vbox, _("_Hide new IM conversations:"),
1536 PURPLE_PREF_STRING, PIDGIN_PREFS_ROOT "/conversations/im/hide_new",
1537 _("Never"), "never",
1538 _("When away"), "away",
1539 _("Always"), "always",
1540 NULL);
1541 gtk_size_group_add_widget(sg, label);
1542 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1544 #ifdef _WIN32
1545 pidgin_prefs_checkbox(_("Minimi_ze new conversation windows"), PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", vbox);
1546 #endif
1548 /* All the tab options! */
1549 vbox = pidgin_make_frame(ret, _("Tabs"));
1551 pidgin_prefs_checkbox(_("Show IMs and chats in _tabbed windows"),
1552 PIDGIN_PREFS_ROOT "/conversations/tabs", vbox);
1555 * Connect a signal to the above preference. When conversations are not
1556 * shown in a tabbed window then all tabbing options should be disabled.
1558 vbox2 = gtk_vbox_new(FALSE, 9);
1559 gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
1560 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/conversations/tabs",
1561 conversation_usetabs_cb, vbox2);
1562 if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs"))
1563 gtk_widget_set_sensitive(vbox2, FALSE);
1565 pidgin_prefs_checkbox(_("Show close b_utton on tabs"),
1566 PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", vbox2);
1568 label = pidgin_prefs_dropdown(vbox2, _("_Placement:"), PURPLE_PREF_INT,
1569 PIDGIN_PREFS_ROOT "/conversations/tab_side",
1570 _("Top"), GTK_POS_TOP,
1571 _("Bottom"), GTK_POS_BOTTOM,
1572 _("Left"), GTK_POS_LEFT,
1573 _("Right"), GTK_POS_RIGHT,
1574 #if GTK_CHECK_VERSION(2,6,0)
1575 _("Left Vertical"), GTK_POS_LEFT|8,
1576 _("Right Vertical"), GTK_POS_RIGHT|8,
1577 #endif
1578 NULL);
1579 gtk_size_group_add_widget(sg, label);
1580 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1582 names = pidgin_conv_placement_get_options();
1583 label = pidgin_prefs_dropdown_from_list(vbox2, _("N_ew conversations:"),
1584 PURPLE_PREF_STRING, PIDGIN_PREFS_ROOT "/conversations/placement", names);
1585 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1587 gtk_size_group_add_widget(sg, label);
1589 g_list_free(names);
1591 keyboard_shortcuts(ret);
1593 gtk_widget_show_all(ret);
1594 g_object_unref(sg);
1595 return ret;
1598 static GtkWidget *
1599 conv_page(void)
1601 GtkWidget *ret;
1602 GtkWidget *vbox;
1603 GtkWidget *toolbar;
1604 GtkWidget *iconpref1;
1605 GtkWidget *iconpref2;
1606 GtkWidget *imhtml;
1607 GtkWidget *frame;
1609 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
1610 gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
1612 vbox = pidgin_make_frame(ret, _("Conversations"));
1614 pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
1615 PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
1616 pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"),
1617 PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox);
1619 iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"),
1620 PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox);
1621 iconpref2 = pidgin_prefs_checkbox(_("Enable buddy ic_on animation"),
1622 PIDGIN_PREFS_ROOT "/conversations/im/animate_buddy_icons", vbox);
1623 if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons"))
1624 gtk_widget_set_sensitive(iconpref2, FALSE);
1625 g_signal_connect(G_OBJECT(iconpref1), "clicked",
1626 G_CALLBACK(pidgin_toggle_sensitive), iconpref2);
1628 pidgin_prefs_checkbox(_("_Notify buddies that you are typing to them"),
1629 "/purple/conversations/im/send_typing", vbox);
1630 #ifdef USE_GTKSPELL
1631 pidgin_prefs_checkbox(_("Highlight _misspelled words"),
1632 PIDGIN_PREFS_ROOT "/conversations/spellcheck", vbox);
1633 #endif
1635 pidgin_prefs_checkbox(_("Use smooth-scrolling"), PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", vbox);
1637 #ifdef _WIN32
1638 pidgin_prefs_checkbox(_("F_lash window when IMs are received"), PIDGIN_PREFS_ROOT "/win32/blink_im", vbox);
1639 #endif
1641 pidgin_prefs_labeled_spin_button(vbox,
1642 _("Minimum input area height in lines:"),
1643 PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines",
1644 1, 8, NULL);
1646 vbox = pidgin_make_frame(ret, _("Default Formatting"));
1647 gtk_box_set_child_packing(GTK_BOX(vbox->parent), vbox, TRUE, TRUE, 0, GTK_PACK_START);
1649 frame = pidgin_create_imhtml(TRUE, &imhtml, &toolbar, NULL);
1650 gtk_widget_show(frame);
1651 gtk_widget_set_name(imhtml, "pidgin_prefs_font_imhtml");
1652 gtk_widget_set_size_request(frame, 300, -1);
1653 gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(imhtml), TRUE);
1654 gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml),
1655 GTK_IMHTML_BOLD |
1656 GTK_IMHTML_ITALIC |
1657 GTK_IMHTML_UNDERLINE |
1658 GTK_IMHTML_GROW |
1659 GTK_IMHTML_SHRINK |
1660 GTK_IMHTML_FACE |
1661 GTK_IMHTML_FORECOLOR |
1662 GTK_IMHTML_BACKCOLOR |
1663 GTK_IMHTML_BACKGROUND);
1665 gtk_imhtml_append_text(GTK_IMHTML(imhtml), _("This is how your outgoing message text will appear when you use protocols that support formatting."), 0);
1667 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
1669 gtk_imhtml_setup_entry(GTK_IMHTML(imhtml), PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO);
1671 g_signal_connect_after(G_OBJECT(imhtml), "format_function_toggle",
1672 G_CALLBACK(formatting_toggle_cb), toolbar);
1673 g_signal_connect_after(G_OBJECT(imhtml), "format_function_clear",
1674 G_CALLBACK(formatting_clear_cb), NULL);
1677 gtk_widget_show(ret);
1679 return ret;
1682 static void
1683 network_ip_changed(GtkEntry *entry, gpointer data)
1685 const gchar *text = gtk_entry_get_text(entry);
1686 GdkColor color;
1688 if (text && *text) {
1689 if (purple_ip_address_is_valid(text)) {
1690 color.red = 0xAFFF;
1691 color.green = 0xFFFF;
1692 color.blue = 0xAFFF;
1694 purple_network_set_public_ip(text);
1695 } else {
1696 color.red = 0xFFFF;
1697 color.green = 0xAFFF;
1698 color.blue = 0xAFFF;
1701 gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, &color);
1703 } else {
1704 purple_network_set_public_ip("");
1705 gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL);
1709 static gboolean
1710 network_stun_server_changed_cb(GtkWidget *widget,
1711 GdkEventFocus *event, gpointer data)
1713 GtkEntry *entry = GTK_ENTRY(widget);
1714 purple_prefs_set_string("/purple/network/stun_server",
1715 gtk_entry_get_text(entry));
1716 purple_network_set_stun_server(gtk_entry_get_text(entry));
1718 return FALSE;
1721 static gboolean
1722 network_turn_server_changed_cb(GtkWidget *widget,
1723 GdkEventFocus *event, gpointer data)
1725 GtkEntry *entry = GTK_ENTRY(widget);
1726 purple_prefs_set_string("/purple/network/turn_server",
1727 gtk_entry_get_text(entry));
1728 purple_network_set_turn_server(gtk_entry_get_text(entry));
1730 return FALSE;
1733 static void
1734 proxy_changed_cb(const char *name, PurplePrefType type,
1735 gconstpointer value, gpointer data)
1737 GtkWidget *frame = data;
1738 const char *proxy = value;
1740 if (strcmp(proxy, "none") && strcmp(proxy, "envvar"))
1741 gtk_widget_show_all(frame);
1742 else
1743 gtk_widget_hide(frame);
1746 static void
1747 proxy_print_option(GtkEntry *entry, int entrynum)
1749 if (entrynum == PROXYHOST)
1750 purple_prefs_set_string("/purple/proxy/host", gtk_entry_get_text(entry));
1751 else if (entrynum == PROXYPORT)
1752 purple_prefs_set_int("/purple/proxy/port", atoi(gtk_entry_get_text(entry)));
1753 else if (entrynum == PROXYUSER)
1754 purple_prefs_set_string("/purple/proxy/username", gtk_entry_get_text(entry));
1755 else if (entrynum == PROXYPASS)
1756 purple_prefs_set_string("/purple/proxy/password", gtk_entry_get_text(entry));
1759 static void
1760 proxy_button_clicked_cb(GtkWidget *button, gpointer null)
1762 GError *err = NULL;
1764 if (g_spawn_command_line_async ("gnome-network-preferences", &err))
1765 return;
1767 purple_notify_error(NULL, NULL, _("Cannot start proxy configuration program."), err->message);
1768 g_error_free(err);
1771 static void
1772 browser_button_clicked_cb(GtkWidget *button, gpointer null)
1774 GError *err = NULL;
1776 if (g_spawn_command_line_async ("gnome-default-applications-properties", &err))
1777 return;
1779 purple_notify_error(NULL, NULL, _("Cannot start browser configuration program."), err->message);
1780 g_error_free(err);
1783 static void
1784 auto_ip_button_clicked_cb(GtkWidget *button, gpointer null)
1786 const char *ip;
1787 PurpleStunNatDiscovery *stun;
1788 char *auto_ip_text;
1790 /* purple_network_get_my_ip will return the IP that was set by the user with
1791 purple_network_set_public_ip, so make a lookup for the auto-detected IP
1792 ourselves. */
1794 if (purple_prefs_get_bool("/purple/network/auto_ip")) {
1795 /* Check if STUN discovery was already done */
1796 stun = purple_stun_discover(NULL);
1797 if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) {
1798 ip = stun->publicip;
1799 } else {
1800 /* Attempt to get the IP from a NAT device using UPnP */
1801 ip = purple_upnp_get_public_ip();
1802 if (ip == NULL) {
1803 /* Attempt to get the IP from a NAT device using NAT-PMP */
1804 ip = purple_pmp_get_public_ip();
1805 if (ip == NULL) {
1806 /* Just fetch the IP of the local system */
1807 ip = purple_network_get_local_system_ip(-1);
1812 else
1813 ip = _("Disabled");
1815 auto_ip_text = g_strdup_printf(_("Use _automatically detected IP address: %s"), ip);
1816 gtk_button_set_label(GTK_BUTTON(button), auto_ip_text);
1817 g_free(auto_ip_text);
1820 static GtkWidget *
1821 network_page(void)
1823 GtkWidget *ret;
1824 GtkWidget *vbox, *hbox, *entry;
1825 GtkWidget *label, *auto_ip_checkbox, *ports_checkbox, *spin_button;
1826 GtkSizeGroup *sg;
1828 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
1829 gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
1831 vbox = pidgin_make_frame (ret, _("IP Address"));
1832 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1834 entry = gtk_entry_new();
1835 gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_string(
1836 "/purple/network/stun_server"));
1837 g_signal_connect(G_OBJECT(entry), "focus-out-event",
1838 G_CALLBACK(network_stun_server_changed_cb), NULL);
1839 gtk_widget_show(entry);
1841 pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("ST_UN server:"),
1842 sg, entry, TRUE, NULL);
1844 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
1845 gtk_container_add(GTK_CONTAINER(vbox), hbox);
1847 label = gtk_label_new(NULL);
1848 gtk_container_add(GTK_CONTAINER(hbox), label);
1849 gtk_size_group_add_widget(sg, label);
1851 label = gtk_label_new(NULL);
1852 gtk_label_set_markup(GTK_LABEL(label),
1853 _("<span style=\"italic\">Example: stunserver.org</span>"));
1854 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1855 gtk_container_add(GTK_CONTAINER(hbox), label);
1857 auto_ip_checkbox = pidgin_prefs_checkbox("Use _automatically detected IP address",
1858 "/purple/network/auto_ip", vbox);
1859 g_signal_connect(G_OBJECT(auto_ip_checkbox), "clicked",
1860 G_CALLBACK(auto_ip_button_clicked_cb), NULL);
1861 auto_ip_button_clicked_cb(auto_ip_checkbox, NULL); /* Update label */
1863 entry = gtk_entry_new();
1864 gtk_entry_set_text(GTK_ENTRY(entry), purple_network_get_public_ip());
1865 g_signal_connect(G_OBJECT(entry), "changed",
1866 G_CALLBACK(network_ip_changed), NULL);
1868 hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Public _IP:"),
1869 sg, entry, TRUE, NULL);
1871 if (purple_prefs_get_bool("/purple/network/auto_ip")) {
1872 gtk_widget_set_sensitive(GTK_WIDGET(hbox), FALSE);
1875 g_signal_connect(G_OBJECT(auto_ip_checkbox), "clicked",
1876 G_CALLBACK(pidgin_toggle_sensitive), hbox);
1878 g_object_unref(sg);
1880 vbox = pidgin_make_frame (ret, _("Ports"));
1881 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1883 pidgin_prefs_checkbox(_("_Enable automatic router port forwarding"),
1884 "/purple/network/map_ports", vbox);
1886 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
1888 ports_checkbox = pidgin_prefs_checkbox(_("_Manually specify range of ports to listen on:"),
1889 "/purple/network/ports_range_use", hbox);
1891 spin_button = pidgin_prefs_labeled_spin_button(hbox, _("_Start:"),
1892 "/purple/network/ports_range_start", 0, 65535, sg);
1893 if (!purple_prefs_get_bool("/purple/network/ports_range_use"))
1894 gtk_widget_set_sensitive(GTK_WIDGET(spin_button), FALSE);
1895 g_signal_connect(G_OBJECT(ports_checkbox), "clicked",
1896 G_CALLBACK(pidgin_toggle_sensitive), spin_button);
1898 spin_button = pidgin_prefs_labeled_spin_button(hbox, _("_End:"),
1899 "/purple/network/ports_range_end", 0, 65535, sg);
1900 if (!purple_prefs_get_bool("/purple/network/ports_range_use"))
1901 gtk_widget_set_sensitive(GTK_WIDGET(spin_button), FALSE);
1902 g_signal_connect(G_OBJECT(ports_checkbox), "clicked",
1903 G_CALLBACK(pidgin_toggle_sensitive), spin_button);
1905 pidgin_add_widget_to_vbox(GTK_BOX(vbox), NULL, NULL, hbox, TRUE, NULL);
1907 g_object_unref(sg);
1909 /* TURN server */
1910 vbox = pidgin_make_frame(ret, _("Relay Server (TURN)"));
1911 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1913 entry = gtk_entry_new();
1914 gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_string(
1915 "/purple/network/turn_server"));
1916 g_signal_connect(G_OBJECT(entry), "focus-out-event",
1917 G_CALLBACK(network_turn_server_changed_cb), NULL);
1918 gtk_widget_show(entry);
1920 hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_TURN server:"),
1921 sg, entry, TRUE, NULL);
1923 pidgin_prefs_labeled_spin_button(hbox, _("_Port:"),
1924 "/purple/network/turn_port", 0, 65535, NULL);
1925 hbox = pidgin_prefs_labeled_entry(vbox, _("Use_rname:"),
1926 "/purple/network/turn_username", sg);
1927 pidgin_prefs_labeled_password(hbox, _("Pass_word:"),
1928 "/purple/network/turn_password", NULL);
1930 gtk_widget_show_all(ret);
1931 g_object_unref(sg);
1933 return ret;
1936 #ifndef _WIN32
1937 static gboolean
1938 manual_browser_set(GtkWidget *entry, GdkEventFocus *event, gpointer data)
1940 const char *program = gtk_entry_get_text(GTK_ENTRY(entry));
1942 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/browsers/command", program);
1944 /* carry on normally */
1945 return FALSE;
1948 static GList *
1949 get_available_browsers(void)
1951 struct browser {
1952 char *name;
1953 char *command;
1956 /* Sorted reverse alphabetically */
1957 static const struct browser possible_browsers[] = {
1958 {N_("Seamonkey"), "seamonkey"},
1959 {N_("Opera"), "opera"},
1960 {N_("Netscape"), "netscape"},
1961 {N_("Mozilla"), "mozilla"},
1962 {N_("Konqueror"), "kfmclient"},
1963 {N_("Desktop Default"), "xdg-open"},
1964 {N_("GNOME Default"), "gnome-open"},
1965 {N_("Galeon"), "galeon"},
1966 {N_("Firefox"), "firefox"},
1967 {N_("Firebird"), "mozilla-firebird"},
1968 {N_("Epiphany"), "epiphany"}
1970 static const int num_possible_browsers = G_N_ELEMENTS(possible_browsers);
1972 GList *browsers = NULL;
1973 int i = 0;
1974 char *browser_setting = (char *)purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser");
1976 browsers = g_list_prepend(browsers, (gpointer)"custom");
1977 browsers = g_list_prepend(browsers, (gpointer)_("Manual"));
1979 for (i = 0; i < num_possible_browsers; i++) {
1980 if (purple_program_is_valid(possible_browsers[i].command)) {
1981 browsers = g_list_prepend(browsers,
1982 possible_browsers[i].command);
1983 browsers = g_list_prepend(browsers, (gpointer)_(possible_browsers[i].name));
1984 if(browser_setting && !strcmp(possible_browsers[i].command, browser_setting))
1985 browser_setting = NULL;
1986 /* If xdg-open is valid, prefer it over gnome-open and skip forward */
1987 if(!strcmp(possible_browsers[i].command, "xdg-open")) {
1988 if (browser_setting && !strcmp("gnome-open", browser_setting)) {
1989 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/browsers/browser", possible_browsers[i].command);
1990 browser_setting = NULL;
1992 i++;
1997 if(browser_setting)
1998 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/browsers/browser", "custom");
2000 return browsers;
2003 static void
2004 browser_changed1_cb(const char *name, PurplePrefType type,
2005 gconstpointer value, gpointer data)
2007 GtkWidget *hbox = data;
2008 const char *browser = value;
2010 gtk_widget_set_sensitive(hbox, strcmp(browser, "custom"));
2013 static void
2014 browser_changed2_cb(const char *name, PurplePrefType type,
2015 gconstpointer value, gpointer data)
2017 GtkWidget *hbox = data;
2018 const char *browser = value;
2020 gtk_widget_set_sensitive(hbox, !strcmp(browser, "custom"));
2023 static GtkWidget *
2024 browser_page(void)
2026 GtkWidget *ret, *vbox, *hbox, *label, *entry, *browser_button;
2027 GtkSizeGroup *sg;
2028 GList *browsers = NULL;
2030 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
2031 gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
2033 vbox = pidgin_make_frame (ret, _("Browser Selection"));
2035 if(purple_running_gnome()) {
2036 gchar *path = g_find_program_in_path("gnome-default-applications-properties");
2038 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2039 label = gtk_label_new(_("Browser preferences are configured in GNOME preferences"));
2040 gtk_container_add(GTK_CONTAINER(vbox), hbox);
2041 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2043 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2044 gtk_container_add(GTK_CONTAINER(vbox), hbox);
2046 if(path == NULL) {
2047 label = gtk_label_new(NULL);
2048 gtk_label_set_markup(GTK_LABEL(label),
2049 _("<b>Browser configuration program was not found.</b>"));
2050 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2051 } else {
2052 browser_button = gtk_button_new_with_mnemonic(_("Configure _Browser"));
2053 g_signal_connect(G_OBJECT(browser_button), "clicked",
2054 G_CALLBACK(browser_button_clicked_cb), NULL);
2055 gtk_box_pack_start(GTK_BOX(hbox), browser_button, FALSE, FALSE, 0);
2058 gtk_widget_show_all(ret);
2059 } else {
2060 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2062 browsers = get_available_browsers();
2063 if (browsers != NULL) {
2064 label = pidgin_prefs_dropdown_from_list(vbox,_("_Browser:"), PURPLE_PREF_STRING,
2065 PIDGIN_PREFS_ROOT "/browsers/browser",
2066 browsers);
2067 g_list_free(browsers);
2068 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
2069 gtk_size_group_add_widget(sg, label);
2071 hbox = gtk_hbox_new(FALSE, 0);
2072 label = pidgin_prefs_dropdown(hbox, _("_Open link in:"), PURPLE_PREF_INT,
2073 PIDGIN_PREFS_ROOT "/browsers/place",
2074 _("Browser default"), PIDGIN_BROWSER_DEFAULT,
2075 _("Existing window"), PIDGIN_BROWSER_CURRENT,
2076 _("New window"), PIDGIN_BROWSER_NEW_WINDOW,
2077 _("New tab"), PIDGIN_BROWSER_NEW_TAB,
2078 NULL);
2079 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
2080 gtk_size_group_add_widget(sg, label);
2081 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2083 if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"), "custom"))
2084 gtk_widget_set_sensitive(hbox, FALSE);
2085 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/browsers/browser",
2086 browser_changed1_cb, hbox);
2089 entry = gtk_entry_new();
2090 gtk_entry_set_text(GTK_ENTRY(entry),
2091 purple_prefs_get_path(PIDGIN_PREFS_ROOT "/browsers/command"));
2092 g_signal_connect(G_OBJECT(entry), "focus-out-event",
2093 G_CALLBACK(manual_browser_set), NULL);
2094 hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Manual:\n(%s for URL)"), sg, entry, TRUE, NULL);
2095 if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"), "custom"))
2096 gtk_widget_set_sensitive(hbox, FALSE);
2097 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/browsers/browser",
2098 browser_changed2_cb, hbox);
2100 gtk_widget_show_all(ret);
2101 g_object_unref(sg);
2104 return ret;
2106 #endif /*_WIN32*/
2108 static GtkWidget *
2109 proxy_page(void)
2111 GtkWidget *ret = NULL, *vbox = NULL, *hbox = NULL;
2112 GtkWidget *table = NULL, *entry = NULL, *label = NULL, *proxy_button = NULL;
2113 PurpleProxyInfo *proxy_info;
2115 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
2116 gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
2117 vbox = pidgin_make_frame(ret, _("Proxy Server"));
2118 prefs_proxy_frame = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2120 if(purple_running_gnome()) {
2121 gchar *path = g_find_program_in_path("gnome-network-preferences");
2123 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2124 label = gtk_label_new(_("Proxy preferences are configured in GNOME preferences"));
2125 gtk_container_add(GTK_CONTAINER(vbox), hbox);
2126 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2128 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2129 gtk_container_add(GTK_CONTAINER(vbox), hbox);
2131 if(path == NULL) {
2132 label = gtk_label_new(NULL);
2133 gtk_label_set_markup(GTK_LABEL(label),
2134 _("<b>Proxy configuration program was not found.</b>"));
2135 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2136 } else {
2137 proxy_button = gtk_button_new_with_mnemonic(_("Configure _Proxy"));
2138 g_signal_connect(G_OBJECT(proxy_button), "clicked",
2139 G_CALLBACK(proxy_button_clicked_cb), NULL);
2140 gtk_box_pack_start(GTK_BOX(hbox), proxy_button, FALSE, FALSE, 0);
2143 gtk_widget_show_all(ret);
2144 } else {
2145 prefs_proxy_subframe = gtk_vbox_new(FALSE, 0);
2147 /* This is a global option that affects SOCKS4 usage even with
2148 * account-specific proxy settings */
2149 pidgin_prefs_checkbox(_("Use remote _DNS with SOCKS4 proxies"),
2150 "/purple/proxy/socks4_remotedns", prefs_proxy_frame);
2151 gtk_box_pack_start(GTK_BOX(vbox), prefs_proxy_frame, 0, 0, 0);
2153 pidgin_prefs_dropdown(prefs_proxy_frame, _("Proxy t_ype:"), PURPLE_PREF_STRING,
2154 "/purple/proxy/type",
2155 _("No proxy"), "none",
2156 "SOCKS 4", "socks4",
2157 "SOCKS 5", "socks5",
2158 "HTTP", "http",
2159 _("Use Environmental Settings"), "envvar",
2160 NULL);
2161 gtk_box_pack_start(GTK_BOX(prefs_proxy_frame), prefs_proxy_subframe, 0, 0, 0);
2162 proxy_info = purple_global_proxy_get_info();
2164 gtk_widget_show_all(ret);
2166 purple_prefs_connect_callback(prefs, "/purple/proxy/type",
2167 proxy_changed_cb, prefs_proxy_subframe);
2169 table = gtk_table_new(4, 2, FALSE);
2170 gtk_container_set_border_width(GTK_CONTAINER(table), 0);
2171 gtk_table_set_col_spacings(GTK_TABLE(table), 5);
2172 gtk_table_set_row_spacings(GTK_TABLE(table), 10);
2173 gtk_container_add(GTK_CONTAINER(prefs_proxy_subframe), table);
2176 label = gtk_label_new_with_mnemonic(_("_Host:"));
2177 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
2178 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
2180 entry = gtk_entry_new();
2181 gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
2182 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
2183 g_signal_connect(G_OBJECT(entry), "changed",
2184 G_CALLBACK(proxy_print_option), (void *)PROXYHOST);
2186 if (proxy_info != NULL && purple_proxy_info_get_host(proxy_info))
2187 gtk_entry_set_text(GTK_ENTRY(entry),
2188 purple_proxy_info_get_host(proxy_info));
2190 hbox = gtk_hbox_new(TRUE, 5);
2191 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2192 pidgin_set_accessible_label (entry, label);
2194 label = gtk_label_new_with_mnemonic(_("P_ort:"));
2195 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
2196 gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);
2198 entry = gtk_spin_button_new_with_range(0, 65535, 1);
2199 gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
2200 gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 0, 1, GTK_FILL, 0, 0, 0);
2201 g_signal_connect(G_OBJECT(entry), "changed",
2202 G_CALLBACK(proxy_print_option), (void *)PROXYPORT);
2204 if (proxy_info != NULL && purple_proxy_info_get_port(proxy_info) != 0) {
2205 char buf[128];
2206 g_snprintf(buf, sizeof(buf), "%d",
2207 purple_proxy_info_get_port(proxy_info));
2209 gtk_entry_set_text(GTK_ENTRY(entry), buf);
2211 pidgin_set_accessible_label (entry, label);
2213 label = gtk_label_new_with_mnemonic(_("User_name:"));
2214 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
2215 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
2217 entry = gtk_entry_new();
2218 gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
2219 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 1, 2, GTK_FILL, 0, 0, 0);
2220 g_signal_connect(G_OBJECT(entry), "changed",
2221 G_CALLBACK(proxy_print_option), (void *)PROXYUSER);
2223 if (proxy_info != NULL && purple_proxy_info_get_username(proxy_info) != NULL)
2224 gtk_entry_set_text(GTK_ENTRY(entry),
2225 purple_proxy_info_get_username(proxy_info));
2227 hbox = gtk_hbox_new(TRUE, 5);
2228 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2229 pidgin_set_accessible_label (entry, label);
2231 label = gtk_label_new_with_mnemonic(_("Pa_ssword:"));
2232 gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
2233 gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);
2235 entry = gtk_entry_new();
2236 gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
2237 gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 1, 2, GTK_FILL , 0, 0, 0);
2238 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
2239 #if !GTK_CHECK_VERSION(2,16,0)
2240 if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*')
2241 gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR);
2242 #endif /* Less than GTK+ 2.16 */
2243 g_signal_connect(G_OBJECT(entry), "changed",
2244 G_CALLBACK(proxy_print_option), (void *)PROXYPASS);
2246 if (proxy_info != NULL && purple_proxy_info_get_password(proxy_info) != NULL)
2247 gtk_entry_set_text(GTK_ENTRY(entry),
2248 purple_proxy_info_get_password(proxy_info));
2249 pidgin_set_accessible_label (entry, label);
2252 return ret;
2255 static GtkWidget *
2256 logging_page(void)
2258 GtkWidget *ret;
2259 GtkWidget *vbox;
2260 GList *names;
2262 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
2263 gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
2266 vbox = pidgin_make_frame (ret, _("Logging"));
2267 names = purple_log_logger_get_options();
2269 pidgin_prefs_dropdown_from_list(vbox, _("Log _format:"), PURPLE_PREF_STRING,
2270 "/purple/logging/format", names);
2272 g_list_free(names);
2274 pidgin_prefs_checkbox(_("Log all _instant messages"),
2275 "/purple/logging/log_ims", vbox);
2276 pidgin_prefs_checkbox(_("Log all c_hats"),
2277 "/purple/logging/log_chats", vbox);
2278 pidgin_prefs_checkbox(_("Log all _status changes to system log"),
2279 "/purple/logging/log_system", vbox);
2281 gtk_widget_show_all(ret);
2283 return ret;
2286 #ifndef _WIN32
2287 static gint
2288 sound_cmd_yeah(GtkEntry *entry, gpointer d)
2290 purple_prefs_set_path(PIDGIN_PREFS_ROOT "/sound/command",
2291 gtk_entry_get_text(GTK_ENTRY(entry)));
2292 return TRUE;
2295 static void
2296 sound_changed1_cb(const char *name, PurplePrefType type,
2297 gconstpointer value, gpointer data)
2299 GtkWidget *hbox = data;
2300 const char *method = value;
2302 gtk_widget_set_sensitive(hbox, !strcmp(method, "custom"));
2305 static void
2306 sound_changed2_cb(const char *name, PurplePrefType type,
2307 gconstpointer value, gpointer data)
2309 GtkWidget *vbox = data;
2310 const char *method = value;
2312 gtk_widget_set_sensitive(vbox, strcmp(method, "none"));
2314 #endif /* !_WIN32 */
2316 #ifdef USE_GSTREAMER
2317 static void
2318 sound_changed3_cb(const char *name, PurplePrefType type,
2319 gconstpointer value, gpointer data)
2321 GtkWidget *hbox = data;
2322 const char *method = value;
2324 gtk_widget_set_sensitive(hbox,
2325 !strcmp(method, "automatic") ||
2326 !strcmp(method, "esd"));
2328 #endif /* USE_GSTREAMER */
2331 static void
2332 event_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer data)
2334 GtkTreeModel *model = (GtkTreeModel *)data;
2335 GtkTreeIter iter;
2336 GtkTreePath *path = gtk_tree_path_new_from_string(pth);
2337 char *pref;
2339 gtk_tree_model_get_iter (model, &iter, path);
2340 gtk_tree_model_get (model, &iter,
2341 2, &pref,
2342 -1);
2344 purple_prefs_set_bool(pref, !gtk_cell_renderer_toggle_get_active(cell));
2345 g_free(pref);
2347 gtk_list_store_set(GTK_LIST_STORE (model), &iter,
2348 0, !gtk_cell_renderer_toggle_get_active(cell),
2349 -1);
2351 gtk_tree_path_free(path);
2354 static void
2355 test_sound(GtkWidget *button, gpointer i_am_NULL)
2357 char *pref;
2358 gboolean temp_enabled;
2359 gboolean temp_mute;
2361 pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/enabled/%s",
2362 pidgin_sound_get_event_option(sound_row_sel));
2364 temp_enabled = purple_prefs_get_bool(pref);
2365 temp_mute = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/mute");
2367 if (!temp_enabled) purple_prefs_set_bool(pref, TRUE);
2368 if (temp_mute) purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE);
2370 purple_sound_play_event(sound_row_sel, NULL);
2372 if (!temp_enabled) purple_prefs_set_bool(pref, FALSE);
2373 if (temp_mute) purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", TRUE);
2375 g_free(pref);
2379 * Resets a sound file back to default.
2381 static void
2382 reset_sound(GtkWidget *button, gpointer i_am_also_NULL)
2384 gchar *pref;
2386 pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
2387 pidgin_sound_get_event_option(sound_row_sel));
2388 purple_prefs_set_path(pref, "");
2389 g_free(pref);
2391 gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
2393 pref_sound_generate_markup();
2396 static void
2397 sound_chosen_cb(void *user_data, const char *filename)
2399 gchar *pref;
2400 int sound;
2402 sound = GPOINTER_TO_INT(user_data);
2404 /* Set it -- and forget it */
2405 pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
2406 pidgin_sound_get_event_option(sound));
2407 purple_prefs_set_path(pref, filename);
2408 g_free(pref);
2411 * If the sound we just changed is still the currently selected
2412 * sound, then update the box showing the file name.
2414 if (sound == sound_row_sel)
2415 gtk_entry_set_text(GTK_ENTRY(sound_entry), filename);
2417 pref_sound_generate_markup();
2420 static void
2421 select_sound(GtkWidget *button, gpointer being_NULL_is_fun)
2423 gchar *pref;
2424 const char *filename;
2426 pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
2427 pidgin_sound_get_event_option(sound_row_sel));
2428 filename = purple_prefs_get_path(pref);
2429 g_free(pref);
2431 if (*filename == '\0')
2432 filename = NULL;
2434 purple_request_file(prefs, _("Sound Selection"), filename, FALSE,
2435 G_CALLBACK(sound_chosen_cb), NULL,
2436 NULL, NULL, NULL,
2437 GINT_TO_POINTER(sound_row_sel));
2440 #ifdef USE_GSTREAMER
2441 static gchar *
2442 prefs_sound_volume_format(GtkScale *scale, gdouble val)
2444 if(val < 15) {
2445 return g_strdup_printf(_("Quietest"));
2446 } else if(val < 30) {
2447 return g_strdup_printf(_("Quieter"));
2448 } else if(val < 45) {
2449 return g_strdup_printf(_("Quiet"));
2450 } else if(val < 55) {
2451 return g_strdup_printf(_("Normal"));
2452 } else if(val < 70) {
2453 return g_strdup_printf(_("Loud"));
2454 } else if(val < 85) {
2455 return g_strdup_printf(_("Louder"));
2456 } else {
2457 return g_strdup_printf(_("Loudest"));
2461 static void
2462 prefs_sound_volume_changed(GtkRange *range)
2464 int val = (int)gtk_range_get_value(GTK_RANGE(range));
2465 purple_prefs_set_int(PIDGIN_PREFS_ROOT "/sound/volume", val);
2467 #endif
2469 static void
2470 prefs_sound_sel(GtkTreeSelection *sel, GtkTreeModel *model)
2472 GtkTreeIter iter;
2473 GValue val;
2474 const char *file;
2475 char *pref;
2477 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
2478 return;
2480 val.g_type = 0;
2481 gtk_tree_model_get_value (model, &iter, 3, &val);
2482 sound_row_sel = g_value_get_uint(&val);
2484 pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
2485 pidgin_sound_get_event_option(sound_row_sel));
2486 file = purple_prefs_get_path(pref);
2487 g_free(pref);
2488 if (sound_entry)
2489 gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)"));
2490 g_value_unset (&val);
2492 pref_sound_generate_markup();
2496 static void
2497 mute_changed_cb(const char *pref_name,
2498 PurplePrefType pref_type,
2499 gconstpointer val,
2500 gpointer data)
2502 GtkToggleButton *button = data;
2503 gboolean muted = GPOINTER_TO_INT(val);
2505 g_return_if_fail(!strcmp (pref_name, PIDGIN_PREFS_ROOT "/sound/mute"));
2507 /* Block the handler that re-sets the preference. */
2508 g_signal_handlers_block_matched(button, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (gpointer)pref_name);
2509 gtk_toggle_button_set_active (button, muted);
2510 g_signal_handlers_unblock_matched(button, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (gpointer)pref_name);
2514 static GtkWidget *
2515 sound_page(void)
2517 GtkWidget *ret;
2518 GtkWidget *vbox, *vbox2, *sw, *button;
2519 GtkSizeGroup *sg;
2520 GtkTreeIter iter;
2521 GtkWidget *event_view;
2522 GtkListStore *event_store;
2523 GtkCellRenderer *rend;
2524 GtkTreeViewColumn *col;
2525 GtkTreeSelection *sel;
2526 GtkTreePath *path;
2527 GtkWidget *hbox;
2528 int j;
2529 const char *file;
2530 char *pref;
2531 #ifndef _WIN32
2532 GtkWidget *dd;
2533 GtkWidget *entry;
2534 const char *cmd;
2535 #endif
2537 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
2538 gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
2540 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2542 vbox2 = pidgin_make_frame(ret, _("Sound Options"));
2544 #ifndef _WIN32
2545 dd = pidgin_prefs_dropdown(vbox2, _("_Method:"), PURPLE_PREF_STRING,
2546 PIDGIN_PREFS_ROOT "/sound/method",
2547 _("Console beep"), "beep",
2548 #ifdef USE_GSTREAMER
2549 _("Automatic"), "automatic",
2550 "ESD", "esd",
2551 "ALSA", "alsa",
2552 #endif
2553 _("Command"), "custom",
2554 _("No sounds"), "none",
2555 NULL);
2556 gtk_size_group_add_widget(sg, dd);
2557 gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
2559 vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2560 gtk_box_pack_start(GTK_BOX(vbox2), vbox, FALSE, FALSE, 0);
2562 entry = gtk_entry_new();
2563 gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);
2564 cmd = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/sound/command");
2565 if(cmd)
2566 gtk_entry_set_text(GTK_ENTRY(entry), cmd);
2567 g_signal_connect(G_OBJECT(entry), "changed",
2568 G_CALLBACK(sound_cmd_yeah), NULL);
2570 hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Sound c_ommand:\n(%s for filename)"), sg, entry, TRUE, NULL);
2571 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
2572 sound_changed1_cb, hbox);
2573 gtk_widget_set_sensitive(hbox,
2574 !strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"),
2575 "custom"));
2576 #endif /* _WIN32 */
2578 button = pidgin_prefs_checkbox(_("M_ute sounds"), PIDGIN_PREFS_ROOT "/sound/mute", vbox);
2579 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/mute", mute_changed_cb, button);
2581 pidgin_prefs_checkbox(_("Sounds when conversation has _focus"),
2582 PIDGIN_PREFS_ROOT "/sound/conv_focus", vbox);
2583 pidgin_prefs_dropdown(vbox, _("_Enable sounds:"),
2584 PURPLE_PREF_INT, "/purple/sound/while_status",
2585 _("Only when available"), 1,
2586 _("Only when not available"), 2,
2587 _("Always"), 3,
2588 NULL);
2590 #ifdef USE_GSTREAMER
2591 sw = gtk_hscale_new_with_range(0.0, 100.0, 5.0);
2592 gtk_range_set_increments(GTK_RANGE(sw), 5.0, 25.0);
2593 gtk_range_set_value(GTK_RANGE(sw), purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume"));
2594 g_signal_connect (G_OBJECT (sw), "format-value",
2595 G_CALLBACK (prefs_sound_volume_format),
2596 NULL);
2597 g_signal_connect (G_OBJECT (sw), "value-changed",
2598 G_CALLBACK (prefs_sound_volume_changed),
2599 NULL);
2600 hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("V_olume:"), NULL, sw, TRUE, NULL);
2602 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
2603 sound_changed3_cb, hbox);
2604 sound_changed3_cb(PIDGIN_PREFS_ROOT "/sound/method", PURPLE_PREF_STRING,
2605 purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), hbox);
2606 #endif
2608 #ifndef _WIN32
2609 gtk_widget_set_sensitive(vbox,
2610 strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none"));
2611 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
2612 sound_changed2_cb, vbox);
2613 #endif
2614 vbox = pidgin_make_frame(ret, _("Sound Events"));
2616 /* The following is an ugly hack to make the frame expand so the
2617 * sound events list is big enough to be usable */
2618 gtk_box_set_child_packing(GTK_BOX(vbox->parent), vbox, TRUE, TRUE, 0,
2619 GTK_PACK_START);
2620 gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent), vbox->parent, TRUE,
2621 TRUE, 0, GTK_PACK_START);
2622 gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent->parent),
2623 vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START);
2625 /* SOUND SELECTION */
2626 sw = gtk_scrolled_window_new(NULL,NULL);
2627 gtk_widget_set_size_request(sw, -1, 100);
2628 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2629 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
2631 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
2632 event_store = gtk_list_store_new (4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
2634 for (j=0; j < PURPLE_NUM_SOUNDS; j++) {
2635 char *pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/enabled/%s",
2636 pidgin_sound_get_event_option(j));
2637 const char *label = pidgin_sound_get_event_label(j);
2639 if (label == NULL) {
2640 g_free(pref);
2641 continue;
2644 gtk_list_store_append (event_store, &iter);
2645 gtk_list_store_set(event_store, &iter,
2646 0, purple_prefs_get_bool(pref),
2647 1, _(label),
2648 2, pref,
2649 3, j,
2650 -1);
2651 g_free(pref);
2654 event_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(event_store));
2656 rend = gtk_cell_renderer_toggle_new();
2657 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
2658 g_signal_connect (G_OBJECT (sel), "changed",
2659 G_CALLBACK (prefs_sound_sel),
2660 NULL);
2661 g_signal_connect (G_OBJECT(rend), "toggled",
2662 G_CALLBACK(event_toggled), event_store);
2663 path = gtk_tree_path_new_first();
2664 gtk_tree_selection_select_path(sel, path);
2665 gtk_tree_path_free(path);
2667 col = gtk_tree_view_column_new_with_attributes (_("Play"),
2668 rend,
2669 "active", 0,
2670 NULL);
2671 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
2673 rend = gtk_cell_renderer_text_new();
2674 col = gtk_tree_view_column_new_with_attributes (_("Event"),
2675 rend,
2676 "text", 1,
2677 NULL);
2678 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
2679 g_object_unref(G_OBJECT(event_store));
2680 gtk_container_add(GTK_CONTAINER(sw), event_view);
2682 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2683 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2684 sound_entry = gtk_entry_new();
2685 pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
2686 pidgin_sound_get_event_option(0));
2687 file = purple_prefs_get_path(pref);
2688 g_free(pref);
2689 gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)"));
2690 gtk_editable_set_editable(GTK_EDITABLE(sound_entry), FALSE);
2691 gtk_box_pack_start(GTK_BOX(hbox), sound_entry, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
2693 button = gtk_button_new_with_mnemonic(_("_Browse..."));
2694 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(select_sound), NULL);
2695 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
2697 button = gtk_button_new_with_mnemonic(_("Pre_view"));
2698 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(test_sound), NULL);
2699 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
2701 button = gtk_button_new_with_mnemonic(_("_Reset"));
2702 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(reset_sound), NULL);
2703 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
2705 gtk_widget_show_all(ret);
2706 g_object_unref(sg);
2708 return ret;
2712 static void
2713 set_idle_away(PurpleSavedStatus *status)
2715 purple_prefs_set_int("/purple/savedstatus/idleaway", purple_savedstatus_get_creation_time(status));
2718 static void
2719 set_startupstatus(PurpleSavedStatus *status)
2721 purple_prefs_set_int("/purple/savedstatus/startup", purple_savedstatus_get_creation_time(status));
2724 static GtkWidget *
2725 away_page(void)
2727 GtkWidget *ret;
2728 GtkWidget *vbox;
2729 GtkWidget *hbox;
2730 GtkWidget *dd;
2731 GtkWidget *label;
2732 GtkWidget *button;
2733 GtkWidget *select;
2734 GtkWidget *menu;
2735 GtkSizeGroup *sg;
2737 ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
2738 gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
2740 sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2742 /* Idle stuff */
2743 vbox = pidgin_make_frame(ret, _("Idle"));
2745 dd = pidgin_prefs_dropdown(vbox, _("_Report idle time:"),
2746 PURPLE_PREF_STRING, "/purple/away/idle_reporting",
2747 _("Never"), "none",
2748 _("From last sent message"), "purple",
2749 #if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT)
2750 _("Based on keyboard or mouse use"), "system",
2751 #endif
2752 NULL);
2753 gtk_size_group_add_widget(sg, dd);
2754 gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
2756 select = pidgin_prefs_labeled_spin_button(vbox,
2757 _("_Minutes before becoming idle:"), "/purple/away/mins_before_away",
2758 1, 24 * 60, sg);
2760 hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
2761 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2763 button = pidgin_prefs_checkbox(_("Change to this status when _idle:"),
2764 "/purple/away/away_when_idle", hbox);
2765 gtk_size_group_add_widget(sg, button);
2767 /* TODO: Show something useful if we don't have any saved statuses. */
2768 menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away));
2769 gtk_size_group_add_widget(sg, menu);
2770 gtk_box_pack_start(GTK_BOX(hbox), menu, FALSE, FALSE, 0);
2772 g_signal_connect(G_OBJECT(button), "clicked",
2773 G_CALLBACK(pidgin_toggle_sensitive), menu);
2775 if (!purple_prefs_get_bool("/purple/away/away_when_idle")) {
2776 gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE);
2777 gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE);
2780 /* Away stuff */
2781 vbox = pidgin_make_frame(ret, _("Away"));
2783 dd = pidgin_prefs_dropdown(vbox, _("_Auto-reply:"),
2784 PURPLE_PREF_STRING, "/purple/away/auto_reply",
2785 _("Never"), "never",
2786 _("When away"), "away",
2787 _("When both away and idle"), "awayidle",
2788 NULL);
2789 gtk_size_group_add_widget(sg, dd);
2790 gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
2792 /* Signon status stuff */
2793 vbox = pidgin_make_frame(ret, _("Status at Startup"));
2795 button = pidgin_prefs_checkbox(_("Use status from last _exit at startup"),
2796 "/purple/savedstatus/startup_current_status", vbox);
2797 gtk_size_group_add_widget(sg, button);
2799 /* TODO: Show something useful if we don't have any saved statuses. */
2800 menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus));
2801 gtk_size_group_add_widget(sg, menu);
2802 g_signal_connect(G_OBJECT(button), "clicked",
2803 G_CALLBACK(pidgin_toggle_sensitive), menu);
2804 pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Status to a_pply at startup:"), sg, menu, TRUE, &label);
2805 g_signal_connect(G_OBJECT(button), "clicked",
2806 G_CALLBACK(pidgin_toggle_sensitive), label);
2808 if (purple_prefs_get_bool("/purple/savedstatus/startup_current_status")) {
2809 gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE);
2810 gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE);
2813 gtk_widget_show_all(ret);
2814 g_object_unref(sg);
2816 return ret;
2819 static int
2820 prefs_notebook_add_page(const char *text, GtkWidget *page, int ind)
2822 #if GTK_CHECK_VERSION(2,4,0)
2823 return gtk_notebook_append_page(GTK_NOTEBOOK(prefsnotebook), page, gtk_label_new(text));
2824 #else
2825 gtk_notebook_append_page(GTK_NOTEBOOK(prefsnotebook), page, gtk_label_new(text));
2826 return gtk_notebook_page_num(GTK_NOTEBOOK(prefsnotebook), page);
2827 #endif
2830 static void
2831 prefs_notebook_init(void)
2833 prefs_notebook_add_page(_("Interface"), interface_page(), notebook_page++);
2835 #ifndef _WIN32
2836 /* We use the registered default browser in windows */
2837 /* if the user is running Mac OS X, hide the browsers tab */
2838 if(purple_running_osx() == FALSE)
2839 prefs_notebook_add_page(_("Browser"), browser_page(), notebook_page++);
2840 #endif
2842 prefs_notebook_add_page(_("Conversations"), conv_page(), notebook_page++);
2843 prefs_notebook_add_page(_("Logging"), logging_page(), notebook_page++);
2844 prefs_notebook_add_page(_("Network"), network_page(), notebook_page++);
2845 prefs_notebook_add_page(_("Proxy"), proxy_page(), notebook_page++);
2847 prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++);
2848 prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++);
2849 prefs_notebook_add_page(_("Themes"), theme_page(), notebook_page++);
2852 void
2853 pidgin_prefs_show(void)
2855 GtkWidget *vbox;
2856 GtkWidget *notebook;
2857 GtkWidget *button;
2859 if (prefs) {
2860 gtk_window_present(GTK_WINDOW(prefs));
2861 return;
2864 /* copy the preferences to tmp values...
2865 * I liked "take affect immediately" Oh well :-( */
2866 /* (that should have been "effect," right?) */
2868 /* Back to instant-apply! I win! BU-HAHAHA! */
2870 /* Create the window */
2871 prefs = pidgin_create_dialog(_("Preferences"), PIDGIN_HIG_BORDER, "preferences", FALSE);
2872 g_signal_connect(G_OBJECT(prefs), "destroy",
2873 G_CALLBACK(delete_prefs), NULL);
2875 vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(prefs), FALSE, PIDGIN_HIG_BORDER);
2877 /* The notebook */
2878 prefsnotebook = notebook = gtk_notebook_new ();
2879 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_LEFT);
2880 gtk_box_pack_start(GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
2881 gtk_widget_show(prefsnotebook);
2883 button = pidgin_dialog_add_button(GTK_DIALOG(prefs), GTK_STOCK_CLOSE, NULL, NULL);
2884 g_signal_connect_swapped(G_OBJECT(button), "clicked",
2885 G_CALLBACK(gtk_widget_destroy), prefs);
2887 prefs_notebook_init();
2889 /* Refresh the list of themes before showing the preferences window */
2890 prefs_themes_refresh();
2892 /* Show everything. */
2893 gtk_widget_show(prefs);
2896 static void
2897 set_bool_pref(GtkWidget *w, const char *key)
2899 purple_prefs_set_bool(key,
2900 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));
2903 GtkWidget *
2904 pidgin_prefs_checkbox(const char *text, const char *key, GtkWidget *page)
2906 GtkWidget *button;
2908 button = gtk_check_button_new_with_mnemonic(text);
2909 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
2910 purple_prefs_get_bool(key));
2912 gtk_box_pack_start(GTK_BOX(page), button, FALSE, FALSE, 0);
2914 g_signal_connect(G_OBJECT(button), "clicked",
2915 G_CALLBACK(set_bool_pref), (char *)key);
2917 gtk_widget_show(button);
2919 return button;
2922 static void
2923 smiley_theme_pref_cb(const char *name, PurplePrefType type,
2924 gconstpointer value, gpointer data)
2926 const char *themename = value;
2927 GSList *themes;
2929 for (themes = smiley_themes; themes; themes = themes->next) {
2930 struct smiley_theme *smile = themes->data;
2931 if (smile->name && strcmp(themename, smile->name) == 0) {
2932 pidgin_themes_load_smiley_theme(smile->path, TRUE);
2933 break;
2938 void
2939 pidgin_prefs_init(void)
2941 purple_prefs_add_none(PIDGIN_PREFS_ROOT "");
2942 purple_prefs_add_none("/plugins/gtk");
2944 #ifndef _WIN32
2945 /* Browsers */
2946 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/browsers");
2947 purple_prefs_add_int(PIDGIN_PREFS_ROOT "/browsers/place", PIDGIN_BROWSER_DEFAULT);
2948 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/browsers/command", "");
2949 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/browsers/browser", "mozilla");
2950 #endif
2952 /* Plugins */
2953 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/plugins");
2954 purple_prefs_add_path_list(PIDGIN_PREFS_ROOT "/plugins/loaded", NULL);
2956 /* File locations */
2957 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/filelocations");
2958 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder", "");
2959 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", "");
2960 purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder", "");
2962 /* Themes */
2963 prefs_themes_init();
2965 /* Smiley Themes */
2966 purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys");
2967 purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
2969 /* Smiley Callbacks */
2970 purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
2971 smiley_theme_pref_cb, NULL);
2973 pidgin_prefs_update_old();
2976 void
2977 pidgin_prefs_update_old(void)
2979 const char *str;
2981 purple_prefs_rename("/gaim/gtk", PIDGIN_PREFS_ROOT);
2983 /* Rename some old prefs */
2984 purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_ims", "/purple/logging/log_ims");
2985 purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_chats", "/purple/logging/log_chats");
2986 purple_prefs_rename("/purple/conversations/placement",
2987 PIDGIN_PREFS_ROOT "/conversations/placement");
2989 purple_prefs_rename(PIDGIN_PREFS_ROOT "/debug/timestamps", "/purple/debug/timestamps");
2990 purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/im/raise_on_events", "/plugins/gtk/X11/notify/method_raise");
2992 purple_prefs_rename_boolean_toggle(PIDGIN_PREFS_ROOT "/conversations/ignore_colors",
2993 PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting");
2995 /* this string pref moved into the core, try to be friendly */
2996 purple_prefs_rename(PIDGIN_PREFS_ROOT "/idle/reporting_method", "/purple/away/idle_reporting");
2997 if ((str = purple_prefs_get_string("/purple/away/idle_reporting")) &&
2998 strcmp(str, "gaim") == 0)
2999 purple_prefs_set_string("/purple/away/idle_reporting", "purple");
3001 /* Remove some no-longer-used prefs */
3002 purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/auto_expand_contacts");
3003 purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/button_style");
3004 purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/grey_idle_buddies");
3005 purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/raise_on_events");
3006 purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_group_count");
3007 purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_warning_level");
3008 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/button_type");
3009 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ctrl_enter_sends");
3010 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/enter_sends");
3011 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/escape_closes");
3012 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/html_shortcuts");
3013 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/icons_on_tabs");
3014 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/send_formatting");
3015 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/show_smileys");
3016 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/show_urls_as_links");
3017 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/smiley_shortcuts");
3018 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_bgcolor");
3019 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_fgcolor");
3020 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_font");
3021 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/custom_font");
3022 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_size");
3023 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/old_tab_complete");
3024 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/tab_completion");
3025 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/hide_on_send");
3026 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/color_nicks");
3027 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/raise_on_events");
3028 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_fonts");
3029 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_font_sizes");
3030 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/passthrough_unknown_commands");
3031 purple_prefs_remove(PIDGIN_PREFS_ROOT "/idle");
3032 purple_prefs_remove(PIDGIN_PREFS_ROOT "/logging/individual_logs");
3033 purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/signon");
3034 purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/silent_signon");
3036 /* Convert old queuing prefs to hide_new 3-way pref. */
3037 if (purple_prefs_exists("/plugins/gtk/docklet/queue_messages") &&
3038 purple_prefs_get_bool("/plugins/gtk/docklet/queue_messages"))
3040 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "always");
3042 else if (purple_prefs_exists(PIDGIN_PREFS_ROOT "/away/queue_messages") &&
3043 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/away/queue_messages"))
3045 purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "away");
3047 purple_prefs_remove(PIDGIN_PREFS_ROOT "/away/queue_messages");
3048 purple_prefs_remove(PIDGIN_PREFS_ROOT "/away");
3049 purple_prefs_remove("/plugins/gtk/docklet/queue_messages");
3051 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_width");
3052 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_height");
3053 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_width");
3054 purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_height");
3055 purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/x",
3056 PIDGIN_PREFS_ROOT "/conversations/im/x");
3057 purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/y",
3058 PIDGIN_PREFS_ROOT "/conversations/im/y");