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 "pidginavatar.h"
@@ -5074,7 +5073,6 @@
#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 "gtkpluginpref.h"
+#include "pidginprefs.h" 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 is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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
-#include <glib/gi18n-lib.h>
-#include <glib/gstdio.h>
-#include "gtksavedstatuses.h"
-#include "gtksmiley-theme.h"
-#include "gtkstatus-icon-theme.h"
-#include "pidgindebug.h"
-#include "pidginstock.h"
-#include <gst/video/videooverlay.h>
-#ifdef GDK_WINDOWING_WIN32
-#include <gdk/gdkwin32.h>
-#ifdef GDK_WINDOWING_X11
-#ifdef GDK_WINDOWING_QUARTZ
-#include <gdk/gdkquartz.h>
-#include <libsoup/soup.h>
-#define PREFS_OPTIMAL_ICON_SIZE 32
-#define PREFS_MAX_DOWNLOADED_THEME_SIZE 26214400
-typedef struct _PidginPrefCombo PidginPrefCombo;
-typedef void (*PidginPrefsBindDropdownCallback)(GtkComboBox *combo_box,
- PidginPrefCombo *combo);
-struct _PidginPrefCombo {
- gint previously_active;
- PidginPrefsBindDropdownCallback cb;
-struct _PidginPrefsWindow {
- PidginPrefCombo hide_new;
- GtkWidget *minimize_new_convs;
- GtkWidget *close_on_tabs;
- PidginPrefCombo tab_side;
- /* Conversations page */
- PidginPrefCombo notification_chat;
- GtkWidget *show_incoming_formatting;
- GtkWidget *close_immediately;
- GtkWidget *send_typing;
- GtkWidget *use_smooth_scrolling;
- GtkWidget *resize_custom_smileys;
- GtkWidget *custom_smileys_size;
- GtkWidget *minimum_entry_lines;
- GtkTextBuffer *format_buffer;
- GtkWidget *format_view;
- /* Win32 specific frame */
- GtkWidget *use_theme_font;
- GtkWidget *custom_font_hbox;
- GtkWidget *custom_font;
- PidginPrefCombo format;
- GtkWidget *stun_server;
- GtkWidget *public_ip_hbox;
- 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;
- GtkWidget *gnome_not_found;
- GtkWidget *gnome_program;
- gchar *gnome_program_path;
- /* Non-GNOME version */
- GtkWidget *socks4_remotedns;
- PidginPrefCombo idle_reporting;
- GtkWidget *mins_before_away;
- GtkWidget *away_when_idle;
- PidginPrefCombo auto_reply;
- GtkWidget *startup_current_status;
- GtkWidget *startup_hbox;
- GtkWidget *startup_label;
- PidginPrefCombo output;
- PidginPrefCombo output;
- GtkWidget *sink_widget;
-static PidginPrefsWindow *prefs = NULL;
-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;
-G_DEFINE_TYPE(PidginPrefsWindow, pidgin_prefs_window, GTK_TYPE_DIALOG);
-static void delete_prefs(GtkWidget *, void *);
-update_spin_value(GtkWidget *w, GtkWidget *spin)
- const char *key = g_object_get_data(G_OBJECT(spin), "val");
- value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
- purple_prefs_set_int(key, value);
-pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title,
- const char *key, int min, int max, GtkSizeGroup *sg)
- 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);
- gtk_widget_set_size_request(spin, 50, -1);
- gtk_widget_set_size_request(spin, 60, -1);
- g_signal_connect(G_OBJECT(adjust), "value-changed",
- G_CALLBACK(update_spin_value), GTK_WIDGET(spin));
- return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL);
-pidgin_prefs_bind_spin_button(const char *key, GtkWidget *spin)
- 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));
-entry_set(GtkEntry *entry, gpointer data)
- const char *key = (const char*)data;
- purple_prefs_set_string(key, gtk_entry_get_text(entry));
-pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title,
- const char *key, GtkSizeGroup *sg)
- 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);
-pidgin_prefs_bind_entry(const char *key, GtkWidget *entry)
- 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),
-pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title,
- const char *key, GtkSizeGroup *sg)
- 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... */
-typedef void (*PidginPrefsDropdownCallback)(GtkComboBox *combo_box,
- PidginPrefValue value);
-dropdown_set(GtkComboBox *combo_box, gpointer _cb)
- PidginPrefsDropdownCallback cb = _cb;
- GtkTreeModel *tree_model;
- PidginPrefValue active;
- tree_model = gtk_combo_box_get_model(combo_box);
- if (!gtk_combo_box_get_active_iter(combo_box, &iter))
- active.type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo_box),
- 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);
-pidgin_prefs_dropdown_from_list_with_cb(GtkWidget *box, const gchar *title,
- GtkComboBox **dropdown_out, GList *menuitems,
- PidginPrefValue initial, PidginPrefsDropdownCallback cb)
- GtkWidget *label = NULL;
- GtkListStore *store = NULL;
- 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);
- 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;
- const char *str_value = NULL;
- gboolean bool_value = FALSE;
- if (menu_item->key == NULL) {
- gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter,
- PREF_DROPDOWN_TEXT, menu_item->key,
- 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,
- 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,
- 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,
- 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))) {
- 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,
- gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dropdown), &active);
- current_active = GINT_TO_POINTER(gtk_combo_box_get_active(GTK_COMBO_BOX(
- 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);
-pidgin_prefs_dropdown_from_list_cb(GtkComboBox *combo_box,
- 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);
-pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title,
- PurplePrefType type, const char *key, GList *menuitems)
- PidginPrefValue initial;
- GtkComboBox *dropdown = NULL;
- 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);
- 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);
-pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type,
- GList *menuitems = NULL;
- GtkWidget *dropdown = NULL;
- g_return_val_if_fail(type == PURPLE_PREF_BOOLEAN || type == PURPLE_PREF_INT ||
- type == PURPLE_PREF_STRING, NULL);
- 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)));
- kvp = purple_key_value_pair_new(name, va_arg(ap, char *));
- menuitems = g_list_prepend(menuitems, kvp);
- g_return_val_if_fail(menuitems != NULL, NULL);
- menuitems = g_list_reverse(menuitems);
- dropdown = pidgin_prefs_dropdown_from_list(box, title, type, key,
- g_list_free_full(menuitems, (GDestroyNotify)purple_key_value_pair_free);
-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);
-bind_dropdown_set(GtkComboBox *combo_box, gpointer data)
- PidginPrefCombo *combo = data;
- GtkTreeModel *tree_model;
- tree_model = gtk_combo_box_get_model(combo_box);
- if (!gtk_combo_box_get_active_iter(combo_box, &iter))
- 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);
-pidgin_prefs_bind_dropdown_from_list(PidginPrefCombo *combo, GList *menuitems)
- GtkListStore *store = NULL;
- 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);
- store = GTK_LIST_STORE(
- gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)));
- while (menuitems != NULL && (text = (char *)menuitems->data) != NULL) {
- 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,
- if (combo->type == PURPLE_PREF_INT) {
- int_value = GPOINTER_TO_INT(menuitems->data);
- gtk_list_store_set(store, &iter,
- PREF_DROPDOWN_VALUE, int_value,
- else if (combo->type == PURPLE_PREF_STRING) {
- str_value = (const char *)menuitems->data;
- gtk_list_store_set(store, &iter,
- PREF_DROPDOWN_VALUE, str_value,
- 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,
- 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))) {
- 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);
-pidgin_prefs_bind_dropdown(PidginPrefCombo *combo)
- GtkTreeModel *store = 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);
- store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo));
- if (!gtk_tree_model_get_iter_first(store, &iter)) {
- 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,
- if (combo->value.integer == int_value) {
- else if (combo->type == PURPLE_PREF_STRING) {
- gtk_tree_model_get(store, &iter,
- PREF_DROPDOWN_VALUE, &str_value,
- if (purple_strequal(combo->value.string, str_value)) {
- else if (combo->type == PURPLE_PREF_BOOLEAN) {
- gtk_tree_model_get(store, &iter,
- PREF_DROPDOWN_VALUE, &bool_value,
- if (combo->value.boolean == bool_value) {
- } 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);
-set_bool_pref(GtkWidget *w, const char *key)
- purple_prefs_set_bool(key,
- gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));
-pidgin_prefs_checkbox(const char *text, const char *key, GtkWidget *page)
- 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);
-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);
-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);
- prefs_status_themes_combo_box = NULL;
- prefs_smiley_themes_combo_box = NULL;
- g_free(prefs->proxy.gnome_program_path);
-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 : "");
-smileys_refresh_theme_list(void)
- 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);
- 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),
- 2, pidgin_smiley_theme_get_name(theme),
-/* adds the themes to the theme list from the manager so they can be displayed in prefs */
-prefs_themes_sort(PurpleTheme *theme)
- GdkPixbuf *pixbuf = NULL;
- gchar *image_full = NULL, *markup;
- const gchar *name, *author, *description;
- if (PIDGIN_IS_STATUS_ICON_THEME(theme)){
- 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);
- 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_object_unref(G_OBJECT(pixbuf));
-prefs_set_active_theme_combo(GtkWidget *combo_box, GtkListStore *store, const gchar *current_theme)
- if (current_theme && *current_theme && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
- 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);
- } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
- gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
-prefs_themes_refresh(void)
- GdkPixbuf *pixbuf = NULL;
- /* 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);
- /* 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_object_unref(G_OBJECT(pixbuf));
- gtk_list_store_clear(prefs_smiley_themes);
- purple_theme_manager_for_each_theme(prefs_themes_sort);
- smileys_refresh_theme_list();
- 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 */
- 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
- * 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.
-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);
- 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);
-/* Eww. Seriously ewww. But thanks, grim! This is taken from guifications2 */
-purple_theme_file_copy(const gchar *source, const gchar *destination)
- if(!(src = g_fopen(source, "rb")))
- if(!(dest = g_fopen(destination, "wb"))) {
- while((chr = fgetc(src)) != EOF) {
-free_theme_info(struct theme_info *info)
- g_free(info->extension);
- g_free(info->original_name);
-/* installs a theme, info is freed by function */
-theme_install_theme(char *path, struct theme_info *info)
- gboolean is_smiley_theme, is_archive;
- PurpleTheme *theme = NULL;
- /* check the extension */
- tail = info->extension ? info->extension : strrchr(path, '.');
- is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz");
- if ((is_smiley_theme = purple_strequal(info->type, "smiley")))
- destdir = g_build_filename(purple_data_dir(), "smileys", NULL);
- 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 */
- gchar *path_escaped = g_shell_quote(path);
- gchar *destdir_escaped = g_shell_quote(destdir);
- 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(destdir_escaped);
- purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL);
- if (!winpidgin_gz_untar(path, destdir)) {
- purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL);
- /* 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);
- 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,
- if (g_remove(destdir) != 0) {
- purple_debug_error("gtkprefs",
- "couldn't remove temp (dest) path\n");
- prefs_themes_refresh();
- /* something was wrong with the theme archive */
- 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",
- 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);
- gchar *source_name = g_path_get_basename(path);
- temp_file = g_build_filename(temp_path, source_name, NULL);
- 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)) {
- g_build_filename(purple_data_dir(), "themes",
- purple_theme_get_name(theme), "purple",
- 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,
- prefs_themes_refresh();
- 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);
- purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL, NULL);
-theme_got_url(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
- struct theme_info *info = _info;
- if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
- f = purple_mkstemp(&path, TRUE);
- wc = fwrite(msg->response_body->data, msg->response_body->length, 1, f);
- purple_debug_warning("theme_got_url", "Unable to write theme data.\n");
- theme_install_theme(path, info);
-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 */
- 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;
- /* 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");
- theme_install_theme(tmp, info);
- } 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 */
- 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,
- 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 */
-prefs_build_theme_combo_box(GtkWidget *combo_box, GtkListStore *store,
- const char *current_theme, const char *type)
- GtkTargetEntry te[3] = {
- {"text/uri-list", 0, 1},
- 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 */
-prefs_set_smiley_theme_cb(GtkComboBox *combo_box, gpointer user_data)
- 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);
-/* Does same as normal sort, except "none" is sorted first */
-static gint pidgin_sort_smileys (GtkTreeModel *model,
- 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))
- } else if (!g_ascii_strcasecmp(name2, "none")) {
- /* Neither string is "none", default to normal sort */
- ret = purple_utf8_strcasecmp(name1, name2);
-/* sets the current icon theme */
-prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
- PidginStatusIconTheme *theme = 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);
- theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon"));
- pidgin_stock_load_status_icon_theme(theme);
- pidgin_blist_refresh(purple_blist_get_default());
-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",
- prefs_status_themes_combo_box = win->theme.status;
- prefs_build_theme_combo_box(win->theme.smiley, prefs_smiley_themes,
- PIDGIN_PREFS_ROOT "/smileys/theme",
- 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);
-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",
- } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_ITALIC, name) != 0) {
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic",
- } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_UNDERLINE, name) != 0) {
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline",
- } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_STRIKETHROUGH, name) != 0) {
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike",
-bind_interface_page(PidginPrefsWindow *win)
- 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);
- pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs",
- win->iface.win32.minimize_new_convs);
- gtk_widget_hide(win->iface.win32.minimize_new_convs);
- /* 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. */
-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);
- pango_font_description_free(desc);
-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);
-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);
- pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/blink_im",
- win->conversations.win32.blink_im);
- gtk_widget_hide(win->conversations.win32.blink_im);
- /* 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);
- pidgin_prefs_bind_spin_button(
- PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines",
- win->conversations.minimum_entry_lines);
- 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),
- g_object_bind_property(win->conversations.use_theme_font, "active",
- win->conversations.custom_font_hbox, "sensitive",
- G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN);
- 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);
-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 (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");
- gtk_style_context_add_class(context, "bad-ip");
- gtk_style_context_remove_class(context, "good-ip");
- purple_network_set_public_ip("");
- gtk_style_context_remove_class(context, "bad-ip");
- gtk_style_context_remove_class(context, "good-ip");
-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));
-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));
-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);
- gtk_widget_hide(win->proxy.options);
-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)));
-proxy_button_clicked_cb(GtkWidget *button, PidginPrefsWindow *win)
- if (g_spawn_command_line_async(win->proxy.gnome_program_path, &err))
- purple_notify_error(NULL, NULL, _("Cannot start proxy configuration program."), err->message, NULL);
-auto_ip_button_clicked_cb(GtkWidget *button, gpointer null)
- PurpleStunNatDiscovery *stun;
- /* 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)) {
- /* Attempt to get the IP from a NAT device using UPnP */
- ip = purple_upnp_get_public_ip();
- /* Attempt to get the IP from a NAT device using NAT-PMP */
- ip = purple_pmp_get_public_ip();
- /* Just fetch the first IP of the local system */
- list = nice_interfaces_get_local_ips(FALSE);
- auto_ip_text = g_strdup_printf(_("Use _automatically detected IP address: %s"), ip);
- gtk_button_set_label(GTK_BUTTON(button), auto_ip_text);
- g_list_free_full(list, g_free);
-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",
- 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);
- 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);
-bind_proxy_page(PidginPrefsWindow *win)
- PurpleProxyInfo *proxy_info;
- if(purple_running_gnome()) {
- gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack),
- path = g_find_program_in_path("gnome-network-properties");
- path = g_find_program_in_path("gnome-network-preferences");
- path = g_find_program_in_path("gnome-control-center");
- char *tmp = g_strdup_printf("%s network", path);
- win->proxy.gnome_program_path = path;
- gtk_widget_set_visible(win->proxy.gnome_not_found,
- gtk_widget_set_visible(win->proxy.gnome_program,
- gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack),
- /* 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"),
-bind_logging_page(PidginPrefsWindow *win)
- 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);
- pidgin_prefs_bind_checkbox("/purple/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);
-set_idle_away(PurpleSavedStatus *status)
- purple_prefs_set_int("/purple/savedstatus/idleaway", purple_savedstatus_get_creation_time(status));
-set_startupstatus(PurpleSavedStatus *status)
- purple_prefs_set_int("/purple/savedstatus/startup", purple_savedstatus_get_creation_time(status));
-bind_away_page(PidginPrefsWindow *win)
- 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",
- G_BINDING_SYNC_CREATE);
- 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);
-get_vv_device_menuitems(PurpleMediaElementType type)
- i = purple_media_manager_enumerate_elements(purple_media_manager_get(),
- 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));
-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,
-vv_test_switch_page_cb(GtkStack *stack, G_GNUC_UNUSED GParamSpec *pspec,
- 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);
-create_voice_pipeline(void)
- GstElement *src, *sink;
- 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");
-on_volume_change_cb(GtkWidget *w, gdouble value, gpointer data)
- PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
- if (!win->vv.voice.pipeline) {
- 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);
-gst_msg_db_to_percent(GstMessage *msg, gchar *value_name)
- 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;
-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")) {
- 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)) /
- 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") : " ",
-voice_test_destroy_cb(GtkWidget *w, gpointer data)
- PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
- if (!win->vv.voice.pipeline) {
- gst_element_set_state(win->vv.voice.pipeline, GST_STATE_NULL);
- g_clear_pointer(&win->vv.voice.pipeline, gst_object_unref);
-enable_voice_test(PidginPrefsWindow *win)
- 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);
-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);
- 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,
- voice_test_destroy_cb(NULL, win);
-volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data)
- purple_prefs_set_int("/purple/media/audio/volume/input", value * 100);
-threshold_value_changed_cb(GtkScale *scale, GtkWidget *label)
- 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);
- purple_prefs_set_int("/purple/media/audio/silence_threshold", value);
-bind_voice_test(PidginPrefsWindow *win, GtkBuilder *builder)
- 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);
- 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);
- 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);
-create_video_pipeline(void)
- 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,
- gst_element_link_many(src, videoconvert, videoscale, sink, NULL);
-video_test_destroy_cb(GtkWidget *w, gpointer data)
- PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
- if (!win->vv.video.pipeline) {
- gst_element_set_state(win->vv.video.pipeline, GST_STATE_NULL);
- g_clear_pointer(&win->vv.video.pipeline, gst_object_unref);
-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),
-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);
- g_object_disconnect(test, "any-signal::destroy",
- G_CALLBACK(video_test_destroy_cb), win,
- video_test_destroy_cb(NULL, win);
-bind_video_test(PidginPrefsWindow *win, GtkBuilder *builder)
- 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);
-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);
-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";
-bind_vv_dropdown(PidginPrefCombo *combo, PurpleMediaElementType element_type)
- const gchar *preference_key;
- 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);
- 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);
-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),
- g_object_set_data(G_OBJECT(combo->combo), "vv_media_type",
- g_object_set_data(G_OBJECT(combo->combo), "vv_combo", combo);
-device_list_changed_cb(PurpleMediaManager *manager, GtkWidget *widget)
- PidginPrefCombo *combo;
- PurpleMediaElementType media_type;
- combo = g_object_get_data(G_OBJECT(widget), "vv_combo");
- media_type = (PurpleMediaElementType)g_object_get_data(G_OBJECT(widget),
- /* 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);
-vv_page(PidginPrefsWindow *win)
- 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_unref(builder);
-prefs_stack_init(PidginPrefsWindow *win)
- GtkStack *stack = GTK_STACK(win->stack);
- bind_interface_page(win);
- bind_logging_page(win);
- bind_network_page(win);
- gtk_container_add_with_properties(GTK_CONTAINER(stack), vv, "name",
- "vv", "title", _("Voice/Video"),
-pidgin_prefs_window_class_init(PidginPrefsWindowClass *klass)
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- gtk_widget_class_set_template_from_resource(
- "/im/pidgin/Pidgin/Prefs/prefs.ui"
- gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
- gtk_widget_class_bind_template_callback(widget_class, delete_prefs);
- 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);
- 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);
- /* Even though Win32-specific, must be bound to avoid Glade warnings. */
- gtk_widget_class_bind_template_callback(widget_class,
- gtk_widget_class_bind_template_callback(widget_class,
- pidgin_custom_font_set);
- 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);
- 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,
- gtk_widget_class_bind_template_callback(widget_class,
- network_turn_server_changed_cb);
- 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,
- 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);
- 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);
-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));
- /* Refresh the list of themes before showing the preferences window */
- prefs_themes_refresh();
- prefs = PIDGIN_PREFS_WINDOW(g_object_new(
- pidgin_prefs_window_get_type(), NULL));
- gtk_window_present(GTK_WINDOW(prefs));
-smiley_theme_pref_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
- const gchar *theme_name = value;
- if (purple_strequal(theme_name, "none")) {
- purple_smiley_theme_set_current(NULL);
- /* 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))
- purple_smiley_theme_set_current(PURPLE_SMILEY_THEME(theme));
- purple_prefs_add_none(PIDGIN_PREFS_ROOT "");
- purple_prefs_add_none("/plugins/gtk");
- purple_prefs_add_none(PIDGIN_PREFS_ROOT "/plugins");
- purple_prefs_add_path_list(PIDGIN_PREFS_ROOT "/plugins/loaded", NULL);
- 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", "");
- purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys");
- purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
- purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
- smiley_theme_pref_cb, NULL);
- 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", "");
- pidgin_prefs_update_old();
-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";
- /* 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 is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * 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"
- * @section_id: pidgin-gtkprefs
- * @short_description: <filename>gtkprefs.h</filename>
-#define PIDGIN_TYPE_PREFS_WINDOW (pidgin_prefs_window_get_type())
-G_DECLARE_FINAL_TYPE(PidginPrefsWindow, pidgin_prefs_window, PIDGIN, PREFS_WINDOW, GtkDialog)
- * Initializes all UI-specific preferences.
-void pidgin_prefs_init(void);
- * 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,
- * 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
-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
-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
-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,
- * pidgin_prefs_update_old:
- * Rename legacy prefs and delete some that no longer exist.
-void pidgin_prefs_update_old(void);
-#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 @@
--- 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 @@
@@ -54,6 +53,7 @@
#include "pidginplugininfo.h"
+#include "pidginprefs.h" #include "pidginprivate.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 @@
@@ -44,8 +43,6 @@
'pidgincontactcompletion.c',
'pidginconversationwindow.c',
- 'pidgincredentialproviderrow.c',
- 'pidgincredentialspage.c',
@@ -66,6 +63,9 @@
+ 'prefs/pidgincredentialproviderrow.c', + 'prefs/pidgincredentialspage.c', @@ -82,7 +82,6 @@
@@ -114,8 +113,6 @@
'pidginconversationwindow.h',
- 'pidgincredentialproviderrow.h',
- 'pidgincredentialspage.h',
@@ -139,6 +136,12 @@
+libpidgin_prefs_headers = [ + 'prefs/pidgincredentialproviderrow.h', + 'prefs/pidgincredentialspage.h', libpidgin_enum_headers = [
@@ -200,7 +203,7 @@
libpidgin_enums_h = libpidgin_enums[1]
- foreach header : libpidgin_headers + ['pidginenums.h']
+ foreach header : libpidgin_headers + libpidgin_prefs_headers + ['pidginenums.h'] PIDGIN_H_INCLUDES += '#include <pidgin/@0@>'.format(header)
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 = [
@@ -245,7 +249,7 @@
libpidgin_SOURCES + libpidgin_built_sources + libpidgin_built_headers + ['pidginprivate.h'],
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,
@@ -311,7 +315,7 @@
- introspection_sources = libpidgin_headers
+ introspection_sources = libpidgin_headers + libpidgin_prefs_headers '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 "gtksmiley-manager.h"
@@ -49,6 +48,7 @@
#include "pidginmooddialog.h"
#include "pidgin/pidginpluginsdialog.h"
+#include "pidginprefs.h" struct _PidginApplication {
--- 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
- * 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 "pidgincredentialproviderrow.h"
-struct _PidginCredentialProviderRow {
- PurpleCredentialProvider *provider;
-static GParamSpec *properties[N_PROPERTIES] = {NULL, };
-G_DEFINE_TYPE(PidginCredentialProviderRow, pidgin_credential_provider_row,
-/******************************************************************************
- *****************************************************************************/
-pidgin_credential_provider_row_set_provider(PidginCredentialProviderRow *row,
- PurpleCredentialProvider *provider)
- if(!g_set_object(&row->provider, provider)) {
- 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(
- 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
- *****************************************************************************/
-pidgin_credential_provider_row_get_property(GObject *obj, guint param_id,
- GValue *value, GParamSpec *pspec)
- PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj);
- g_value_set_object(value,
- pidgin_credential_provider_row_get_provider(row));
- g_value_set_boolean(value,
- pidgin_credential_provider_row_get_active(row));
- G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-pidgin_credential_provider_row_set_property(GObject *obj, guint param_id,
- PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj);
- pidgin_credential_provider_row_set_provider(row,
- g_value_get_object(value));
- pidgin_credential_provider_row_set_active(row,
- g_value_get_boolean(value));
- G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-pidgin_credential_provider_row_finalize(GObject *obj)
- PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj);
- g_clear_object(&row->provider);
-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",
-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(
- "Whether the PurpleCredentialProvider is active",
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
- gtk_widget_class_set_template_from_resource(
- "/im/pidgin/Pidgin/Prefs/credentialprovider.ui"
- gtk_widget_class_bind_template_child(widget_class,
- PidginCredentialProviderRow,
- gtk_widget_class_bind_template_child(widget_class,
- PidginCredentialProviderRow,
-/******************************************************************************
- *****************************************************************************/
-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,
-PurpleCredentialProvider *
-pidgin_credential_provider_row_get_provider(PidginCredentialProviderRow *row) {
- g_return_val_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row), NULL);
-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);
-pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row,
- 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
- * 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"
-#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.
- * PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW:
- * The standard _get_type macro for #PidginCredentialProviderRow.
-#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.
- * pidgin_credential_provider_row_new:
- * @provider: The credential provider to bind.
- * Creates a new #PidginCredentialProviderRow instance.
- * Returns: (transfer full): The new #PidginCredentialProviderRow instance.
-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.
-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.
-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.
-void pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row, gboolean active);
-#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
- * 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 "pidgincredentialspage.h"
-#include "pidgincredentialproviderrow.h"
-struct _PidginCredentialsPage {
- HdyPreferencesPage parent;
- GtkWidget *credential_list;
-G_DEFINE_TYPE(PidginCredentialsPage, pidgin_credentials_page,
- HDY_TYPE_PREFERENCES_PAGE)
-/******************************************************************************
- *****************************************************************************/
-pidgin_credentials_page_create_row(PurpleCredentialProvider *provider,
- GtkListBox *box = GTK_LIST_BOX(data);
- row = pidgin_credential_provider_row_new(provider);
- gtk_list_box_prepend(box, row);
-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) {
- } else if (is_noop1 && !is_noop2) {
- } else if (!is_noop1 && is_noop2) {
- /* Sort normally by ID. */
- return g_strcmp0(id1, id2);
-pidgin_credential_page_list_row_activated_cb(GtkListBox *box,
- G_GNUC_UNUSED gpointer data)
- PurpleCredentialManager *manager = NULL;
- PurpleCredentialProvider *provider = NULL;
- const gchar *id = 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);
- purple_debug_warning("credentials-page", "failed to set the active "
- "credential provider to '%s': %s",
- id, error ? error->message : "unknown error");
-pidgin_credentials_page_set_active_provider(PidginCredentialsPage *page,
- 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));
-pidgin_credentials_page_active_provider_changed_cb(const gchar *name,
- PidginCredentialsPage *page = PIDGIN_CREDENTIALS_PAGE(data);
- pidgin_credentials_page_set_active_provider(page, (const gchar *)value);
-/******************************************************************************
- * GObject Implementation
- *****************************************************************************/
-pidgin_credentials_page_finalize(GObject *obj) {
- purple_prefs_disconnect_by_handle(obj);
- G_OBJECT_CLASS(pidgin_credentials_page_parent_class)->finalize(obj);
-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(
- 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,
- active = purple_prefs_get_string("/purple/credentials/active-provider");
- pidgin_credentials_page_set_active_provider(page, active);
-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(
- "/im/pidgin/Pidgin/Prefs/credentials.ui"
- gtk_widget_class_bind_template_child(widget_class, PidginCredentialsPage,
- gtk_widget_class_bind_template_callback(widget_class,
- pidgin_credential_page_list_row_activated_cb);
-/******************************************************************************
- *****************************************************************************/
-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
- * 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"
-#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.
- * PIDGIN_TYPE_CREDENTIALS_PAGE:
- * The standard _get_type macro for #PidginCredentialsPage.
-#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.
- * pidgin_credentials_page_new:
- * Creates a new #PidginCredentialsPage instance.
- * Returns: (transfer full): The new #PidginCredentialsPage instance.
-GtkWidget *pidgin_credentials_page_new(void);
-#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 + * 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 "pidgincredentialproviderrow.h" +struct _PidginCredentialProviderRow { + PurpleCredentialProvider *provider; +static GParamSpec *properties[N_PROPERTIES] = {NULL, }; +G_DEFINE_TYPE(PidginCredentialProviderRow, pidgin_credential_provider_row, +/****************************************************************************** + *****************************************************************************/ +pidgin_credential_provider_row_set_provider(PidginCredentialProviderRow *row, + PurpleCredentialProvider *provider) + if(!g_set_object(&row->provider, provider)) { + 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( + 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 + *****************************************************************************/ +pidgin_credential_provider_row_get_property(GObject *obj, guint param_id, + GValue *value, GParamSpec *pspec) + PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); + g_value_set_object(value, + pidgin_credential_provider_row_get_provider(row)); + g_value_set_boolean(value, + pidgin_credential_provider_row_get_active(row)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +pidgin_credential_provider_row_set_property(GObject *obj, guint param_id, + PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); + pidgin_credential_provider_row_set_provider(row, + g_value_get_object(value)); + pidgin_credential_provider_row_set_active(row, + g_value_get_boolean(value)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +pidgin_credential_provider_row_finalize(GObject *obj) + PidginCredentialProviderRow *row = PIDGIN_CREDENTIAL_PROVIDER_ROW(obj); + g_clear_object(&row->provider); +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", +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( + "Whether the PurpleCredentialProvider is active", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); + gtk_widget_class_set_template_from_resource( + "/im/pidgin/Pidgin/Prefs/credentialprovider.ui" + gtk_widget_class_bind_template_child(widget_class, + PidginCredentialProviderRow, + gtk_widget_class_bind_template_child(widget_class, + PidginCredentialProviderRow, +/****************************************************************************** + *****************************************************************************/ +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, +PurpleCredentialProvider * +pidgin_credential_provider_row_get_provider(PidginCredentialProviderRow *row) { + g_return_val_if_fail(PIDGIN_IS_CREDENTIAL_PROVIDER_ROW(row), NULL); +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); +pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row, + 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 + * 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" +#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. + * PIDGIN_TYPE_CREDENTIAL_PROVIDER_ROW: + * The standard _get_type macro for #PidginCredentialProviderRow. +#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. + * pidgin_credential_provider_row_new: + * @provider: The credential provider to bind. + * Creates a new #PidginCredentialProviderRow instance. + * Returns: (transfer full): The new #PidginCredentialProviderRow instance. +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. +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. +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. +void pidgin_credential_provider_row_set_active(PidginCredentialProviderRow *row, gboolean active); +#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 + * 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 "pidgincredentialspage.h" +#include "pidgincredentialproviderrow.h" +struct _PidginCredentialsPage { + HdyPreferencesPage parent; + GtkWidget *credential_list; +G_DEFINE_TYPE(PidginCredentialsPage, pidgin_credentials_page, + HDY_TYPE_PREFERENCES_PAGE) +/****************************************************************************** + *****************************************************************************/ +pidgin_credentials_page_create_row(PurpleCredentialProvider *provider, + GtkListBox *box = GTK_LIST_BOX(data); + row = pidgin_credential_provider_row_new(provider); + gtk_list_box_prepend(box, row); +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) { + } else if (is_noop1 && !is_noop2) { + } else if (!is_noop1 && is_noop2) { + /* Sort normally by ID. */ + return g_strcmp0(id1, id2); +pidgin_credential_page_list_row_activated_cb(GtkListBox *box, + G_GNUC_UNUSED gpointer data) + PurpleCredentialManager *manager = NULL; + PurpleCredentialProvider *provider = NULL; + const gchar *id = 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); + purple_debug_warning("credentials-page", "failed to set the active " + "credential provider to '%s': %s", + id, error ? error->message : "unknown error"); +pidgin_credentials_page_set_active_provider(PidginCredentialsPage *page, + 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)); +pidgin_credentials_page_active_provider_changed_cb(const gchar *name, + PidginCredentialsPage *page = PIDGIN_CREDENTIALS_PAGE(data); + pidgin_credentials_page_set_active_provider(page, (const gchar *)value); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +pidgin_credentials_page_finalize(GObject *obj) { + purple_prefs_disconnect_by_handle(obj); + G_OBJECT_CLASS(pidgin_credentials_page_parent_class)->finalize(obj); +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( + 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, + active = purple_prefs_get_string("/purple/credentials/active-provider"); + pidgin_credentials_page_set_active_provider(page, active); +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( + "/im/pidgin/Pidgin/Prefs/credentials.ui" + gtk_widget_class_bind_template_child(widget_class, PidginCredentialsPage, + gtk_widget_class_bind_template_callback(widget_class, + pidgin_credential_page_list_row_activated_cb); +/****************************************************************************** + *****************************************************************************/ +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 + * 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" +#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. + * PIDGIN_TYPE_CREDENTIALS_PAGE: + * The standard _get_type macro for #PidginCredentialsPage. +#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. + * pidgin_credentials_page_new: + * Creates a new #PidginCredentialsPage instance. + * Returns: (transfer full): The new #PidginCredentialsPage instance. +GtkWidget *pidgin_credentials_page_new(void); +#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 is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * 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 +#include <glib/gi18n-lib.h> +#include <glib/gstdio.h> +#include "gtksavedstatuses.h" +#include "gtksmiley-theme.h" +#include "gtkstatus-icon-theme.h" +#include "pidgindebug.h" +#include "pidginprefs.h" +#include "pidginstock.h" +#include <gst/video/videooverlay.h> +#ifdef GDK_WINDOWING_WIN32 +#include <gdk/gdkwin32.h> +#ifdef GDK_WINDOWING_X11 +#ifdef GDK_WINDOWING_QUARTZ +#include <gdk/gdkquartz.h> +#include <libsoup/soup.h> +#define PREFS_OPTIMAL_ICON_SIZE 32 +#define PREFS_MAX_DOWNLOADED_THEME_SIZE 26214400 +typedef struct _PidginPrefCombo PidginPrefCombo; +typedef void (*PidginPrefsBindDropdownCallback)(GtkComboBox *combo_box, + PidginPrefCombo *combo); +struct _PidginPrefCombo { + gint previously_active; + PidginPrefsBindDropdownCallback cb; +struct _PidginPrefsWindow { + PidginPrefCombo hide_new; + GtkWidget *minimize_new_convs; + GtkWidget *close_on_tabs; + PidginPrefCombo tab_side; + /* Conversations page */ + PidginPrefCombo notification_chat; + GtkWidget *show_incoming_formatting; + GtkWidget *close_immediately; + GtkWidget *send_typing; + GtkWidget *use_smooth_scrolling; + GtkWidget *resize_custom_smileys; + GtkWidget *custom_smileys_size; + GtkWidget *minimum_entry_lines; + GtkTextBuffer *format_buffer; + GtkWidget *format_view; + /* Win32 specific frame */ + GtkWidget *use_theme_font; + GtkWidget *custom_font_hbox; + GtkWidget *custom_font; + PidginPrefCombo format; + GtkWidget *stun_server; + GtkWidget *public_ip_hbox; + 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; + GtkWidget *gnome_not_found; + GtkWidget *gnome_program; + gchar *gnome_program_path; + /* Non-GNOME version */ + GtkWidget *socks4_remotedns; + PidginPrefCombo idle_reporting; + GtkWidget *mins_before_away; + GtkWidget *away_when_idle; + PidginPrefCombo auto_reply; + GtkWidget *startup_current_status; + GtkWidget *startup_hbox; + GtkWidget *startup_label; + PidginPrefCombo output; + PidginPrefCombo output; + GtkWidget *sink_widget; +static PidginPrefsWindow *prefs = NULL; +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; +G_DEFINE_TYPE(PidginPrefsWindow, pidgin_prefs_window, GTK_TYPE_DIALOG); +static void delete_prefs(GtkWidget *, void *); +update_spin_value(GtkWidget *w, GtkWidget *spin) + const char *key = g_object_get_data(G_OBJECT(spin), "val"); + value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); + purple_prefs_set_int(key, value); +pidgin_prefs_labeled_spin_button(GtkWidget *box, const gchar *title, + const char *key, int min, int max, GtkSizeGroup *sg) + 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); + gtk_widget_set_size_request(spin, 50, -1); + gtk_widget_set_size_request(spin, 60, -1); + g_signal_connect(G_OBJECT(adjust), "value-changed", + G_CALLBACK(update_spin_value), GTK_WIDGET(spin)); + return pidgin_add_widget_to_vbox(GTK_BOX(box), title, sg, spin, FALSE, NULL); +pidgin_prefs_bind_spin_button(const char *key, GtkWidget *spin) + 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)); +entry_set(GtkEntry *entry, gpointer data) + const char *key = (const char*)data; + purple_prefs_set_string(key, gtk_entry_get_text(entry)); +pidgin_prefs_labeled_entry(GtkWidget *page, const gchar *title, + const char *key, GtkSizeGroup *sg) + 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); +pidgin_prefs_bind_entry(const char *key, GtkWidget *entry) + 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), +pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title, + const char *key, GtkSizeGroup *sg) + 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... */ +typedef void (*PidginPrefsDropdownCallback)(GtkComboBox *combo_box, + PidginPrefValue value); +dropdown_set(GtkComboBox *combo_box, gpointer _cb) + PidginPrefsDropdownCallback cb = _cb; + GtkTreeModel *tree_model; + PidginPrefValue active; + tree_model = gtk_combo_box_get_model(combo_box); + if (!gtk_combo_box_get_active_iter(combo_box, &iter)) + active.type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo_box), + 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); +pidgin_prefs_dropdown_from_list_with_cb(GtkWidget *box, const gchar *title, + GtkComboBox **dropdown_out, GList *menuitems, + PidginPrefValue initial, PidginPrefsDropdownCallback cb) + GtkWidget *label = NULL; + GtkListStore *store = NULL; + 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); + 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; + const char *str_value = NULL; + gboolean bool_value = FALSE; + if (menu_item->key == NULL) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_TEXT, menu_item->key, + 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, + 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, + 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, + 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))) { + 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, + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dropdown), &active); + current_active = GINT_TO_POINTER(gtk_combo_box_get_active(GTK_COMBO_BOX( + 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); +pidgin_prefs_dropdown_from_list_cb(GtkComboBox *combo_box, + 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); +pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title, + PurplePrefType type, const char *key, GList *menuitems) + PidginPrefValue initial; + GtkComboBox *dropdown = NULL; + 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); + 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); +pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type, + GList *menuitems = NULL; + GtkWidget *dropdown = NULL; + g_return_val_if_fail(type == PURPLE_PREF_BOOLEAN || type == PURPLE_PREF_INT || + type == PURPLE_PREF_STRING, NULL); + 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))); + kvp = purple_key_value_pair_new(name, va_arg(ap, char *)); + menuitems = g_list_prepend(menuitems, kvp); + g_return_val_if_fail(menuitems != NULL, NULL); + menuitems = g_list_reverse(menuitems); + dropdown = pidgin_prefs_dropdown_from_list(box, title, type, key, + g_list_free_full(menuitems, (GDestroyNotify)purple_key_value_pair_free); +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); +bind_dropdown_set(GtkComboBox *combo_box, gpointer data) + PidginPrefCombo *combo = data; + GtkTreeModel *tree_model; + tree_model = gtk_combo_box_get_model(combo_box); + if (!gtk_combo_box_get_active_iter(combo_box, &iter)) + 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); +pidgin_prefs_bind_dropdown_from_list(PidginPrefCombo *combo, GList *menuitems) + GtkListStore *store = NULL; + 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); + store = GTK_LIST_STORE( + gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo))); + while (menuitems != NULL && (text = (char *)menuitems->data) != NULL) { + 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, + if (combo->type == PURPLE_PREF_INT) { + int_value = GPOINTER_TO_INT(menuitems->data); + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, int_value, + else if (combo->type == PURPLE_PREF_STRING) { + str_value = (const char *)menuitems->data; + gtk_list_store_set(store, &iter, + PREF_DROPDOWN_VALUE, str_value, + 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, + 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))) { + 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); +pidgin_prefs_bind_dropdown(PidginPrefCombo *combo) + GtkTreeModel *store = 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); + store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)); + if (!gtk_tree_model_get_iter_first(store, &iter)) { + 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, + if (combo->value.integer == int_value) { + else if (combo->type == PURPLE_PREF_STRING) { + gtk_tree_model_get(store, &iter, + PREF_DROPDOWN_VALUE, &str_value, + if (purple_strequal(combo->value.string, str_value)) { + else if (combo->type == PURPLE_PREF_BOOLEAN) { + gtk_tree_model_get(store, &iter, + PREF_DROPDOWN_VALUE, &bool_value, + if (combo->value.boolean == bool_value) { + } 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); +set_bool_pref(GtkWidget *w, const char *key) + purple_prefs_set_bool(key, + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))); +pidgin_prefs_checkbox(const char *text, const char *key, GtkWidget *page) + 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); +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); +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); + prefs_status_themes_combo_box = NULL; + prefs_smiley_themes_combo_box = NULL; + g_free(prefs->proxy.gnome_program_path); +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 : ""); +smileys_refresh_theme_list(void) + 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); + 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), + 2, pidgin_smiley_theme_get_name(theme), +/* adds the themes to the theme list from the manager so they can be displayed in prefs */ +prefs_themes_sort(PurpleTheme *theme) + GdkPixbuf *pixbuf = NULL; + gchar *image_full = NULL, *markup; + const gchar *name, *author, *description; + if (PIDGIN_IS_STATUS_ICON_THEME(theme)){ + 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); + 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_object_unref(G_OBJECT(pixbuf)); +prefs_set_active_theme_combo(GtkWidget *combo_box, GtkListStore *store, const gchar *current_theme) + if (current_theme && *current_theme && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) { + 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); + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0); +prefs_themes_refresh(void) + GdkPixbuf *pixbuf = NULL; + /* 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); + /* 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_object_unref(G_OBJECT(pixbuf)); + gtk_list_store_clear(prefs_smiley_themes); + purple_theme_manager_for_each_theme(prefs_themes_sort); + smileys_refresh_theme_list(); + 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 */ + 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 + * 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. +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); + 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); +/* Eww. Seriously ewww. But thanks, grim! This is taken from guifications2 */ +purple_theme_file_copy(const gchar *source, const gchar *destination) + if(!(src = g_fopen(source, "rb"))) + if(!(dest = g_fopen(destination, "wb"))) { + while((chr = fgetc(src)) != EOF) { +free_theme_info(struct theme_info *info) + g_free(info->extension); + g_free(info->original_name); +/* installs a theme, info is freed by function */ +theme_install_theme(char *path, struct theme_info *info) + gboolean is_smiley_theme, is_archive; + PurpleTheme *theme = NULL; + /* check the extension */ + tail = info->extension ? info->extension : strrchr(path, '.'); + is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz"); + if ((is_smiley_theme = purple_strequal(info->type, "smiley"))) + destdir = g_build_filename(purple_data_dir(), "smileys", NULL); + 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 */ + gchar *path_escaped = g_shell_quote(path); + gchar *destdir_escaped = g_shell_quote(destdir); + 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(destdir_escaped); + purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL); + if (!winpidgin_gz_untar(path, destdir)) { + purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL); + /* 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); + 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, + if (g_remove(destdir) != 0) { + purple_debug_error("gtkprefs", + "couldn't remove temp (dest) path\n"); + prefs_themes_refresh(); + /* something was wrong with the theme archive */ + 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", + 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); + gchar *source_name = g_path_get_basename(path); + temp_file = g_build_filename(temp_path, source_name, NULL); + 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)) { + g_build_filename(purple_data_dir(), "themes", + purple_theme_get_name(theme), "purple", + 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, + prefs_themes_refresh(); + 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); + purple_notify_error(NULL, NULL, _("Theme failed to copy."), NULL, NULL); +theme_got_url(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg, + struct theme_info *info = _info; + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + f = purple_mkstemp(&path, TRUE); + wc = fwrite(msg->response_body->data, msg->response_body->length, 1, f); + purple_debug_warning("theme_got_url", "Unable to write theme data.\n"); + theme_install_theme(path, info); +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 */ + 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; + /* 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"); + theme_install_theme(tmp, info); + } 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 */ + 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, + 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 */ +prefs_build_theme_combo_box(GtkWidget *combo_box, GtkListStore *store, + const char *current_theme, const char *type) + GtkTargetEntry te[3] = { + {"text/uri-list", 0, 1}, + 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 */ +prefs_set_smiley_theme_cb(GtkComboBox *combo_box, gpointer user_data) + 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); +/* Does same as normal sort, except "none" is sorted first */ +static gint pidgin_sort_smileys (GtkTreeModel *model, + 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)) + } else if (!g_ascii_strcasecmp(name2, "none")) { + /* Neither string is "none", default to normal sort */ + ret = purple_utf8_strcasecmp(name1, name2); +/* sets the current icon theme */ +prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data) + PidginStatusIconTheme *theme = 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); + theme = PIDGIN_STATUS_ICON_THEME(purple_theme_manager_find_theme(name, "status-icon")); + pidgin_stock_load_status_icon_theme(theme); + pidgin_blist_refresh(purple_blist_get_default()); +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", + prefs_status_themes_combo_box = win->theme.status; + prefs_build_theme_combo_box(win->theme.smiley, prefs_smiley_themes, + PIDGIN_PREFS_ROOT "/smileys/theme", + 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); +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", + } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_ITALIC, name) != 0) { + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic", + } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_UNDERLINE, name) != 0) { + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", + } else if(g_ascii_strcasecmp(TALKATU_ACTION_FORMAT_STRIKETHROUGH, name) != 0) { + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike", +bind_interface_page(PidginPrefsWindow *win) + 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); + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", + win->iface.win32.minimize_new_convs); + gtk_widget_hide(win->iface.win32.minimize_new_convs); + /* 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. */ +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); + pango_font_description_free(desc); +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); +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); + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/win32/blink_im", + win->conversations.win32.blink_im); + gtk_widget_hide(win->conversations.win32.blink_im); + /* 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); + pidgin_prefs_bind_spin_button( + PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines", + win->conversations.minimum_entry_lines); + 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), + g_object_bind_property(win->conversations.use_theme_font, "active", + win->conversations.custom_font_hbox, "sensitive", + G_BINDING_SYNC_CREATE|G_BINDING_INVERT_BOOLEAN); + 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); +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 (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"); + gtk_style_context_add_class(context, "bad-ip"); + gtk_style_context_remove_class(context, "good-ip"); + purple_network_set_public_ip(""); + gtk_style_context_remove_class(context, "bad-ip"); + gtk_style_context_remove_class(context, "good-ip"); +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)); +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)); +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); + gtk_widget_hide(win->proxy.options); +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))); +proxy_button_clicked_cb(GtkWidget *button, PidginPrefsWindow *win) + if (g_spawn_command_line_async(win->proxy.gnome_program_path, &err)) + purple_notify_error(NULL, NULL, _("Cannot start proxy configuration program."), err->message, NULL); +auto_ip_button_clicked_cb(GtkWidget *button, gpointer null) + PurpleStunNatDiscovery *stun; + /* 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)) { + /* Attempt to get the IP from a NAT device using UPnP */ + ip = purple_upnp_get_public_ip(); + /* Attempt to get the IP from a NAT device using NAT-PMP */ + ip = purple_pmp_get_public_ip(); + /* Just fetch the first IP of the local system */ + list = nice_interfaces_get_local_ips(FALSE); + auto_ip_text = g_strdup_printf(_("Use _automatically detected IP address: %s"), ip); + gtk_button_set_label(GTK_BUTTON(button), auto_ip_text); + g_list_free_full(list, g_free); +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", + 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); + 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); +bind_proxy_page(PidginPrefsWindow *win) + PurpleProxyInfo *proxy_info; + if(purple_running_gnome()) { + gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack), + path = g_find_program_in_path("gnome-network-properties"); + path = g_find_program_in_path("gnome-network-preferences"); + path = g_find_program_in_path("gnome-control-center"); + char *tmp = g_strdup_printf("%s network", path); + win->proxy.gnome_program_path = path; + gtk_widget_set_visible(win->proxy.gnome_not_found, + gtk_widget_set_visible(win->proxy.gnome_program, + gtk_stack_set_visible_child_name(GTK_STACK(win->proxy.stack), + /* 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"), +bind_logging_page(PidginPrefsWindow *win) + 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); + pidgin_prefs_bind_checkbox("/purple/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); +set_idle_away(PurpleSavedStatus *status) + purple_prefs_set_int("/purple/savedstatus/idleaway", purple_savedstatus_get_creation_time(status)); +set_startupstatus(PurpleSavedStatus *status) + purple_prefs_set_int("/purple/savedstatus/startup", purple_savedstatus_get_creation_time(status)); +bind_away_page(PidginPrefsWindow *win) + 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", + G_BINDING_SYNC_CREATE); + 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); +get_vv_device_menuitems(PurpleMediaElementType type) + i = purple_media_manager_enumerate_elements(purple_media_manager_get(), + 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)); +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, +vv_test_switch_page_cb(GtkStack *stack, G_GNUC_UNUSED GParamSpec *pspec, + 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); +create_voice_pipeline(void) + GstElement *src, *sink; + 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"); +on_volume_change_cb(GtkWidget *w, gdouble value, gpointer data) + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + if (!win->vv.voice.pipeline) { + 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); +gst_msg_db_to_percent(GstMessage *msg, gchar *value_name) + 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; +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")) { + 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)) / + 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") : " ", +voice_test_destroy_cb(GtkWidget *w, gpointer data) + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + if (!win->vv.voice.pipeline) { + gst_element_set_state(win->vv.voice.pipeline, GST_STATE_NULL); + g_clear_pointer(&win->vv.voice.pipeline, gst_object_unref); +enable_voice_test(PidginPrefsWindow *win) + 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); +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); + 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, + voice_test_destroy_cb(NULL, win); +volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data) + purple_prefs_set_int("/purple/media/audio/volume/input", value * 100); +threshold_value_changed_cb(GtkScale *scale, GtkWidget *label) + 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); + purple_prefs_set_int("/purple/media/audio/silence_threshold", value); +bind_voice_test(PidginPrefsWindow *win, GtkBuilder *builder) + 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); + 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); + 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); +create_video_pipeline(void) + 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, + gst_element_link_many(src, videoconvert, videoscale, sink, NULL); +video_test_destroy_cb(GtkWidget *w, gpointer data) + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data); + if (!win->vv.video.pipeline) { + gst_element_set_state(win->vv.video.pipeline, GST_STATE_NULL); + g_clear_pointer(&win->vv.video.pipeline, gst_object_unref); +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), +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); + g_object_disconnect(test, "any-signal::destroy", + G_CALLBACK(video_test_destroy_cb), win, + video_test_destroy_cb(NULL, win); +bind_video_test(PidginPrefsWindow *win, GtkBuilder *builder) + 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); +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); +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"; +bind_vv_dropdown(PidginPrefCombo *combo, PurpleMediaElementType element_type) + const gchar *preference_key; + 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); + 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); +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), + g_object_set_data(G_OBJECT(combo->combo), "vv_media_type", + g_object_set_data(G_OBJECT(combo->combo), "vv_combo", combo); +device_list_changed_cb(PurpleMediaManager *manager, GtkWidget *widget) + PidginPrefCombo *combo; + PurpleMediaElementType media_type; + combo = g_object_get_data(G_OBJECT(widget), "vv_combo"); + media_type = (PurpleMediaElementType)g_object_get_data(G_OBJECT(widget), + /* 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); +vv_page(PidginPrefsWindow *win) + 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_unref(builder); +prefs_stack_init(PidginPrefsWindow *win) + GtkStack *stack = GTK_STACK(win->stack); + bind_interface_page(win); + bind_logging_page(win); + bind_network_page(win); + gtk_container_add_with_properties(GTK_CONTAINER(stack), vv, "name", + "vv", "title", _("Voice/Video"), +pidgin_prefs_window_class_init(PidginPrefsWindowClass *klass) + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + gtk_widget_class_set_template_from_resource( + "/im/pidgin/Pidgin/Prefs/prefs.ui" + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow, + gtk_widget_class_bind_template_callback(widget_class, delete_prefs); + 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); + 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); + /* Even though Win32-specific, must be bound to avoid Glade warnings. */ + gtk_widget_class_bind_template_callback(widget_class, + gtk_widget_class_bind_template_callback(widget_class, + pidgin_custom_font_set); + 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); + 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, + gtk_widget_class_bind_template_callback(widget_class, + network_turn_server_changed_cb); + 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, + 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); + 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); +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)); + /* Refresh the list of themes before showing the preferences window */ + prefs_themes_refresh(); + prefs = PIDGIN_PREFS_WINDOW(g_object_new( + pidgin_prefs_window_get_type(), NULL)); + gtk_window_present(GTK_WINDOW(prefs)); +smiley_theme_pref_cb(const char *name, PurplePrefType type, + gconstpointer value, gpointer data) + const gchar *theme_name = value; + if (purple_strequal(theme_name, "none")) { + purple_smiley_theme_set_current(NULL); + /* 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)) + purple_smiley_theme_set_current(PURPLE_SMILEY_THEME(theme)); + purple_prefs_add_none(PIDGIN_PREFS_ROOT ""); + purple_prefs_add_none("/plugins/gtk"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/plugins"); + purple_prefs_add_path_list(PIDGIN_PREFS_ROOT "/plugins/loaded", NULL); + 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", ""); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default"); + purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme", + smiley_theme_pref_cb, NULL); + 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", ""); + pidgin_prefs_update_old(); +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"; + /* 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 is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * 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" + * @section_id: pidgin-pidginprefs + * @short_description: <filename>pidginprefs.h</filename> +#define PIDGIN_TYPE_PREFS_WINDOW (pidgin_prefs_window_get_type()) +G_DECLARE_FINAL_TYPE(PidginPrefsWindow, pidgin_prefs_window, PIDGIN, PREFS_WINDOW, GtkDialog) + * Initializes all UI-specific preferences. +void pidgin_prefs_init(void); + * 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, + * 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 +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 +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 +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, + * pidgin_prefs_update_old: + * Rename legacy prefs and delete some that no longer exist. +void pidgin_prefs_update_old(void); +#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 @@
@@ -353,8 +352,6 @@
pidgin/pidgincontactcompletion.c
pidgin/pidgincontactlist.c
pidgin/pidginconversationwindow.c
-pidgin/pidgincredentialproviderrow.c
-pidgin/pidgincredentialspage.c
@@ -391,6 +388,9 @@
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