Tue, 18 May 2021 02:08:18 -0500
Move Pidgin preferences code into a subdirectory.
Testing Done:
Compiled, installed, and compiled a simple `#include <pidgin.h>` file.
Reviewed at https://reviews.imfreedom.org/r/657/
--- a/doc/reference/pidgin/pidgin-docs.xml Tue May 18 02:04:53 2021 -0500 +++ b/doc/reference/pidgin/pidgin-docs.xml Tue May 18 02:08:18 2021 -0500 @@ -32,7 +32,6 @@ <xi:include href="xml/gtkmedia.xml" /> <xi:include href="xml/gtknotify.xml" /> <xi:include href="xml/gtkpluginpref.xml" /> - <xi:include href="xml/gtkprefs.xml" /> <xi:include href="xml/gtkprivacy.xml" /> <xi:include href="xml/gtkrequest.xml" /> <xi:include href="xml/gtkroomlist.xml" /> @@ -79,6 +78,7 @@ <xi:include href="xml/pidginplugininfo.xml" /> <xi:include href="xml/pidginpluginsdialog.xml" /> <xi:include href="xml/pidginpluginsmenu.xml" /> + <xi:include href="xml/pidginprefs.xml" /> <xi:include href="xml/pidginpresence.xml" /> <xi:include href="xml/pidginpresenceicon.xml" /> <xi:include href="xml/pidginprotocolchooser.xml" />
--- a/pidgin/gtkconv.c Tue May 18 02:04:53 2021 -0500 +++ b/pidgin/gtkconv.c Tue May 18 02:08:18 2021 -0500 @@ -46,7 +46,6 @@ #include "gtkconv.h" #include "gtkconvwin.h" #include "gtkdialogs.h" -#include "gtkprefs.h" #include "gtkprivacy.h" #include "gtkutils.h" #include "pidginavatar.h" @@ -5074,7 +5073,6 @@ #include "gtkblist.h" #include "gtkconv.h" #include "gtkdialogs.h" -#include "gtkprefs.h" #include "gtkprivacy.h" #include "gtkutils.h" #include "pidginmenutray.h"
--- a/pidgin/gtkpluginpref.c Tue May 18 02:04:53 2021 -0500 +++ b/pidgin/gtkpluginpref.c Tue May 18 02:08:18 2021 -0500 @@ -23,8 +23,8 @@ #include <purple.h> #include "gtkpluginpref.h" -#include "gtkprefs.h" #include "gtkutils.h" +#include "pidginprefs.h" static gboolean entry_cb(GtkWidget *entry, gpointer data) {
--- a/pidgin/gtkprefs.c Tue May 18 02:04:53 2021 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2914 +0,0 @@ -/* pidgin - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <errno.h> -#include <math.h> - -#include <glib/gi18n-lib.h> -#include <glib/gstdio.h> -#include <nice.h> -#include <talkatu.h> - -#include <purple.h> - -#include "gtkblist.h" -#include "gtkconv.h" -#include "gtkdialogs.h" -#include "gtkprefs.h" -#include "gtksavedstatuses.h" -#include "gtksmiley-theme.h" -#include "gtkstatus-icon-theme.h" -#include "gtkutils.h" -#include "pidgincore.h" -#include "pidgindebug.h" -#include "pidginstock.h" -#ifdef USE_VV -#include <gst/video/videooverlay.h> -#ifdef GDK_WINDOWING_WIN32 -#include <gdk/gdkwin32.h> -#endif -#ifdef GDK_WINDOWING_X11 -#include <gdk/gdkx.h> -#endif -#ifdef GDK_WINDOWING_QUARTZ -#include <gdk/gdkquartz.h> -#endif -#endif -#include <libsoup/soup.h> - -#define PREFS_OPTIMAL_ICON_SIZE 32 - -/* 25MB */ -#define PREFS_MAX_DOWNLOADED_THEME_SIZE 26214400 - -struct theme_info { - gchar *type; - gchar *extension; - gchar *original_name; -}; - -typedef struct _PidginPrefCombo PidginPrefCombo; - -typedef void (*PidginPrefsBindDropdownCallback)(GtkComboBox *combo_box, - PidginPrefCombo *combo); - -struct _PidginPrefCombo { - GtkWidget *combo; - PurplePrefType type; - const gchar *key; - union { - const char *string; - int integer; - gboolean boolean; - } value; - gint previously_active; - gint current_active; - PidginPrefsBindDropdownCallback cb; -}; - -struct _PidginPrefsWindow { - GtkDialog parent; - - /* Stack */ - GtkWidget *stack; - - /* Interface page */ - struct { - struct { - PidginPrefCombo hide_new; - } im; - struct { - GtkWidget *minimize_new_convs; - } win32; - struct { - GtkWidget *tabs; - GtkWidget *tabs_vbox; - GtkWidget *close_on_tabs; - PidginPrefCombo tab_side; - } conversations; - } iface; - - /* Conversations page */ - struct { - PidginPrefCombo notification_chat; - GtkWidget *show_incoming_formatting; - struct { - GtkWidget *close_immediately; - GtkWidget *send_typing; - } im; - GtkWidget *use_smooth_scrolling; - struct { - GtkWidget *blink_im; - } win32; - GtkWidget *resize_custom_smileys; - GtkWidget *custom_smileys_size; - GtkWidget *minimum_entry_lines; - GtkTextBuffer *format_buffer; - GtkWidget *format_view; - /* Win32 specific frame */ - GtkWidget *font_frame; - GtkWidget *use_theme_font; - GtkWidget *custom_font_hbox; - GtkWidget *custom_font; - } conversations; - - /* Logging page */ - struct { - PidginPrefCombo format; - GtkWidget *log_ims; - GtkWidget *log_chats; - GtkWidget *log_system; - } logging; - - /* Network page */ - struct { - GtkWidget *stun_server; - GtkWidget *auto_ip; - GtkWidget *public_ip; - GtkWidget *public_ip_hbox; - GtkWidget *map_ports; - GtkWidget *ports_range_use; - GtkWidget *ports_range_hbox; - GtkWidget *ports_range_start; - GtkWidget *ports_range_end; - GtkWidget *turn_server; - GtkWidget *turn_port_udp; - GtkWidget *turn_port_tcp; - GtkWidget *turn_username; - GtkWidget *turn_password; - } network; - - /* Proxy page */ - struct { - GtkWidget *stack; - /* GNOME version */ - GtkWidget *gnome_not_found; - GtkWidget *gnome_program; - gchar *gnome_program_path; - /* Non-GNOME version */ - GtkWidget *socks4_remotedns; - PidginPrefCombo type; - GtkWidget *options; - GtkWidget *host; - GtkWidget *port; - GtkWidget *username; - GtkWidget *password; - } proxy; - - /* Away page */ - struct { - PidginPrefCombo idle_reporting; - GtkWidget *mins_before_away; - GtkWidget *idle_hbox; - GtkWidget *away_when_idle; - PidginPrefCombo auto_reply; - GtkWidget *startup_current_status; - GtkWidget *startup_hbox; - GtkWidget *startup_label; - } away; - - /* Themes page */ - struct { - SoupSession *session; - GtkWidget *status; - GtkWidget *smiley; - } theme; - -#ifdef USE_VV - /* Voice/Video page */ - struct { - struct { - PidginPrefCombo input; - PidginPrefCombo output; - GtkWidget *level; - GtkWidget *threshold; - GtkWidget *volume; - GtkWidget *test; - GstElement *pipeline; - } voice; - - struct { - PidginPrefCombo input; - PidginPrefCombo output; - GtkWidget *frame; - GtkWidget *sink_widget; - GtkWidget *test; - GstElement *pipeline; - } video; - } vv; -#endif -}; - -/* Main dialog */ -static PidginPrefsWindow *prefs = NULL; - -/* Themes page */ -static GtkWidget *prefs_status_themes_combo_box; -static GtkWidget *prefs_smiley_themes_combo_box; - -/* These exist outside the lifetime of the prefs dialog */ -static GtkListStore *prefs_status_icon_themes; -static GtkListStore *prefs_smiley_themes; - -/* - * PROTOTYPES - */ -G_DEFINE_TYPE(PidginPrefsWindow, pidgin_prefs_window, GTK_TYPE_DIALOG); -static void delete_prefs(GtkWidget *, void *); - -static void -update_spin_value(GtkWidget *w, GtkWidget *spin) -{ - const char *key = g_object_get_data(G_OBJECT(spin), "val"); - int value; - - value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); - - purple_prefs_set_int(key, value); -} - -GtkWidget * -pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title, - const char *key, int min, int max, GtkSizeGroup *sg) -{ - GtkWidget *spin; - GtkAdjustment *adjust; - int val; - - val = purple_prefs_get_int(key); - - adjust = GTK_ADJUSTMENT(gtk_adjustment_new(val, min, max, 1, 1, 0)); - spin = gtk_spin_button_new(adjust, 1, 0); - g_object_set_data(G_OBJECT(spin), "val", (char *)key); - if (max < 10000) - gtk_widget_set_size_request(spin, 50, -1); - else - gtk_widget_set_size_request(spin, 60, -1); - g_signal_connect(G_OBJECT(adjust), "value-changed", - G_CALLBACK(update_spin_value), GTK_WIDGET(spin)); - gtk_widget_show(spin); - - return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL); -} - -static void -pidgin_prefs_bind_spin_button(const char *key, GtkWidget *spin) -{ - GtkAdjustment *adjust; - int val; - - val = purple_prefs_get_int(key); - - adjust = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(spin)); - gtk_adjustment_set_value(adjust, val); - g_object_set_data(G_OBJECT(spin), "val", (char *)key); - g_signal_connect(G_OBJECT(adjust), "value-changed", - G_CALLBACK(update_spin_value), GTK_WIDGET(spin)); -} - -static void -entry_set(GtkEntry *entry, gpointer data) -{ - const char *key = (const char*)data; - - purple_prefs_set_string(key, gtk_entry_get_text(entry)); -} - -GtkWidget * -pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title, - const char *key, GtkSizeGroup *sg) -{ - GtkWidget *entry; - const gchar *value; - - value = purple_prefs_get_string(key); - - entry = gtk_entry_new(); - gtk_entry_set_text(GTK_ENTRY(entry), value); - g_signal_connect(G_OBJECT(entry), "changed", - G_CALLBACK(entry_set), (char*)key); - gtk_widget_show(entry); - - return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL); -} - -static void -pidgin_prefs_bind_entry(const char *key, GtkWidget *entry) -{ - const gchar *value; - - value = purple_prefs_get_string(key); - - gtk_entry_set_text(GTK_ENTRY(entry), value); - g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_set), - (char*)key); -} - -GtkWidget * -pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title, - const char *key, GtkSizeGroup *sg) -{ - GtkWidget *entry; - const gchar *value; - - value = purple_prefs_get_string(key); - - entry = gtk_entry_new(); - gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); - gtk_entry_set_text(GTK_ENTRY(entry), value); - g_signal_connect(G_OBJECT(entry), "changed", - G_CALLBACK(entry_set), (char*)key); - gtk_widget_show(entry); - - return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL); -} - -/* TODO: Maybe move this up somewheres... */ -enum { - PREF_DROPDOWN_TEXT, - PREF_DROPDOWN_VALUE, - PREF_DROPDOWN_COUNT -}; - -typedef struct -{ - PurplePrefType type; - union { - const char *string; - int integer; - gboolean boolean; - } value; -} PidginPrefValue; - -typedef void (*PidginPrefsDropdownCallback)(GtkComboBox *combo_box, - PidginPrefValue value); - -static void -dropdown_set(GtkComboBox *combo_box, gpointer _cb) -{ - PidginPrefsDropdownCallback cb = _cb; - GtkTreeIter iter; - GtkTreeModel *tree_model; - PidginPrefValue active; - - tree_model = gtk_combo_box_get_model(combo_box); - if (!gtk_combo_box_get_active_iter(combo_box, &iter)) - return; - active.type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo_box), - "type")); - - g_object_set_data(G_OBJECT(combo_box), "previously_active", - g_object_get_data(G_OBJECT(combo_box), "current_active")); - g_object_set_data(G_OBJECT(combo_box), "current_active", - GINT_TO_POINTER(gtk_combo_box_get_active(combo_box))); - - if (active.type == PURPLE_PREF_INT) { - gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, - &active.value.integer, -1); - } - else if (active.type == PURPLE_PREF_STRING) { - gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, - &active.value.string, -1); - } - else if (active.type == PURPLE_PREF_BOOLEAN) { - gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, - &active.value.boolean, -1); - } - - cb(combo_box, active); -} - -static GtkWidget * -pidgin_prefs_dropdown_from_list_with_cb(GtkWidget *box, const gchar *title, - GtkComboBox **dropdown_out, GList *menuitems, - PidginPrefValue initial, PidginPrefsDropdownCallback cb) -{ - GtkWidget *dropdown; - GtkWidget *label = NULL; - GtkListStore *store = NULL; - GtkTreeIter iter; - GtkTreeIter active; - GtkCellRenderer *renderer; - gpointer current_active; - - g_return_val_if_fail(menuitems != NULL, NULL); - - if (initial.type == PURPLE_PREF_INT) { - store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_INT); - } else if (initial.type == PURPLE_PREF_STRING) { - store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_STRING); - } else if (initial.type == PURPLE_PREF_BOOLEAN) { - store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_BOOLEAN); - } else { - g_warn_if_reached(); - return NULL; - } - - dropdown = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); - if (dropdown_out != NULL) - *dropdown_out = GTK_COMBO_BOX(dropdown); - g_object_set_data(G_OBJECT(dropdown), "type", GINT_TO_POINTER(initial.type)); - - for (; menuitems != NULL; menuitems = g_list_next(menuitems)) { - const PurpleKeyValuePair *menu_item = menuitems->data; - int int_value = 0; - const char *str_value = NULL; - gboolean bool_value = FALSE; - - if (menu_item->key == NULL) { - break; - } - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_TEXT, menu_item->key, - -1); - - if (initial.type == PURPLE_PREF_INT) { - int_value = GPOINTER_TO_INT(menu_item->value); - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_VALUE, int_value, - -1); - } - else if (initial.type == PURPLE_PREF_STRING) { - str_value = (const char *)menu_item->value; - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_VALUE, str_value, - -1); - } - else if (initial.type == PURPLE_PREF_BOOLEAN) { - bool_value = (gboolean)GPOINTER_TO_INT(menu_item->value); - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_VALUE, bool_value, - -1); - } - - if ((initial.type == PURPLE_PREF_INT && - initial.value.integer == int_value) || - (initial.type == PURPLE_PREF_STRING && - purple_strequal(initial.value.string, str_value)) || - (initial.type == PURPLE_PREF_BOOLEAN && - (initial.value.boolean == bool_value))) { - - active = iter; - } - } - - renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer, - "text", 0, - NULL); - - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dropdown), &active); - current_active = GINT_TO_POINTER(gtk_combo_box_get_active(GTK_COMBO_BOX( - dropdown))); - g_object_set_data(G_OBJECT(dropdown), "current_active", current_active); - g_object_set_data(G_OBJECT(dropdown), "previously_active", current_active); - - g_signal_connect(G_OBJECT(dropdown), "changed", - G_CALLBACK(dropdown_set), cb); - - pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label); - - return label; -} - -static void -pidgin_prefs_dropdown_from_list_cb(GtkComboBox *combo_box, - PidginPrefValue value) -{ - const char *key; - - key = g_object_get_data(G_OBJECT(combo_box), "key"); - - if (value.type == PURPLE_PREF_INT) { - purple_prefs_set_int(key, value.value.integer); - } else if (value.type == PURPLE_PREF_STRING) { - purple_prefs_set_string(key, value.value.string); - } else if (value.type == PURPLE_PREF_BOOLEAN) { - purple_prefs_set_bool(key, value.value.boolean); - } else { - g_return_if_reached(); - } -} - -GtkWidget * -pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title, - PurplePrefType type, const char *key, GList *menuitems) -{ - PidginPrefValue initial; - GtkComboBox *dropdown = NULL; - GtkWidget *label; - - initial.type = type; - if (type == PURPLE_PREF_INT) { - initial.value.integer = purple_prefs_get_int(key); - } else if (type == PURPLE_PREF_STRING) { - initial.value.string = purple_prefs_get_string(key); - } else if (type == PURPLE_PREF_BOOLEAN) { - initial.value.boolean = purple_prefs_get_bool(key); - } else { - g_return_val_if_reached(NULL); - } - - label = pidgin_prefs_dropdown_from_list_with_cb(box, title, &dropdown, - menuitems, initial, pidgin_prefs_dropdown_from_list_cb); - - g_object_set_data(G_OBJECT(dropdown), "key", (gpointer)key); - - return label; -} - -GtkWidget * -pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type, - const char *key, ...) -{ - va_list ap; - GList *menuitems = NULL; - GtkWidget *dropdown = NULL; - char *name; - - g_return_val_if_fail(type == PURPLE_PREF_BOOLEAN || type == PURPLE_PREF_INT || - type == PURPLE_PREF_STRING, NULL); - - va_start(ap, key); - while ((name = va_arg(ap, char *)) != NULL) { - PurpleKeyValuePair *kvp; - - if (type == PURPLE_PREF_INT || type == PURPLE_PREF_BOOLEAN) { - kvp = purple_key_value_pair_new(name, GINT_TO_POINTER(va_arg(ap, int))); - } else { - kvp = purple_key_value_pair_new(name, va_arg(ap, char *)); - } - menuitems = g_list_prepend(menuitems, kvp); - } - va_end(ap); - - g_return_val_if_fail(menuitems != NULL, NULL); - - menuitems = g_list_reverse(menuitems); - - dropdown = pidgin_prefs_dropdown_from_list(box, title, type, key, - menuitems); - - g_list_free_full(menuitems, (GDestroyNotify)purple_key_value_pair_free); - - return dropdown; -} - -static void -pidgin_prefs_bind_dropdown_from_list_cb(GtkComboBox *combo_box, - PidginPrefCombo *combo) -{ - if (combo->type == PURPLE_PREF_INT) { - purple_prefs_set_int(combo->key, combo->value.integer); - } else if (combo->type == PURPLE_PREF_STRING) { - purple_prefs_set_string(combo->key, combo->value.string); - } else if (combo->type == PURPLE_PREF_BOOLEAN) { - purple_prefs_set_bool(combo->key, combo->value.boolean); - } else { - g_return_if_reached(); - } -} - -static void -bind_dropdown_set(GtkComboBox *combo_box, gpointer data) -{ - PidginPrefCombo *combo = data; - GtkTreeIter iter; - GtkTreeModel *tree_model; - - tree_model = gtk_combo_box_get_model(combo_box); - if (!gtk_combo_box_get_active_iter(combo_box, &iter)) - return; - - combo->previously_active = combo->current_active; - combo->current_active = gtk_combo_box_get_active(combo_box); - - if (combo->type == PURPLE_PREF_INT) { - gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, - &combo->value.integer, -1); - } - else if (combo->type == PURPLE_PREF_STRING) { - gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, - &combo->value.string, -1); - } - else if (combo->type == PURPLE_PREF_BOOLEAN) { - gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, - &combo->value.boolean, -1); - } - - combo->cb(combo_box, combo); -} - -static void -pidgin_prefs_bind_dropdown_from_list(PidginPrefCombo *combo, GList *menuitems) -{ - gchar *text; - GtkListStore *store = NULL; - GtkTreeIter iter; - GtkTreeIter active; - - g_return_if_fail(menuitems != NULL); - - if (combo->type == PURPLE_PREF_INT) { - combo->value.integer = purple_prefs_get_int(combo->key); - } else if (combo->type == PURPLE_PREF_STRING) { - combo->value.string = purple_prefs_get_string(combo->key); - } else if (combo->type == PURPLE_PREF_BOOLEAN) { - combo->value.boolean = purple_prefs_get_bool(combo->key); - } else { - g_return_if_reached(); - } - - store = GTK_LIST_STORE( - gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo))); - - while (menuitems != NULL && (text = (char *)menuitems->data) != NULL) { - int int_value = 0; - const char *str_value = NULL; - gboolean bool_value = FALSE; - - menuitems = g_list_next(menuitems); - g_return_if_fail(menuitems != NULL); - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_TEXT, text, - -1); - - if (combo->type == PURPLE_PREF_INT) { - int_value = GPOINTER_TO_INT(menuitems->data); - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_VALUE, int_value, - -1); - } - else if (combo->type == PURPLE_PREF_STRING) { - str_value = (const char *)menuitems->data; - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_VALUE, str_value, - -1); - } - else if (combo->type == PURPLE_PREF_BOOLEAN) { - bool_value = (gboolean)GPOINTER_TO_INT(menuitems->data); - gtk_list_store_set(store, &iter, - PREF_DROPDOWN_VALUE, bool_value, - -1); - } - - if ((combo->type == PURPLE_PREF_INT && - combo->value.integer == int_value) || - (combo->type == PURPLE_PREF_STRING && - purple_strequal(combo->value.string, str_value)) || - (combo->type == PURPLE_PREF_BOOLEAN && - (combo->value.boolean == bool_value))) { - - active = iter; - } - - menuitems = g_list_next(menuitems); - } - - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo->combo), &active); - combo->current_active = gtk_combo_box_get_active( - GTK_COMBO_BOX(combo->combo)); - combo->previously_active = combo->current_active; - - combo->cb = pidgin_prefs_bind_dropdown_from_list_cb; - g_signal_connect(G_OBJECT(combo->combo), "changed", - G_CALLBACK(bind_dropdown_set), combo); -} - -static void -pidgin_prefs_bind_dropdown(PidginPrefCombo *combo) -{ - GtkTreeModel *store = NULL; - GtkTreeIter iter; - GtkTreeIter active; - - if (combo->type == PURPLE_PREF_INT) { - combo->value.integer = purple_prefs_get_int(combo->key); - } else if (combo->type == PURPLE_PREF_STRING) { - combo->value.string = purple_prefs_get_string(combo->key); - } else if (combo->type == PURPLE_PREF_BOOLEAN) { - combo->value.boolean = purple_prefs_get_bool(combo->key); - } else { - g_return_if_reached(); - } - - store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)); - - if (!gtk_tree_model_get_iter_first(store, &iter)) { - g_return_if_reached(); - } - - do { - int int_value = 0; - const char *str_value = NULL; - gboolean bool_value = FALSE; - - if (combo->type == PURPLE_PREF_INT) { - gtk_tree_model_get(store, &iter, - PREF_DROPDOWN_VALUE, &int_value, - -1); - if (combo->value.integer == int_value) { - active = iter; - break; - } - } - else if (combo->type == PURPLE_PREF_STRING) { - gtk_tree_model_get(store, &iter, - PREF_DROPDOWN_VALUE, &str_value, - -1); - if (purple_strequal(combo->value.string, str_value)) { - active = iter; - break; - } - } - else if (combo->type == PURPLE_PREF_BOOLEAN) { - gtk_tree_model_get(store, &iter, - PREF_DROPDOWN_VALUE, &bool_value, - -1); - if (combo->value.boolean == bool_value) { - active = iter; - break; - } - } - } while (gtk_tree_model_iter_next(store, &iter)); - - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo->combo), &active); - - combo->current_active = gtk_combo_box_get_active( - GTK_COMBO_BOX(combo->combo)); - combo->previously_active = combo->current_active; - - combo->cb = pidgin_prefs_bind_dropdown_from_list_cb; - g_signal_connect(G_OBJECT(combo->combo), "changed", - G_CALLBACK(bind_dropdown_set), combo); -} - -static void -set_bool_pref(GtkWidget *w, const char *key) -{ - purple_prefs_set_bool(key, - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))); -} - -GtkWidget * -pidgin_prefs_checkbox(const char *text, const char *key, GtkWidget *page) -{ - GtkWidget *button; - - button = gtk_check_button_new_with_mnemonic(text); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), - purple_prefs_get_bool(key)); - - gtk_box_pack_start(GTK_BOX(page), button, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(set_bool_pref), (char *)key); - - gtk_widget_show(button); - - return button; -} - -static void -pidgin_prefs_bind_checkbox(const char *key, GtkWidget *button) -{ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), - purple_prefs_get_bool(key)); - g_signal_connect(G_OBJECT(button), "toggled", - G_CALLBACK(set_bool_pref), (char *)key); -} - -static void -delete_prefs(GtkWidget *asdf, void *gdsa) -{ - /* Close any request dialogs */ - purple_request_close_with_handle(prefs); - - purple_notify_close_with_handle(prefs); - - g_clear_object(&prefs->theme.session); - - /* Unregister callbacks. */ - purple_prefs_disconnect_by_handle(prefs); - - /* NULL-ify globals */ - prefs_status_themes_combo_box = NULL; - prefs_smiley_themes_combo_box = NULL; - - g_free(prefs->proxy.gnome_program_path); - prefs = NULL; -} - -static gchar * -get_theme_markup(const char *name, gboolean custom, const char *author, - const char *description) -{ - - return g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>", - name, custom ? " " : "", custom ? _("(Custom)") : "", - author != NULL ? " - " : "", author != NULL ? author : "", - description != NULL ? description : ""); -} - -static void -smileys_refresh_theme_list(void) -{ - GList *it; - GtkTreeIter iter; - gchar *description; - - description = get_theme_markup(_("none"), FALSE, _("Penguin Pimps"), - _("Selecting this disables graphical emoticons.")); - gtk_list_store_append(prefs_smiley_themes, &iter); - gtk_list_store_set(prefs_smiley_themes, &iter, - 0, NULL, 1, description, 2, "none", -1); - g_free(description); - - for (it = pidgin_smiley_theme_get_all(); it; it = g_list_next(it)) { - PidginSmileyTheme *theme = it->data; - - description = get_theme_markup( - _(pidgin_smiley_theme_get_name(theme)), FALSE, - _(pidgin_smiley_theme_get_author(theme)), - _(pidgin_smiley_theme_get_description(theme))); - - gtk_list_store_append(prefs_smiley_themes, &iter); - gtk_list_store_set(prefs_smiley_themes, &iter, - 0, pidgin_smiley_theme_get_icon(theme), - 1, description, - 2, pidgin_smiley_theme_get_name(theme), - -1); - - g_free(description); - } -} - -/* adds the themes to the theme list from the manager so they can be displayed in prefs */ -static void -prefs_themes_sort(PurpleTheme *theme) -{ - GdkPixbuf *pixbuf = NULL; - GtkTreeIter iter; - gchar *image_full = NULL, *markup; - const gchar *name, *author, *description; - - if (PIDGIN_IS_STATUS_ICON_THEME(theme)){ - GtkListStore *store; - - store = prefs_status_icon_themes; - - image_full = purple_theme_get_image_full(theme); - if (image_full != NULL){ - pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE); - g_free(image_full); - } else - pixbuf = NULL; - - name = purple_theme_get_name(theme); - author = purple_theme_get_author(theme); - description = purple_theme_get_description(theme); - - markup = get_theme_markup(name, FALSE, author, description); - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, pixbuf, 1, markup, 2, name, -1); - - g_free(markup); - if (pixbuf != NULL) - g_object_unref(G_OBJECT(pixbuf)); - - } -} - -static void -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 (purple_strequal(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 *tmp; - GtkTreeIter iter; - - /* refresh the list of themes in the manager */ - purple_theme_manager_refresh(); - - tmp = g_build_filename(PURPLE_DATADIR, "icons", "hicolor", "32x32", - "apps", "im.pidgin.Pidgin3.png", NULL); - pixbuf = pidgin_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE); - g_free(tmp); - - /* status icon themes */ - gtk_list_store_clear(prefs_status_icon_themes); - gtk_list_store_append(prefs_status_icon_themes, &iter); - tmp = get_theme_markup(_("Default"), FALSE, _("Penguin Pimps"), - _("The default Pidgin status icon theme")); - gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1); - g_free(tmp); - if (pixbuf) - g_object_unref(G_OBJECT(pixbuf)); - - /* smiley themes */ - gtk_list_store_clear(prefs_smiley_themes); - - purple_theme_manager_for_each_theme(prefs_themes_sort); - smileys_refresh_theme_list(); - - /* set active */ - prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme")); - prefs_set_active_theme_combo(prefs_smiley_themes_combo_box, prefs_smiley_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/smileys/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_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); - - prefs_smiley_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); -} - -/* - * prefs_theme_find_theme: - * @path: A directory containing a theme. The theme could be at the - * top level of this directory or in any subdirectory thereof. - * @type: The type of theme to load. The loader for this theme type - * will be used and this loader will determine what constitutes a - * "theme." - * - * Attempt to load the given directory as a theme. If we are unable to - * open the path as a theme then we recurse into path and attempt to - * load each subdirectory that we encounter. - * - * Returns: A new reference to a #PurpleTheme. - */ -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; -} - -static void -free_theme_info(struct theme_info *info) -{ - if (info != NULL) { - g_free(info->type); - g_free(info->extension); - g_free(info->original_name); - g_free(info); - } -} - -/* installs a theme, info is freed by function */ -static void -theme_install_theme(char *path, struct theme_info *info) -{ - gchar *destdir; - const char *tail; - gboolean is_smiley_theme, is_archive; - PurpleTheme *theme = NULL; - - if (info == NULL) - return; - - /* check the extension */ - tail = info->extension ? info->extension : strrchr(path, '.'); - - if (!tail) { - free_theme_info(info); - return; - } - - is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz"); - - /* Just to be safe */ - g_strchomp(path); - - if ((is_smiley_theme = purple_strequal(info->type, "smiley"))) - destdir = g_build_filename(purple_data_dir(), "smileys", NULL); - else - destdir = g_build_filename(purple_data_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); - gchar *command; - - if (!g_file_test(destdir, G_FILE_TEST_IS_DIR)) { - g_mkdir_with_parents(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, NULL); - g_free(command); - g_free(destdir); - free_theme_info(info); - return; - } - g_free(command); -#else - if (!winpidgin_gz_untar(path, destdir)) { - purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL); - g_free(destdir); - free_theme_info(info); - return; - } -#endif - } - - if (is_smiley_theme) { - /* just extract the folder to the smiley directory */ - prefs_themes_refresh(); - - } else if (is_archive) { - theme = prefs_theme_find_theme(destdir, info->type); - - if (PURPLE_IS_THEME(theme)) { - /* create the location for the theme */ - gchar *theme_dest = g_build_filename(purple_data_dir(), "themes", - purple_theme_get_name(theme), - "purple", info->type, NULL); - - if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) { - g_mkdir_with_parents(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); - } - - g_free(theme_dest); - theme_dest = g_build_filename(purple_data_dir(), "themes", - purple_theme_get_name(theme), - "purple", info->type, NULL); - - /* move the entire directory to new location */ - if (g_rename(purple_theme_get_dir(theme), theme_dest)) { - purple_debug_error("gtkprefs", "Error renaming %s to %s: " - "%s\n", purple_theme_get_dir(theme), theme_dest, - g_strerror(errno)); - } - - g_free(theme_dest); - if (g_remove(destdir) != 0) { - purple_debug_error("gtkprefs", - "couldn't remove temp (dest) path\n"); - } - 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, 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_data_dir(), "themes", "temp", - "sub_folder", NULL); - - if (info->original_name != NULL) { - /* name was changed from the original (probably a dnd) change it back before loading */ - temp_file = g_build_filename(temp_path, info->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)) { - g_mkdir_with_parents(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, info->type); - - if (PURPLE_IS_THEME(theme)) { - gchar *theme_dest = - g_build_filename(purple_data_dir(), "themes", - purple_theme_get_name(theme), "purple", - info->type, NULL); - - if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) { - g_mkdir_with_parents(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); - } - - if (g_rename(purple_theme_get_dir(theme), theme_dest)) { - purple_debug_error("gtkprefs", "Error renaming %s to %s: " - "%s\n", purple_theme_get_dir(theme), theme_dest, - g_strerror(errno)); - } - - g_free(theme_dest); - g_object_unref(theme); - - prefs_themes_refresh(); - } else { - if (g_remove(temp_path) != 0) { - purple_debug_error("gtkprefs", - "couldn't remove temp path"); - } - purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL, NULL); - } - } else { - purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL, NULL); - } - - g_free(temp_file); - g_free(temp_path); - } - - g_free(destdir); - free_theme_info(info); -} - -static void -theme_got_url(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, - gpointer _info) -{ - struct theme_info *info = _info; - FILE *f; - gchar *path; - size_t wc; - - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { - free_theme_info(info); - return; - } - - f = purple_mkstemp(&path, TRUE); - wc = fwrite(msg->response_body->data, msg->response_body->length, 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); - free_theme_info(info); - return; - } - fclose(f); - - theme_install_theme(path, info); - - 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 *)gtk_selection_data_get_data(sd)); - - if ((gtk_selection_data_get_length(sd) >= 0) - && (gtk_selection_data_get_format(sd) == 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_error("theme dnd", "%s", - converr ? converr->message : - "g_filename_from_uri error"); - free_theme_info(info); - return; - } - theme_install_theme(tmp, info); - g_free(tmp); - } else if (!g_ascii_strncasecmp(name, "http://", 7) || - !g_ascii_strncasecmp(name, "https://", 8)) { - /* Oo, a web drag and drop. This is where things - * will start to get interesting */ - SoupMessage *msg; - - if (prefs->theme.session == NULL) { - prefs->theme.session = soup_session_new(); - } - - soup_session_abort(prefs->theme.session); - - msg = soup_message_new("GET", name); - // purple_http_request_set_max_len(msg, PREFS_MAX_DOWNLOADED_THEME_SIZE); - soup_session_queue_message(prefs->theme.session, msg, theme_got_url, - info); - } else - free_theme_info(info); - - 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 void -prefs_build_theme_combo_box(GtkWidget *combo_box, GtkListStore *store, - const char *current_theme, const char *type) -{ - GtkTargetEntry te[3] = { - {"text/plain", 0, 0}, - {"text/uri-list", 0, 1}, - {"STRING", 0, 2} - }; - - g_return_if_fail(store != NULL && current_theme != NULL); - - gtk_combo_box_set_model(GTK_COMBO_BOX(combo_box), - GTK_TREE_MODEL(store)); - - 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), (gpointer) type); -} - -/* sets the current smiley theme */ -static void -prefs_set_smiley_theme_cb(GtkComboBox *combo_box, gpointer user_data) -{ - gchar *new_theme; - GtkTreeIter new_iter; - - if (gtk_combo_box_get_active_iter(combo_box, &new_iter)) { - - gtk_tree_model_get(GTK_TREE_MODEL(prefs_smiley_themes), &new_iter, 2, &new_theme, -1); - - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme); - - g_free(new_theme); - } -} - - -/* Does same as normal sort, except "none" is sorted first */ -static gint pidgin_sort_smileys (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer userdata) -{ - gint ret = 0; - gchar *name1 = NULL, *name2 = NULL; - - gtk_tree_model_get(model, a, 2, &name1, -1); - gtk_tree_model_get(model, b, 2, &name2, -1); - - if (name1 == NULL || name2 == NULL) { - if (!(name1 == NULL && name2 == NULL)) - ret = (name1 == NULL) ? -1: 1; - } else if (!g_ascii_strcasecmp(name1, "none")) { - if (!g_utf8_collate(name1, name2)) - ret = 0; - else - /* Sort name1 first */ - ret = -1; - } else if (!g_ascii_strcasecmp(name2, "none")) { - /* Sort name2 first */ - ret = 1; - } else { - /* Neither string is "none", default to normal sort */ - ret = purple_utf8_strcasecmp(name1, name2); - } - - g_free(name1); - g_free(name2); - - return ret; -} - -/* sets the current icon theme */ -static void -prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data) -{ - PidginStatusIconTheme *theme = NULL; - GtkTreeIter iter; - gchar *name = NULL; - - 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 || *name) - theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon")); - - g_free(name); - - pidgin_stock_load_status_icon_theme(theme); - pidgin_blist_refresh(purple_blist_get_default()); - } -} - -static void -bind_theme_page(PidginPrefsWindow *win) -{ - /* Status Icon Themes */ - prefs_build_theme_combo_box(win->theme.status, prefs_status_icon_themes, - PIDGIN_PREFS_ROOT "/status/icon-theme", - "icon"); - prefs_status_themes_combo_box = win->theme.status; - - /* Smiley Themes */ - prefs_build_theme_combo_box(win->theme.smiley, prefs_smiley_themes, - PIDGIN_PREFS_ROOT "/smileys/theme", - "smiley"); - prefs_smiley_themes_combo_box = win->theme.smiley; - - /* Custom sort so "none" theme is at top of list */ - gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(prefs_smiley_themes), - 2, pidgin_sort_smileys, NULL, NULL); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(prefs_smiley_themes), - 2, GTK_SORT_ASCENDING); -} - -static void -formatting_toggle_cb(TalkatuActionGroup *ag, GAction *action, const gchar *name, gpointer data) -{ - gboolean activated = talkatu_action_group_get_action_activated(ag, name); - if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_BOLD, name) != 0) { - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", - activated); - } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_ITALIC, name) != 0) { - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", - activated); - } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_UNDERLINE, name) != 0) { - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", - activated); - } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_STRIKETHROUGH, name) != 0) { - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike", - activated); - } -} - -static void -bind_interface_page(PidginPrefsWindow *win) -{ - /* System Tray */ - win->iface.im.hide_new.type = PURPLE_PREF_STRING; - win->iface.im.hide_new.key = PIDGIN_PREFS_ROOT "/conversations/im/hide_new"; - pidgin_prefs_bind_dropdown(&win->iface.im.hide_new); - -#ifdef _WIN32 - pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", - win->iface.win32.minimize_new_convs); -#else - gtk_widget_hide(win->iface.win32.minimize_new_convs); -#endif - - /* All the tab options! */ - pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/tabs", - win->iface.conversations.tabs); - - /* - * Connect a signal to the above preference. When conversations are not - * shown in a tabbed window then all tabbing options should be disabled. - */ - g_object_bind_property(win->iface.conversations.tabs, "active", - win->iface.conversations.tabs_vbox, "sensitive", - G_BINDING_SYNC_CREATE); - - pidgin_prefs_bind_checkbox( - PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", - win->iface.conversations.close_on_tabs); - - win->iface.conversations.tab_side.type = PURPLE_PREF_INT; - win->iface.conversations.tab_side.key = PIDGIN_PREFS_ROOT "/conversations/tab_side"; - pidgin_prefs_bind_dropdown(&win->iface.conversations.tab_side); -} - -/* This is also Win32-specific, but must be visible for Glade binding. */ -static void -apply_custom_font(GtkWidget *unused, PidginPrefsWindow *win) -{ - PangoFontDescription *desc = NULL; - if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font")) { - const char *font = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font"); - desc = pango_font_description_from_string(font); - } - - gtk_widget_override_font(win->conversations.format_view, desc); - if (desc) - pango_font_description_free(desc); - -} - -static void -pidgin_custom_font_set(GtkWidget *font_button, PidginPrefsWindow *win) -{ - - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/custom_font", - gtk_font_chooser_get_font(GTK_FONT_CHOOSER(font_button))); - - apply_custom_font(font_button, win); -} - -static void -bind_conv_page(PidginPrefsWindow *win) -{ - GSimpleActionGroup *ag = NULL; - - win->conversations.notification_chat.type = PURPLE_PREF_INT; - win->conversations.notification_chat.key = PIDGIN_PREFS_ROOT "/conversations/notification_chat"; - pidgin_prefs_bind_dropdown(&win->conversations.notification_chat); - - pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", - win->conversations.show_incoming_formatting); - pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", - win->conversations.im.close_immediately); - - pidgin_prefs_bind_checkbox("/purple/conversations/im/send_typing", - win->conversations.im.send_typing); - - pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", - win->conversations.use_smooth_scrolling); - -#ifdef _WIN32 - pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/blink_im", - win->conversations.win32.blink_im); -#else - gtk_widget_hide(win->conversations.win32.blink_im); -#endif - -#if 0 - /* TODO: it's not implemented */ - pidgin_prefs_bind_checkbox( - PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys", - win->conversations.resize_custom_smileys); - - pidgin_prefs_bind_spin_button( - PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size", - win->conversations.custom_smileys_size); - - g_object_bind_property(win->conversations.resize_custom_smileys, "active", - win->conversations.custom_smileys_size, "sensitive", - G_BINDING_SYNC_CREATE); -#endif - - pidgin_prefs_bind_spin_button( - PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines", - win->conversations.minimum_entry_lines); - -#ifdef _WIN32 - { - const char *font_name; - gtk_widget_show(win->conversations.font_frame); - - pidgin_prefs_bind_checkbox( - PIDGIN_PREFS_ROOT "/conversations/use_theme_font", - win->conversations.use_theme_font); - - font_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font"); - if (font_name != NULL && *font_name != '\0') { - gtk_font_chooser_set_font( - GTK_FONT_CHOOSER(win->conversations.custom_font), - font_name); - } - - g_object_bind_property(win->conversations.use_theme_font, "active", - win->conversations.custom_font_hbox, "sensitive", - G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN); - } -#endif - - ag = talkatu_buffer_get_action_group(TALKATU_BUFFER(win->conversations.format_buffer)); - g_signal_connect_after(G_OBJECT(ag), "action-activated", - G_CALLBACK(formatting_toggle_cb), NULL); -} - -static void -network_ip_changed(GtkEntry *entry, gpointer data) -{ - const gchar *text = gtk_entry_get_text(entry); - GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(entry)); - - if (text && *text) { - if (g_hostname_is_ip_address(text)) { - purple_network_set_public_ip(text); - gtk_style_context_add_class(context, "good-ip"); - gtk_style_context_remove_class(context, "bad-ip"); - } else { - gtk_style_context_add_class(context, "bad-ip"); - gtk_style_context_remove_class(context, "good-ip"); - } - - } else { - purple_network_set_public_ip(""); - gtk_style_context_remove_class(context, "bad-ip"); - gtk_style_context_remove_class(context, "good-ip"); - } -} - -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) -{ - 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; -} - -static void -proxy_changed_cb(const char *name, PurplePrefType type, - gconstpointer value, gpointer data) -{ - PidginPrefsWindow *win = data; - const char *proxy = value; - - if (!purple_strequal(proxy, "none") && !purple_strequal(proxy, "envvar")) - gtk_widget_show_all(win->proxy.options); - else - gtk_widget_hide(win->proxy.options); -} - -static void -proxy_print_option(GtkWidget *entry, PidginPrefsWindow *win) -{ - if (entry == win->proxy.host) { - purple_prefs_set_string("/purple/proxy/host", - gtk_entry_get_text(GTK_ENTRY(entry))); - } else if (entry == win->proxy.port) { - purple_prefs_set_int("/purple/proxy/port", - gtk_spin_button_get_value_as_int( - GTK_SPIN_BUTTON(entry))); - } else if (entry == win->proxy.username) { - purple_prefs_set_string("/purple/proxy/username", - gtk_entry_get_text(GTK_ENTRY(entry))); - } else if (entry == win->proxy.password) { - purple_prefs_set_string("/purple/proxy/password", - gtk_entry_get_text(GTK_ENTRY(entry))); - } -} - -static void -proxy_button_clicked_cb(GtkWidget *button, PidginPrefsWindow *win) -{ - GError *err = NULL; - - if (g_spawn_command_line_async(win->proxy.gnome_program_path, &err)) - return; - - purple_notify_error(NULL, NULL, _("Cannot start proxy configuration program."), err->message, NULL); - g_error_free(err); -} - -static void -auto_ip_button_clicked_cb(GtkWidget *button, gpointer null) -{ - const char *ip; - PurpleStunNatDiscovery *stun; - char *auto_ip_text; - GList *list = NULL; - - /* Make a lookup for the auto-detected IP ourselves. */ - if (purple_prefs_get_bool("/purple/network/auto_ip")) { - /* 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 first IP of the local system */ - list = nice_interfaces_get_local_ips(FALSE); - if (list) { - ip = list->data; - } else { - ip = "0.0.0.0"; - } - } - } - } - } else{ - ip = _("Disabled"); - } - - auto_ip_text = g_strdup_printf(_("Use _automatically detected IP address: %s"), ip); - gtk_button_set_label(GTK_BUTTON(button), auto_ip_text); - g_free(auto_ip_text); - g_list_free_full(list, g_free); -} - -static void -bind_network_page(PidginPrefsWindow *win) -{ - GtkStyleContext *context; - GtkCssProvider *ip_css; - const gchar *res = "/im/pidgin/Pidgin/Prefs/ip.css"; - - gtk_entry_set_text(GTK_ENTRY(win->network.stun_server), - purple_prefs_get_string("/purple/network/stun_server")); - - pidgin_prefs_bind_checkbox("/purple/network/auto_ip", - win->network.auto_ip); - auto_ip_button_clicked_cb(win->network.auto_ip, NULL); /* Update label */ - - gtk_entry_set_text(GTK_ENTRY(win->network.public_ip), - purple_network_get_public_ip()); - - ip_css = gtk_css_provider_new(); - gtk_css_provider_load_from_resource(ip_css, res); - - context = gtk_widget_get_style_context(win->network.public_ip); - gtk_style_context_add_provider(context, - GTK_STYLE_PROVIDER(ip_css), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - - g_object_bind_property(win->network.auto_ip, "active", - win->network.public_ip_hbox, "sensitive", - G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN); - - pidgin_prefs_bind_checkbox("/purple/network/map_ports", - win->network.map_ports); - - pidgin_prefs_bind_checkbox("/purple/network/ports_range_use", - win->network.ports_range_use); - g_object_bind_property(win->network.ports_range_use, "active", - win->network.ports_range_hbox, "sensitive", - G_BINDING_SYNC_CREATE); - - pidgin_prefs_bind_spin_button("/purple/network/ports_range_start", - win->network.ports_range_start); - pidgin_prefs_bind_spin_button("/purple/network/ports_range_end", - win->network.ports_range_end); - - /* TURN server */ - gtk_entry_set_text(GTK_ENTRY(win->network.turn_server), - purple_prefs_get_string("/purple/network/turn_server")); - - pidgin_prefs_bind_spin_button("/purple/network/turn_port", - win->network.turn_port_udp); - - pidgin_prefs_bind_spin_button("/purple/network/turn_port_tcp", - win->network.turn_port_tcp); - - pidgin_prefs_bind_entry("/purple/network/turn_username", - win->network.turn_username); - pidgin_prefs_bind_entry("/purple/network/turn_password", - win->network.turn_password); -} - -static void -bind_proxy_page(PidginPrefsWindow *win) -{ - PurpleProxyInfo *proxy_info; - - if(purple_running_gnome()) { - gchar *path = NULL; - - gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack), - "gnome"); - - path = g_find_program_in_path("gnome-network-properties"); - if (path == NULL) - path = g_find_program_in_path("gnome-network-preferences"); - if (path == NULL) { - path = g_find_program_in_path("gnome-control-center"); - if (path != NULL) { - char *tmp = g_strdup_printf("%s network", path); - g_free(path); - path = tmp; - } - } - - win->proxy.gnome_program_path = path; - gtk_widget_set_visible(win->proxy.gnome_not_found, - path == NULL); - gtk_widget_set_visible(win->proxy.gnome_program, - path != NULL); - } else { - gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack), - "nongnome"); - - /* This is a global option that affects SOCKS4 usage even with - * account-specific proxy settings */ - pidgin_prefs_bind_checkbox("/purple/proxy/socks4_remotedns", - win->proxy.socks4_remotedns); - - win->proxy.type.type = PURPLE_PREF_STRING; - win->proxy.type.key = "/purple/proxy/type"; - pidgin_prefs_bind_dropdown(&win->proxy.type); - proxy_info = purple_global_proxy_get_info(); - - purple_prefs_connect_callback(prefs, "/purple/proxy/type", - proxy_changed_cb, win); - - if (proxy_info != NULL) { - if (purple_proxy_info_get_host(proxy_info)) { - gtk_entry_set_text(GTK_ENTRY(win->proxy.host), - purple_proxy_info_get_host(proxy_info)); - } - - if (purple_proxy_info_get_port(proxy_info) != 0) { - gtk_spin_button_set_value( - GTK_SPIN_BUTTON(win->proxy.port), - purple_proxy_info_get_port(proxy_info)); - } - - if (purple_proxy_info_get_username(proxy_info) != NULL) { - gtk_entry_set_text(GTK_ENTRY(win->proxy.username), - purple_proxy_info_get_username(proxy_info)); - } - - if (purple_proxy_info_get_password(proxy_info) != NULL) { - gtk_entry_set_text(GTK_ENTRY(win->proxy.password), - purple_proxy_info_get_password(proxy_info)); - } - } - - proxy_changed_cb("/purple/proxy/type", PURPLE_PREF_STRING, - purple_prefs_get_string("/purple/proxy/type"), - win); - } -} - -static void -bind_logging_page(PidginPrefsWindow *win) -{ - GList *names; - - win->logging.format.type = PURPLE_PREF_STRING; - win->logging.format.key = "/purple/logging/format"; - names = purple_log_logger_get_options(); - pidgin_prefs_bind_dropdown_from_list(&win->logging.format, names); - g_list_free(names); - - pidgin_prefs_bind_checkbox("/purple/logging/log_ims", - win->logging.log_ims); - pidgin_prefs_bind_checkbox("/purple/logging/log_chats", - win->logging.log_chats); - pidgin_prefs_bind_checkbox("/purple/logging/log_system", - win->logging.log_system); -} - -static void -set_idle_away(PurpleSavedStatus *status) -{ - purple_prefs_set_int("/purple/savedstatus/idleaway", purple_savedstatus_get_creation_time(status)); -} - -static void -set_startupstatus(PurpleSavedStatus *status) -{ - purple_prefs_set_int("/purple/savedstatus/startup", purple_savedstatus_get_creation_time(status)); -} - -static void -bind_away_page(PidginPrefsWindow *win) -{ - GtkWidget *menu; - - /* Idle stuff */ - win->away.idle_reporting.type = PURPLE_PREF_STRING; - win->away.idle_reporting.key = "/purple/away/idle_reporting"; - pidgin_prefs_bind_dropdown(&win->away.idle_reporting); - - pidgin_prefs_bind_spin_button("/purple/away/mins_before_away", - win->away.mins_before_away); - - pidgin_prefs_bind_checkbox("/purple/away/away_when_idle", - win->away.away_when_idle); - - /* TODO: Show something useful if we don't have any saved statuses. */ - menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away)); - gtk_widget_show_all(menu); - gtk_box_pack_start(GTK_BOX(win->away.idle_hbox), menu, FALSE, FALSE, 0); - - g_object_bind_property(win->away.away_when_idle, "active", - menu, "sensitive", - G_BINDING_SYNC_CREATE); - - /* Away stuff */ - win->away.auto_reply.type = PURPLE_PREF_STRING; - win->away.auto_reply.key = "/purple/away/auto_reply"; - pidgin_prefs_bind_dropdown(&win->away.auto_reply); - - /* Signon status stuff */ - pidgin_prefs_bind_checkbox("/purple/savedstatus/startup_current_status", - win->away.startup_current_status); - - /* TODO: Show something useful if we don't have any saved statuses. */ - menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus)); - gtk_widget_show_all(menu); - gtk_box_pack_start(GTK_BOX(win->away.startup_hbox), menu, FALSE, FALSE, 0); - gtk_label_set_mnemonic_widget(GTK_LABEL(win->away.startup_label), menu); - pidgin_set_accessible_label(menu, GTK_LABEL(win->away.startup_label)); - g_object_bind_property(win->away.startup_current_status, "active", - win->away.startup_hbox, "sensitive", - G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN); -} - -#ifdef USE_VV -static GList * -get_vv_device_menuitems(PurpleMediaElementType type) -{ - GList *result = NULL; - GList *i; - - i = purple_media_manager_enumerate_elements(purple_media_manager_get(), - type); - for (; i; i = g_list_delete_link(i, i)) { - PurpleMediaElementInfo *info = i->data; - - result = g_list_append(result, - purple_media_element_info_get_name(info)); - result = g_list_append(result, - purple_media_element_info_get_id(info)); - g_object_unref(info); - } - - return result; -} - -static GstElement * -create_test_element(PurpleMediaElementType type) -{ - PurpleMediaElementInfo *element_info; - - element_info = purple_media_manager_get_active_element(purple_media_manager_get(), type); - - g_return_val_if_fail(element_info, NULL); - - return purple_media_element_info_call_create(element_info, - NULL, NULL, NULL); -} - -static void -vv_test_switch_page_cb(GtkStack *stack, G_GNUC_UNUSED GParamSpec *pspec, - gpointer data) -{ - PidginPrefsWindow *win = data; - - if (!g_str_equal(gtk_stack_get_visible_child_name(stack), "vv")) { - /* Disable any running test pipelines. */ - gtk_toggle_button_set_active( - GTK_TOGGLE_BUTTON(win->vv.voice.test), FALSE); - gtk_toggle_button_set_active( - GTK_TOGGLE_BUTTON(win->vv.video.test), FALSE); - } -} - -static GstElement * -create_voice_pipeline(void) -{ - GstElement *pipeline; - GstElement *src, *sink; - GstElement *volume; - GstElement *level; - GstElement *valve; - - pipeline = gst_pipeline_new("voicetest"); - - src = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC); - sink = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK); - volume = gst_element_factory_make("volume", "volume"); - level = gst_element_factory_make("level", "level"); - valve = gst_element_factory_make("valve", "valve"); - - gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL); - gst_element_link_many(src, volume, level, valve, sink, NULL); - - purple_debug_info("gtkprefs", "create_voice_pipeline: setting pipeline " - "state to GST_STATE_PLAYING - it may hang here on win32\n"); - gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); - purple_debug_info("gtkprefs", "create_voice_pipeline: state is set\n"); - - return pipeline; -} - -static void -on_volume_change_cb(GtkWidget *w, gdouble value, gpointer data) -{ - PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); - GstElement *volume; - - if (!win->vv.voice.pipeline) { - return; - } - - volume = gst_bin_get_by_name(GST_BIN(win->vv.voice.pipeline), "volume"); - g_object_set(volume, "volume", - gtk_scale_button_get_value(GTK_SCALE_BUTTON(w)) / 100.0, NULL); -} - -static gdouble -gst_msg_db_to_percent(GstMessage *msg, gchar *value_name) -{ - const GValue *list; - const GValue *value; - gdouble value_db; - gdouble percent; - - list = gst_structure_get_value(gst_message_get_structure(msg), value_name); -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - value = g_value_array_get_nth(g_value_get_boxed(list), 0); -G_GNUC_END_IGNORE_DEPRECATIONS - value_db = g_value_get_double(value); - percent = pow(10, value_db / 20); - return (percent > 1.0) ? 1.0 : percent; -} - -static gboolean -gst_bus_cb(GstBus *bus, GstMessage *msg, gpointer data) -{ - PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); - - if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT && - gst_structure_has_name(gst_message_get_structure(msg), "level")) { - - GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); - gchar *name = gst_element_get_name(src); - - if (purple_strequal(name, "level")) { - gdouble percent; - gdouble threshold; - GstElement *valve; - - percent = gst_msg_db_to_percent(msg, "rms"); - gtk_progress_bar_set_fraction( - GTK_PROGRESS_BAR(win->vv.voice.level), percent); - - percent = gst_msg_db_to_percent(msg, "decay"); - threshold = gtk_range_get_value(GTK_RANGE( - win->vv.voice.threshold)) / - 100.0; - valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve"); - g_object_set(valve, "drop", (percent < threshold), NULL); - g_object_set(win->vv.voice.level, "text", - (percent < threshold) ? _("DROP") : " ", - NULL); - } - - g_free(name); - } - - return TRUE; -} - -static void -voice_test_destroy_cb(GtkWidget *w, gpointer data) -{ - PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); - - if (!win->vv.voice.pipeline) { - return; - } - - gst_element_set_state(win->vv.voice.pipeline, GST_STATE_NULL); - g_clear_pointer(&win->vv.voice.pipeline, gst_object_unref); -} - -static void -enable_voice_test(PidginPrefsWindow *win) -{ - GstBus *bus; - - win->vv.voice.pipeline = create_voice_pipeline(); - bus = gst_pipeline_get_bus(GST_PIPELINE(win->vv.voice.pipeline)); - gst_bus_add_signal_watch(bus); - g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), win); - gst_object_unref(bus); -} - -static void -toggle_voice_test_cb(GtkToggleButton *test, gpointer data) -{ - PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); - - if (gtk_toggle_button_get_active(test)) { - gtk_widget_set_sensitive(win->vv.voice.level, TRUE); - enable_voice_test(win); - - g_signal_connect(win->vv.voice.volume, "value-changed", - G_CALLBACK(on_volume_change_cb), win); - g_signal_connect(test, "destroy", - G_CALLBACK(voice_test_destroy_cb), win); - } else { - gtk_progress_bar_set_fraction( - GTK_PROGRESS_BAR(win->vv.voice.level), 0.0); - gtk_widget_set_sensitive(win->vv.voice.level, FALSE); - g_object_disconnect(win->vv.voice.volume, - "any-signal::value-changed", - G_CALLBACK(on_volume_change_cb), win, NULL); - g_object_disconnect(test, "any-signal::destroy", - G_CALLBACK(voice_test_destroy_cb), win, - NULL); - voice_test_destroy_cb(NULL, win); - } -} - -static void -volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data) -{ - purple_prefs_set_int("/purple/media/audio/volume/input", value * 100); -} - -static void -threshold_value_changed_cb(GtkScale *scale, GtkWidget *label) -{ - int value; - char *tmp; - - value = (int)gtk_range_get_value(GTK_RANGE(scale)); - tmp = g_strdup_printf(_("Silence threshold: %d%%"), value); - gtk_label_set_label(GTK_LABEL(label), tmp); - g_free(tmp); - - purple_prefs_set_int("/purple/media/audio/silence_threshold", value); -} - -static void -bind_voice_test(PidginPrefsWindow *win, GtkBuilder *builder) -{ - GObject *test; - GObject *label; - GObject *volume; - GObject *threshold; - char *tmp; - - volume = gtk_builder_get_object(builder, "vv.voice.volume"); - win->vv.voice.volume = GTK_WIDGET(volume); - gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), - purple_prefs_get_int("/purple/media/audio/volume/input") / 100.0); - g_signal_connect(volume, "value-changed", - G_CALLBACK(volume_changed_cb), NULL); - - label = gtk_builder_get_object(builder, "vv.voice.threshold_label"); - tmp = g_strdup_printf(_("Silence threshold: %d%%"), - purple_prefs_get_int("/purple/media/audio/silence_threshold")); - gtk_label_set_text(GTK_LABEL(label), tmp); - g_free(tmp); - - threshold = gtk_builder_get_object(builder, "vv.voice.threshold"); - win->vv.voice.threshold = GTK_WIDGET(threshold); - gtk_range_set_value(GTK_RANGE(threshold), - purple_prefs_get_int("/purple/media/audio/silence_threshold")); - g_signal_connect(threshold, "value-changed", - G_CALLBACK(threshold_value_changed_cb), label); - - win->vv.voice.level = - GTK_WIDGET(gtk_builder_get_object(builder, "vv.voice.level")); - - test = gtk_builder_get_object(builder, "vv.voice.test"); - g_signal_connect(test, "toggled", - G_CALLBACK(toggle_voice_test_cb), win); - win->vv.voice.test = GTK_WIDGET(test); -} - -static GstElement * -create_video_pipeline(void) -{ - GstElement *pipeline; - GstElement *src, *sink; - GstElement *videoconvert; - GstElement *videoscale; - - pipeline = gst_pipeline_new("videotest"); - src = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC); - sink = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK); - videoconvert = gst_element_factory_make("videoconvert", NULL); - videoscale = gst_element_factory_make("videoscale", NULL); - - g_object_set_data(G_OBJECT(pipeline), "sink", sink); - - gst_bin_add_many(GST_BIN(pipeline), src, videoconvert, videoscale, sink, - NULL); - gst_element_link_many(src, videoconvert, videoscale, sink, NULL); - - return pipeline; -} - -static void -video_test_destroy_cb(GtkWidget *w, gpointer data) -{ - PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); - - if (!win->vv.video.pipeline) { - return; - } - - gst_element_set_state(win->vv.video.pipeline, GST_STATE_NULL); - g_clear_pointer(&win->vv.video.pipeline, gst_object_unref); -} - -static void -enable_video_test(PidginPrefsWindow *win) -{ - GtkWidget *video = NULL; - GstElement *sink = NULL; - - win->vv.video.pipeline = create_video_pipeline(); - - sink = g_object_get_data(G_OBJECT(win->vv.video.pipeline), "sink"); - g_object_get(sink, "widget", &video, NULL); - gtk_widget_show(video); - - g_clear_pointer(&win->vv.video.sink_widget, gtk_widget_destroy); - gtk_container_add(GTK_CONTAINER(win->vv.video.frame), video); - win->vv.video.sink_widget = video; - - gst_element_set_state(GST_ELEMENT(win->vv.video.pipeline), - GST_STATE_PLAYING); -} - -static void -toggle_video_test_cb(GtkToggleButton *test, gpointer data) -{ - PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); - - if (gtk_toggle_button_get_active(test)) { - enable_video_test(win); - g_signal_connect(test, "destroy", - G_CALLBACK(video_test_destroy_cb), win); - } else { - g_object_disconnect(test, "any-signal::destroy", - G_CALLBACK(video_test_destroy_cb), win, - NULL); - video_test_destroy_cb(NULL, win); - } -} - -static void -bind_video_test(PidginPrefsWindow *win, GtkBuilder *builder) -{ - GObject *test; - - win->vv.video.frame = GTK_WIDGET( - gtk_builder_get_object(builder, "vv.video.frame")); - test = gtk_builder_get_object(builder, "vv.video.test"); - g_signal_connect(test, "toggled", - G_CALLBACK(toggle_video_test_cb), win); - win->vv.video.test = GTK_WIDGET(test); -} - -static void -vv_device_changed_cb(const gchar *name, PurplePrefType type, - gconstpointer value, gpointer data) -{ - PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); - - PurpleMediaManager *manager; - PurpleMediaElementInfo *info; - - manager = purple_media_manager_get(); - info = purple_media_manager_get_element_info(manager, value); - purple_media_manager_set_active_element(manager, info); - - /* Refresh test viewers */ - if (strstr(name, "audio") && win->vv.voice.pipeline) { - voice_test_destroy_cb(NULL, win); - enable_voice_test(win); - } else if (strstr(name, "video") && win->vv.video.pipeline) { - video_test_destroy_cb(NULL, win); - enable_video_test(win); - } -} - -static const char * -purple_media_type_to_preference_key(PurpleMediaElementType type) -{ - if (type & PURPLE_MEDIA_ELEMENT_AUDIO) { - if (type & PURPLE_MEDIA_ELEMENT_SRC) { - return PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device"; - } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { - return PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device"; - } - } else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) { - if (type & PURPLE_MEDIA_ELEMENT_SRC) { - return PIDGIN_PREFS_ROOT "/vvconfig/video/src/device"; - } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { - return PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device"; - } - } - - return NULL; -} - -static void -bind_vv_dropdown(PidginPrefCombo *combo, PurpleMediaElementType element_type) -{ - const gchar *preference_key; - GList *devices; - - preference_key = purple_media_type_to_preference_key(element_type); - devices = get_vv_device_menuitems(element_type); - - if (g_list_find_custom(devices, purple_prefs_get_string(preference_key), - (GCompareFunc)strcmp) == NULL) - { - GList *next = g_list_next(devices); - if (next) - purple_prefs_set_string(preference_key, next->data); - } - - combo->type = PURPLE_PREF_STRING; - combo->key = preference_key; - pidgin_prefs_bind_dropdown_from_list(combo, devices); - g_list_free_full(devices, g_free); -} - -static void -bind_vv_frame(PidginPrefsWindow *win, PidginPrefCombo *combo, - PurpleMediaElementType type) -{ - bind_vv_dropdown(combo, type); - - purple_prefs_connect_callback(combo->combo, - purple_media_type_to_preference_key(type), - vv_device_changed_cb, win); - g_signal_connect_swapped(combo->combo, "destroy", - G_CALLBACK(purple_prefs_disconnect_by_handle), - combo->combo); - - g_object_set_data(G_OBJECT(combo->combo), "vv_media_type", - (gpointer)type); - g_object_set_data(G_OBJECT(combo->combo), "vv_combo", combo); -} - -static void -device_list_changed_cb(PurpleMediaManager *manager, GtkWidget *widget) -{ - PidginPrefCombo *combo; - PurpleMediaElementType media_type; - GtkTreeModel *model; - - combo = g_object_get_data(G_OBJECT(widget), "vv_combo"); - media_type = (PurpleMediaElementType)g_object_get_data(G_OBJECT(widget), - "vv_media_type"); - - /* Unbind original connections so we can repopulate the combo box. */ - g_object_disconnect(combo->combo, "any-signal::changed", - G_CALLBACK(bind_dropdown_set), combo, NULL); - model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)); - gtk_list_store_clear(GTK_LIST_STORE(model)); - - bind_vv_dropdown(combo, media_type); -} - -static GtkWidget * -vv_page(PidginPrefsWindow *win) -{ - GtkBuilder *builder; - GtkWidget *ret; - PurpleMediaManager *manager; - - builder = gtk_builder_new_from_resource("/im/pidgin/Pidgin/Prefs/vv.ui"); - gtk_builder_set_translation_domain(builder, PACKAGE); - - ret = GTK_WIDGET(gtk_builder_get_object(builder, "vv.page")); - - manager = purple_media_manager_get(); - - win->vv.voice.input.combo = GTK_WIDGET( - gtk_builder_get_object(builder, "vv.voice.input.combo")); - bind_vv_frame(win, &win->vv.voice.input, - PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC); - g_signal_connect_object(manager, "elements-changed::audiosrc", - G_CALLBACK(device_list_changed_cb), - win->vv.voice.input.combo, 0); - - win->vv.voice.output.combo = GTK_WIDGET( - gtk_builder_get_object(builder, "vv.voice.output.combo")); - bind_vv_frame(win, &win->vv.voice.output, - PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK); - g_signal_connect_object(manager, "elements-changed::audiosink", - G_CALLBACK(device_list_changed_cb), - win->vv.voice.output.combo, 0); - - bind_voice_test(win, builder); - - win->vv.video.input.combo = GTK_WIDGET( - gtk_builder_get_object(builder, "vv.video.input.combo")); - bind_vv_frame(win, &win->vv.video.input, - PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC); - g_signal_connect_object(manager, "elements-changed::videosrc", - G_CALLBACK(device_list_changed_cb), - win->vv.video.input.combo, 0); - - win->vv.video.output.combo = GTK_WIDGET( - gtk_builder_get_object(builder, "vv.video.output.combo")); - bind_vv_frame(win, &win->vv.video.output, - PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK); - g_signal_connect_object(manager, "elements-changed::videosink", - G_CALLBACK(device_list_changed_cb), - win->vv.video.output.combo, 0); - - bind_video_test(win, builder); - - g_signal_connect(win->stack, "notify::visible-child", - G_CALLBACK(vv_test_switch_page_cb), win); - - g_object_ref(ret); - g_object_unref(builder); - - return ret; -} -#endif - -static void -prefs_stack_init(PidginPrefsWindow *win) -{ -#ifdef USE_VV - GtkStack *stack = GTK_STACK(win->stack); - GtkWidget *vv; -#endif - - bind_interface_page(win); - bind_conv_page(win); - bind_logging_page(win); - bind_network_page(win); - bind_proxy_page(win); - bind_away_page(win); - bind_theme_page(win); -#ifdef USE_VV - vv = vv_page(win); - gtk_container_add_with_properties(GTK_CONTAINER(stack), vv, "name", - "vv", "title", _("Voice/Video"), - NULL); - g_object_unref(vv); -#endif -} - -static void -pidgin_prefs_window_class_init(PidginPrefsWindowClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - - gtk_widget_class_set_template_from_resource( - widget_class, - "/im/pidgin/Pidgin/Prefs/prefs.ui" - ); - - /* Main window */ - gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow, - stack); - gtk_widget_class_bind_template_callback(widget_class, delete_prefs); - - /* Interface page */ - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - iface.im.hide_new.combo); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - iface.win32.minimize_new_convs); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - iface.conversations.tabs); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - iface.conversations.tabs_vbox); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - iface.conversations.close_on_tabs); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - iface.conversations.tab_side.combo); - - /* Conversations page */ - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.notification_chat.combo); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.show_incoming_formatting); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.im.close_immediately); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.im.send_typing); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.use_smooth_scrolling); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.win32.blink_im); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.resize_custom_smileys); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.custom_smileys_size); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.minimum_entry_lines); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.format_buffer); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.format_view); -#ifdef WIN32 - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.font_frame); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.use_theme_font); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.custom_font_hbox); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - conversations.custom_font); -#endif - /* Even though Win32-specific, must be bound to avoid Glade warnings. */ - gtk_widget_class_bind_template_callback(widget_class, - apply_custom_font); - gtk_widget_class_bind_template_callback(widget_class, - pidgin_custom_font_set); - - /* Logging page */ - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, logging.format.combo); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, logging.log_ims); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, logging.log_chats); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, logging.log_system); - - /* Network page */ - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, network.stun_server); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, network.auto_ip); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, network.public_ip); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.public_ip_hbox); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, network.map_ports); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.ports_range_use); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.ports_range_hbox); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.ports_range_start); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.ports_range_end); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, network.turn_server); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.turn_port_udp); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.turn_port_tcp); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.turn_username); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - network.turn_password); - gtk_widget_class_bind_template_callback(widget_class, - network_stun_server_changed_cb); - gtk_widget_class_bind_template_callback(widget_class, - auto_ip_button_clicked_cb); - gtk_widget_class_bind_template_callback(widget_class, - network_ip_changed); - gtk_widget_class_bind_template_callback(widget_class, - network_turn_server_changed_cb); - - /* Proxy page */ - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.stack); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.gnome_not_found); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.gnome_program); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - proxy.socks4_remotedns); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.type.combo); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.options); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.host); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.port); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.username); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, proxy.password); - gtk_widget_class_bind_template_callback(widget_class, - proxy_button_clicked_cb); - gtk_widget_class_bind_template_callback(widget_class, - proxy_print_option); - - /* Away page */ - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - away.idle_reporting.combo); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - away.mins_before_away); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, away.away_when_idle); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, away.idle_hbox); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - away.auto_reply.combo); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, - away.startup_current_status); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, away.startup_hbox); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, away.startup_label); - - /* Themes page */ - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, theme.status); - gtk_widget_class_bind_template_child( - widget_class, PidginPrefsWindow, theme.smiley); - gtk_widget_class_bind_template_callback(widget_class, - prefs_set_status_icon_theme_cb); - gtk_widget_class_bind_template_callback(widget_class, - prefs_set_smiley_theme_cb); -} - -static void -pidgin_prefs_window_init(PidginPrefsWindow *win) -{ - /* copy the preferences to tmp values... - * I liked "take affect immediately" Oh well :-( */ - /* (that should have been "effect," right?) */ - - /* Back to instant-apply! I win! BU-HAHAHA! */ - - /* Create the window */ - gtk_widget_init_template(GTK_WIDGET(win)); - - prefs_stack_init(win); - - /* Refresh the list of themes before showing the preferences window */ - prefs_themes_refresh(); -} - -void -pidgin_prefs_show(void) -{ - if (prefs == NULL) { - prefs = PIDGIN_PREFS_WINDOW(g_object_new( - pidgin_prefs_window_get_type(), NULL)); - } - - gtk_window_present(GTK_WINDOW(prefs)); -} - -static void -smiley_theme_pref_cb(const char *name, PurplePrefType type, - gconstpointer value, gpointer data) -{ - const gchar *theme_name = value; - GList *themes, *it; - - if (purple_strequal(theme_name, "none")) { - purple_smiley_theme_set_current(NULL); - return; - } - - /* XXX: could be cached when initializing prefs view */ - themes = pidgin_smiley_theme_get_all(); - - for (it = themes; it; it = g_list_next(it)) { - PidginSmileyTheme *theme = it->data; - - if (!purple_strequal(pidgin_smiley_theme_get_name(theme), theme_name)) - continue; - - purple_smiley_theme_set_current(PURPLE_SMILEY_THEME(theme)); - } -} - -void -pidgin_prefs_init(void) -{ - purple_prefs_add_none(PIDGIN_PREFS_ROOT ""); - purple_prefs_add_none("/plugins/gtk"); - - /* Plugins */ - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/plugins"); - purple_prefs_add_path_list(PIDGIN_PREFS_ROOT "/plugins/loaded", NULL); - - /* File locations */ - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/filelocations"); - purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder", ""); - purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", ""); - purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder", ""); - - /* Themes */ - prefs_themes_init(); - - /* Smiley Themes */ - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys"); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default"); - - /* Smiley Callbacks */ - purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme", - smiley_theme_pref_cb, NULL); - -#ifdef USE_VV - /* Voice/Video */ - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig"); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio"); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/src"); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", ""); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink"); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", ""); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video"); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/src"); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", ""); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video"); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/sink"); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", ""); -#endif - - pidgin_prefs_update_old(); -} - -void -pidgin_prefs_update_old(void) -{ - const gchar *video_sink = NULL; - - /* Rename some old prefs */ - purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_ims", "/purple/logging/log_ims"); - purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_chats", "/purple/logging/log_chats"); - - purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/im/raise_on_events", "/plugins/gtk/X11/notify/method_raise"); - - purple_prefs_rename_boolean_toggle(PIDGIN_PREFS_ROOT "/conversations/ignore_colors", - PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting"); - - /* Remove some no-longer-used prefs */ - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/auto_expand_contacts"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/button_style"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/grey_idle_buddies"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/raise_on_events"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_group_count"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_warning_level"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/x"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/y"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/browser"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/command"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/place"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/manual_command"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/button_type"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ctrl_enter_sends"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/enter_sends"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/escape_closes"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/html_shortcuts"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/icons_on_tabs"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/send_formatting"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/show_smileys"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/show_urls_as_links"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/smiley_shortcuts"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_bgcolor"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_fgcolor"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_font"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_size"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/old_tab_complete"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/tab_completion"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/hide_on_send"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/color_nicks"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/raise_on_events"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_fonts"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_font_sizes"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/passthrough_unknown_commands"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/debug/timestamps"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/idle"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/logging/individual_logs"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/signon"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/silent_signon"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/command"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/conv_focus"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/chat_msg_recv"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/first_im_recv"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/got_attention"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/im_recv"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/join_chat"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/left_chat"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/login"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/logout"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/nick_said"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/pounce_default"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/send_chat_msg"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/send_im"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/sent_attention"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/chat_msg_recv"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/first_im_recv"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/got_attention"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/im_recv"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/join_chat"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/left_chat"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/login"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/logout"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/nick_said"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/pounce_default"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/send_chat_msg"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/send_im"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/sent_attention"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/method"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/mute"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/theme"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound"); - - /* Convert old queuing prefs to hide_new 3-way pref. */ - if (purple_prefs_exists("/plugins/gtk/docklet/queue_messages") && - purple_prefs_get_bool("/plugins/gtk/docklet/queue_messages")) - { - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "always"); - } - else if (purple_prefs_exists(PIDGIN_PREFS_ROOT "/away/queue_messages") && - purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/away/queue_messages")) - { - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "away"); - } - purple_prefs_remove(PIDGIN_PREFS_ROOT "/away/queue_messages"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/away"); - purple_prefs_remove("/plugins/gtk/docklet/queue_messages"); - - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_width"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_height"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_width"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_height"); - purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/x", - PIDGIN_PREFS_ROOT "/conversations/im/x"); - purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/y", - PIDGIN_PREFS_ROOT "/conversations/im/y"); - - /* Fixup vvconfig plugin prefs */ - if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/device")) { - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", - purple_prefs_get_string("/plugins/core/vvconfig/audio/src/device")); - } - if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/device")) { - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", - purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/device")); - } - if (purple_prefs_exists("/plugins/core/vvconfig/video/src/device")) { - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", - purple_prefs_get_string("/plugins/core/vvconfig/video/src/device")); - } - if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/device")) { - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", - purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/device")); - } - - video_sink = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device"); - if (purple_strequal(video_sink, "glimagesink") || purple_strequal(video_sink, "directdrawsink")) { - /* Accelerated sinks move to GTK GL. */ - /* video_sink = "gtkglsink"; */ - /* FIXME: I haven't been able to get gtkglsink to work yet: */ - video_sink = "gtksink"; - } else { - /* Everything else, including default will be moved to GTK sink. */ - video_sink = "gtksink"; - } - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", video_sink); - - purple_prefs_remove("/plugins/core/vvconfig"); - purple_prefs_remove("/plugins/gtk/vvconfig"); - - purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin"); - purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin"); -} -
--- a/pidgin/gtkprefs.h Tue May 18 02:04:53 2021 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/* pidgin - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - * - */ - -#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) -# error "only <pidgin.h> may be included directly" -#endif - -#ifndef _PIDGINPREFS_H_ -#define _PIDGINPREFS_H_ -/** - * SECTION:gtkprefs - * @section_id: pidgin-gtkprefs - * @short_description: <filename>gtkprefs.h</filename> - * @title: Preferences - */ - -#include <purple.h> - -G_BEGIN_DECLS - -#define PIDGIN_TYPE_PREFS_WINDOW (pidgin_prefs_window_get_type()) -G_DECLARE_FINAL_TYPE(PidginPrefsWindow, pidgin_prefs_window, PIDGIN, PREFS_WINDOW, GtkDialog) - -/** - * pidgin_prefs_init: - * - * Initializes all UI-specific preferences. - */ -void pidgin_prefs_init(void); - -/** - * pidgin_prefs_show: - * - * Shows the preferences dialog. - */ -void pidgin_prefs_show(void); - -/** - * pidgin_prefs_checkbox: - * @title: The text to be displayed as the checkbox label - * @key: The key of the purple bool pref that will be represented by the checkbox - * @page: The page to which the new checkbox will be added - * - * Add a new checkbox for a boolean preference - * - * Returns: (transfer full): The new checkbox - */ -GtkWidget *pidgin_prefs_checkbox(const char *title, const char *key, - GtkWidget *page); - -/** - * pidgin_prefs_labeled_spin_button: - * @page: The page to which the spin button will be added - * @title: The text to be displayed as the spin button label - * @key: The key of the int pref that will be represented by the spin button - * @min: The minimum value of the spin button - * @max: The maximum value of the spin button - * @sg: If not NULL, the size group to which the spin button will be added - * - * Add a new spin button representing an int preference - * - * Returns: (transfer full): An hbox containing both the label and the spinner. Can be - * used to set the widgets to sensitive or insensitive based on the - * value of a checkbox. - */ -GtkWidget *pidgin_prefs_labeled_spin_button(GtkWidget *page, - const gchar *title, const char *key, int min, int max, GtkSizeGroup *sg); - -/** - * pidgin_prefs_labeled_entry: - * @page: The page to which the entry will be added - * @title: The text to be displayed as the entry label - * @key: The key of the string pref that will be represented by the entry - * @sg: If not NULL, the size group to which the entry will be added - * - * Add a new entry representing a string preference - * - * Returns: (transfer full) :An hbox containing both the label and the entry. Can be used to set - * the widgets to sensitive or insensitive based on the value of a - * checkbox. - */ -GtkWidget *pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title, - const char *key, GtkSizeGroup *sg); - -/** - * pidgin_prefs_labeled_password: - * @page: The page to which the entry will be added - * @title: The text to be displayed as the entry label - * @key: The key of the string pref that will be represented by the entry - * @sg: If not NULL, the size group to which the entry will be added - * - * Add a new entry representing a password (string) preference - * The entry will use a password-style text entry (the text is substituded) - * - * Returns: (transfer full): An hbox containing both the label and the entry. Can be used to set - * the widgets to sensitive or insensitive based on the value of a - * checkbox. - */ -GtkWidget *pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title, - const char *key, GtkSizeGroup *sg); - -/** - * pidgin_prefs_dropdown: - * @page: The page to which the dropdown will be added - * @title: The text to be displayed as the dropdown label - * @type: The type of preference to be stored in the generated dropdown - * @key: The key of the pref that will be represented by the dropdown - * @...: The choices to be added to the dropdown, choices should be - * paired as label/value - * - * Add a new dropdown representing a preference of the specified type - * - * Returns: (transfer full): The new dropdown. - */ -GtkWidget *pidgin_prefs_dropdown(GtkWidget *page, const gchar *title, - PurplePrefType type, const char *key, ...); - -/** - * pidgin_prefs_dropdown_from_list: - * @page: The page to which the dropdown will be added - * @title: The text to be displayed as the dropdown label - * @type: The type of preference to be stored in the dropdown - * @key: The key of the pref that will be represented by the dropdown - * @menuitems: (element-type PurpleKeyValuePair): The choices to be added to the dropdown, choices should - * be paired as label/value - * - * Add a new dropdown representing a preference of the specified type - * - * Returns: (transfer full): The new dropdown. - */ -GtkWidget *pidgin_prefs_dropdown_from_list(GtkWidget *page, - const gchar * title, PurplePrefType type, const char *key, - GList *menuitems); - -/** - * pidgin_prefs_update_old: - * - * Rename legacy prefs and delete some that no longer exist. - */ -void pidgin_prefs_update_old(void); - -G_END_DECLS - -#endif /* _PIDGINPREFS_H_ */
--- a/pidgin/gtkutils.c Tue May 18 02:04:53 2021 -0500 +++ b/pidgin/gtkutils.c Tue May 18 02:08:18 2021 -0500 @@ -40,7 +40,6 @@ #include <talkatu.h> #include "gtkaccount.h" -#include "gtkprefs.h" #include "gtkconv.h" #include "gtkdialogs.h"
--- a/pidgin/libpidgin.c Tue May 18 02:04:53 2021 -0500 +++ b/pidgin/libpidgin.c Tue May 18 02:08:18 2021 -0500 @@ -41,7 +41,6 @@ #include "gtkidle.h" #include "gtkmedia.h" #include "gtknotify.h" -#include "gtkprefs.h" #include "gtkprivacy.h" #include "gtkrequest.h" #include "gtkroomlist.h" @@ -54,6 +53,7 @@ #include "pidgindebug.h" #include "pidginlog.h" #include "pidginplugininfo.h" +#include "pidginprefs.h" #include "pidginprivate.h" #include "pidginstock.h"
--- a/pidgin/meson.build Tue May 18 02:04:53 2021 -0500 +++ b/pidgin/meson.build Tue May 18 02:08:18 2021 -0500 @@ -12,7 +12,6 @@ 'gtkmedia.c', 'gtknotify.c', 'gtkpluginpref.c', - 'gtkprefs.c', 'gtkprivacy.c', 'gtkrequest.c', 'gtkroomlist.c', @@ -44,8 +43,6 @@ 'pidgincontactcompletion.c', 'pidginconversationwindow.c', 'pidgincontactlist.c', - 'pidgincredentialproviderrow.c', - 'pidgincredentialspage.c', 'pidgindebug.c', 'pidgindialog.c', 'pidgingdkpixbuf.c', @@ -66,6 +63,9 @@ 'pidgintalkatu.c', 'pidgintooltip.c', 'pidginwindow.c', + 'prefs/pidginprefs.c', + 'prefs/pidgincredentialproviderrow.c', + 'prefs/pidgincredentialspage.c', ] libpidgin_headers = [ @@ -82,7 +82,6 @@ 'gtkmedia.h', 'gtknotify.h', 'gtkpluginpref.h', - 'gtkprefs.h', 'gtkprivacy.h', 'gtkrequest.h', 'gtkroomlist.h', @@ -114,8 +113,6 @@ 'pidginconversationwindow.h', 'pidgincontactlist.h', 'pidgincore.h', - 'pidgincredentialproviderrow.h', - 'pidgincredentialspage.h', 'pidgindialog.h', 'pidgindebug.h', 'pidgingdkpixbuf.h', @@ -139,6 +136,12 @@ 'pidginwindow.h', ] +libpidgin_prefs_headers = [ + 'prefs/pidginprefs.h', + 'prefs/pidgincredentialproviderrow.h', + 'prefs/pidgincredentialspage.h', +] + libpidgin_enum_headers = [ 'gtkaccount.h', 'gtkblist.h', @@ -200,7 +203,7 @@ libpidgin_enums_h = libpidgin_enums[1] PIDGIN_H_INCLUDES = [] - foreach header : libpidgin_headers + ['pidginenums.h'] + foreach header : libpidgin_headers + libpidgin_prefs_headers + ['pidginenums.h'] PIDGIN_H_INCLUDES += '#include <pidgin/@0@>'.format(header) endforeach pidgin_h_conf = configuration_data() @@ -212,6 +215,7 @@ install_dir : get_option('includedir') / pidgin_filebase) install_headers(libpidgin_headers, subdir : pidgin_include_base) + install_headers(libpidgin_prefs_headers, subdir : pidgin_include_base / 'prefs') _libpidgin_dependencies = [ glib, @@ -245,7 +249,7 @@ libpidgin_SOURCES + libpidgin_built_sources + libpidgin_built_headers + ['pidginprivate.h'], package_revision, c_args : ['-DPIDGIN_COMPILATION', '-DG_LOG_DOMAIN="Pidgin"'], - include_directories : [toplevel_inc], + include_directories : [toplevel_inc, include_directories('prefs')], version : PURPLE_LIB_VERSION, dependencies : _libpidgin_dependencies, install : true) @@ -311,7 +315,7 @@ endif # INSTALL_I18N if enable_introspection - introspection_sources = libpidgin_headers + introspection_sources = libpidgin_headers + libpidgin_prefs_headers Pidgin_gir_includes = [ 'GObject-2.0', 'Gtk-3.0',
--- a/pidgin/pidginapplication.c Tue May 18 02:04:53 2021 -0500 +++ b/pidgin/pidginapplication.c Tue May 18 02:08:18 2021 -0500 @@ -38,7 +38,6 @@ #include "gtkaccount.h" #include "gtkblist.h" #include "gtkdialogs.h" -#include "gtkprefs.h" #include "gtkprivacy.h" #include "gtkroomlist.h" #include "gtksmiley-manager.h" @@ -49,6 +48,7 @@ #include "pidginlog.h" #include "pidginmooddialog.h" #include "pidgin/pidginpluginsdialog.h" +#include "pidginprefs.h" struct _PidginApplication { GtkApplication parent;
--- a/pidgin/pidgincredentialproviderrow.c Tue May 18 02:04:53 2021 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,224 +0,0 @@ -/* - * Pidgin - Internet Messenger - * Copyright (C) Pidgin Developers <devel@pidgin.im> - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - */ - -#include <purple.h> - -#include <handy.h> - -#include "pidgincredentialproviderrow.h" - -struct _PidginCredentialProviderRow { - HdyActionRow parent; - - PurpleCredentialProvider *provider; - - GtkWidget *active; - GtkWidget *configure; -}; - -enum { - PROP_0, - PROP_PROVIDER, - PROP_ACTIVE, - N_PROPERTIES, -}; -static GParamSpec *properties[N_PROPERTIES] = {NULL, }; - -G_DEFINE_TYPE(PidginCredentialProviderRow, pidgin_credential_provider_row, - HDY_TYPE_ACTION_ROW) - -/****************************************************************************** - * Helpers - *****************************************************************************/ -static void -pidgin_credential_provider_row_set_provider(PidginCredentialProviderRow *row, - PurpleCredentialProvider *provider) -{ - if(!g_set_object(&row->provider, provider)) { - return; - } - - if(PURPLE_IS_CREDENTIAL_PROVIDER(provider)) { - hdy_preferences_row_set_title( - HDY_PREFERENCES_ROW(row), - purple_credential_provider_get_name(provider)); - hdy_action_row_set_subtitle( - HDY_ACTION_ROW(row), - purple_credential_provider_get_description(provider)); - /* Not implemented yet, so always hide the configure button. */ - gtk_widget_set_visible(row->configure, FALSE); - } - - /* Notify that we changed. */ - g_object_notify_by_pspec(G_OBJECT(row), properties[PROP_PROVIDER]); -} - - -/****************************************************************************** - * GObject Implementation - *****************************************************************************/ -static void -pidgin_credential_provider_row_get_property(GObject *obj, guint param_id, - GValue *value, GParamSpec *pspec) -{ - PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); - - switch(param_id) { - case PROP_PROVIDER: - g_value_set_object(value, - pidgin_credential_provider_row_get_provider(row)); - break; - case PROP_ACTIVE: - g_value_set_boolean(value, - pidgin_credential_provider_row_get_active(row)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); - break; - } -} - -static void -pidgin_credential_provider_row_set_property(GObject *obj, guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); - - switch(param_id) { - case PROP_PROVIDER: - pidgin_credential_provider_row_set_provider(row, - g_value_get_object(value)); - break; - case PROP_ACTIVE: - pidgin_credential_provider_row_set_active(row, - g_value_get_boolean(value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); - break; - } -} - -static void -pidgin_credential_provider_row_finalize(GObject *obj) -{ - PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); - - g_clear_object(&row->provider); -} - -static void -pidgin_credential_provider_row_init(PidginCredentialProviderRow *row) -{ - gtk_widget_init_template(GTK_WIDGET(row)); - - /* If this row is active, then enable the provider properties button (which - * may or may not be visible). */ - g_object_bind_property(G_OBJECT(row), "active", - G_OBJECT(row->configure), "sensitive", - G_BINDING_DEFAULT); -} - -static void -pidgin_credential_provider_row_class_init(PidginCredentialProviderRowClass *klass) -{ - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - - obj_class->get_property = pidgin_credential_provider_row_get_property; - obj_class->set_property = pidgin_credential_provider_row_set_property; - obj_class->finalize = pidgin_credential_provider_row_finalize; - - /** - * PidginCredentialProviderRow::provider - * - * The #PurpleCredentialProvider whose information will be displayed. - */ - properties[PROP_PROVIDER] = g_param_spec_object( - "provider", "provider", - "The PurpleCredentialProvider instance", - PURPLE_TYPE_CREDENTIAL_PROVIDER, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - /** - * PidginCredentialProviderRow::active - * - * Whether the #PurpleCredentialProvider is currently active. - */ - properties[PROP_ACTIVE] = g_param_spec_boolean( - "active", "active", - "Whether the PurpleCredentialProvider is active", - FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(obj_class, N_PROPERTIES, properties); - - gtk_widget_class_set_template_from_resource( - widget_class, - "/im/pidgin/Pidgin/Prefs/credentialprovider.ui" - ); - - gtk_widget_class_bind_template_child(widget_class, - PidginCredentialProviderRow, - active); - gtk_widget_class_bind_template_child(widget_class, - PidginCredentialProviderRow, - configure); -} - -/****************************************************************************** - * API - *****************************************************************************/ -GtkWidget * -pidgin_credential_provider_row_new(PurpleCredentialProvider *provider) { - g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), NULL); - - return GTK_WIDGET(g_object_new(PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW, - "provider", provider, - NULL)); -} - -PurpleCredentialProvider * -pidgin_credential_provider_row_get_provider(PidginCredentialProviderRow *row) { - g_return_val_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row), NULL); - - return row->provider; -} - -gboolean -pidgin_credential_provider_row_get_active(PidginCredentialProviderRow *row) { - g_return_val_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row), FALSE); - - return gtk_widget_get_visible(row->active); -} - -void -pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row, - gboolean active) -{ - g_return_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row)); - - gtk_widget_set_visible(row->active, active); - - g_object_notify_by_pspec(G_OBJECT(row), properties[PROP_ACTIVE]); -}
--- a/pidgin/pidgincredentialproviderrow.h Tue May 18 02:04:53 2021 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * Pidgin - Internet Messenger - * Copyright (C) Pidgin Developers <devel@pidgin.im> - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - */ - -#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) -# error "only <pidgin.h> may be included directly" -#endif - -#ifndef PIDGIN_CREDENTIAL_PROVIDER_ROW_H -#define PIDGIN_CREDENTIAL_PROVIDER_ROW_H - -/** - * SECTION:pidgincredentialproviderrow - * @section_id: pidgin-pidgincredentialproviderrow - * @short_description: The preferences widget for a credential provider. - * @title: Credential provider widget - * - * #PidginCredentialProviderRow is a widget for the preferences window to let - * users configure their credential provider. - */ - -#include <glib.h> - -#include <gtk/gtk.h> - -#include <handy.h> - -G_BEGIN_DECLS - -/** - * PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW: - * - * The standard _get_type macro for #PidginCredentialProviderRow. - * - * Since: 3.0.0 - */ -#define PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW (pidgin_credential_provider_row_get_type()) -G_DECLARE_FINAL_TYPE(PidginCredentialProviderRow, - pidgin_credential_provider_row, - PIDGIN, CREDENTIAL_PROVIDER_ROW, HdyActionRow) - -/** - * PidginCredentialProviderRow: - * - * A widget that displays a credential provider. - * - * Since: 3.0.0 - */ - -/** - * pidgin_credential_provider_row_new: - * @provider: The credential provider to bind. - * - * Creates a new #PidginCredentialProviderRow instance. - * - * Returns: (transfer full): The new #PidginCredentialProviderRow instance. - * - * Since: 3.0.0 - */ -GtkWidget *pidgin_credential_provider_row_new(PurpleCredentialProvider *provider); - -/** - * pidgin_credential_provider_row_get_provider: - * @row: The row instance. - * - * Gets the #PurpleCredentialProvider displayed by this widget. - * - * Returns: (transfer none): The displayed #PurpleCredentialProvider. - * - * Since: 3.0.0 - */ -PurpleCredentialProvider *pidgin_credential_provider_row_get_provider(PidginCredentialProviderRow *row); - -/** - * pidgin_credential_provider_row_get_active: - * @row: The row instance. - * - * Gets whether the row is displayed as active. - * - * Returns: Whether the row is active. - * - * Since: 3.0.0 - */ -gboolean pidgin_credential_provider_row_get_active(PidginCredentialProviderRow *row); - -/** - * pidgin_credential_provider_row_set_active: - * @row: The row instance. - * @active: Whether to display as active. - * - * Sets whether the row is displayed as active. - * - * Since: 3.0.0 - */ -void pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row, gboolean active); - -G_END_DECLS - -#endif /* PIDGIN_CREDENTIAL_PROVIDER_ROW_H */
--- a/pidgin/pidgincredentialspage.c Tue May 18 02:04:53 2021 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,209 +0,0 @@ -/* - * Pidgin - Internet Messenger - * Copyright (C) Pidgin Developers <devel@pidgin.im> - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - */ - -#include <purple.h> - -#include <handy.h> - -#include "pidgincredentialspage.h" - -#include "pidgincredentialproviderrow.h" - -struct _PidginCredentialsPage { - HdyPreferencesPage parent; - - GtkWidget *credential_list; -}; - -G_DEFINE_TYPE(PidginCredentialsPage, pidgin_credentials_page, - HDY_TYPE_PREFERENCES_PAGE) - -/****************************************************************************** - * Helpers - *****************************************************************************/ -static void -pidgin_credentials_page_create_row(PurpleCredentialProvider *provider, - gpointer data) -{ - GtkListBox *box = GTK_LIST_BOX(data); - GtkWidget *row = NULL; - - row = pidgin_credential_provider_row_new(provider); - gtk_list_box_prepend(box, row); -} - -static gint -pidgin_credentials_page_sort_rows(GtkListBoxRow *row1, GtkListBoxRow *row2, - G_GNUC_UNUSED gpointer user_data) -{ - PidginCredentialProviderRow *pcprow = NULL; - PurpleCredentialProvider *provider = NULL; - const gchar *id1 = NULL; - gboolean is_noop1 = FALSE; - const gchar *id2 = NULL; - gboolean is_noop2 = FALSE; - - pcprow = PIDGIN_CREDENTIAL_PROVIDER_ROW(row1); - provider = pidgin_credential_provider_row_get_provider(pcprow); - id1 = purple_credential_provider_get_id(provider); - is_noop1 = purple_strequal(id1, "noop-provider"); - - pcprow = PIDGIN_CREDENTIAL_PROVIDER_ROW(row2); - provider = pidgin_credential_provider_row_get_provider(pcprow); - id2 = purple_credential_provider_get_id(provider); - is_noop2 = purple_strequal(id2, "noop-provider"); - - /* Sort None provider after everything else. */ - if (is_noop1 && is_noop2) { - return 0; - } else if (is_noop1 && !is_noop2) { - return 1; - } else if (!is_noop1 && is_noop2) { - return -1; - } - /* Sort normally by ID. */ - return g_strcmp0(id1, id2); -} - -static void -pidgin_credential_page_list_row_activated_cb(GtkListBox *box, - GtkListBoxRow *row, - G_GNUC_UNUSED gpointer data) -{ - PurpleCredentialManager *manager = NULL; - PurpleCredentialProvider *provider = NULL; - const gchar *id = NULL; - GError *error = NULL; - - provider = pidgin_credential_provider_row_get_provider( - PIDGIN_CREDENTIAL_PROVIDER_ROW(row)); - id = purple_credential_provider_get_id(provider); - - manager = purple_credential_manager_get_default(); - if(purple_credential_manager_set_active_provider(manager, id, &error)) { - purple_prefs_set_string("/purple/credentials/active-provider", id); - - return; - } - - purple_debug_warning("credentials-page", "failed to set the active " - "credential provider to '%s': %s", - id, error ? error->message : "unknown error"); - - g_clear_error(&error); -} - -static void -pidgin_credentials_page_set_active_provider(PidginCredentialsPage *page, - const gchar *new_id) -{ - GList *rows = NULL; - - rows = gtk_container_get_children(GTK_CONTAINER(page->credential_list)); - for (; rows; rows = g_list_delete_link(rows, rows)) { - PidginCredentialProviderRow *row = NULL; - PurpleCredentialProvider *provider = NULL; - const gchar *id = NULL; - - row = PIDGIN_CREDENTIAL_PROVIDER_ROW(rows->data); - provider = pidgin_credential_provider_row_get_provider(row); - id = purple_credential_provider_get_id(provider); - - pidgin_credential_provider_row_set_active(row, - purple_strequal(new_id, id)); - } -} - -static void -pidgin_credentials_page_active_provider_changed_cb(const gchar *name, - PurplePrefType type, - gconstpointer value, - gpointer data) -{ - PidginCredentialsPage *page = PIDGIN_CREDENTIALS_PAGE(data); - - pidgin_credentials_page_set_active_provider(page, (const gchar *)value); -} - -/****************************************************************************** - * GObject Implementation - *****************************************************************************/ -static void -pidgin_credentials_page_finalize(GObject *obj) { - purple_prefs_disconnect_by_handle(obj); - - G_OBJECT_CLASS(pidgin_credentials_page_parent_class)->finalize(obj); -} - -static void -pidgin_credentials_page_init(PidginCredentialsPage *page) { - PurpleCredentialManager *manager = NULL; - const gchar *active = NULL; - - gtk_widget_init_template(GTK_WIDGET(page)); - - purple_prefs_add_none("/purple/credentials"); - purple_prefs_add_string("/purple/credentials/active-provider", NULL); - - manager = purple_credential_manager_get_default(); - purple_credential_manager_foreach_provider( - manager, - pidgin_credentials_page_create_row, - page->credential_list); - gtk_list_box_set_sort_func(GTK_LIST_BOX(page->credential_list), - pidgin_credentials_page_sort_rows, NULL, NULL); - - purple_prefs_connect_callback(page, "/purple/credentials/active-provider", - pidgin_credentials_page_active_provider_changed_cb, - page); - - active = purple_prefs_get_string("/purple/credentials/active-provider"); - if(active != NULL) { - pidgin_credentials_page_set_active_provider(page, active); - } -} - -static void -pidgin_credentials_page_class_init(PidginCredentialsPageClass *klass) { - GObjectClass *obj_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - - obj_class->finalize = pidgin_credentials_page_finalize; - - gtk_widget_class_set_template_from_resource( - widget_class, - "/im/pidgin/Pidgin/Prefs/credentials.ui" - ); - - gtk_widget_class_bind_template_child(widget_class, PidginCredentialsPage, - credential_list); - gtk_widget_class_bind_template_callback(widget_class, - pidgin_credential_page_list_row_activated_cb); -} - -/****************************************************************************** - * API - *****************************************************************************/ -GtkWidget * -pidgin_credentials_page_new(void) { - return GTK_WIDGET(g_object_new(PIDGIN_TYPE_CREDENTIALS_PAGE, NULL)); -}
--- a/pidgin/pidgincredentialspage.h Tue May 18 02:04:53 2021 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * Pidgin - Internet Messenger - * Copyright (C) Pidgin Developers <devel@pidgin.im> - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - */ - -#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) -# error "only <pidgin.h> may be included directly" -#endif - -#ifndef PIDGIN_CREDENTIALS_PAGE_H -#define PIDGIN_CREDENTIALS_PAGE_H - -/** - * SECTION:pidgincredentialspage - * @section_id: pidgin-pidgincredentialspage - * @short_description: The preferences page for credential management. - * @title: Credential management widget - * - * #PidginCredentialsPage is a widget for the preferences window to let users - * choose and configure their credential provider. - */ - -#include <glib.h> - -#include <gtk/gtk.h> -#include <handy.h> - -G_BEGIN_DECLS - -/** - * PIDGIN_TYPE_CREDENTIALS_PAGE: - * - * The standard _get_type macro for #PidginCredentialsPage. - * - * Since: 3.0.0 - */ -#define PIDGIN_TYPE_CREDENTIALS_PAGE (pidgin_credentials_page_get_type()) -G_DECLARE_FINAL_TYPE(PidginCredentialsPage, pidgin_credentials_page, - PIDGIN, CREDENTIALS_PAGE, HdyPreferencesPage) - -/** - * PidginCredentialsPage: - * - * A widget that displays a page of credential settings. - * - * Since: 3.0.0 - */ - -/** - * pidgin_credentials_page_new: - * - * Creates a new #PidginCredentialsPage instance. - * - * Returns: (transfer full): The new #PidginCredentialsPage instance. - * - * Since: 3.0.0 - */ -GtkWidget *pidgin_credentials_page_new(void); - -G_END_DECLS - -#endif /* PIDGIN_CREDENTIALS_PAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/prefs/pidgincredentialproviderrow.c Tue May 18 02:08:18 2021 -0500 @@ -0,0 +1,224 @@ +/* + * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <purple.h> + +#include <handy.h> + +#include "pidgincredentialproviderrow.h" + +struct _PidginCredentialProviderRow { + HdyActionRow parent; + + PurpleCredentialProvider *provider; + + GtkWidget *active; + GtkWidget *configure; +}; + +enum { + PROP_0, + PROP_PROVIDER, + PROP_ACTIVE, + N_PROPERTIES, +}; +static GParamSpec *properties[N_PROPERTIES] = {NULL, }; + +G_DEFINE_TYPE(PidginCredentialProviderRow, pidgin_credential_provider_row, + HDY_TYPE_ACTION_ROW) + +/****************************************************************************** + * Helpers + *****************************************************************************/ +static void +pidgin_credential_provider_row_set_provider(PidginCredentialProviderRow *row, + PurpleCredentialProvider *provider) +{ + if(!g_set_object(&row->provider, provider)) { + return; + } + + if(PURPLE_IS_CREDENTIAL_PROVIDER(provider)) { + hdy_preferences_row_set_title( + HDY_PREFERENCES_ROW(row), + purple_credential_provider_get_name(provider)); + hdy_action_row_set_subtitle( + HDY_ACTION_ROW(row), + purple_credential_provider_get_description(provider)); + /* Not implemented yet, so always hide the configure button. */ + gtk_widget_set_visible(row->configure, FALSE); + } + + /* Notify that we changed. */ + g_object_notify_by_pspec(G_OBJECT(row), properties[PROP_PROVIDER]); +} + + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +static void +pidgin_credential_provider_row_get_property(GObject *obj, guint param_id, + GValue *value, GParamSpec *pspec) +{ + PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); + + switch(param_id) { + case PROP_PROVIDER: + g_value_set_object(value, + pidgin_credential_provider_row_get_provider(row)); + break; + case PROP_ACTIVE: + g_value_set_boolean(value, + pidgin_credential_provider_row_get_active(row)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +static void +pidgin_credential_provider_row_set_property(GObject *obj, guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); + + switch(param_id) { + case PROP_PROVIDER: + pidgin_credential_provider_row_set_provider(row, + g_value_get_object(value)); + break; + case PROP_ACTIVE: + pidgin_credential_provider_row_set_active(row, + g_value_get_boolean(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +static void +pidgin_credential_provider_row_finalize(GObject *obj) +{ + PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); + + g_clear_object(&row->provider); +} + +static void +pidgin_credential_provider_row_init(PidginCredentialProviderRow *row) +{ + gtk_widget_init_template(GTK_WIDGET(row)); + + /* If this row is active, then enable the provider properties button (which + * may or may not be visible). */ + g_object_bind_property(G_OBJECT(row), "active", + G_OBJECT(row->configure), "sensitive", + G_BINDING_DEFAULT); +} + +static void +pidgin_credential_provider_row_class_init(PidginCredentialProviderRowClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + obj_class->get_property = pidgin_credential_provider_row_get_property; + obj_class->set_property = pidgin_credential_provider_row_set_property; + obj_class->finalize = pidgin_credential_provider_row_finalize; + + /** + * PidginCredentialProviderRow::provider + * + * The #PurpleCredentialProvider whose information will be displayed. + */ + properties[PROP_PROVIDER] = g_param_spec_object( + "provider", "provider", + "The PurpleCredentialProvider instance", + PURPLE_TYPE_CREDENTIAL_PROVIDER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + /** + * PidginCredentialProviderRow::active + * + * Whether the #PurpleCredentialProvider is currently active. + */ + properties[PROP_ACTIVE] = g_param_spec_boolean( + "active", "active", + "Whether the PurpleCredentialProvider is active", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); + + gtk_widget_class_set_template_from_resource( + widget_class, + "/im/pidgin/Pidgin/Prefs/credentialprovider.ui" + ); + + gtk_widget_class_bind_template_child(widget_class, + PidginCredentialProviderRow, + active); + gtk_widget_class_bind_template_child(widget_class, + PidginCredentialProviderRow, + configure); +} + +/****************************************************************************** + * API + *****************************************************************************/ +GtkWidget * +pidgin_credential_provider_row_new(PurpleCredentialProvider *provider) { + g_return_val_if_fail(PURPLE_IS_CREDENTIAL_PROVIDER(provider), NULL); + + return GTK_WIDGET(g_object_new(PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW, + "provider", provider, + NULL)); +} + +PurpleCredentialProvider * +pidgin_credential_provider_row_get_provider(PidginCredentialProviderRow *row) { + g_return_val_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row), NULL); + + return row->provider; +} + +gboolean +pidgin_credential_provider_row_get_active(PidginCredentialProviderRow *row) { + g_return_val_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row), FALSE); + + return gtk_widget_get_visible(row->active); +} + +void +pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row, + gboolean active) +{ + g_return_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row)); + + gtk_widget_set_visible(row->active, active); + + g_object_notify_by_pspec(G_OBJECT(row), properties[PROP_ACTIVE]); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/prefs/pidgincredentialproviderrow.h Tue May 18 02:08:18 2021 -0500 @@ -0,0 +1,117 @@ +/* + * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) +# error "only <pidgin.h> may be included directly" +#endif + +#ifndef PIDGIN_CREDENTIAL_PROVIDER_ROW_H +#define PIDGIN_CREDENTIAL_PROVIDER_ROW_H + +/** + * SECTION:pidgincredentialproviderrow + * @section_id: pidgin-pidgincredentialproviderrow + * @short_description: The preferences widget for a credential provider. + * @title: Credential provider widget + * + * #PidginCredentialProviderRow is a widget for the preferences window to let + * users configure their credential provider. + */ + +#include <glib.h> + +#include <gtk/gtk.h> + +#include <handy.h> + +G_BEGIN_DECLS + +/** + * PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW: + * + * The standard _get_type macro for #PidginCredentialProviderRow. + * + * Since: 3.0.0 + */ +#define PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW (pidgin_credential_provider_row_get_type()) +G_DECLARE_FINAL_TYPE(PidginCredentialProviderRow, + pidgin_credential_provider_row, + PIDGIN, CREDENTIAL_PROVIDER_ROW, HdyActionRow) + +/** + * PidginCredentialProviderRow: + * + * A widget that displays a credential provider. + * + * Since: 3.0.0 + */ + +/** + * pidgin_credential_provider_row_new: + * @provider: The credential provider to bind. + * + * Creates a new #PidginCredentialProviderRow instance. + * + * Returns: (transfer full): The new #PidginCredentialProviderRow instance. + * + * Since: 3.0.0 + */ +GtkWidget *pidgin_credential_provider_row_new(PurpleCredentialProvider *provider); + +/** + * pidgin_credential_provider_row_get_provider: + * @row: The row instance. + * + * Gets the #PurpleCredentialProvider displayed by this widget. + * + * Returns: (transfer none): The displayed #PurpleCredentialProvider. + * + * Since: 3.0.0 + */ +PurpleCredentialProvider *pidgin_credential_provider_row_get_provider(PidginCredentialProviderRow *row); + +/** + * pidgin_credential_provider_row_get_active: + * @row: The row instance. + * + * Gets whether the row is displayed as active. + * + * Returns: Whether the row is active. + * + * Since: 3.0.0 + */ +gboolean pidgin_credential_provider_row_get_active(PidginCredentialProviderRow *row); + +/** + * pidgin_credential_provider_row_set_active: + * @row: The row instance. + * @active: Whether to display as active. + * + * Sets whether the row is displayed as active. + * + * Since: 3.0.0 + */ +void pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row, gboolean active); + +G_END_DECLS + +#endif /* PIDGIN_CREDENTIAL_PROVIDER_ROW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/prefs/pidgincredentialspage.c Tue May 18 02:08:18 2021 -0500 @@ -0,0 +1,209 @@ +/* + * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <purple.h> + +#include <handy.h> + +#include "pidgincredentialspage.h" + +#include "pidgincredentialproviderrow.h" + +struct _PidginCredentialsPage { + HdyPreferencesPage parent; + + GtkWidget *credential_list; +}; + +G_DEFINE_TYPE(PidginCredentialsPage, pidgin_credentials_page, + HDY_TYPE_PREFERENCES_PAGE) + +/****************************************************************************** + * Helpers + *****************************************************************************/ +static void +pidgin_credentials_page_create_row(PurpleCredentialProvider *provider, + gpointer data) +{ + GtkListBox *box = GTK_LIST_BOX(data); + GtkWidget *row = NULL; + + row = pidgin_credential_provider_row_new(provider); + gtk_list_box_prepend(box, row); +} + +static gint +pidgin_credentials_page_sort_rows(GtkListBoxRow *row1, GtkListBoxRow *row2, + G_GNUC_UNUSED gpointer user_data) +{ + PidginCredentialProviderRow *pcprow = NULL; + PurpleCredentialProvider *provider = NULL; + const gchar *id1 = NULL; + gboolean is_noop1 = FALSE; + const gchar *id2 = NULL; + gboolean is_noop2 = FALSE; + + pcprow = PIDGIN_CREDENTIAL_PROVIDER_ROW(row1); + provider = pidgin_credential_provider_row_get_provider(pcprow); + id1 = purple_credential_provider_get_id(provider); + is_noop1 = purple_strequal(id1, "noop-provider"); + + pcprow = PIDGIN_CREDENTIAL_PROVIDER_ROW(row2); + provider = pidgin_credential_provider_row_get_provider(pcprow); + id2 = purple_credential_provider_get_id(provider); + is_noop2 = purple_strequal(id2, "noop-provider"); + + /* Sort None provider after everything else. */ + if (is_noop1 && is_noop2) { + return 0; + } else if (is_noop1 && !is_noop2) { + return 1; + } else if (!is_noop1 && is_noop2) { + return -1; + } + /* Sort normally by ID. */ + return g_strcmp0(id1, id2); +} + +static void +pidgin_credential_page_list_row_activated_cb(GtkListBox *box, + GtkListBoxRow *row, + G_GNUC_UNUSED gpointer data) +{ + PurpleCredentialManager *manager = NULL; + PurpleCredentialProvider *provider = NULL; + const gchar *id = NULL; + GError *error = NULL; + + provider = pidgin_credential_provider_row_get_provider( + PIDGIN_CREDENTIAL_PROVIDER_ROW(row)); + id = purple_credential_provider_get_id(provider); + + manager = purple_credential_manager_get_default(); + if(purple_credential_manager_set_active_provider(manager, id, &error)) { + purple_prefs_set_string("/purple/credentials/active-provider", id); + + return; + } + + purple_debug_warning("credentials-page", "failed to set the active " + "credential provider to '%s': %s", + id, error ? error->message : "unknown error"); + + g_clear_error(&error); +} + +static void +pidgin_credentials_page_set_active_provider(PidginCredentialsPage *page, + const gchar *new_id) +{ + GList *rows = NULL; + + rows = gtk_container_get_children(GTK_CONTAINER(page->credential_list)); + for (; rows; rows = g_list_delete_link(rows, rows)) { + PidginCredentialProviderRow *row = NULL; + PurpleCredentialProvider *provider = NULL; + const gchar *id = NULL; + + row = PIDGIN_CREDENTIAL_PROVIDER_ROW(rows->data); + provider = pidgin_credential_provider_row_get_provider(row); + id = purple_credential_provider_get_id(provider); + + pidgin_credential_provider_row_set_active(row, + purple_strequal(new_id, id)); + } +} + +static void +pidgin_credentials_page_active_provider_changed_cb(const gchar *name, + PurplePrefType type, + gconstpointer value, + gpointer data) +{ + PidginCredentialsPage *page = PIDGIN_CREDENTIALS_PAGE(data); + + pidgin_credentials_page_set_active_provider(page, (const gchar *)value); +} + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +static void +pidgin_credentials_page_finalize(GObject *obj) { + purple_prefs_disconnect_by_handle(obj); + + G_OBJECT_CLASS(pidgin_credentials_page_parent_class)->finalize(obj); +} + +static void +pidgin_credentials_page_init(PidginCredentialsPage *page) { + PurpleCredentialManager *manager = NULL; + const gchar *active = NULL; + + gtk_widget_init_template(GTK_WIDGET(page)); + + purple_prefs_add_none("/purple/credentials"); + purple_prefs_add_string("/purple/credentials/active-provider", NULL); + + manager = purple_credential_manager_get_default(); + purple_credential_manager_foreach_provider( + manager, + pidgin_credentials_page_create_row, + page->credential_list); + gtk_list_box_set_sort_func(GTK_LIST_BOX(page->credential_list), + pidgin_credentials_page_sort_rows, NULL, NULL); + + purple_prefs_connect_callback(page, "/purple/credentials/active-provider", + pidgin_credentials_page_active_provider_changed_cb, + page); + + active = purple_prefs_get_string("/purple/credentials/active-provider"); + if(active != NULL) { + pidgin_credentials_page_set_active_provider(page, active); + } +} + +static void +pidgin_credentials_page_class_init(PidginCredentialsPageClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + obj_class->finalize = pidgin_credentials_page_finalize; + + gtk_widget_class_set_template_from_resource( + widget_class, + "/im/pidgin/Pidgin/Prefs/credentials.ui" + ); + + gtk_widget_class_bind_template_child(widget_class, PidginCredentialsPage, + credential_list); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_credential_page_list_row_activated_cb); +} + +/****************************************************************************** + * API + *****************************************************************************/ +GtkWidget * +pidgin_credentials_page_new(void) { + return GTK_WIDGET(g_object_new(PIDGIN_TYPE_CREDENTIALS_PAGE, NULL)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/prefs/pidgincredentialspage.h Tue May 18 02:08:18 2021 -0500 @@ -0,0 +1,79 @@ +/* + * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) +# error "only <pidgin.h> may be included directly" +#endif + +#ifndef PIDGIN_CREDENTIALS_PAGE_H +#define PIDGIN_CREDENTIALS_PAGE_H + +/** + * SECTION:pidgincredentialspage + * @section_id: pidgin-pidgincredentialspage + * @short_description: The preferences page for credential management. + * @title: Credential management widget + * + * #PidginCredentialsPage is a widget for the preferences window to let users + * choose and configure their credential provider. + */ + +#include <glib.h> + +#include <gtk/gtk.h> +#include <handy.h> + +G_BEGIN_DECLS + +/** + * PIDGIN_TYPE_CREDENTIALS_PAGE: + * + * The standard _get_type macro for #PidginCredentialsPage. + * + * Since: 3.0.0 + */ +#define PIDGIN_TYPE_CREDENTIALS_PAGE (pidgin_credentials_page_get_type()) +G_DECLARE_FINAL_TYPE(PidginCredentialsPage, pidgin_credentials_page, + PIDGIN, CREDENTIALS_PAGE, HdyPreferencesPage) + +/** + * PidginCredentialsPage: + * + * A widget that displays a page of credential settings. + * + * Since: 3.0.0 + */ + +/** + * pidgin_credentials_page_new: + * + * Creates a new #PidginCredentialsPage instance. + * + * Returns: (transfer full): The new #PidginCredentialsPage instance. + * + * Since: 3.0.0 + */ +GtkWidget *pidgin_credentials_page_new(void); + +G_END_DECLS + +#endif /* PIDGIN_CREDENTIALS_PAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/prefs/pidginprefs.c Tue May 18 02:08:18 2021 -0500 @@ -0,0 +1,2914 @@ +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <math.h> + +#include <glib/gi18n-lib.h> +#include <glib/gstdio.h> +#include <nice.h> +#include <talkatu.h> + +#include <purple.h> + +#include "gtkblist.h" +#include "gtkconv.h" +#include "gtkdialogs.h" +#include "gtksavedstatuses.h" +#include "gtksmiley-theme.h" +#include "gtkstatus-icon-theme.h" +#include "gtkutils.h" +#include "pidgincore.h" +#include "pidgindebug.h" +#include "pidginprefs.h" +#include "pidginstock.h" +#ifdef USE_VV +#include <gst/video/videooverlay.h> +#ifdef GDK_WINDOWING_WIN32 +#include <gdk/gdkwin32.h> +#endif +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif +#ifdef GDK_WINDOWING_QUARTZ +#include <gdk/gdkquartz.h> +#endif +#endif +#include <libsoup/soup.h> + +#define PREFS_OPTIMAL_ICON_SIZE 32 + +/* 25MB */ +#define PREFS_MAX_DOWNLOADED_THEME_SIZE 26214400 + +struct theme_info { + gchar *type; + gchar *extension; + gchar *original_name; +}; + +typedef struct _PidginPrefCombo PidginPrefCombo; + +typedef void (*PidginPrefsBindDropdownCallback)(GtkComboBox *combo_box, + PidginPrefCombo *combo); + +struct _PidginPrefCombo { + GtkWidget *combo; + PurplePrefType type; + const gchar *key; + union { + const char *string; + int integer; + gboolean boolean; + } value; + gint previously_active; + gint current_active; + PidginPrefsBindDropdownCallback cb; +}; + +struct _PidginPrefsWindow { + GtkDialog parent; + + /* Stack */ + GtkWidget *stack; + + /* Interface page */ + struct { + struct { + PidginPrefCombo hide_new; + } im; + struct { + GtkWidget *minimize_new_convs; + } win32; + struct { + GtkWidget *tabs; + GtkWidget *tabs_vbox; + GtkWidget *close_on_tabs; + PidginPrefCombo tab_side; + } conversations; + } iface; + + /* Conversations page */ + struct { + PidginPrefCombo notification_chat; + GtkWidget *show_incoming_formatting; + struct { + GtkWidget *close_immediately; + GtkWidget *send_typing; + } im; + GtkWidget *use_smooth_scrolling; + struct { + GtkWidget *blink_im; + } win32; + GtkWidget *resize_custom_smileys; + GtkWidget *custom_smileys_size; + GtkWidget *minimum_entry_lines; + GtkTextBuffer *format_buffer; + GtkWidget *format_view; + /* Win32 specific frame */ + GtkWidget *font_frame; + GtkWidget *use_theme_font; + GtkWidget *custom_font_hbox; + GtkWidget *custom_font; + } conversations; + + /* Logging page */ + struct { + PidginPrefCombo format; + GtkWidget *log_ims; + GtkWidget *log_chats; + GtkWidget *log_system; + } logging; + + /* Network page */ + struct { + GtkWidget *stun_server; + GtkWidget *auto_ip; + GtkWidget *public_ip; + GtkWidget *public_ip_hbox; + GtkWidget *map_ports; + GtkWidget *ports_range_use; + GtkWidget *ports_range_hbox; + GtkWidget *ports_range_start; + GtkWidget *ports_range_end; + GtkWidget *turn_server; + GtkWidget *turn_port_udp; + GtkWidget *turn_port_tcp; + GtkWidget *turn_username; + GtkWidget *turn_password; + } network; + + /* Proxy page */ + struct { + GtkWidget *stack; + /* GNOME version */ + GtkWidget *gnome_not_found; + GtkWidget *gnome_program; + gchar *gnome_program_path; + /* Non-GNOME version */ + GtkWidget *socks4_remotedns; + PidginPrefCombo type; + GtkWidget *options; + GtkWidget *host; + GtkWidget *port; + GtkWidget *username; + GtkWidget *password; + } proxy; + + /* Away page */ + struct { + PidginPrefCombo idle_reporting; + GtkWidget *mins_before_away; + GtkWidget *idle_hbox; + GtkWidget *away_when_idle; + PidginPrefCombo auto_reply; + GtkWidget *startup_current_status; + GtkWidget *startup_hbox; + GtkWidget *startup_label; + } away; + + /* Themes page */ + struct { + SoupSession *session; + GtkWidget *status; + GtkWidget *smiley; + } theme; + +#ifdef USE_VV + /* Voice/Video page */ + struct { + struct { + PidginPrefCombo input; + PidginPrefCombo output; + GtkWidget *level; + GtkWidget *threshold; + GtkWidget *volume; + GtkWidget *test; + GstElement *pipeline; + } voice; + + struct { + PidginPrefCombo input; + PidginPrefCombo output; + GtkWidget *frame; + GtkWidget *sink_widget; + GtkWidget *test; + GstElement *pipeline; + } video; + } vv; +#endif +}; + +/* Main dialog */ +static PidginPrefsWindow *prefs = NULL; + +/* Themes page */ +static GtkWidget *prefs_status_themes_combo_box; +static GtkWidget *prefs_smiley_themes_combo_box; + +/* These exist outside the lifetime of the prefs dialog */ +static GtkListStore *prefs_status_icon_themes; +static GtkListStore *prefs_smiley_themes; + +/* + * PROTOTYPES + */ +G_DEFINE_TYPE(PidginPrefsWindow, pidgin_prefs_window, GTK_TYPE_DIALOG); +static void delete_prefs(GtkWidget *, void *); + +static void +update_spin_value(GtkWidget *w, GtkWidget *spin) +{ + const char *key = g_object_get_data(G_OBJECT(spin), "val"); + int value; + + value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); + + purple_prefs_set_int(key, value); +} + +GtkWidget * +pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title, + const char *key, int min, int max, GtkSizeGroup *sg) +{ + GtkWidget *spin; + GtkAdjustment *adjust; + int val; + + val = purple_prefs_get_int(key); + + adjust = GTK_ADJUSTMENT(gtk_adjustment_new(val, min, max, 1, 1, 0)); + spin = gtk_spin_button_new(adjust, 1, 0); + g_object_set_data(G_OBJECT(spin), "val", (char *)key); + if (max < 10000) + gtk_widget_set_size_request(spin, 50, -1); + else + gtk_widget_set_size_request(spin, 60, -1); + g_signal_connect(G_OBJECT(adjust), "value-changed", + G_CALLBACK(update_spin_value), GTK_WIDGET(spin)); + gtk_widget_show(spin); + + return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL); +} + +static void +pidgin_prefs_bind_spin_button(const char *key, GtkWidget *spin) +{ + GtkAdjustment *adjust; + int val; + + val = purple_prefs_get_int(key); + + adjust = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(spin)); + gtk_adjustment_set_value(adjust, val); + g_object_set_data(G_OBJECT(spin), "val", (char *)key); + g_signal_connect(G_OBJECT(adjust), "value-changed", + G_CALLBACK(update_spin_value), GTK_WIDGET(spin)); +} + +static void +entry_set(GtkEntry *entry, gpointer data) +{ + const char *key = (const char*)data; + + purple_prefs_set_string(key, gtk_entry_get_text(entry)); +} + +GtkWidget * +pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title, + const char *key, GtkSizeGroup *sg) +{ + GtkWidget *entry; + const gchar *value; + + value = purple_prefs_get_string(key); + + entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(entry), value); + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(entry_set), (char*)key); + gtk_widget_show(entry); + + return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL); +} + +static void +pidgin_prefs_bind_entry(const char *key, GtkWidget *entry) +{ + const gchar *value; + + value = purple_prefs_get_string(key); + + gtk_entry_set_text(GTK_ENTRY(entry), value); + g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_set), + (char*)key); +} + +GtkWidget * +pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title, + const char *key, GtkSizeGroup *sg) +{ + GtkWidget *entry; + const gchar *value; + + value = purple_prefs_get_string(key); + + entry = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_entry_set_text(GTK_ENTRY(entry), value); + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(entry_set), (char*)key); + gtk_widget_show(entry); + + return pidgin_add_widget_to_vbox(GTK_BOX(page), title, sg, entry, TRUE, NULL); +} + +/* TODO: Maybe move this up somewheres... */ +enum { + PREF_DROPDOWN_TEXT, + PREF_DROPDOWN_VALUE, + PREF_DROPDOWN_COUNT +}; + +typedef struct +{ + PurplePrefType type; + union { + const char *string; + int integer; + gboolean boolean; + } value; +} PidginPrefValue; + +typedef void (*PidginPrefsDropdownCallback)(GtkComboBox *combo_box, + PidginPrefValue value); + +static void +dropdown_set(GtkComboBox *combo_box, gpointer _cb) +{ + PidginPrefsDropdownCallback cb = _cb; + GtkTreeIter iter; + GtkTreeModel *tree_model; + PidginPrefValue active; + + tree_model = gtk_combo_box_get_model(combo_box); + if (!gtk_combo_box_get_active_iter(combo_box, &iter)) + return; + active.type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo_box), + "type")); + + g_object_set_data(G_OBJECT(combo_box), "previously_active", + g_object_get_data(G_OBJECT(combo_box), "current_active")); + g_object_set_data(G_OBJECT(combo_box), "current_active", + GINT_TO_POINTER(gtk_combo_box_get_active(combo_box))); + + if (active.type == PURPLE_PREF_INT) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &active.value.integer, -1); + } + else if (active.type == PURPLE_PREF_STRING) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &active.value.string, -1); + } + else if (active.type == PURPLE_PREF_BOOLEAN) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &active.value.boolean, -1); + } + + cb(combo_box, active); +} + +static GtkWidget * +pidgin_prefs_dropdown_from_list_with_cb(GtkWidget *box, const gchar *title, + GtkComboBox **dropdown_out, GList *menuitems, + PidginPrefValue initial, PidginPrefsDropdownCallback cb) +{ + GtkWidget *dropdown; + GtkWidget *label = NULL; + GtkListStore *store = NULL; + GtkTreeIter iter; + GtkTreeIter active; + GtkCellRenderer *renderer; + gpointer current_active; + + g_return_val_if_fail(menuitems != NULL, NULL); + + if (initial.type == PURPLE_PREF_INT) { + store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_INT); + } else if (initial.type == PURPLE_PREF_STRING) { + store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_STRING); + } else if (initial.type == PURPLE_PREF_BOOLEAN) { + store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_BOOLEAN); + } else { + g_warn_if_reached(); + return NULL; + } + + dropdown = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); + if (dropdown_out != NULL) + *dropdown_out = GTK_COMBO_BOX(dropdown); + g_object_set_data(G_OBJECT(dropdown), "type", GINT_TO_POINTER(initial.type)); + + for (; menuitems != NULL; menuitems = g_list_next(menuitems)) { + const PurpleKeyValuePair *menu_item = menuitems->data; + int int_value = 0; + const char *str_value = NULL; + gboolean bool_value = FALSE; + + if (menu_item->key == NULL) { + break; + } + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_TEXT, menu_item->key, + -1); + + if (initial.type == PURPLE_PREF_INT) { + int_value = GPOINTER_TO_INT(menu_item->value); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, int_value, + -1); + } + else if (initial.type == PURPLE_PREF_STRING) { + str_value = (const char *)menu_item->value; + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, str_value, + -1); + } + else if (initial.type == PURPLE_PREF_BOOLEAN) { + bool_value = (gboolean)GPOINTER_TO_INT(menu_item->value); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, bool_value, + -1); + } + + if ((initial.type == PURPLE_PREF_INT && + initial.value.integer == int_value) || + (initial.type == PURPLE_PREF_STRING && + purple_strequal(initial.value.string, str_value)) || + (initial.type == PURPLE_PREF_BOOLEAN && + (initial.value.boolean == bool_value))) { + + active = iter; + } + } + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer, + "text", 0, + NULL); + + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dropdown), &active); + current_active = GINT_TO_POINTER(gtk_combo_box_get_active(GTK_COMBO_BOX( + dropdown))); + g_object_set_data(G_OBJECT(dropdown), "current_active", current_active); + g_object_set_data(G_OBJECT(dropdown), "previously_active", current_active); + + g_signal_connect(G_OBJECT(dropdown), "changed", + G_CALLBACK(dropdown_set), cb); + + pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label); + + return label; +} + +static void +pidgin_prefs_dropdown_from_list_cb(GtkComboBox *combo_box, + PidginPrefValue value) +{ + const char *key; + + key = g_object_get_data(G_OBJECT(combo_box), "key"); + + if (value.type == PURPLE_PREF_INT) { + purple_prefs_set_int(key, value.value.integer); + } else if (value.type == PURPLE_PREF_STRING) { + purple_prefs_set_string(key, value.value.string); + } else if (value.type == PURPLE_PREF_BOOLEAN) { + purple_prefs_set_bool(key, value.value.boolean); + } else { + g_return_if_reached(); + } +} + +GtkWidget * +pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title, + PurplePrefType type, const char *key, GList *menuitems) +{ + PidginPrefValue initial; + GtkComboBox *dropdown = NULL; + GtkWidget *label; + + initial.type = type; + if (type == PURPLE_PREF_INT) { + initial.value.integer = purple_prefs_get_int(key); + } else if (type == PURPLE_PREF_STRING) { + initial.value.string = purple_prefs_get_string(key); + } else if (type == PURPLE_PREF_BOOLEAN) { + initial.value.boolean = purple_prefs_get_bool(key); + } else { + g_return_val_if_reached(NULL); + } + + label = pidgin_prefs_dropdown_from_list_with_cb(box, title, &dropdown, + menuitems, initial, pidgin_prefs_dropdown_from_list_cb); + + g_object_set_data(G_OBJECT(dropdown), "key", (gpointer)key); + + return label; +} + +GtkWidget * +pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type, + const char *key, ...) +{ + va_list ap; + GList *menuitems = NULL; + GtkWidget *dropdown = NULL; + char *name; + + g_return_val_if_fail(type == PURPLE_PREF_BOOLEAN || type == PURPLE_PREF_INT || + type == PURPLE_PREF_STRING, NULL); + + va_start(ap, key); + while ((name = va_arg(ap, char *)) != NULL) { + PurpleKeyValuePair *kvp; + + if (type == PURPLE_PREF_INT || type == PURPLE_PREF_BOOLEAN) { + kvp = purple_key_value_pair_new(name, GINT_TO_POINTER(va_arg(ap, int))); + } else { + kvp = purple_key_value_pair_new(name, va_arg(ap, char *)); + } + menuitems = g_list_prepend(menuitems, kvp); + } + va_end(ap); + + g_return_val_if_fail(menuitems != NULL, NULL); + + menuitems = g_list_reverse(menuitems); + + dropdown = pidgin_prefs_dropdown_from_list(box, title, type, key, + menuitems); + + g_list_free_full(menuitems, (GDestroyNotify)purple_key_value_pair_free); + + return dropdown; +} + +static void +pidgin_prefs_bind_dropdown_from_list_cb(GtkComboBox *combo_box, + PidginPrefCombo *combo) +{ + if (combo->type == PURPLE_PREF_INT) { + purple_prefs_set_int(combo->key, combo->value.integer); + } else if (combo->type == PURPLE_PREF_STRING) { + purple_prefs_set_string(combo->key, combo->value.string); + } else if (combo->type == PURPLE_PREF_BOOLEAN) { + purple_prefs_set_bool(combo->key, combo->value.boolean); + } else { + g_return_if_reached(); + } +} + +static void +bind_dropdown_set(GtkComboBox *combo_box, gpointer data) +{ + PidginPrefCombo *combo = data; + GtkTreeIter iter; + GtkTreeModel *tree_model; + + tree_model = gtk_combo_box_get_model(combo_box); + if (!gtk_combo_box_get_active_iter(combo_box, &iter)) + return; + + combo->previously_active = combo->current_active; + combo->current_active = gtk_combo_box_get_active(combo_box); + + if (combo->type == PURPLE_PREF_INT) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &combo->value.integer, -1); + } + else if (combo->type == PURPLE_PREF_STRING) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &combo->value.string, -1); + } + else if (combo->type == PURPLE_PREF_BOOLEAN) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &combo->value.boolean, -1); + } + + combo->cb(combo_box, combo); +} + +static void +pidgin_prefs_bind_dropdown_from_list(PidginPrefCombo *combo, GList *menuitems) +{ + gchar *text; + GtkListStore *store = NULL; + GtkTreeIter iter; + GtkTreeIter active; + + g_return_if_fail(menuitems != NULL); + + if (combo->type == PURPLE_PREF_INT) { + combo->value.integer = purple_prefs_get_int(combo->key); + } else if (combo->type == PURPLE_PREF_STRING) { + combo->value.string = purple_prefs_get_string(combo->key); + } else if (combo->type == PURPLE_PREF_BOOLEAN) { + combo->value.boolean = purple_prefs_get_bool(combo->key); + } else { + g_return_if_reached(); + } + + store = GTK_LIST_STORE( + gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo))); + + while (menuitems != NULL && (text = (char *)menuitems->data) != NULL) { + int int_value = 0; + const char *str_value = NULL; + gboolean bool_value = FALSE; + + menuitems = g_list_next(menuitems); + g_return_if_fail(menuitems != NULL); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_TEXT, text, + -1); + + if (combo->type == PURPLE_PREF_INT) { + int_value = GPOINTER_TO_INT(menuitems->data); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, int_value, + -1); + } + else if (combo->type == PURPLE_PREF_STRING) { + str_value = (const char *)menuitems->data; + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, str_value, + -1); + } + else if (combo->type == PURPLE_PREF_BOOLEAN) { + bool_value = (gboolean)GPOINTER_TO_INT(menuitems->data); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, bool_value, + -1); + } + + if ((combo->type == PURPLE_PREF_INT && + combo->value.integer == int_value) || + (combo->type == PURPLE_PREF_STRING && + purple_strequal(combo->value.string, str_value)) || + (combo->type == PURPLE_PREF_BOOLEAN && + (combo->value.boolean == bool_value))) { + + active = iter; + } + + menuitems = g_list_next(menuitems); + } + + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo->combo), &active); + combo->current_active = gtk_combo_box_get_active( + GTK_COMBO_BOX(combo->combo)); + combo->previously_active = combo->current_active; + + combo->cb = pidgin_prefs_bind_dropdown_from_list_cb; + g_signal_connect(G_OBJECT(combo->combo), "changed", + G_CALLBACK(bind_dropdown_set), combo); +} + +static void +pidgin_prefs_bind_dropdown(PidginPrefCombo *combo) +{ + GtkTreeModel *store = NULL; + GtkTreeIter iter; + GtkTreeIter active; + + if (combo->type == PURPLE_PREF_INT) { + combo->value.integer = purple_prefs_get_int(combo->key); + } else if (combo->type == PURPLE_PREF_STRING) { + combo->value.string = purple_prefs_get_string(combo->key); + } else if (combo->type == PURPLE_PREF_BOOLEAN) { + combo->value.boolean = purple_prefs_get_bool(combo->key); + } else { + g_return_if_reached(); + } + + store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)); + + if (!gtk_tree_model_get_iter_first(store, &iter)) { + g_return_if_reached(); + } + + do { + int int_value = 0; + const char *str_value = NULL; + gboolean bool_value = FALSE; + + if (combo->type == PURPLE_PREF_INT) { + gtk_tree_model_get(store, &iter, + PREF_DROPDOWN_VALUE, &int_value, + -1); + if (combo->value.integer == int_value) { + active = iter; + break; + } + } + else if (combo->type == PURPLE_PREF_STRING) { + gtk_tree_model_get(store, &iter, + PREF_DROPDOWN_VALUE, &str_value, + -1); + if (purple_strequal(combo->value.string, str_value)) { + active = iter; + break; + } + } + else if (combo->type == PURPLE_PREF_BOOLEAN) { + gtk_tree_model_get(store, &iter, + PREF_DROPDOWN_VALUE, &bool_value, + -1); + if (combo->value.boolean == bool_value) { + active = iter; + break; + } + } + } while (gtk_tree_model_iter_next(store, &iter)); + + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo->combo), &active); + + combo->current_active = gtk_combo_box_get_active( + GTK_COMBO_BOX(combo->combo)); + combo->previously_active = combo->current_active; + + combo->cb = pidgin_prefs_bind_dropdown_from_list_cb; + g_signal_connect(G_OBJECT(combo->combo), "changed", + G_CALLBACK(bind_dropdown_set), combo); +} + +static void +set_bool_pref(GtkWidget *w, const char *key) +{ + purple_prefs_set_bool(key, + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))); +} + +GtkWidget * +pidgin_prefs_checkbox(const char *text, const char *key, GtkWidget *page) +{ + GtkWidget *button; + + button = gtk_check_button_new_with_mnemonic(text); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), + purple_prefs_get_bool(key)); + + gtk_box_pack_start(GTK_BOX(page), button, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(set_bool_pref), (char *)key); + + gtk_widget_show(button); + + return button; +} + +static void +pidgin_prefs_bind_checkbox(const char *key, GtkWidget *button) +{ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), + purple_prefs_get_bool(key)); + g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(set_bool_pref), (char *)key); +} + +static void +delete_prefs(GtkWidget *asdf, void *gdsa) +{ + /* Close any request dialogs */ + purple_request_close_with_handle(prefs); + + purple_notify_close_with_handle(prefs); + + g_clear_object(&prefs->theme.session); + + /* Unregister callbacks. */ + purple_prefs_disconnect_by_handle(prefs); + + /* NULL-ify globals */ + prefs_status_themes_combo_box = NULL; + prefs_smiley_themes_combo_box = NULL; + + g_free(prefs->proxy.gnome_program_path); + prefs = NULL; +} + +static gchar * +get_theme_markup(const char *name, gboolean custom, const char *author, + const char *description) +{ + + return g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>", + name, custom ? " " : "", custom ? _("(Custom)") : "", + author != NULL ? " - " : "", author != NULL ? author : "", + description != NULL ? description : ""); +} + +static void +smileys_refresh_theme_list(void) +{ + GList *it; + GtkTreeIter iter; + gchar *description; + + description = get_theme_markup(_("none"), FALSE, _("Penguin Pimps"), + _("Selecting this disables graphical emoticons.")); + gtk_list_store_append(prefs_smiley_themes, &iter); + gtk_list_store_set(prefs_smiley_themes, &iter, + 0, NULL, 1, description, 2, "none", -1); + g_free(description); + + for (it = pidgin_smiley_theme_get_all(); it; it = g_list_next(it)) { + PidginSmileyTheme *theme = it->data; + + description = get_theme_markup( + _(pidgin_smiley_theme_get_name(theme)), FALSE, + _(pidgin_smiley_theme_get_author(theme)), + _(pidgin_smiley_theme_get_description(theme))); + + gtk_list_store_append(prefs_smiley_themes, &iter); + gtk_list_store_set(prefs_smiley_themes, &iter, + 0, pidgin_smiley_theme_get_icon(theme), + 1, description, + 2, pidgin_smiley_theme_get_name(theme), + -1); + + g_free(description); + } +} + +/* adds the themes to the theme list from the manager so they can be displayed in prefs */ +static void +prefs_themes_sort(PurpleTheme *theme) +{ + GdkPixbuf *pixbuf = NULL; + GtkTreeIter iter; + gchar *image_full = NULL, *markup; + const gchar *name, *author, *description; + + if (PIDGIN_IS_STATUS_ICON_THEME(theme)){ + GtkListStore *store; + + store = prefs_status_icon_themes; + + image_full = purple_theme_get_image_full(theme); + if (image_full != NULL){ + pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE); + g_free(image_full); + } else + pixbuf = NULL; + + name = purple_theme_get_name(theme); + author = purple_theme_get_author(theme); + description = purple_theme_get_description(theme); + + markup = get_theme_markup(name, FALSE, author, description); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, pixbuf, 1, markup, 2, name, -1); + + g_free(markup); + if (pixbuf != NULL) + g_object_unref(G_OBJECT(pixbuf)); + + } +} + +static void +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 (purple_strequal(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 *tmp; + GtkTreeIter iter; + + /* refresh the list of themes in the manager */ + purple_theme_manager_refresh(); + + tmp = g_build_filename(PURPLE_DATADIR, "icons", "hicolor", "32x32", + "apps", "im.pidgin.Pidgin3.png", NULL); + pixbuf = pidgin_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE); + g_free(tmp); + + /* status icon themes */ + gtk_list_store_clear(prefs_status_icon_themes); + gtk_list_store_append(prefs_status_icon_themes, &iter); + tmp = get_theme_markup(_("Default"), FALSE, _("Penguin Pimps"), + _("The default Pidgin status icon theme")); + gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1); + g_free(tmp); + if (pixbuf) + g_object_unref(G_OBJECT(pixbuf)); + + /* smiley themes */ + gtk_list_store_clear(prefs_smiley_themes); + + purple_theme_manager_for_each_theme(prefs_themes_sort); + smileys_refresh_theme_list(); + + /* set active */ + prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme")); + prefs_set_active_theme_combo(prefs_smiley_themes_combo_box, prefs_smiley_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/smileys/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_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); + + prefs_smiley_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); +} + +/* + * prefs_theme_find_theme: + * @path: A directory containing a theme. The theme could be at the + * top level of this directory or in any subdirectory thereof. + * @type: The type of theme to load. The loader for this theme type + * will be used and this loader will determine what constitutes a + * "theme." + * + * Attempt to load the given directory as a theme. If we are unable to + * open the path as a theme then we recurse into path and attempt to + * load each subdirectory that we encounter. + * + * Returns: A new reference to a #PurpleTheme. + */ +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; +} + +static void +free_theme_info(struct theme_info *info) +{ + if (info != NULL) { + g_free(info->type); + g_free(info->extension); + g_free(info->original_name); + g_free(info); + } +} + +/* installs a theme, info is freed by function */ +static void +theme_install_theme(char *path, struct theme_info *info) +{ + gchar *destdir; + const char *tail; + gboolean is_smiley_theme, is_archive; + PurpleTheme *theme = NULL; + + if (info == NULL) + return; + + /* check the extension */ + tail = info->extension ? info->extension : strrchr(path, '.'); + + if (!tail) { + free_theme_info(info); + return; + } + + is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz"); + + /* Just to be safe */ + g_strchomp(path); + + if ((is_smiley_theme = purple_strequal(info->type, "smiley"))) + destdir = g_build_filename(purple_data_dir(), "smileys", NULL); + else + destdir = g_build_filename(purple_data_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); + gchar *command; + + if (!g_file_test(destdir, G_FILE_TEST_IS_DIR)) { + g_mkdir_with_parents(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, NULL); + g_free(command); + g_free(destdir); + free_theme_info(info); + return; + } + g_free(command); +#else + if (!winpidgin_gz_untar(path, destdir)) { + purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL); + g_free(destdir); + free_theme_info(info); + return; + } +#endif + } + + if (is_smiley_theme) { + /* just extract the folder to the smiley directory */ + prefs_themes_refresh(); + + } else if (is_archive) { + theme = prefs_theme_find_theme(destdir, info->type); + + if (PURPLE_IS_THEME(theme)) { + /* create the location for the theme */ + gchar *theme_dest = g_build_filename(purple_data_dir(), "themes", + purple_theme_get_name(theme), + "purple", info->type, NULL); + + if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) { + g_mkdir_with_parents(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); + } + + g_free(theme_dest); + theme_dest = g_build_filename(purple_data_dir(), "themes", + purple_theme_get_name(theme), + "purple", info->type, NULL); + + /* move the entire directory to new location */ + if (g_rename(purple_theme_get_dir(theme), theme_dest)) { + purple_debug_error("gtkprefs", "Error renaming %s to %s: " + "%s\n", purple_theme_get_dir(theme), theme_dest, + g_strerror(errno)); + } + + g_free(theme_dest); + if (g_remove(destdir) != 0) { + purple_debug_error("gtkprefs", + "couldn't remove temp (dest) path\n"); + } + 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, 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_data_dir(), "themes", "temp", + "sub_folder", NULL); + + if (info->original_name != NULL) { + /* name was changed from the original (probably a dnd) change it back before loading */ + temp_file = g_build_filename(temp_path, info->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)) { + g_mkdir_with_parents(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, info->type); + + if (PURPLE_IS_THEME(theme)) { + gchar *theme_dest = + g_build_filename(purple_data_dir(), "themes", + purple_theme_get_name(theme), "purple", + info->type, NULL); + + if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) { + g_mkdir_with_parents(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); + } + + if (g_rename(purple_theme_get_dir(theme), theme_dest)) { + purple_debug_error("gtkprefs", "Error renaming %s to %s: " + "%s\n", purple_theme_get_dir(theme), theme_dest, + g_strerror(errno)); + } + + g_free(theme_dest); + g_object_unref(theme); + + prefs_themes_refresh(); + } else { + if (g_remove(temp_path) != 0) { + purple_debug_error("gtkprefs", + "couldn't remove temp path"); + } + purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL, NULL); + } + } else { + purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL, NULL); + } + + g_free(temp_file); + g_free(temp_path); + } + + g_free(destdir); + free_theme_info(info); +} + +static void +theme_got_url(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, + gpointer _info) +{ + struct theme_info *info = _info; + FILE *f; + gchar *path; + size_t wc; + + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + free_theme_info(info); + return; + } + + f = purple_mkstemp(&path, TRUE); + wc = fwrite(msg->response_body->data, msg->response_body->length, 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); + free_theme_info(info); + return; + } + fclose(f); + + theme_install_theme(path, info); + + 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 *)gtk_selection_data_get_data(sd)); + + if ((gtk_selection_data_get_length(sd) >= 0) + && (gtk_selection_data_get_format(sd) == 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_error("theme dnd", "%s", + converr ? converr->message : + "g_filename_from_uri error"); + free_theme_info(info); + return; + } + theme_install_theme(tmp, info); + g_free(tmp); + } else if (!g_ascii_strncasecmp(name, "http://", 7) || + !g_ascii_strncasecmp(name, "https://", 8)) { + /* Oo, a web drag and drop. This is where things + * will start to get interesting */ + SoupMessage *msg; + + if (prefs->theme.session == NULL) { + prefs->theme.session = soup_session_new(); + } + + soup_session_abort(prefs->theme.session); + + msg = soup_message_new("GET", name); + // purple_http_request_set_max_len(msg, PREFS_MAX_DOWNLOADED_THEME_SIZE); + soup_session_queue_message(prefs->theme.session, msg, theme_got_url, + info); + } else + free_theme_info(info); + + 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 void +prefs_build_theme_combo_box(GtkWidget *combo_box, GtkListStore *store, + const char *current_theme, const char *type) +{ + GtkTargetEntry te[3] = { + {"text/plain", 0, 0}, + {"text/uri-list", 0, 1}, + {"STRING", 0, 2} + }; + + g_return_if_fail(store != NULL && current_theme != NULL); + + gtk_combo_box_set_model(GTK_COMBO_BOX(combo_box), + GTK_TREE_MODEL(store)); + + 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), (gpointer) type); +} + +/* sets the current smiley theme */ +static void +prefs_set_smiley_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + gchar *new_theme; + GtkTreeIter new_iter; + + if (gtk_combo_box_get_active_iter(combo_box, &new_iter)) { + + gtk_tree_model_get(GTK_TREE_MODEL(prefs_smiley_themes), &new_iter, 2, &new_theme, -1); + + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme); + + g_free(new_theme); + } +} + + +/* Does same as normal sort, except "none" is sorted first */ +static gint pidgin_sort_smileys (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer userdata) +{ + gint ret = 0; + gchar *name1 = NULL, *name2 = NULL; + + gtk_tree_model_get(model, a, 2, &name1, -1); + gtk_tree_model_get(model, b, 2, &name2, -1); + + if (name1 == NULL || name2 == NULL) { + if (!(name1 == NULL && name2 == NULL)) + ret = (name1 == NULL) ? -1: 1; + } else if (!g_ascii_strcasecmp(name1, "none")) { + if (!g_utf8_collate(name1, name2)) + ret = 0; + else + /* Sort name1 first */ + ret = -1; + } else if (!g_ascii_strcasecmp(name2, "none")) { + /* Sort name2 first */ + ret = 1; + } else { + /* Neither string is "none", default to normal sort */ + ret = purple_utf8_strcasecmp(name1, name2); + } + + g_free(name1); + g_free(name2); + + return ret; +} + +/* sets the current icon theme */ +static void +prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data) +{ + PidginStatusIconTheme *theme = NULL; + GtkTreeIter iter; + gchar *name = NULL; + + 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 || *name) + theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon")); + + g_free(name); + + pidgin_stock_load_status_icon_theme(theme); + pidgin_blist_refresh(purple_blist_get_default()); + } +} + +static void +bind_theme_page(PidginPrefsWindow *win) +{ + /* Status Icon Themes */ + prefs_build_theme_combo_box(win->theme.status, prefs_status_icon_themes, + PIDGIN_PREFS_ROOT "/status/icon-theme", + "icon"); + prefs_status_themes_combo_box = win->theme.status; + + /* Smiley Themes */ + prefs_build_theme_combo_box(win->theme.smiley, prefs_smiley_themes, + PIDGIN_PREFS_ROOT "/smileys/theme", + "smiley"); + prefs_smiley_themes_combo_box = win->theme.smiley; + + /* Custom sort so "none" theme is at top of list */ + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(prefs_smiley_themes), + 2, pidgin_sort_smileys, NULL, NULL); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(prefs_smiley_themes), + 2, GTK_SORT_ASCENDING); +} + +static void +formatting_toggle_cb(TalkatuActionGroup *ag, GAction *action, const gchar *name, gpointer data) +{ + gboolean activated = talkatu_action_group_get_action_activated(ag, name); + if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_BOLD, name) != 0) { + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", + activated); + } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_ITALIC, name) != 0) { + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", + activated); + } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_UNDERLINE, name) != 0) { + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", + activated); + } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_STRIKETHROUGH, name) != 0) { + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike", + activated); + } +} + +static void +bind_interface_page(PidginPrefsWindow *win) +{ + /* System Tray */ + win->iface.im.hide_new.type = PURPLE_PREF_STRING; + win->iface.im.hide_new.key = PIDGIN_PREFS_ROOT "/conversations/im/hide_new"; + pidgin_prefs_bind_dropdown(&win->iface.im.hide_new); + +#ifdef _WIN32 + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", + win->iface.win32.minimize_new_convs); +#else + gtk_widget_hide(win->iface.win32.minimize_new_convs); +#endif + + /* All the tab options! */ + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/tabs", + win->iface.conversations.tabs); + + /* + * Connect a signal to the above preference. When conversations are not + * shown in a tabbed window then all tabbing options should be disabled. + */ + g_object_bind_property(win->iface.conversations.tabs, "active", + win->iface.conversations.tabs_vbox, "sensitive", + G_BINDING_SYNC_CREATE); + + pidgin_prefs_bind_checkbox( + PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", + win->iface.conversations.close_on_tabs); + + win->iface.conversations.tab_side.type = PURPLE_PREF_INT; + win->iface.conversations.tab_side.key = PIDGIN_PREFS_ROOT "/conversations/tab_side"; + pidgin_prefs_bind_dropdown(&win->iface.conversations.tab_side); +} + +/* This is also Win32-specific, but must be visible for Glade binding. */ +static void +apply_custom_font(GtkWidget *unused, PidginPrefsWindow *win) +{ + PangoFontDescription *desc = NULL; + if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font")) { + const char *font = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font"); + desc = pango_font_description_from_string(font); + } + + gtk_widget_override_font(win->conversations.format_view, desc); + if (desc) + pango_font_description_free(desc); + +} + +static void +pidgin_custom_font_set(GtkWidget *font_button, PidginPrefsWindow *win) +{ + + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/custom_font", + gtk_font_chooser_get_font(GTK_FONT_CHOOSER(font_button))); + + apply_custom_font(font_button, win); +} + +static void +bind_conv_page(PidginPrefsWindow *win) +{ + GSimpleActionGroup *ag = NULL; + + win->conversations.notification_chat.type = PURPLE_PREF_INT; + win->conversations.notification_chat.key = PIDGIN_PREFS_ROOT "/conversations/notification_chat"; + pidgin_prefs_bind_dropdown(&win->conversations.notification_chat); + + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", + win->conversations.show_incoming_formatting); + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", + win->conversations.im.close_immediately); + + pidgin_prefs_bind_checkbox("/purple/conversations/im/send_typing", + win->conversations.im.send_typing); + + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", + win->conversations.use_smooth_scrolling); + +#ifdef _WIN32 + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/blink_im", + win->conversations.win32.blink_im); +#else + gtk_widget_hide(win->conversations.win32.blink_im); +#endif + +#if 0 + /* TODO: it's not implemented */ + pidgin_prefs_bind_checkbox( + PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys", + win->conversations.resize_custom_smileys); + + pidgin_prefs_bind_spin_button( + PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size", + win->conversations.custom_smileys_size); + + g_object_bind_property(win->conversations.resize_custom_smileys, "active", + win->conversations.custom_smileys_size, "sensitive", + G_BINDING_SYNC_CREATE); +#endif + + pidgin_prefs_bind_spin_button( + PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines", + win->conversations.minimum_entry_lines); + +#ifdef _WIN32 + { + const char *font_name; + gtk_widget_show(win->conversations.font_frame); + + pidgin_prefs_bind_checkbox( + PIDGIN_PREFS_ROOT "/conversations/use_theme_font", + win->conversations.use_theme_font); + + font_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font"); + if (font_name != NULL && *font_name != '\0') { + gtk_font_chooser_set_font( + GTK_FONT_CHOOSER(win->conversations.custom_font), + font_name); + } + + g_object_bind_property(win->conversations.use_theme_font, "active", + win->conversations.custom_font_hbox, "sensitive", + G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN); + } +#endif + + ag = talkatu_buffer_get_action_group(TALKATU_BUFFER(win->conversations.format_buffer)); + g_signal_connect_after(G_OBJECT(ag), "action-activated", + G_CALLBACK(formatting_toggle_cb), NULL); +} + +static void +network_ip_changed(GtkEntry *entry, gpointer data) +{ + const gchar *text = gtk_entry_get_text(entry); + GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(entry)); + + if (text && *text) { + if (g_hostname_is_ip_address(text)) { + purple_network_set_public_ip(text); + gtk_style_context_add_class(context, "good-ip"); + gtk_style_context_remove_class(context, "bad-ip"); + } else { + gtk_style_context_add_class(context, "bad-ip"); + gtk_style_context_remove_class(context, "good-ip"); + } + + } else { + purple_network_set_public_ip(""); + gtk_style_context_remove_class(context, "bad-ip"); + gtk_style_context_remove_class(context, "good-ip"); + } +} + +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) +{ + 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; +} + +static void +proxy_changed_cb(const char *name, PurplePrefType type, + gconstpointer value, gpointer data) +{ + PidginPrefsWindow *win = data; + const char *proxy = value; + + if (!purple_strequal(proxy, "none") && !purple_strequal(proxy, "envvar")) + gtk_widget_show_all(win->proxy.options); + else + gtk_widget_hide(win->proxy.options); +} + +static void +proxy_print_option(GtkWidget *entry, PidginPrefsWindow *win) +{ + if (entry == win->proxy.host) { + purple_prefs_set_string("/purple/proxy/host", + gtk_entry_get_text(GTK_ENTRY(entry))); + } else if (entry == win->proxy.port) { + purple_prefs_set_int("/purple/proxy/port", + gtk_spin_button_get_value_as_int( + GTK_SPIN_BUTTON(entry))); + } else if (entry == win->proxy.username) { + purple_prefs_set_string("/purple/proxy/username", + gtk_entry_get_text(GTK_ENTRY(entry))); + } else if (entry == win->proxy.password) { + purple_prefs_set_string("/purple/proxy/password", + gtk_entry_get_text(GTK_ENTRY(entry))); + } +} + +static void +proxy_button_clicked_cb(GtkWidget *button, PidginPrefsWindow *win) +{ + GError *err = NULL; + + if (g_spawn_command_line_async(win->proxy.gnome_program_path, &err)) + return; + + purple_notify_error(NULL, NULL, _("Cannot start proxy configuration program."), err->message, NULL); + g_error_free(err); +} + +static void +auto_ip_button_clicked_cb(GtkWidget *button, gpointer null) +{ + const char *ip; + PurpleStunNatDiscovery *stun; + char *auto_ip_text; + GList *list = NULL; + + /* Make a lookup for the auto-detected IP ourselves. */ + if (purple_prefs_get_bool("/purple/network/auto_ip")) { + /* 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 first IP of the local system */ + list = nice_interfaces_get_local_ips(FALSE); + if (list) { + ip = list->data; + } else { + ip = "0.0.0.0"; + } + } + } + } + } else{ + ip = _("Disabled"); + } + + auto_ip_text = g_strdup_printf(_("Use _automatically detected IP address: %s"), ip); + gtk_button_set_label(GTK_BUTTON(button), auto_ip_text); + g_free(auto_ip_text); + g_list_free_full(list, g_free); +} + +static void +bind_network_page(PidginPrefsWindow *win) +{ + GtkStyleContext *context; + GtkCssProvider *ip_css; + const gchar *res = "/im/pidgin/Pidgin/Prefs/ip.css"; + + gtk_entry_set_text(GTK_ENTRY(win->network.stun_server), + purple_prefs_get_string("/purple/network/stun_server")); + + pidgin_prefs_bind_checkbox("/purple/network/auto_ip", + win->network.auto_ip); + auto_ip_button_clicked_cb(win->network.auto_ip, NULL); /* Update label */ + + gtk_entry_set_text(GTK_ENTRY(win->network.public_ip), + purple_network_get_public_ip()); + + ip_css = gtk_css_provider_new(); + gtk_css_provider_load_from_resource(ip_css, res); + + context = gtk_widget_get_style_context(win->network.public_ip); + gtk_style_context_add_provider(context, + GTK_STYLE_PROVIDER(ip_css), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + g_object_bind_property(win->network.auto_ip, "active", + win->network.public_ip_hbox, "sensitive", + G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN); + + pidgin_prefs_bind_checkbox("/purple/network/map_ports", + win->network.map_ports); + + pidgin_prefs_bind_checkbox("/purple/network/ports_range_use", + win->network.ports_range_use); + g_object_bind_property(win->network.ports_range_use, "active", + win->network.ports_range_hbox, "sensitive", + G_BINDING_SYNC_CREATE); + + pidgin_prefs_bind_spin_button("/purple/network/ports_range_start", + win->network.ports_range_start); + pidgin_prefs_bind_spin_button("/purple/network/ports_range_end", + win->network.ports_range_end); + + /* TURN server */ + gtk_entry_set_text(GTK_ENTRY(win->network.turn_server), + purple_prefs_get_string("/purple/network/turn_server")); + + pidgin_prefs_bind_spin_button("/purple/network/turn_port", + win->network.turn_port_udp); + + pidgin_prefs_bind_spin_button("/purple/network/turn_port_tcp", + win->network.turn_port_tcp); + + pidgin_prefs_bind_entry("/purple/network/turn_username", + win->network.turn_username); + pidgin_prefs_bind_entry("/purple/network/turn_password", + win->network.turn_password); +} + +static void +bind_proxy_page(PidginPrefsWindow *win) +{ + PurpleProxyInfo *proxy_info; + + if(purple_running_gnome()) { + gchar *path = NULL; + + gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack), + "gnome"); + + path = g_find_program_in_path("gnome-network-properties"); + if (path == NULL) + path = g_find_program_in_path("gnome-network-preferences"); + if (path == NULL) { + path = g_find_program_in_path("gnome-control-center"); + if (path != NULL) { + char *tmp = g_strdup_printf("%s network", path); + g_free(path); + path = tmp; + } + } + + win->proxy.gnome_program_path = path; + gtk_widget_set_visible(win->proxy.gnome_not_found, + path == NULL); + gtk_widget_set_visible(win->proxy.gnome_program, + path != NULL); + } else { + gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack), + "nongnome"); + + /* This is a global option that affects SOCKS4 usage even with + * account-specific proxy settings */ + pidgin_prefs_bind_checkbox("/purple/proxy/socks4_remotedns", + win->proxy.socks4_remotedns); + + win->proxy.type.type = PURPLE_PREF_STRING; + win->proxy.type.key = "/purple/proxy/type"; + pidgin_prefs_bind_dropdown(&win->proxy.type); + proxy_info = purple_global_proxy_get_info(); + + purple_prefs_connect_callback(prefs, "/purple/proxy/type", + proxy_changed_cb, win); + + if (proxy_info != NULL) { + if (purple_proxy_info_get_host(proxy_info)) { + gtk_entry_set_text(GTK_ENTRY(win->proxy.host), + purple_proxy_info_get_host(proxy_info)); + } + + if (purple_proxy_info_get_port(proxy_info) != 0) { + gtk_spin_button_set_value( + GTK_SPIN_BUTTON(win->proxy.port), + purple_proxy_info_get_port(proxy_info)); + } + + if (purple_proxy_info_get_username(proxy_info) != NULL) { + gtk_entry_set_text(GTK_ENTRY(win->proxy.username), + purple_proxy_info_get_username(proxy_info)); + } + + if (purple_proxy_info_get_password(proxy_info) != NULL) { + gtk_entry_set_text(GTK_ENTRY(win->proxy.password), + purple_proxy_info_get_password(proxy_info)); + } + } + + proxy_changed_cb("/purple/proxy/type", PURPLE_PREF_STRING, + purple_prefs_get_string("/purple/proxy/type"), + win); + } +} + +static void +bind_logging_page(PidginPrefsWindow *win) +{ + GList *names; + + win->logging.format.type = PURPLE_PREF_STRING; + win->logging.format.key = "/purple/logging/format"; + names = purple_log_logger_get_options(); + pidgin_prefs_bind_dropdown_from_list(&win->logging.format, names); + g_list_free(names); + + pidgin_prefs_bind_checkbox("/purple/logging/log_ims", + win->logging.log_ims); + pidgin_prefs_bind_checkbox("/purple/logging/log_chats", + win->logging.log_chats); + pidgin_prefs_bind_checkbox("/purple/logging/log_system", + win->logging.log_system); +} + +static void +set_idle_away(PurpleSavedStatus *status) +{ + purple_prefs_set_int("/purple/savedstatus/idleaway", purple_savedstatus_get_creation_time(status)); +} + +static void +set_startupstatus(PurpleSavedStatus *status) +{ + purple_prefs_set_int("/purple/savedstatus/startup", purple_savedstatus_get_creation_time(status)); +} + +static void +bind_away_page(PidginPrefsWindow *win) +{ + GtkWidget *menu; + + /* Idle stuff */ + win->away.idle_reporting.type = PURPLE_PREF_STRING; + win->away.idle_reporting.key = "/purple/away/idle_reporting"; + pidgin_prefs_bind_dropdown(&win->away.idle_reporting); + + pidgin_prefs_bind_spin_button("/purple/away/mins_before_away", + win->away.mins_before_away); + + pidgin_prefs_bind_checkbox("/purple/away/away_when_idle", + win->away.away_when_idle); + + /* TODO: Show something useful if we don't have any saved statuses. */ + menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away)); + gtk_widget_show_all(menu); + gtk_box_pack_start(GTK_BOX(win->away.idle_hbox), menu, FALSE, FALSE, 0); + + g_object_bind_property(win->away.away_when_idle, "active", + menu, "sensitive", + G_BINDING_SYNC_CREATE); + + /* Away stuff */ + win->away.auto_reply.type = PURPLE_PREF_STRING; + win->away.auto_reply.key = "/purple/away/auto_reply"; + pidgin_prefs_bind_dropdown(&win->away.auto_reply); + + /* Signon status stuff */ + pidgin_prefs_bind_checkbox("/purple/savedstatus/startup_current_status", + win->away.startup_current_status); + + /* TODO: Show something useful if we don't have any saved statuses. */ + menu = pidgin_status_menu(purple_savedstatus_get_startup(), G_CALLBACK(set_startupstatus)); + gtk_widget_show_all(menu); + gtk_box_pack_start(GTK_BOX(win->away.startup_hbox), menu, FALSE, FALSE, 0); + gtk_label_set_mnemonic_widget(GTK_LABEL(win->away.startup_label), menu); + pidgin_set_accessible_label(menu, GTK_LABEL(win->away.startup_label)); + g_object_bind_property(win->away.startup_current_status, "active", + win->away.startup_hbox, "sensitive", + G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN); +} + +#ifdef USE_VV +static GList * +get_vv_device_menuitems(PurpleMediaElementType type) +{ + GList *result = NULL; + GList *i; + + i = purple_media_manager_enumerate_elements(purple_media_manager_get(), + type); + for (; i; i = g_list_delete_link(i, i)) { + PurpleMediaElementInfo *info = i->data; + + result = g_list_append(result, + purple_media_element_info_get_name(info)); + result = g_list_append(result, + purple_media_element_info_get_id(info)); + g_object_unref(info); + } + + return result; +} + +static GstElement * +create_test_element(PurpleMediaElementType type) +{ + PurpleMediaElementInfo *element_info; + + element_info = purple_media_manager_get_active_element(purple_media_manager_get(), type); + + g_return_val_if_fail(element_info, NULL); + + return purple_media_element_info_call_create(element_info, + NULL, NULL, NULL); +} + +static void +vv_test_switch_page_cb(GtkStack *stack, G_GNUC_UNUSED GParamSpec *pspec, + gpointer data) +{ + PidginPrefsWindow *win = data; + + if (!g_str_equal(gtk_stack_get_visible_child_name(stack), "vv")) { + /* Disable any running test pipelines. */ + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(win->vv.voice.test), FALSE); + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(win->vv.video.test), FALSE); + } +} + +static GstElement * +create_voice_pipeline(void) +{ + GstElement *pipeline; + GstElement *src, *sink; + GstElement *volume; + GstElement *level; + GstElement *valve; + + pipeline = gst_pipeline_new("voicetest"); + + src = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC); + sink = create_test_element(PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK); + volume = gst_element_factory_make("volume", "volume"); + level = gst_element_factory_make("level", "level"); + valve = gst_element_factory_make("valve", "valve"); + + gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL); + gst_element_link_many(src, volume, level, valve, sink, NULL); + + purple_debug_info("gtkprefs", "create_voice_pipeline: setting pipeline " + "state to GST_STATE_PLAYING - it may hang here on win32\n"); + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); + purple_debug_info("gtkprefs", "create_voice_pipeline: state is set\n"); + + return pipeline; +} + +static void +on_volume_change_cb(GtkWidget *w, gdouble value, gpointer data) +{ + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + GstElement *volume; + + if (!win->vv.voice.pipeline) { + return; + } + + volume = gst_bin_get_by_name(GST_BIN(win->vv.voice.pipeline), "volume"); + g_object_set(volume, "volume", + gtk_scale_button_get_value(GTK_SCALE_BUTTON(w)) / 100.0, NULL); +} + +static gdouble +gst_msg_db_to_percent(GstMessage *msg, gchar *value_name) +{ + const GValue *list; + const GValue *value; + gdouble value_db; + gdouble percent; + + list = gst_structure_get_value(gst_message_get_structure(msg), value_name); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + value = g_value_array_get_nth(g_value_get_boxed(list), 0); +G_GNUC_END_IGNORE_DEPRECATIONS + value_db = g_value_get_double(value); + percent = pow(10, value_db / 20); + return (percent > 1.0) ? 1.0 : percent; +} + +static gboolean +gst_bus_cb(GstBus *bus, GstMessage *msg, gpointer data) +{ + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT && + gst_structure_has_name(gst_message_get_structure(msg), "level")) { + + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + gchar *name = gst_element_get_name(src); + + if (purple_strequal(name, "level")) { + gdouble percent; + gdouble threshold; + GstElement *valve; + + percent = gst_msg_db_to_percent(msg, "rms"); + gtk_progress_bar_set_fraction( + GTK_PROGRESS_BAR(win->vv.voice.level), percent); + + percent = gst_msg_db_to_percent(msg, "decay"); + threshold = gtk_range_get_value(GTK_RANGE( + win->vv.voice.threshold)) / + 100.0; + valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve"); + g_object_set(valve, "drop", (percent < threshold), NULL); + g_object_set(win->vv.voice.level, "text", + (percent < threshold) ? _("DROP") : " ", + NULL); + } + + g_free(name); + } + + return TRUE; +} + +static void +voice_test_destroy_cb(GtkWidget *w, gpointer data) +{ + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + + if (!win->vv.voice.pipeline) { + return; + } + + gst_element_set_state(win->vv.voice.pipeline, GST_STATE_NULL); + g_clear_pointer(&win->vv.voice.pipeline, gst_object_unref); +} + +static void +enable_voice_test(PidginPrefsWindow *win) +{ + GstBus *bus; + + win->vv.voice.pipeline = create_voice_pipeline(); + bus = gst_pipeline_get_bus(GST_PIPELINE(win->vv.voice.pipeline)); + gst_bus_add_signal_watch(bus); + g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), win); + gst_object_unref(bus); +} + +static void +toggle_voice_test_cb(GtkToggleButton *test, gpointer data) +{ + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + + if (gtk_toggle_button_get_active(test)) { + gtk_widget_set_sensitive(win->vv.voice.level, TRUE); + enable_voice_test(win); + + g_signal_connect(win->vv.voice.volume, "value-changed", + G_CALLBACK(on_volume_change_cb), win); + g_signal_connect(test, "destroy", + G_CALLBACK(voice_test_destroy_cb), win); + } else { + gtk_progress_bar_set_fraction( + GTK_PROGRESS_BAR(win->vv.voice.level), 0.0); + gtk_widget_set_sensitive(win->vv.voice.level, FALSE); + g_object_disconnect(win->vv.voice.volume, + "any-signal::value-changed", + G_CALLBACK(on_volume_change_cb), win, NULL); + g_object_disconnect(test, "any-signal::destroy", + G_CALLBACK(voice_test_destroy_cb), win, + NULL); + voice_test_destroy_cb(NULL, win); + } +} + +static void +volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data) +{ + purple_prefs_set_int("/purple/media/audio/volume/input", value * 100); +} + +static void +threshold_value_changed_cb(GtkScale *scale, GtkWidget *label) +{ + int value; + char *tmp; + + value = (int)gtk_range_get_value(GTK_RANGE(scale)); + tmp = g_strdup_printf(_("Silence threshold: %d%%"), value); + gtk_label_set_label(GTK_LABEL(label), tmp); + g_free(tmp); + + purple_prefs_set_int("/purple/media/audio/silence_threshold", value); +} + +static void +bind_voice_test(PidginPrefsWindow *win, GtkBuilder *builder) +{ + GObject *test; + GObject *label; + GObject *volume; + GObject *threshold; + char *tmp; + + volume = gtk_builder_get_object(builder, "vv.voice.volume"); + win->vv.voice.volume = GTK_WIDGET(volume); + gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), + purple_prefs_get_int("/purple/media/audio/volume/input") / 100.0); + g_signal_connect(volume, "value-changed", + G_CALLBACK(volume_changed_cb), NULL); + + label = gtk_builder_get_object(builder, "vv.voice.threshold_label"); + tmp = g_strdup_printf(_("Silence threshold: %d%%"), + purple_prefs_get_int("/purple/media/audio/silence_threshold")); + gtk_label_set_text(GTK_LABEL(label), tmp); + g_free(tmp); + + threshold = gtk_builder_get_object(builder, "vv.voice.threshold"); + win->vv.voice.threshold = GTK_WIDGET(threshold); + gtk_range_set_value(GTK_RANGE(threshold), + purple_prefs_get_int("/purple/media/audio/silence_threshold")); + g_signal_connect(threshold, "value-changed", + G_CALLBACK(threshold_value_changed_cb), label); + + win->vv.voice.level = + GTK_WIDGET(gtk_builder_get_object(builder, "vv.voice.level")); + + test = gtk_builder_get_object(builder, "vv.voice.test"); + g_signal_connect(test, "toggled", + G_CALLBACK(toggle_voice_test_cb), win); + win->vv.voice.test = GTK_WIDGET(test); +} + +static GstElement * +create_video_pipeline(void) +{ + GstElement *pipeline; + GstElement *src, *sink; + GstElement *videoconvert; + GstElement *videoscale; + + pipeline = gst_pipeline_new("videotest"); + src = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC); + sink = create_test_element(PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK); + videoconvert = gst_element_factory_make("videoconvert", NULL); + videoscale = gst_element_factory_make("videoscale", NULL); + + g_object_set_data(G_OBJECT(pipeline), "sink", sink); + + gst_bin_add_many(GST_BIN(pipeline), src, videoconvert, videoscale, sink, + NULL); + gst_element_link_many(src, videoconvert, videoscale, sink, NULL); + + return pipeline; +} + +static void +video_test_destroy_cb(GtkWidget *w, gpointer data) +{ + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + + if (!win->vv.video.pipeline) { + return; + } + + gst_element_set_state(win->vv.video.pipeline, GST_STATE_NULL); + g_clear_pointer(&win->vv.video.pipeline, gst_object_unref); +} + +static void +enable_video_test(PidginPrefsWindow *win) +{ + GtkWidget *video = NULL; + GstElement *sink = NULL; + + win->vv.video.pipeline = create_video_pipeline(); + + sink = g_object_get_data(G_OBJECT(win->vv.video.pipeline), "sink"); + g_object_get(sink, "widget", &video, NULL); + gtk_widget_show(video); + + g_clear_pointer(&win->vv.video.sink_widget, gtk_widget_destroy); + gtk_container_add(GTK_CONTAINER(win->vv.video.frame), video); + win->vv.video.sink_widget = video; + + gst_element_set_state(GST_ELEMENT(win->vv.video.pipeline), + GST_STATE_PLAYING); +} + +static void +toggle_video_test_cb(GtkToggleButton *test, gpointer data) +{ + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + + if (gtk_toggle_button_get_active(test)) { + enable_video_test(win); + g_signal_connect(test, "destroy", + G_CALLBACK(video_test_destroy_cb), win); + } else { + g_object_disconnect(test, "any-signal::destroy", + G_CALLBACK(video_test_destroy_cb), win, + NULL); + video_test_destroy_cb(NULL, win); + } +} + +static void +bind_video_test(PidginPrefsWindow *win, GtkBuilder *builder) +{ + GObject *test; + + win->vv.video.frame = GTK_WIDGET( + gtk_builder_get_object(builder, "vv.video.frame")); + test = gtk_builder_get_object(builder, "vv.video.test"); + g_signal_connect(test, "toggled", + G_CALLBACK(toggle_video_test_cb), win); + win->vv.video.test = GTK_WIDGET(test); +} + +static void +vv_device_changed_cb(const gchar *name, PurplePrefType type, + gconstpointer value, gpointer data) +{ + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + + PurpleMediaManager *manager; + PurpleMediaElementInfo *info; + + manager = purple_media_manager_get(); + info = purple_media_manager_get_element_info(manager, value); + purple_media_manager_set_active_element(manager, info); + + /* Refresh test viewers */ + if (strstr(name, "audio") && win->vv.voice.pipeline) { + voice_test_destroy_cb(NULL, win); + enable_voice_test(win); + } else if (strstr(name, "video") && win->vv.video.pipeline) { + video_test_destroy_cb(NULL, win); + enable_video_test(win); + } +} + +static const char * +purple_media_type_to_preference_key(PurpleMediaElementType type) +{ + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) { + if (type & PURPLE_MEDIA_ELEMENT_SRC) { + return PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device"; + } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { + return PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device"; + } + } else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) { + if (type & PURPLE_MEDIA_ELEMENT_SRC) { + return PIDGIN_PREFS_ROOT "/vvconfig/video/src/device"; + } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { + return PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device"; + } + } + + return NULL; +} + +static void +bind_vv_dropdown(PidginPrefCombo *combo, PurpleMediaElementType element_type) +{ + const gchar *preference_key; + GList *devices; + + preference_key = purple_media_type_to_preference_key(element_type); + devices = get_vv_device_menuitems(element_type); + + if (g_list_find_custom(devices, purple_prefs_get_string(preference_key), + (GCompareFunc)strcmp) == NULL) + { + GList *next = g_list_next(devices); + if (next) + purple_prefs_set_string(preference_key, next->data); + } + + combo->type = PURPLE_PREF_STRING; + combo->key = preference_key; + pidgin_prefs_bind_dropdown_from_list(combo, devices); + g_list_free_full(devices, g_free); +} + +static void +bind_vv_frame(PidginPrefsWindow *win, PidginPrefCombo *combo, + PurpleMediaElementType type) +{ + bind_vv_dropdown(combo, type); + + purple_prefs_connect_callback(combo->combo, + purple_media_type_to_preference_key(type), + vv_device_changed_cb, win); + g_signal_connect_swapped(combo->combo, "destroy", + G_CALLBACK(purple_prefs_disconnect_by_handle), + combo->combo); + + g_object_set_data(G_OBJECT(combo->combo), "vv_media_type", + (gpointer)type); + g_object_set_data(G_OBJECT(combo->combo), "vv_combo", combo); +} + +static void +device_list_changed_cb(PurpleMediaManager *manager, GtkWidget *widget) +{ + PidginPrefCombo *combo; + PurpleMediaElementType media_type; + GtkTreeModel *model; + + combo = g_object_get_data(G_OBJECT(widget), "vv_combo"); + media_type = (PurpleMediaElementType)g_object_get_data(G_OBJECT(widget), + "vv_media_type"); + + /* Unbind original connections so we can repopulate the combo box. */ + g_object_disconnect(combo->combo, "any-signal::changed", + G_CALLBACK(bind_dropdown_set), combo, NULL); + model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)); + gtk_list_store_clear(GTK_LIST_STORE(model)); + + bind_vv_dropdown(combo, media_type); +} + +static GtkWidget * +vv_page(PidginPrefsWindow *win) +{ + GtkBuilder *builder; + GtkWidget *ret; + PurpleMediaManager *manager; + + builder = gtk_builder_new_from_resource("/im/pidgin/Pidgin/Prefs/vv.ui"); + gtk_builder_set_translation_domain(builder, PACKAGE); + + ret = GTK_WIDGET(gtk_builder_get_object(builder, "vv.page")); + + manager = purple_media_manager_get(); + + win->vv.voice.input.combo = GTK_WIDGET( + gtk_builder_get_object(builder, "vv.voice.input.combo")); + bind_vv_frame(win, &win->vv.voice.input, + PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC); + g_signal_connect_object(manager, "elements-changed::audiosrc", + G_CALLBACK(device_list_changed_cb), + win->vv.voice.input.combo, 0); + + win->vv.voice.output.combo = GTK_WIDGET( + gtk_builder_get_object(builder, "vv.voice.output.combo")); + bind_vv_frame(win, &win->vv.voice.output, + PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK); + g_signal_connect_object(manager, "elements-changed::audiosink", + G_CALLBACK(device_list_changed_cb), + win->vv.voice.output.combo, 0); + + bind_voice_test(win, builder); + + win->vv.video.input.combo = GTK_WIDGET( + gtk_builder_get_object(builder, "vv.video.input.combo")); + bind_vv_frame(win, &win->vv.video.input, + PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC); + g_signal_connect_object(manager, "elements-changed::videosrc", + G_CALLBACK(device_list_changed_cb), + win->vv.video.input.combo, 0); + + win->vv.video.output.combo = GTK_WIDGET( + gtk_builder_get_object(builder, "vv.video.output.combo")); + bind_vv_frame(win, &win->vv.video.output, + PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK); + g_signal_connect_object(manager, "elements-changed::videosink", + G_CALLBACK(device_list_changed_cb), + win->vv.video.output.combo, 0); + + bind_video_test(win, builder); + + g_signal_connect(win->stack, "notify::visible-child", + G_CALLBACK(vv_test_switch_page_cb), win); + + g_object_ref(ret); + g_object_unref(builder); + + return ret; +} +#endif + +static void +prefs_stack_init(PidginPrefsWindow *win) +{ +#ifdef USE_VV + GtkStack *stack = GTK_STACK(win->stack); + GtkWidget *vv; +#endif + + bind_interface_page(win); + bind_conv_page(win); + bind_logging_page(win); + bind_network_page(win); + bind_proxy_page(win); + bind_away_page(win); + bind_theme_page(win); +#ifdef USE_VV + vv = vv_page(win); + gtk_container_add_with_properties(GTK_CONTAINER(stack), vv, "name", + "vv", "title", _("Voice/Video"), + NULL); + g_object_unref(vv); +#endif +} + +static void +pidgin_prefs_window_class_init(PidginPrefsWindowClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + gtk_widget_class_set_template_from_resource( + widget_class, + "/im/pidgin/Pidgin/Prefs/prefs.ui" + ); + + /* Main window */ + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow, + stack); + gtk_widget_class_bind_template_callback(widget_class, delete_prefs); + + /* Interface page */ + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + iface.im.hide_new.combo); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + iface.win32.minimize_new_convs); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + iface.conversations.tabs); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + iface.conversations.tabs_vbox); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + iface.conversations.close_on_tabs); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + iface.conversations.tab_side.combo); + + /* Conversations page */ + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.notification_chat.combo); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.show_incoming_formatting); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.im.close_immediately); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.im.send_typing); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.use_smooth_scrolling); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.win32.blink_im); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.resize_custom_smileys); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.custom_smileys_size); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.minimum_entry_lines); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.format_buffer); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.format_view); +#ifdef WIN32 + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.font_frame); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.use_theme_font); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.custom_font_hbox); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + conversations.custom_font); +#endif + /* Even though Win32-specific, must be bound to avoid Glade warnings. */ + gtk_widget_class_bind_template_callback(widget_class, + apply_custom_font); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_custom_font_set); + + /* Logging page */ + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, logging.format.combo); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, logging.log_ims); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, logging.log_chats); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, logging.log_system); + + /* Network page */ + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, network.stun_server); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, network.auto_ip); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, network.public_ip); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.public_ip_hbox); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, network.map_ports); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.ports_range_use); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.ports_range_hbox); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.ports_range_start); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.ports_range_end); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, network.turn_server); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.turn_port_udp); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.turn_port_tcp); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.turn_username); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + network.turn_password); + gtk_widget_class_bind_template_callback(widget_class, + network_stun_server_changed_cb); + gtk_widget_class_bind_template_callback(widget_class, + auto_ip_button_clicked_cb); + gtk_widget_class_bind_template_callback(widget_class, + network_ip_changed); + gtk_widget_class_bind_template_callback(widget_class, + network_turn_server_changed_cb); + + /* Proxy page */ + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.stack); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.gnome_not_found); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.gnome_program); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + proxy.socks4_remotedns); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.type.combo); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.options); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.host); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.port); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.username); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, proxy.password); + gtk_widget_class_bind_template_callback(widget_class, + proxy_button_clicked_cb); + gtk_widget_class_bind_template_callback(widget_class, + proxy_print_option); + + /* Away page */ + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + away.idle_reporting.combo); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + away.mins_before_away); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, away.away_when_idle); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, away.idle_hbox); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + away.auto_reply.combo); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, + away.startup_current_status); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, away.startup_hbox); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, away.startup_label); + + /* Themes page */ + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, theme.status); + gtk_widget_class_bind_template_child( + widget_class, PidginPrefsWindow, theme.smiley); + gtk_widget_class_bind_template_callback(widget_class, + prefs_set_status_icon_theme_cb); + gtk_widget_class_bind_template_callback(widget_class, + prefs_set_smiley_theme_cb); +} + +static void +pidgin_prefs_window_init(PidginPrefsWindow *win) +{ + /* copy the preferences to tmp values... + * I liked "take affect immediately" Oh well :-( */ + /* (that should have been "effect," right?) */ + + /* Back to instant-apply! I win! BU-HAHAHA! */ + + /* Create the window */ + gtk_widget_init_template(GTK_WIDGET(win)); + + prefs_stack_init(win); + + /* Refresh the list of themes before showing the preferences window */ + prefs_themes_refresh(); +} + +void +pidgin_prefs_show(void) +{ + if (prefs == NULL) { + prefs = PIDGIN_PREFS_WINDOW(g_object_new( + pidgin_prefs_window_get_type(), NULL)); + } + + gtk_window_present(GTK_WINDOW(prefs)); +} + +static void +smiley_theme_pref_cb(const char *name, PurplePrefType type, + gconstpointer value, gpointer data) +{ + const gchar *theme_name = value; + GList *themes, *it; + + if (purple_strequal(theme_name, "none")) { + purple_smiley_theme_set_current(NULL); + return; + } + + /* XXX: could be cached when initializing prefs view */ + themes = pidgin_smiley_theme_get_all(); + + for (it = themes; it; it = g_list_next(it)) { + PidginSmileyTheme *theme = it->data; + + if (!purple_strequal(pidgin_smiley_theme_get_name(theme), theme_name)) + continue; + + purple_smiley_theme_set_current(PURPLE_SMILEY_THEME(theme)); + } +} + +void +pidgin_prefs_init(void) +{ + purple_prefs_add_none(PIDGIN_PREFS_ROOT ""); + purple_prefs_add_none("/plugins/gtk"); + + /* Plugins */ + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/plugins"); + purple_prefs_add_path_list(PIDGIN_PREFS_ROOT "/plugins/loaded", NULL); + + /* File locations */ + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/filelocations"); + purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder", ""); + purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", ""); + purple_prefs_add_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder", ""); + + /* Themes */ + prefs_themes_init(); + + /* Smiley Themes */ + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default"); + + /* Smiley Callbacks */ + purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme", + smiley_theme_pref_cb, NULL); + +#ifdef USE_VV + /* Voice/Video */ + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/src"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", ""); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", ""); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/src"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", ""); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/sink"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", ""); +#endif + + pidgin_prefs_update_old(); +} + +void +pidgin_prefs_update_old(void) +{ + const gchar *video_sink = NULL; + + /* Rename some old prefs */ + purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_ims", "/purple/logging/log_ims"); + purple_prefs_rename(PIDGIN_PREFS_ROOT "/logging/log_chats", "/purple/logging/log_chats"); + + purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/im/raise_on_events", "/plugins/gtk/X11/notify/method_raise"); + + purple_prefs_rename_boolean_toggle(PIDGIN_PREFS_ROOT "/conversations/ignore_colors", + PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting"); + + /* Remove some no-longer-used prefs */ + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/auto_expand_contacts"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/button_style"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/grey_idle_buddies"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/raise_on_events"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_group_count"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_warning_level"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/tooltip_delay"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/x"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/y"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/browser"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/command"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/place"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/browsers/manual_command"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/button_type"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ctrl_enter_sends"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/enter_sends"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/escape_closes"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/html_shortcuts"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/icons_on_tabs"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/send_formatting"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/show_smileys"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/show_urls_as_links"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/smiley_shortcuts"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_bgcolor"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_fgcolor"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_font"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/use_custom_size"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/old_tab_complete"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/tab_completion"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/hide_on_send"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/color_nicks"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/raise_on_events"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_fonts"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ignore_font_sizes"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/passthrough_unknown_commands"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/debug/timestamps"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/idle"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/logging/individual_logs"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/signon"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/silent_signon"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/command"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/conv_focus"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/chat_msg_recv"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/first_im_recv"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/got_attention"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/im_recv"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/join_chat"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/left_chat"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/login"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/logout"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/nick_said"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/pounce_default"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/send_chat_msg"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/send_im"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled/sent_attention"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/enabled"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/chat_msg_recv"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/first_im_recv"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/got_attention"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/im_recv"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/join_chat"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/left_chat"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/login"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/logout"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/nick_said"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/pounce_default"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/send_chat_msg"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/send_im"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file/sent_attention"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/file"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/method"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/mute"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound/theme"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/sound"); + + /* Convert old queuing prefs to hide_new 3-way pref. */ + if (purple_prefs_exists("/plugins/gtk/docklet/queue_messages") && + purple_prefs_get_bool("/plugins/gtk/docklet/queue_messages")) + { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "always"); + } + else if (purple_prefs_exists(PIDGIN_PREFS_ROOT "/away/queue_messages") && + purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/away/queue_messages")) + { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "away"); + } + purple_prefs_remove(PIDGIN_PREFS_ROOT "/away/queue_messages"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/away"); + purple_prefs_remove("/plugins/gtk/docklet/queue_messages"); + + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_width"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/chat/default_height"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_width"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/im/default_height"); + purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/x", + PIDGIN_PREFS_ROOT "/conversations/im/x"); + purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/y", + PIDGIN_PREFS_ROOT "/conversations/im/y"); + + /* Fixup vvconfig plugin prefs */ + if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", + purple_prefs_get_string("/plugins/core/vvconfig/audio/src/device")); + } + if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", + purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/device")); + } + if (purple_prefs_exists("/plugins/core/vvconfig/video/src/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", + purple_prefs_get_string("/plugins/core/vvconfig/video/src/device")); + } + if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", + purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/device")); + } + + video_sink = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device"); + if (purple_strequal(video_sink, "glimagesink") || purple_strequal(video_sink, "directdrawsink")) { + /* Accelerated sinks move to GTK GL. */ + /* video_sink = "gtkglsink"; */ + /* FIXME: I haven't been able to get gtkglsink to work yet: */ + video_sink = "gtksink"; + } else { + /* Everything else, including default will be moved to GTK sink. */ + video_sink = "gtksink"; + } + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", video_sink); + + purple_prefs_remove("/plugins/core/vvconfig"); + purple_prefs_remove("/plugins/gtk/vvconfig"); + + purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin"); + purple_prefs_remove(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin"); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/prefs/pidginprefs.h Tue May 18 02:08:18 2021 -0500 @@ -0,0 +1,163 @@ +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) +# error "only <pidgin.h> may be included directly" +#endif + +#ifndef PIDGIN_PREFS_H +#define PIDGIN_PREFS_H +/** + * SECTION:pidginprefs + * @section_id: pidgin-pidginprefs + * @short_description: <filename>pidginprefs.h</filename> + * @title: Preferences + */ + +#include <purple.h> + +G_BEGIN_DECLS + +#define PIDGIN_TYPE_PREFS_WINDOW (pidgin_prefs_window_get_type()) +G_DECLARE_FINAL_TYPE(PidginPrefsWindow, pidgin_prefs_window, PIDGIN, PREFS_WINDOW, GtkDialog) + +/** + * pidgin_prefs_init: + * + * Initializes all UI-specific preferences. + */ +void pidgin_prefs_init(void); + +/** + * pidgin_prefs_show: + * + * Shows the preferences dialog. + */ +void pidgin_prefs_show(void); + +/** + * pidgin_prefs_checkbox: + * @title: The text to be displayed as the checkbox label + * @key: The key of the purple bool pref that will be represented by the checkbox + * @page: The page to which the new checkbox will be added + * + * Add a new checkbox for a boolean preference + * + * Returns: (transfer full): The new checkbox + */ +GtkWidget *pidgin_prefs_checkbox(const char *title, const char *key, + GtkWidget *page); + +/** + * pidgin_prefs_labeled_spin_button: + * @page: The page to which the spin button will be added + * @title: The text to be displayed as the spin button label + * @key: The key of the int pref that will be represented by the spin button + * @min: The minimum value of the spin button + * @max: The maximum value of the spin button + * @sg: If not NULL, the size group to which the spin button will be added + * + * Add a new spin button representing an int preference + * + * Returns: (transfer full): An hbox containing both the label and the spinner. Can be + * used to set the widgets to sensitive or insensitive based on the + * value of a checkbox. + */ +GtkWidget *pidgin_prefs_labeled_spin_button(GtkWidget *page, + const gchar *title, const char *key, int min, int max, GtkSizeGroup *sg); + +/** + * pidgin_prefs_labeled_entry: + * @page: The page to which the entry will be added + * @title: The text to be displayed as the entry label + * @key: The key of the string pref that will be represented by the entry + * @sg: If not NULL, the size group to which the entry will be added + * + * Add a new entry representing a string preference + * + * Returns: (transfer full) :An hbox containing both the label and the entry. Can be used to set + * the widgets to sensitive or insensitive based on the value of a + * checkbox. + */ +GtkWidget *pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title, + const char *key, GtkSizeGroup *sg); + +/** + * pidgin_prefs_labeled_password: + * @page: The page to which the entry will be added + * @title: The text to be displayed as the entry label + * @key: The key of the string pref that will be represented by the entry + * @sg: If not NULL, the size group to which the entry will be added + * + * Add a new entry representing a password (string) preference + * The entry will use a password-style text entry (the text is substituded) + * + * Returns: (transfer full): An hbox containing both the label and the entry. Can be used to set + * the widgets to sensitive or insensitive based on the value of a + * checkbox. + */ +GtkWidget *pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title, + const char *key, GtkSizeGroup *sg); + +/** + * pidgin_prefs_dropdown: + * @page: The page to which the dropdown will be added + * @title: The text to be displayed as the dropdown label + * @type: The type of preference to be stored in the generated dropdown + * @key: The key of the pref that will be represented by the dropdown + * @...: The choices to be added to the dropdown, choices should be + * paired as label/value + * + * Add a new dropdown representing a preference of the specified type + * + * Returns: (transfer full): The new dropdown. + */ +GtkWidget *pidgin_prefs_dropdown(GtkWidget *page, const gchar *title, + PurplePrefType type, const char *key, ...); + +/** + * pidgin_prefs_dropdown_from_list: + * @page: The page to which the dropdown will be added + * @title: The text to be displayed as the dropdown label + * @type: The type of preference to be stored in the dropdown + * @key: The key of the pref that will be represented by the dropdown + * @menuitems: (element-type PurpleKeyValuePair): The choices to be added to the dropdown, choices should + * be paired as label/value + * + * Add a new dropdown representing a preference of the specified type + * + * Returns: (transfer full): The new dropdown. + */ +GtkWidget *pidgin_prefs_dropdown_from_list(GtkWidget *page, + const gchar * title, PurplePrefType type, const char *key, + GList *menuitems); + +/** + * pidgin_prefs_update_old: + * + * Rename legacy prefs and delete some that no longer exist. + */ +void pidgin_prefs_update_old(void); + +G_END_DECLS + +#endif /* PIDGIN_PREFS_H */
--- a/po/POTFILES.in Tue May 18 02:04:53 2021 -0500 +++ b/po/POTFILES.in Tue May 18 02:08:18 2021 -0500 @@ -320,7 +320,6 @@ pidgin/gtkmedia.c pidgin/gtknotify.c pidgin/gtkpluginpref.c -pidgin/gtkprefs.c pidgin/gtkprivacy.c pidgin/gtkrequest.c pidgin/gtkroomlist.c @@ -353,8 +352,6 @@ pidgin/pidgincontactcompletion.c pidgin/pidgincontactlist.c pidgin/pidginconversationwindow.c -pidgin/pidgincredentialproviderrow.c -pidgin/pidgincredentialspage.c pidgin/pidgindebug.c pidgin/pidgindialog.c pidgin/pidgingdkpixbuf.c @@ -391,6 +388,9 @@ pidgin/plugins/unity.c pidgin/plugins/xmppconsole/console.ui pidgin/plugins/xmppconsole/xmppconsole.c +pidgin/prefs/pidgincredentialproviderrow.c +pidgin/prefs/pidgincredentialspage.c +pidgin/prefs/pidginprefs.c pidgin/resources/About/about.ui pidgin/resources/Accounts/actionsmenu.ui pidgin/resources/Accounts/chooser.ui