grim/pidgin

Move Pidgin preferences code into a subdirectory.

2021-05-18, Elliott Sales de Andrade
198bf5bc58ce
Parents 87f6241da196
Children 536b09356b6b
Move Pidgin preferences code into a subdirectory.

Testing Done:
Compiled, installed, and compiled a simple `#include ` 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