Mercurial > pidgin.yaz
diff pidgin/gtkprefs.c @ 27666:a08e84032814
merge of '2348ff22f0ff3453774b8b25b36238465580c609'
and 'e76f11543c2a4aa05bdf584f087cbe3439029661'
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sun, 12 Jul 2009 05:43:38 +0000 |
parents | 5b07c7253ba4 |
children | 85fa979b08c2 da33da2e1a1e |
line wrap: on
line diff
--- a/pidgin/gtkprefs.c Sun Jul 12 05:42:40 2009 +0000 +++ b/pidgin/gtkprefs.c Sun Jul 12 05:43:38 2009 +0000 @@ -28,6 +28,7 @@ #include "pidgin.h" #include "debug.h" +#include "nat-pmp.h" #include "notify.h" #include "prefs.h" #include "proxy.h" @@ -36,7 +37,9 @@ #include "savedstatuses.h" #include "sound.h" #include "sound-theme.h" +#include "stun.h" #include "theme-manager.h" +#include "upnp.h" #include "util.h" #include "network.h" @@ -61,6 +64,12 @@ #define PREFS_OPTIMAL_ICON_SIZE 32 +struct theme_info { + gchar *type; + gchar *extension; + gchar *original_name; +}; + static int sound_row_sel = 0; static GtkWidget *prefsnotebook; @@ -74,11 +83,13 @@ static int notebook_page = 0; static GtkTreeRowReference *previous_smiley_row = NULL; -static gboolean prefs_themes_unsorted = TRUE; static GtkListStore *prefs_sound_themes; static GtkListStore *prefs_blist_themes; static GtkListStore *prefs_status_icon_themes; +static GtkWidget *prefs_sound_themes_combo_box; +static GtkWidget *prefs_blist_themes_combo_box; +static GtkWidget *prefs_status_themes_combo_box; /* * PROTOTYPES @@ -106,7 +117,7 @@ val = purple_prefs_get_int(key); - adjust = gtk_adjustment_new(val, min, max, 1, 1, 1); + adjust = gtk_adjustment_new(val, min, max, 1, 1, 0); spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); g_object_set_data(G_OBJECT(spin), "val", (char *)key); if (max < 10000) @@ -121,7 +132,8 @@ } static void -entry_set(GtkEntry *entry, gpointer data) { +entry_set(GtkEntry *entry, gpointer data) +{ const char *key = (const char*)data; purple_prefs_set_string(key, gtk_entry_get_text(entry)); @@ -333,7 +345,9 @@ } -static void smiley_sel(GtkTreeSelection *sel, GtkTreeModel *model) { +static void +smiley_sel(GtkTreeSelection *sel, GtkTreeModel *model) +{ GtkTreeIter iter; const char *themename; char *description; @@ -384,7 +398,8 @@ gtk_tree_path_free(path); } -static GtkTreeRowReference *theme_refresh_theme_list(void) +static GtkTreeRowReference * +theme_refresh_theme_list(void) { GdkPixbuf *pixbuf; GSList *themes; @@ -442,144 +457,9 @@ return row_ref; } -static void theme_install_theme(char *path, char *extn) { -#ifndef _WIN32 - gchar *command; -#endif - gchar *destdir; - gchar *tail; - GtkTreeRowReference *theme_rowref; - - /* Just to be safe */ - g_strchomp(path); - - /* I dont know what you are, get out of here */ - if (extn != NULL) - tail = extn; - else if ((tail = strrchr(path, '.')) == NULL) - return; - - destdir = g_strconcat(purple_user_dir(), G_DIR_SEPARATOR_S "smileys", NULL); - - /* We'll check this just to make sure. This also lets us do something different on - * other platforms, if need be */ - if (!g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz")) { -#ifndef _WIN32 - gchar *path_escaped = g_shell_quote(path); - gchar *destdir_escaped = g_shell_quote(destdir); - command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped); - g_free(path_escaped); - g_free(destdir_escaped); -#else - if(!winpidgin_gz_untar(path, destdir)) { - g_free(destdir); - return; - } -#endif - } - else { - g_free(destdir); - return; - } - -#ifndef _WIN32 - /* Fire! */ - if (system(command)) - { - purple_notify_error(NULL, NULL, _("Smiley theme failed to unpack."), NULL); - } - - g_free(command); -#endif - g_free(destdir); - - theme_rowref = theme_refresh_theme_list(); - if (theme_rowref != NULL) { - GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref); - - if (tp) - gtk_tree_selection_select_path(smiley_theme_sel, tp); - gtk_tree_row_reference_free(theme_rowref); - } -} - -static void -theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *themedata, size_t len, const gchar *error_message) -{ - FILE *f; - gchar *path; - size_t wc; - - if ((error_message != NULL) || (len == 0)) - return; - - f = purple_mkstemp(&path, TRUE); - wc = fwrite(themedata, len, 1, f); - if (wc != 1) { - purple_debug_warning("theme_got_url", "Unable to write theme data.\n"); - fclose(f); - g_unlink(path); - g_free(path); - return; - } - fclose(f); - - theme_install_theme(path, user_data); - - g_unlink(path); - g_free(path); -} - -static void -theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, - GtkSelectionData *sd, guint info, guint t, gpointer data) -{ - gchar *name = (gchar *)sd->data; - - if ((sd->length >= 0) && (sd->format == 8)) { - /* Well, it looks like the drag event was cool. - * Let's do something with it */ - - if (!g_ascii_strncasecmp(name, "file://", 7)) { - GError *converr = NULL; - gchar *tmp; - /* It looks like we're dealing with a local file. Let's - * just untar it in the right place */ - if(!(tmp = g_filename_from_uri(name, NULL, &converr))) { - purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n", - (converr ? converr->message : - "g_filename_from_uri error")); - return; - } - theme_install_theme(tmp, NULL); - g_free(tmp); - } else if (!g_ascii_strncasecmp(name, "http://", 7)) { - /* Oo, a web drag and drop. This is where things - * will start to get interesting */ - purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, ".tgz"); - } else if (!g_ascii_strncasecmp(name, "https://", 8)) { - /* purple_util_fetch_url() doesn't support HTTPS, but we want users - * to be able to drag and drop links from the SF trackers, so - * we'll try it as an HTTP URL. */ - char *tmp = g_strdup(name + 1); - tmp[0] = 'h'; - tmp[1] = 't'; - tmp[2] = 't'; - tmp[3] = 'p'; - purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, ".tgz"); - g_free(tmp); - } - - gtk_drag_finish(dc, TRUE, FALSE, t); - } - - gtk_drag_finish(dc, FALSE, FALSE, t); -} - /* Rebuild the markup for the sound theme selection for "(Custom)" themes */ static void -pref_sound_generate_markup() +pref_sound_generate_markup(void) { gboolean print_custom, customized; const gchar *name, *author, *description, *current_theme; @@ -596,7 +476,7 @@ print_custom = customized && g_str_equal(current_theme, name); - if (g_str_equal(name, "")) + if (!name || *name == '\0') markup = g_strdup_printf("<b>(Default)</b>%s%s - None\n<span foreground='dim grey'>The default Pidgin sound theme</span>", print_custom ? " " : "", print_custom ? "(Custom)" : ""); else { @@ -617,7 +497,7 @@ } } -/* adds the themes to the theme list from the manager so they can be sisplayed in prefs */ +/* adds the themes to the theme list from the manager so they can be displayed in prefs */ static void prefs_themes_sort(PurpleTheme *theme) { @@ -667,53 +547,389 @@ if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); } - } -/* init all the theme variables so that the themes can be sorted later and used by pref pages */ static void -prefs_themes_init() +prefs_set_active_theme_combo(GtkWidget *combo_box, GtkListStore *store, const gchar *current_theme) +{ + GtkTreeIter iter; + gchar *theme = NULL; + gboolean unset = TRUE; + + if (current_theme && *current_theme && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) { + do { + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1); + + if (g_str_equal(current_theme, theme)) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter); + unset = FALSE; + } + + g_free(theme); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + } + + if (unset) + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0); +} + +static void +prefs_themes_refresh(void) { GdkPixbuf *pixbuf = NULL; gchar *filename; GtkTreeIter iter; + /* refresh the list of themes in the manager */ + purple_theme_manager_refresh(); + filename = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL); pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); g_free(filename); /* sound themes */ - prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); - + gtk_list_store_clear(prefs_sound_themes); gtk_list_store_append(prefs_sound_themes, &iter); gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, "", -1); /* blist themes */ - prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); - + gtk_list_store_clear(prefs_blist_themes); gtk_list_store_append(prefs_blist_themes, &iter); - gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>" - "The default Pidgin buddy list theme</span>", 2, "", -1); + gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, + "<b>(Default)</b> - None\n<span color='dim grey'>" + "The default Pidgin buddy list theme</span>", 2, "", -1); /* status icon themes */ - prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); - + gtk_list_store_clear(prefs_status_icon_themes); gtk_list_store_append(prefs_status_icon_themes, &iter); - gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>" - "The default Pidgin status icon theme</span>", 2, "", -1); - + gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, + "<b>(Default)</b> - None\n<span color='dim grey'>" + "The default Pidgin status icon theme</span>", 2, "", -1); g_object_unref(G_OBJECT(pixbuf)); + + purple_theme_manager_for_each_theme(prefs_themes_sort); + pref_sound_generate_markup(); + + /* set active */ + prefs_set_active_theme_combo(prefs_sound_themes_combo_box, prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme")); + prefs_set_active_theme_combo(prefs_blist_themes_combo_box, prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme")); + prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme")); +} + +/* init all the theme variables so that the themes can be sorted later and used by pref pages */ +static void +prefs_themes_init(void) +{ + prefs_sound_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); +} + +static PurpleTheme * +prefs_theme_find_theme(const gchar *path, const gchar *type) +{ + PurpleTheme *theme = purple_theme_manager_load_theme(path, type); + GDir *dir = g_dir_open(path, 0, NULL); + const gchar *next; + + while (!PURPLE_IS_THEME(theme) && (next = g_dir_read_name(dir))) { + gchar *next_path = g_build_filename(path, next, NULL); + + if (g_file_test(next_path, G_FILE_TEST_IS_DIR)) + theme = prefs_theme_find_theme(next_path, type); + + g_free(next_path); + } + + g_dir_close(dir); + + return theme; +} + +/* Eww. Seriously ewww. But thanks, grim! This is taken from guifications2 */ +static gboolean +purple_theme_file_copy(const gchar *source, const gchar *destination) +{ + FILE *src, *dest; + gint chr = EOF; + + if(!(src = g_fopen(source, "rb"))) + return FALSE; + if(!(dest = g_fopen(destination, "wb"))) { + fclose(src); + return FALSE; + } + + while((chr = fgetc(src)) != EOF) { + fputc(chr, dest); + } + + fclose(dest); + fclose(src); + + return TRUE; +} + +/* installs a theme, info is freed by function */ +static void +theme_install_theme(char *path, struct theme_info *info) +{ +#ifndef _WIN32 + gchar *command; +#endif + gchar *destdir, *tail, *type, *original_name; + GtkTreeRowReference *theme_rowref; + gboolean is_smiley_theme, is_archive; + PurpleTheme *theme = NULL; + + if (info == NULL) + return; + + original_name = info->original_name; + type = info->type; + + /* check the extension */ + tail = info->extension ? info->extension : g_strdup(strrchr(path, '.')); + + if (!tail) { + g_free(type); + g_free(original_name); + g_free(info); + return; + } else g_free(info); + + is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz"); + + g_free(tail); + + /* Just to be safe */ + g_strchomp(path); + + if ((is_smiley_theme = g_str_equal(type, "smiley"))) + destdir = g_build_filename(purple_user_dir(), "smileys", NULL); + else destdir = g_build_filename(purple_user_dir(), "themes", "temp", NULL); + + /* We'll check this just to make sure. This also lets us do something different on + * other platforms, if need be */ + if (is_archive) { +#ifndef _WIN32 + gchar *path_escaped = g_shell_quote(path); + gchar *destdir_escaped = g_shell_quote(destdir); + + if (!g_file_test(destdir, G_FILE_TEST_IS_DIR)) + purple_build_dir(destdir, S_IRUSR | S_IWUSR | S_IXUSR); + + command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped); + g_free(path_escaped); + g_free(destdir_escaped); + + /* Fire! */ + if (system(command)) { + purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL); + g_free(command); + g_free(destdir); + g_free(type); + g_free(original_name); + return; + } +#else + if(!winpidgin_gz_untar(path, destdir)) { + g_free(destdir); + g_free(type); + g_free(original_name); + return; + } +#endif + } + + if (is_smiley_theme) { + /* just extract the folder to the smiley directory */ + theme_rowref = theme_refresh_theme_list(); + + if (theme_rowref != NULL) { + GtkTreePath *tp = gtk_tree_row_reference_get_path(theme_rowref); + + if (tp) + gtk_tree_selection_select_path(smiley_theme_sel, tp); + + gtk_tree_row_reference_free(theme_rowref); + } + + } else if (is_archive) { + theme = prefs_theme_find_theme(destdir, type); + + if (PURPLE_IS_THEME(theme)) { + /* create the location for the theme */ + gchar *theme_dest = g_build_filename(purple_user_dir(), "themes", + purple_theme_get_name(theme), + "purple", type, NULL); + + if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) + purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); + + g_free(theme_dest); + theme_dest = g_build_filename(purple_user_dir(), "themes", + purple_theme_get_name(theme), + "purple", type, NULL); + + /* move the entire directory to new location */ + g_rename(purple_theme_get_dir(theme), theme_dest); + + g_free(theme_dest); + g_remove(destdir); + g_object_unref(theme); + + prefs_themes_refresh(); + + } else { + /* something was wrong with the theme archive */ + g_unlink(destdir); + purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL); + } + + } else { /* just a single file so copy it to a new temp directory and attempt to load it*/ + gchar *temp_path, *temp_file; + + temp_path = g_build_filename(purple_user_dir(), "themes", "temp", "sub_folder", NULL); + + if (original_name != NULL) { + /* name was changed from the original (probably a dnd) change it back before loading */ + temp_file = g_build_filename(temp_path, original_name, NULL); + + } else { + gchar *source_name = g_path_get_basename(path); + temp_file = g_build_filename(temp_path, source_name, NULL); + g_free(source_name); + } + + if (!g_file_test(temp_path, G_FILE_TEST_IS_DIR)) + purple_build_dir(temp_path, S_IRUSR | S_IWUSR | S_IXUSR); + + if (purple_theme_file_copy(path, temp_file)) { + /* find the theme, could be in subfolder */ + theme = prefs_theme_find_theme(temp_path, type); + + if (PURPLE_IS_THEME(theme)) { + gchar *theme_dest = g_build_filename(purple_user_dir(), "themes", + purple_theme_get_name(theme), + "purple", type, NULL); + + if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) + purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); + + g_rename(purple_theme_get_dir(theme), theme_dest); + + g_free(theme_dest); + g_object_unref(theme); + + prefs_themes_refresh(); + } else { + g_remove(temp_path); + purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL); + } + } else { + purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL); + } + + g_free(temp_file); + g_free(temp_path); + } + + g_free(type); + g_free(original_name); + g_free(destdir); +} + +static void +theme_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data, + const gchar *themedata, size_t len, const gchar *error_message) +{ + FILE *f; + gchar *path; + size_t wc; + + if ((error_message != NULL) || (len == 0)) + return; + + f = purple_mkstemp(&path, TRUE); + wc = fwrite(themedata, len, 1, f); + if (wc != 1) { + purple_debug_warning("theme_got_url", "Unable to write theme data.\n"); + fclose(f); + g_unlink(path); + g_free(path); + return; + } + fclose(f); + + theme_install_theme(path, user_data); + + g_unlink(path); + g_free(path); +} + +static void +theme_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, + GtkSelectionData *sd, guint info, guint t, gpointer user_data) +{ + gchar *name = g_strchomp((gchar *)sd->data); + + if ((sd->length >= 0) && (sd->format == 8)) { + /* Well, it looks like the drag event was cool. + * Let's do something with it */ + gchar *temp; + struct theme_info *info = g_new0(struct theme_info, 1); + info->type = g_strdup((gchar *)user_data); + info->extension = g_strdup(g_strrstr(name,".")); + temp = g_strrstr(name, "/"); + info->original_name = temp ? g_strdup(++temp) : NULL; + + if (!g_ascii_strncasecmp(name, "file://", 7)) { + GError *converr = NULL; + gchar *tmp; + /* It looks like we're dealing with a local file. Let's + * just untar it in the right place */ + if(!(tmp = g_filename_from_uri(name, NULL, &converr))) { + purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n", + (converr ? converr->message : + "g_filename_from_uri error")); + return; + } + theme_install_theme(tmp, info); + g_free(tmp); + } else if (!g_ascii_strncasecmp(name, "http://", 7)) { + /* Oo, a web drag and drop. This is where things + * will start to get interesting */ + purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, info); + } else if (!g_ascii_strncasecmp(name, "https://", 8)) { + /* purple_util_fetch_url() doesn't support HTTPS, but we want users + * to be able to drag and drop links from the SF trackers, so + * we'll try it as an HTTP URL. */ + char *tmp = g_strdup(name + 1); + tmp[0] = 'h'; + tmp[1] = 't'; + tmp[2] = 't'; + tmp[3] = 'p'; + + purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, info); + g_free(tmp); + } + + gtk_drag_finish(dc, TRUE, FALSE, t); + } + + gtk_drag_finish(dc, FALSE, FALSE, t); } /* builds a theme combo box from a list store with colums: icon preview, markup, theme name */ static GtkWidget * -prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme) +prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme, gchar *type) { - GtkWidget *combo_box; GtkCellRenderer *cell_rend; - GtkTreeIter iter; - gchar *theme = NULL; - gboolean unset = TRUE; + GtkWidget *combo_box; + GtkTargetEntry te[3] = {{"text/plain", 0, 0},{"text/uri-list", 0, 1},{"STRING", 0, 2}}; g_return_val_if_fail(store != NULL && current_theme != NULL, NULL); @@ -731,21 +947,10 @@ g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); #endif*/ - if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) { - do { - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &theme, -1); - - if (g_str_equal(current_theme, theme)) { - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter); - unset = FALSE; - } - - g_free(theme); - } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); - } - - if (unset) - gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0); + gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te, + sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE); + + g_signal_connect(G_OBJECT(combo_box), "drag_data_received", G_CALLBACK(theme_dnd_recv), type); return combo_box; } @@ -757,32 +962,32 @@ gint i; gchar *pref; gchar *new_theme; - gboolean success; GtkTreeIter new_iter; - success = gtk_combo_box_get_active_iter(combo_box, &new_iter); - g_return_if_fail(success); - - gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1); - - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme); - - /* New theme removes all customization */ - for(i=0; i < PURPLE_NUM_SOUNDS; i++){ - pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", - pidgin_sound_get_event_option(i)); - purple_prefs_set_path(pref, ""); - g_free(pref); + if(gtk_combo_box_get_active_iter(combo_box, &new_iter)) { + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &new_iter, 2, &new_theme, -1); + + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme); + + /* New theme removes all customization */ + for(i=0; i < PURPLE_NUM_SOUNDS; i++){ + pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", + pidgin_sound_get_event_option(i)); + purple_prefs_set_path(pref, ""); + g_free(pref); + } + + /* gets rid of the "(Custom)" from the last selection */ + pref_sound_generate_markup(); + + gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)")); + + g_free(new_theme); } - - /* gets rid of the "(Custom)" from the last selection */ - pref_sound_generate_markup(); - - gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)")); - - g_free(new_theme); } + /* Does same as normal sort, except "none" is sorted first */ static gint pidgin_sort_smileys (GtkTreeModel *model, GtkTreeIter *a, @@ -821,13 +1026,20 @@ static void request_theme_file_name_cb(gpointer data, char *theme_file_name) { - theme_install_theme(theme_file_name, NULL) ; + struct theme_info *info = g_new0(struct theme_info, 1); + info->type = g_strdup("smiley"); + info->extension = NULL; + info->original_name = NULL; + + theme_install_theme(theme_file_name, info); + + g_free(info); } static void -add_theme_button_clicked_cb(GtkWidget *widget, gpointer null) +add_theme_button_clicked_cb(GtkWidget *widget, gpointer user_data) { - purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL) ; + purple_request_file(NULL, _("Install Theme"), NULL, FALSE, (GCallback)request_theme_file_name_cb, NULL, NULL, NULL, NULL, NULL); } static void @@ -903,7 +1115,7 @@ gtk_drag_dest_set(view, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te, sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), smiley_theme_store); + g_signal_connect(G_OBJECT(view), "drag_data_received", G_CALLBACK(theme_dnd_recv), "smiley"); rend = gtk_cell_renderer_pixbuf_new(); smiley_theme_sel = sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); @@ -1163,35 +1375,42 @@ static void prefs_set_blist_theme_cb(GtkComboBox *combo_box, gpointer user_data) { - PidginBlistTheme *theme = NULL; + PidginBlistTheme *theme = NULL; GtkTreeIter iter; gchar *name = NULL; - g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter)); - gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1); - - if (name && *name) - theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist")); - g_free(name); - - pidgin_blist_set_theme(theme); + if(gtk_combo_box_get_active_iter(combo_box, &iter)) { + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_blist_themes), &iter, 2, &name, -1); + + if(!name || !g_str_equal(name, "")) + theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(name, "blist")); + + g_free(name); + + pidgin_blist_set_theme(theme); + } } /* sets the current icon theme */ static void prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data) { - PidginStatusIconTheme *theme; + PidginStatusIconTheme *theme = NULL; GtkTreeIter iter; gchar *name = NULL; - g_return_if_fail(gtk_combo_box_get_active_iter(combo_box, &iter)); - gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1); - - theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon")); - g_free(name); - - pidgin_stock_load_status_icon_theme(theme); + if(gtk_combo_box_get_active_iter(combo_box, &iter)) { + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_status_icon_themes), &iter, 2, &name, -1); + + if(!name || !g_str_equal(name, "")) + theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon")); + + g_free(name); + + pidgin_stock_load_status_icon_theme(theme); + } } static GtkWidget * @@ -1201,7 +1420,6 @@ GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *label; - GtkWidget *combo_box; GtkSizeGroup *sg; GList *names = NULL; @@ -1213,14 +1431,20 @@ /* Buddy List Themes */ vbox = pidgin_make_frame(ret, _("Buddy List Theme")); - combo_box = prefs_build_theme_combo_box(prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme")); - gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_blist_theme_cb, NULL); + prefs_blist_themes_combo_box = prefs_build_theme_combo_box(prefs_blist_themes, + purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), + "blist"); + + gtk_box_pack_start(GTK_BOX (vbox), prefs_blist_themes_combo_box, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(prefs_blist_themes_combo_box), "changed", (GCallback)prefs_set_blist_theme_cb, NULL); /* Status Icon Themes */ - combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme")); - gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL); + prefs_status_themes_combo_box = prefs_build_theme_combo_box(prefs_status_icon_themes, + purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"), + "icon"); + + gtk_box_pack_start(GTK_BOX (vbox), prefs_status_themes_combo_box, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(prefs_status_themes_combo_box), "changed", (GCallback)prefs_set_status_icon_theme_cb, NULL); /* System Tray */ vbox = pidgin_make_frame(ret, _("System Tray Icon")); @@ -1231,9 +1455,9 @@ _("Never"), "never", NULL); gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - - vbox = pidgin_make_frame(ret, _("Conversation Window Hiding")); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + + vbox = pidgin_make_frame(ret, _("Conversation Window")); label = pidgin_prefs_dropdown(vbox, _("_Hide new IM conversations:"), PURPLE_PREF_STRING, PIDGIN_PREFS_ROOT "/conversations/im/hide_new", _("Never"), "never", @@ -1241,8 +1465,11 @@ _("Always"), "always", NULL); gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + +#ifdef _WIN32 + pidgin_prefs_checkbox(_("Minimi_ze new conversation windows"), PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", vbox); +#endif /* All the tab options! */ vbox = pidgin_make_frame(ret, _("Tabs")); @@ -1259,7 +1486,7 @@ purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/conversations/tabs", conversation_usetabs_cb, vbox2); if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs")) - gtk_widget_set_sensitive(vbox2, FALSE); + gtk_widget_set_sensitive(vbox2, FALSE); pidgin_prefs_checkbox(_("Show close b_utton on tabs"), PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", vbox2); @@ -1276,7 +1503,7 @@ #endif NULL); gtk_size_group_add_widget(sg, label); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); names = pidgin_conv_placement_get_options(); label = pidgin_prefs_dropdown_from_list(vbox2, _("N_ew conversations:"), @@ -1287,10 +1514,8 @@ g_list_free(names); - keyboard_shortcuts(ret); - gtk_widget_show_all(ret); g_object_unref(sg); return ret; @@ -1353,8 +1578,6 @@ #ifdef _WIN32 pidgin_prefs_checkbox(_("F_lash window when IMs are received"), PIDGIN_PREFS_ROOT "/win32/blink_im", vbox); - - pidgin_prefs_checkbox(_("Minimi_ze new conversation windows"), PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", vbox); #endif pidgin_prefs_labeled_spin_button(vbox, @@ -1362,7 +1585,6 @@ PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines", 1, 8, NULL); - #if GTK_CHECK_VERSION(2,4,0) vbox = pidgin_make_frame(ret, _("Font")); if (purple_running_gnome()) @@ -1371,7 +1593,11 @@ fontpref = pidgin_prefs_checkbox(_("Use font from _theme"), PIDGIN_PREFS_ROOT "/conversations/use_theme_font", vbox); font_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font"); - font_button = gtk_font_button_new_with_font(font_name ? font_name : NULL); + if ((font_name == NULL) || (*font_name == '\0')) { + font_button = gtk_font_button_new(); + } else { + font_button = gtk_font_button_new_with_font(font_name); + } gtk_font_button_set_show_style(GTK_FONT_BUTTON(font_button), TRUE); hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Conversation _font:"), NULL, font_button, FALSE, NULL); @@ -1417,35 +1643,54 @@ return ret; } -static void network_ip_changed(GtkEntry *entry, gpointer data) +static void +network_ip_changed(GtkEntry *entry, gpointer data) { - /* - * TODO: It would be nice if we could validate this and show a - * red background in the box when the IP address is invalid - * and a green background when the IP address is valid. - */ - purple_network_set_public_ip(gtk_entry_get_text(entry)); + const gchar *text = gtk_entry_get_text(entry); + GdkColor color; + + if (text && *text) { + if (purple_ip_address_is_valid(text)) { + color.red = 0xAFFF; + color.green = 0xFFFF; + color.blue = 0xAFFF; + + purple_network_set_public_ip(text); + } else { + color.red = 0xFFFF; + color.green = 0xAFFF; + color.blue = 0xAFFF; + } + + gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, &color); + + } else { + purple_network_set_public_ip(""); + gtk_widget_modify_base(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL); + } } -static gboolean network_stun_server_changed_cb(GtkWidget *widget, - GdkEventFocus *event, gpointer data) +static gboolean +network_stun_server_changed_cb(GtkWidget *widget, + GdkEventFocus *event, gpointer data) { GtkEntry *entry = GTK_ENTRY(widget); purple_prefs_set_string("/purple/network/stun_server", gtk_entry_get_text(entry)); purple_network_set_stun_server(gtk_entry_get_text(entry)); - + return FALSE; } -static gboolean network_turn_server_changed_cb(GtkWidget *widget, - GdkEventFocus *event, gpointer data) +static gboolean +network_turn_server_changed_cb(GtkWidget *widget, + GdkEventFocus *event, gpointer data) { GtkEntry *entry = GTK_ENTRY(widget); purple_prefs_set_string("/purple/network/turn_server", gtk_entry_get_text(entry)); purple_network_set_turn_server(gtk_entry_get_text(entry)); - + return FALSE; } @@ -1462,7 +1707,8 @@ gtk_widget_hide(frame); } -static void proxy_print_option(GtkEntry *entry, int entrynum) +static void +proxy_print_option(GtkEntry *entry, int entrynum) { if (entrynum == PROXYHOST) purple_prefs_set_string("/purple/proxy/host", gtk_entry_get_text(entry)); @@ -1508,6 +1754,9 @@ GtkWidget *proxy_button = NULL, *browser_button = NULL; GtkSizeGroup *sg; PurpleProxyInfo *proxy_info = NULL; + const char *ip; + PurpleStunNatDiscovery *stun; + char *auto_ip_text; ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER); @@ -1538,8 +1787,30 @@ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_container_add(GTK_CONTAINER(hbox), label); - auto_ip_checkbox = pidgin_prefs_checkbox(_("_Autodetect IP address"), - "/purple/network/auto_ip", vbox); + /* purple_network_get_my_ip will return the IP that was set by the user with + purple_network_set_public_ip, so make a lookup for the auto-detected IP + ourselves. */ + + /* Check if STUN discovery was already done */ + stun = purple_stun_discover(NULL); + if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) { + ip = stun->publicip; + } else { + /* Attempt to get the IP from a NAT device using UPnP */ + ip = purple_upnp_get_public_ip(); + if (ip == NULL) { + /* Attempt to get the IP from a NAT device using NAT-PMP */ + ip = purple_pmp_get_public_ip(); + if (ip == NULL) { + /* Just fetch the IP of the local system */ + ip = purple_network_get_local_system_ip(-1); + } + } + } + + auto_ip_text = g_strdup_printf(_("Use _automatically detected IP address: %s"), ip); + auto_ip_checkbox = pidgin_prefs_checkbox(auto_ip_text, "/purple/network/auto_ip", vbox); + g_free(auto_ip_text); table = gtk_table_new(2, 2, FALSE); gtk_container_set_border_width(GTK_CONTAINER(table), 0); @@ -1558,16 +1829,9 @@ g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(network_ip_changed), NULL); - /* - * TODO: This could be better by showing the autodeteced - * IP separately from the user-specified IP. - */ - if (purple_network_get_my_ip(-1) != NULL) - gtk_entry_set_text(GTK_ENTRY(entry), - purple_network_get_my_ip(-1)); - - pidgin_set_accessible_label (entry, label); - + gtk_entry_set_text(GTK_ENTRY(entry), purple_network_get_public_ip()); + + pidgin_set_accessible_label(entry, label); if (purple_prefs_get_bool("/purple/network/auto_ip")) { gtk_widget_set_sensitive(GTK_WIDGET(table), FALSE); @@ -1619,7 +1883,7 @@ pidgin_prefs_labeled_spin_button(hbox, _("_Port:"), "/purple/network/turn_port", 0, 65535, NULL); - hbox = pidgin_prefs_labeled_entry(vbox, "_Username:", + hbox = pidgin_prefs_labeled_entry(vbox, "_Username:", "/purple/network/turn_username", sg); pidgin_prefs_labeled_password(hbox, "_Password:", "/purple/network/turn_password", NULL); @@ -1801,7 +2065,9 @@ } #ifndef _WIN32 -static gboolean manual_browser_set(GtkWidget *entry, GdkEventFocus *event, gpointer data) { +static gboolean +manual_browser_set(GtkWidget *entry, GdkEventFocus *event, gpointer data) +{ const char *program = gtk_entry_get_text(GTK_ENTRY(entry)); purple_prefs_set_path(PIDGIN_PREFS_ROOT "/browsers/command", program); @@ -1810,7 +2076,8 @@ return FALSE; } -static GList *get_available_browsers(void) +static GList * +get_available_browsers(void) { struct browser { char *name; @@ -1977,7 +2244,8 @@ } #ifndef _WIN32 -static gint sound_cmd_yeah(GtkEntry *entry, gpointer d) +static gint +sound_cmd_yeah(GtkEntry *entry, gpointer d) { purple_prefs_set_path(PIDGIN_PREFS_ROOT "/sound/command", gtk_entry_get_text(GTK_ENTRY(entry))); @@ -2109,7 +2377,8 @@ pref_sound_generate_markup(); } -static void select_sound(GtkWidget *button, gpointer being_NULL_is_fun) +static void +select_sound(GtkWidget *button, gpointer being_NULL_is_fun) { gchar *pref; const char *filename; @@ -2129,7 +2398,8 @@ } #ifdef USE_GSTREAMER -static gchar* prefs_sound_volume_format(GtkScale *scale, gdouble val) +static gchar * +prefs_sound_volume_format(GtkScale *scale, gdouble val) { if(val < 15) { return g_strdup_printf(_("Quietest")); @@ -2148,14 +2418,17 @@ } } -static void prefs_sound_volume_changed(GtkRange *range) +static void +prefs_sound_volume_changed(GtkRange *range) { int val = (int)gtk_range_get_value(GTK_RANGE(range)); purple_prefs_set_int(PIDGIN_PREFS_ROOT "/sound/volume", val); } #endif -static void prefs_sound_sel(GtkTreeSelection *sel, GtkTreeModel *model) { +static void +prefs_sound_sel(GtkTreeSelection *sel, GtkTreeModel *model) +{ GtkTreeIter iter; GValue val; const char *file; @@ -2202,7 +2475,7 @@ sound_page(void) { GtkWidget *ret; - GtkWidget *vbox, *sw, *button, *combo_box; + GtkWidget *vbox, *sw, *button; GtkSizeGroup *sg; GtkTreeIter iter; GtkWidget *event_view; @@ -2308,11 +2581,14 @@ vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START); /* SOUND THEMES */ - combo_box = prefs_build_theme_combo_box(prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme")); - pref_sound_generate_markup(); - gtk_box_pack_start(GTK_BOX (vbox), combo_box, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL); + prefs_sound_themes_combo_box = prefs_build_theme_combo_box(prefs_sound_themes, + purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"), + "sound"); + + + gtk_box_pack_start(GTK_BOX (vbox), prefs_sound_themes_combo_box, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(prefs_sound_themes_combo_box), "changed", (GCallback)prefs_set_sound_theme_cb, NULL); /* SOUND SELECTION */ sw = gtk_scrolled_window_new(NULL,NULL); @@ -2505,10 +2781,8 @@ } static int -prefs_notebook_add_page(const char *text, - GtkWidget *page, - int ind) { - +prefs_notebook_add_page(const char *text, GtkWidget *page, int ind) +{ #if GTK_CHECK_VERSION(2,4,0) return gtk_notebook_append_page(GTK_NOTEBOOK(prefsnotebook), page, gtk_label_new(text)); #else @@ -2517,7 +2791,9 @@ #endif } -static void prefs_notebook_init(void) { +static void +prefs_notebook_init(void) +{ prefs_notebook_add_page(_("Interface"), interface_page(), notebook_page++); prefs_notebook_add_page(_("Conversations"), conv_page(), notebook_page++); prefs_notebook_add_page(_("Smiley Themes"), theme_page(), notebook_page++); @@ -2534,7 +2810,8 @@ prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++); } -void pidgin_prefs_show(void) +void +pidgin_prefs_show(void) { GtkWidget *vbox; GtkWidget *notebook; @@ -2545,14 +2822,6 @@ return; } - /* Refresh the list of themes before showing the preferences window */ - purple_theme_manager_refresh(); - - /* add everything in the theme manager before the window is loaded */ - if (prefs_themes_unsorted) { - purple_theme_manager_for_each_theme(prefs_themes_sort); - prefs_themes_unsorted = FALSE; - } /* copy the preferences to tmp values... * I liked "take affect immediately" Oh well :-( */ /* (that should have been "effect," right?) */ @@ -2577,6 +2846,9 @@ prefs_notebook_init(); + /* Refresh the list of themes before showing the preferences window */ + prefs_themes_refresh(); + /* Show everything. */ gtk_widget_show(prefs); } @@ -2661,7 +2933,8 @@ pidgin_prefs_update_old(); } -void pidgin_prefs_update_old() +void +pidgin_prefs_update_old(void) { const char *str;