--- a/pidgin/pidginaccounteditor.c Thu Sep 15 20:59:06 2022 -0500
+++ b/pidgin/pidginaccounteditor.c Thu Sep 15 22:32:05 2022 -0500
@@ -22,17 +22,48 @@
#include <glib/gi18n-lib.h>
#include "pidginaccounteditor.h"
-#include "pidginproxyoptions.h"
+#include "pidginprotocolchooser.h" struct _PidginAccountEditor {
+ GtkWidget *login_options; + GList *user_split_entries; + GList *user_split_rows; + GtkFileChooserNative *avatar_dialog; + GdkPixbuf *avatar_pixbuf; + GtkWidget *use_custom_avatar; + GtkWidget *advanced_group; + GtkWidget *advanced_toggle; + GList *advanced_entries; GtkWidget *proxy_options;
+ GtkWidget *proxy_username; + GtkWidget *proxy_password; @@ -42,49 +73,833 @@
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
+/****************************************************************************** + *****************************************************************************/ +static void pidgin_account_editor_connection_changed_cb(GObject *obj, /******************************************************************************
*****************************************************************************/
+pidgin_account_editor_add_user_split(gpointer data, gpointer user_data) { + PurpleAccountUserSplit *split = data; + PidginAccountEditor *editor = user_data; + GtkWidget *entry = NULL; + if(!purple_account_user_split_is_constant(split)) { + row = adw_action_row_new(); + editor->user_split_rows = g_list_append(editor->user_split_rows, row); + adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->login_options), + adw_preferences_row_set_title(ADW_PREFERENCES_ROW(row), + purple_account_user_split_get_text(split)); + entry = gtk_entry_new(); + gtk_widget_set_hexpand(entry, TRUE); + gtk_widget_set_valign(entry, GTK_ALIGN_CENTER); + adw_action_row_add_suffix(ADW_ACTION_ROW(row), entry); + adw_action_row_set_activatable_widget(ADW_ACTION_ROW(row), entry); + editor->user_split_entries = g_list_append(editor->user_split_entries, +pidgin_account_editor_update_login_options(PidginAccountEditor *editor, + PurpleProtocol *protocol) + GList *user_splits = NULL; + GList *split_item = NULL; + GList *entry_item = NULL; + gchar *username = NULL; + /* Clear out the old user splits from our list. */ + g_clear_pointer(&editor->user_split_entries, g_list_free); + /* Now remove the rows we added to the preference group for each non + while(editor->user_split_rows != NULL) { + adw_preferences_group_remove(ADW_PREFERENCES_GROUP(editor->login_options), + editor->user_split_rows->data); + editor->user_split_rows = g_list_delete_link(editor->user_split_rows, + editor->user_split_rows); + /* Add the user splits for the protocol. */ + user_splits = purple_protocol_get_user_splits(protocol); + g_list_foreach(user_splits, pidgin_account_editor_add_user_split, editor); + /* If we have an account, populate its values. */ + if(PURPLE_IS_ACCOUNT(editor->account)) { + /* The username will be split apart below and eventually set as the text + * in the username entry. + username = g_strdup(purple_account_get_username(editor->account)); + /* Filling out the user splits is a pain. If we have an account, we created + * a copy of the username above. We then iterate the user splits backwards + * so we can insert a null terminator at the start of each split we find in + split_item = g_list_last(user_splits); + entry_item = g_list_last(editor->user_split_entries); + while(split_item != NULL && entry_item != NULL) { + GtkWidget *entry = entry_item->data; + PurpleAccountUserSplit *split = split_item->data; + const gchar *value = NULL; + gchar sep = purple_account_user_split_get_separator(split); + if(purple_account_user_split_get_reverse(split)) { + ptr = strrchr(username, sep); + ptr = strchr(username, sep); + /* Insert a null terminator in place of the separator. */ + /* Set the value to the first byte after the separator. */ + value = purple_account_user_split_get_default_value(split); + if(value != NULL && GTK_IS_ENTRY(entry)) { + gtk_editable_set_text(GTK_EDITABLE(entry), value); + split_item = split_item->prev; + entry_item = entry_item->prev; + /* Free the user splits. */ + g_list_free_full(user_splits, + (GDestroyNotify)purple_account_user_split_destroy); + /* Set the username entry to the remaining text in username and free our + * copy of said username. + gtk_editable_set_text(GTK_EDITABLE(editor->username), username); +pidgin_account_editor_update_user_options(PidginAccountEditor *editor, + PurpleProtocol *protocol) + PurpleBuddyIconSpec *icon_spec = NULL; + PurpleImage *image = NULL; + gboolean show_avatar_opts = TRUE; + const gchar *svalue = ""; + gboolean use_global = TRUE; + /* Check if the protocol supports avatars. */ + icon_spec = purple_protocol_get_icon_spec(protocol); + show_avatar_opts = (icon_spec != NULL && icon_spec->format != NULL); + purple_buddy_icon_spec_free(icon_spec); + gtk_widget_set_visible(editor->avatar_row, show_avatar_opts); + /* Determine our values. */ + if(editor->account != NULL) { + svalue = purple_account_get_private_alias(editor->account); + image = purple_buddy_icons_find_account_icon(editor->account); + use_global = purple_account_get_bool(editor->account, + "use-global-buddyicon", TRUE); + gtk_editable_set_text(GTK_EDITABLE(editor->alias), svalue); + gtk_switch_set_active(GTK_SWITCH(editor->use_custom_avatar), !use_global); + g_clear_object(&editor->avatar_pixbuf); + if(PURPLE_IS_IMAGE(image)) { + editor->avatar_pixbuf = purple_gdk_pixbuf_from_image(image); + gtk_image_set_from_pixbuf(GTK_IMAGE(editor->avatar), + editor->avatar_pixbuf); + gtk_image_set_from_icon_name(GTK_IMAGE(editor->avatar), +pidgin_account_editor_advanced_option_use_default(PidginAccountEditor *editor) { + PurpleProtocol *protocol = NULL; + /* If this is the new dialog, use the default value. */ + if(!PURPLE_IS_ACCOUNT(editor->account)) { + /* If we have an existing account, check if the protocol has changed. */ + protocol = pidgin_protocol_chooser_get_protocol(PIDGIN_PROTOCOL_CHOOSER(editor->protocol)); + if(protocol != purple_account_get_protocol(editor->account)) { +pidgin_account_editor_add_advanced_boolean(PidginAccountEditor *editor, + PurpleAccountOption *option) + GtkWidget *toggle = NULL; + gboolean value = FALSE; + if(pidgin_account_editor_advanced_option_use_default(editor)) { + value = purple_account_option_get_default_bool(option); + const gchar *setting = purple_account_option_get_setting(option); + gboolean def_value = purple_account_option_get_default_bool(option); + value = purple_account_get_bool(editor->account, setting, def_value); + /* Create the row and set its title with a mnemonic. */ + row = adw_action_row_new(); + g_object_bind_property(editor->advanced_toggle, "active", row, "visible", + G_BINDING_SYNC_CREATE); + adw_preferences_row_set_use_underline(ADW_PREFERENCES_ROW(row), TRUE); + title = g_strdup_printf("_%s", purple_account_option_get_text(option)); + adw_preferences_row_set_title(ADW_PREFERENCES_ROW(row), title); + adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group), + /* Add the row to the editor's list of advanced rows. */ + editor->advanced_rows = g_list_append(editor->advanced_rows, row); + /* Create the input widget. */ + toggle = gtk_switch_new(); + gtk_switch_set_active(GTK_SWITCH(toggle), value); + gtk_widget_set_valign(toggle, GTK_ALIGN_CENTER); + adw_action_row_add_suffix(ADW_ACTION_ROW(row), toggle); + adw_action_row_set_activatable_widget(ADW_ACTION_ROW(row), toggle); +pidgin_account_editor_add_advanced_int(PidginAccountEditor *editor, + PurpleAccountOption *option) + GtkWidget *entry = NULL; + if(pidgin_account_editor_advanced_option_use_default(editor)) { + value = purple_account_option_get_default_int(option); + const gchar *setting = purple_account_option_get_setting(option); + gint def_value = purple_account_option_get_default_int(option); + value = purple_account_get_int(editor->account, setting, def_value); + /* Create the row and set its title with a mnemonic. */ + row = adw_action_row_new(); + g_object_bind_property(editor->advanced_toggle, "active", row, "visible", + G_BINDING_SYNC_CREATE); + adw_preferences_row_set_use_underline(ADW_PREFERENCES_ROW(row), TRUE); + title = g_strdup_printf("_%s", purple_account_option_get_text(option)); + adw_preferences_row_set_title(ADW_PREFERENCES_ROW(row), title); + adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group), + /* Add the row to the editor's list of advanced rows. */ + editor->advanced_rows = g_list_append(editor->advanced_rows, row); + /* Create the input widget. */ + entry = gtk_entry_new(); + gtk_entry_set_input_purpose(GTK_ENTRY(entry), GTK_INPUT_PURPOSE_DIGITS); + svalue = g_strdup_printf("%d", value); + gtk_editable_set_text(GTK_EDITABLE(entry), svalue); + gtk_widget_set_hexpand(entry, TRUE); + gtk_widget_set_valign(entry, GTK_ALIGN_CENTER); + adw_action_row_add_suffix(ADW_ACTION_ROW(row), entry); + adw_action_row_set_activatable_widget(ADW_ACTION_ROW(row), entry); +pidgin_account_editor_add_advanced_string(PidginAccountEditor *editor, + PurpleAccountOption *option) + GtkWidget *entry = NULL; + const gchar *value = NULL; + if(pidgin_account_editor_advanced_option_use_default(editor)) { + value = purple_account_option_get_default_string(option); + const gchar *setting = purple_account_option_get_setting(option); + const gchar *def_value = NULL; + def_value = purple_account_option_get_default_string(option); + value = purple_account_get_string(editor->account, setting, def_value); + /* Create the row and set its title with a mnemonic. */ + row = adw_action_row_new(); + g_object_bind_property(editor->advanced_toggle, "active", row, "visible", + G_BINDING_SYNC_CREATE); + adw_preferences_row_set_use_underline(ADW_PREFERENCES_ROW(row), TRUE); + title = g_strdup_printf("_%s", purple_account_option_get_text(option)); + adw_preferences_row_set_title(ADW_PREFERENCES_ROW(row), title); + adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group), + /* Add the row to the editor's list of advanced rows. */ + editor->advanced_rows = g_list_append(editor->advanced_rows, row); + /* Create the input widget. */ + if(purple_account_option_string_get_masked(option)) { + entry = gtk_password_entry_new(); + entry = gtk_entry_new(); + gtk_editable_set_text(GTK_EDITABLE(entry), value); + gtk_widget_set_hexpand(entry, TRUE); + gtk_widget_set_valign(entry, GTK_ALIGN_CENTER); + adw_action_row_add_suffix(ADW_ACTION_ROW(row), entry); + adw_action_row_set_activatable_widget(ADW_ACTION_ROW(row), entry); +pidgin_account_editor_add_advanced_list(PidginAccountEditor *editor, + PurpleAccountOption *option) + GtkStringList *model = NULL; + const gchar *value = FALSE; + if(pidgin_account_editor_advanced_option_use_default(editor)) { + value = purple_account_option_get_default_list_value(option); + const gchar *setting = purple_account_option_get_setting(option); + const gchar *def_value = NULL; + def_value = purple_account_option_get_default_list_value(option); + value = purple_account_get_string(editor->account, setting, def_value); + /* Create the row and set its title with a mnemonic. */ + row = adw_combo_row_new(); + g_object_bind_property(editor->advanced_toggle, "active", row, "visible", + G_BINDING_SYNC_CREATE); + adw_preferences_row_set_use_underline(ADW_PREFERENCES_ROW(row), TRUE); + adw_combo_row_set_use_subtitle(ADW_COMBO_ROW(row), TRUE); + title = g_strdup_printf("_%s", purple_account_option_get_text(option)); + adw_preferences_row_set_title(ADW_PREFERENCES_ROW(row), title); + adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group), + /* Add the row to the editor's list of advanced rows. */ + editor->advanced_rows = g_list_append(editor->advanced_rows, row); + /* Create the model and data for the expression. */ + items = purple_account_option_get_list(option); + model = gtk_string_list_new(NULL); + for(GList *l = items; l != NULL; l = l->next) { + PurpleKeyValuePair *kvp = l->data; + if(purple_strequal(kvp->value, value)) { + data = g_list_append(data, kvp->value); + gtk_string_list_append(model, kvp->key); + adw_combo_row_set_model(ADW_COMBO_ROW(row), G_LIST_MODEL(model)); + adw_combo_row_set_selected(ADW_COMBO_ROW(row), position); + g_object_set_data_full(G_OBJECT(row), "keys", data, + (GDestroyNotify)g_list_free); +pidgin_account_editor_add_advanced_option(PidginAccountEditor *editor, + PurpleAccountOption *option) + GtkWidget *widget = NULL; + type = purple_account_option_get_pref_type(option); + case PURPLE_PREF_BOOLEAN: + widget = pidgin_account_editor_add_advanced_boolean(editor, option); + widget = pidgin_account_editor_add_advanced_int(editor, option); + case PURPLE_PREF_STRING: + widget = pidgin_account_editor_add_advanced_string(editor, option); + case PURPLE_PREF_STRING_LIST: + widget = pidgin_account_editor_add_advanced_list(editor, option); + purple_debug_error("PidginAccountEditor", + "Invalid Account Option pref type (%d)", type); + if(GTK_IS_WIDGET(widget)) { + g_object_set_data_full(G_OBJECT(widget), "option", option, + (GDestroyNotify)purple_account_option_destroy); + editor->advanced_entries = g_list_append(editor->advanced_entries, + purple_account_option_destroy(option); +pidgin_account_editor_update_advanced_options(PidginAccountEditor *editor, + PurpleProtocol *protocol) + g_clear_pointer(&editor->advanced_entries, g_list_free); + while(editor->advanced_rows != NULL) { + adw_preferences_group_remove(ADW_PREFERENCES_GROUP(editor->advanced_group), + GTK_WIDGET(editor->advanced_rows->data)); + editor->advanced_rows = g_list_delete_link(editor->advanced_rows, + editor->advanced_rows); + if(!PURPLE_IS_PROTOCOL(protocol)) { + gtk_widget_set_visible(editor->advanced_group, FALSE); + options = purple_protocol_get_account_options(protocol); + gtk_widget_set_visible(editor->advanced_group, FALSE); + /* Iterate the options and call our helper which will take ownership of the + * option itself, but we'll delete the list item as we go. + while(options != NULL) { + pidgin_account_editor_add_advanced_option(editor, options->data); + options = g_list_delete_link(options, options); + gtk_widget_set_visible(editor->advanced_group, TRUE); +pidgin_account_editor_update_proxy_options(PidginAccountEditor *editor) { + PurpleProxyInfo *info = NULL; + GListModel *model = NULL; + const gchar *svalue = NULL; + if(!PURPLE_IS_ACCOUNT(editor->account)) { + info = purple_account_get_proxy_info(editor->account); + switch(purple_proxy_info_get_proxy_type(info)) { + case PURPLE_PROXY_TYPE_USE_GLOBAL: + case PURPLE_PROXY_TYPE_NONE: + case PURPLE_PROXY_TYPE_SOCKS4: + case PURPLE_PROXY_TYPE_SOCKS5: + case PURPLE_PROXY_TYPE_TOR: + case PURPLE_PROXY_TYPE_HTTP: + case PURPLE_PROXY_TYPE_USE_ENVVAR: + model = adw_combo_row_get_model(ADW_COMBO_ROW(editor->proxy_type)); + for(guint i = 0; i < g_list_model_get_n_items(model); i++) { + GtkStringObject *obj = g_list_model_get_item(model, i); + if(purple_strequal(svalue, gtk_string_object_get_string(obj))) { + adw_combo_row_set_selected(ADW_COMBO_ROW(editor->proxy_type), position); + svalue = purple_proxy_info_get_hostname(info); + gtk_editable_set_text(GTK_EDITABLE(editor->proxy_host), svalue); + ivalue = purple_proxy_info_get_port(info); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(editor->proxy_port), + svalue = purple_proxy_info_get_username(info); + gtk_editable_set_text(GTK_EDITABLE(editor->proxy_username), svalue); + svalue = purple_proxy_info_get_password(info); + gtk_editable_set_text(GTK_EDITABLE(editor->proxy_password), svalue); +pidgin_account_editor_update(PidginAccountEditor *editor) { + PurpleProtocol *protocol = NULL; + gboolean sensitive = FALSE; + if(PURPLE_IS_ACCOUNT(editor->account)) { + PurpleConnection *connection = NULL; + connection = purple_account_get_connection(editor->account); + if(PURPLE_IS_CONNECTION(connection)) { + gtk_widget_set_sensitive(editor->protocol, FALSE); + protocol = pidgin_protocol_chooser_get_protocol(PIDGIN_PROTOCOL_CHOOSER(editor->protocol)); + sensitive = pidgin_account_editor_update_login_options(editor, protocol); + pidgin_account_editor_update_user_options(editor, protocol); + pidgin_account_editor_update_advanced_options(editor, protocol); + pidgin_account_editor_update_proxy_options(editor); + gtk_dialog_set_response_sensitive(GTK_DIALOG(editor), GTK_RESPONSE_APPLY, +pidgin_account_editor_login_options_update_editable(PidginAccountEditor *editor) + PurpleConnection *connection = NULL; + gboolean editable = TRUE; + if(PURPLE_IS_ACCOUNT(editor->account)) { + connection = purple_account_get_connection(editor->account); + /* If we have an active connection, we need to disable everything + * related to the protocol and username. + if(PURPLE_IS_CONNECTION(connection)) { + PidginProtocolChooser *chooser = NULL; + PurpleProtocol *connected_protocol = NULL; + PurpleProtocol *selected_protocol = NULL; + /* Check if the user changed the protocol. If they did, switch it + * back and update the editor to reflect what settings are active. + connected_protocol = purple_connection_get_protocol(connection); + chooser = PIDGIN_PROTOCOL_CHOOSER(editor->protocol); + selected_protocol = pidgin_protocol_chooser_get_protocol(chooser); + if(connected_protocol != selected_protocol) { + pidgin_protocol_chooser_set_protocol(chooser, connected_protocol); + pidgin_account_editor_update(editor); + gtk_widget_set_sensitive(editor->protocol, editable); + gtk_editable_set_editable(GTK_EDITABLE(editor->username), editable); + for(GList *l = editor->user_split_entries; l != NULL; l = l->next) { + GtkWidget *widget = l->data; + gtk_editable_set_editable(GTK_EDITABLE(widget), editable); pidgin_account_editor_set_account(PidginAccountEditor *editor,
if(g_set_object(&editor->account, account)) {
- PurpleProxyInfo *proxy_info = NULL;
+ if(PURPLE_IS_ACCOUNT(account)) { + g_signal_connect(account, "notify::connection", + G_CALLBACK(pidgin_account_editor_connection_changed_cb), + g_object_notify_by_pspec(G_OBJECT(editor), properties[PROP_ACCOUNT]); + pidgin_account_editor_update(editor); +pidgin_account_editor_save_login_options(PidginAccountEditor *editor) { + PurpleProtocol *protocol = NULL; + GList *split_item = NULL, *entry_item = NULL; + GString *username = NULL; + const gchar *protocol_id = NULL; + gboolean new_account = FALSE; - if(PURPLE_IS_ACCOUNT(account)) {
- proxy_info = purple_account_get_proxy_info(account);
+ protocol = pidgin_protocol_chooser_get_protocol(PIDGIN_PROTOCOL_CHOOSER(editor->protocol)); + protocol_id = purple_protocol_get_id(protocol); + username = g_string_new(gtk_editable_get_text(GTK_EDITABLE(editor->username))); + split_item = purple_protocol_get_user_splits(protocol); + entry_item = editor->user_split_entries; + while(split_item != NULL && entry_item != NULL) { + PurpleAccountUserSplit *split = split_item->data; + GtkEntry *entry = entry_item->data; + const gchar *value = ""; + sep = purple_account_user_split_get_separator(split); + g_string_append_c(username, sep); + if(GTK_IS_ENTRY(entry)) { + value = gtk_editable_get_text(GTK_EDITABLE(entry)); + if(value == NULL || *value == '\0') { + value = purple_account_user_split_get_default_value(split); - pidgin_proxy_options_set_info(PIDGIN_PROXY_OPTIONS(editor->proxy_options),
+ g_string_append(username, value); + split_item = split_item->next; + entry_item = entry_item->next; + if(!PURPLE_IS_ACCOUNT(editor->account)) { + editor->account = purple_account_new(username->str, protocol_id); + purple_account_set_username(editor->account, username->str); + purple_account_set_protocol_id(editor->account, protocol_id); + g_string_free(username, TRUE); - g_object_notify_by_pspec(G_OBJECT(editor), properties[PROP_ACCOUNT]);
+pidgin_account_editor_save_user_options(PidginAccountEditor *editor) { + const gchar *svalue = NULL; + gboolean bvalue = FALSE; + svalue = gtk_editable_get_text(GTK_EDITABLE(editor->alias)); + purple_account_set_private_alias(editor->account, svalue); + /* Set whether or not to use the global avatar. */ + bvalue = gtk_switch_get_active(GTK_SWITCH(editor->use_custom_avatar)); + purple_account_set_bool(editor->account, "use-global-buddyicon", !bvalue); + if(GDK_IS_PIXBUF(editor->avatar_pixbuf)) { + # warning implement this when buddy icons do not suck so bad. + purple_buddy_icons_set_account_icon(editor->account, NULL, 0); + # warning set the global buddy icon when buddy icons do not suck so bad. +pidgin_account_editor_save_advanced_options(PidginAccountEditor *editor) { + for(GList *l = editor->advanced_entries; l != NULL; l = l->next) { + GtkWidget *widget = l->data; + PurpleAccountOption *option = NULL; + const gchar *setting = NULL; + const gchar *svalue = NULL; + gboolean bvalue = FALSE; + option = g_object_get_data(G_OBJECT(widget), "option"); + setting = purple_account_option_get_setting(option); + switch(purple_account_option_get_pref_type(option)) { + case PURPLE_PREF_STRING: + svalue = gtk_editable_get_text(GTK_EDITABLE(widget)); + purple_account_set_string(editor->account, setting, svalue); + svalue = gtk_editable_get_text(GTK_EDITABLE(widget)); + purple_account_set_int(editor->account, setting, ivalue); + case PURPLE_PREF_BOOLEAN: + bvalue = gtk_switch_get_active(GTK_SWITCH(widget)); + purple_account_set_bool(editor->account, setting, bvalue); + case PURPLE_PREF_STRING_LIST: + keys = g_object_get_data(G_OBJECT(widget), "keys"); + selected = adw_combo_row_get_selected(ADW_COMBO_ROW(widget)); + svalue = g_list_nth_data(keys, selected); + purple_account_set_string(editor->account, setting, svalue); +pidgin_account_editor_save_proxy(PidginAccountEditor *editor, + PurpleProxyInfo *info = NULL; + PurpleProxyType type = PURPLE_PROXY_TYPE_NONE; + const gchar *svalue = NULL; + /* Build the ProxyInfo object */ + info = purple_proxy_info_new(); + purple_account_set_proxy_info(editor->account, info); + info = purple_account_get_proxy_info(editor->account); + item = adw_combo_row_get_selected_item(ADW_COMBO_ROW(editor->proxy_type)); + svalue = gtk_string_object_get_string(GTK_STRING_OBJECT(item)); + if(purple_strequal(svalue, "global")) { + type = PURPLE_PROXY_TYPE_USE_GLOBAL; + } else if(purple_strequal(svalue, "none")) { + type = PURPLE_PROXY_TYPE_NONE; + } else if(purple_strequal(svalue, "socks4")) { + type = PURPLE_PROXY_TYPE_SOCKS4; + } else if(purple_strequal(svalue, "socks5")) { + type = PURPLE_PROXY_TYPE_SOCKS5; + } else if(purple_strequal(svalue, "tor")) { + type = PURPLE_PROXY_TYPE_TOR; + } else if(purple_strequal(svalue, "http")) { + type = PURPLE_PROXY_TYPE_HTTP; + } else if(purple_strequal(svalue, "envvar")) { + type = PURPLE_PROXY_TYPE_USE_ENVVAR; + purple_proxy_info_set_proxy_type(info, type); + svalue = gtk_editable_get_text(GTK_EDITABLE(editor->proxy_host)); + purple_proxy_info_set_hostname(info, svalue); + ivalue = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editor->proxy_port)); + purple_proxy_info_set_port(info, ivalue); + svalue = gtk_editable_get_text(GTK_EDITABLE(editor->proxy_username)); + purple_proxy_info_set_username(info, svalue); + svalue = gtk_editable_get_text(GTK_EDITABLE(editor->proxy_password)); + purple_proxy_info_set_password(info, svalue); pidgin_account_editor_save_account(PidginAccountEditor *editor) {
- PurpleAccountManager *manager = NULL;
- PurpleProxyInfo *info = NULL;
gboolean new_account = FALSE;
- manager = purple_account_manager_get_default();
- if(!PURPLE_IS_ACCOUNT(editor->account)) {
- editor->account = purple_account_new("undefined", "undefined");
- info = pidgin_proxy_options_get_info(PIDGIN_PROXY_OPTIONS(editor->proxy_options));
- purple_account_set_proxy_info(editor->account, info);
+ new_account = pidgin_account_editor_save_login_options(editor); + pidgin_account_editor_save_user_options(editor); + pidgin_account_editor_save_advanced_options(editor); + pidgin_account_editor_save_proxy(editor, new_account); /* If this is a new account, add it to the account manager and bring it
+ PurpleAccountManager *manager = NULL; const PurpleSavedStatus *saved_status;
+ manager = purple_account_manager_get_default(); purple_account_manager_add(manager, editor->account);
saved_status = purple_savedstatus_get_current();
@@ -100,6 +915,16 @@
*****************************************************************************/
+pidgin_account_editor_connection_changed_cb(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + PidginAccountEditor *editor = data; + pidgin_account_editor_login_options_update_editable(editor); pidgin_account_editor_response_cb(GtkDialog *dialog, gint response_id,
G_GNUC_UNUSED gpointer data)
@@ -110,6 +935,141 @@
gtk_window_destroy(GTK_WINDOW(dialog));
+pidgin_account_editor_protocol_changed_cb(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + pidgin_account_editor_update(data); +pidgin_account_editor_username_changed_cb(GtkEditable *self, gpointer data) { + PidginAccountEditor *editor = data; + const gchar *text = gtk_editable_get_text(self); + gboolean sensitive = FALSE; + if(text != NULL && *text != '\0') { + gtk_dialog_set_response_sensitive(GTK_DIALOG(editor), GTK_RESPONSE_APPLY, +pidgin_account_editor_avatar_response_cb(GtkNativeDialog *self, + gint response_id, gpointer data) + PidginAccountEditor *editor = data; + if(response_id == GTK_RESPONSE_ACCEPT) { + gchar *filename = NULL; + file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(self)); + filename = g_file_get_path(file); + g_clear_object(&editor->avatar_pixbuf); + editor->avatar_pixbuf = gdk_pixbuf_new_from_file(filename, &error); + g_warning("Failed to create pixbuf from file %s: %s", filename, + gtk_image_set_from_pixbuf(GTK_IMAGE(editor->avatar), + editor->avatar_pixbuf); + g_clear_object(&editor->avatar_dialog); +pidgin_account_editor_avatar_set_clicked_cb(G_GNUC_UNUSED GtkButton *self, + PidginAccountEditor *editor = data; + editor->avatar_dialog = gtk_file_chooser_native_new(_("Buddy Icon"), + GTK_FILE_CHOOSER_ACTION_OPEN, + gtk_native_dialog_set_transient_for(GTK_NATIVE_DIALOG(editor->avatar_dialog), + g_signal_connect(G_OBJECT(editor->avatar_dialog), "response", + G_CALLBACK(pidgin_account_editor_avatar_response_cb), + gtk_native_dialog_show(GTK_NATIVE_DIALOG(editor->avatar_dialog)); +pidgin_account_editor_avatar_remove_clicked_cb(G_GNUC_UNUSED GtkButton *self, + PidginAccountEditor *editor = data; + gtk_image_set_from_icon_name(GTK_IMAGE(editor->avatar), "select-avatar"); + g_clear_object(&editor->avatar_pixbuf); +pidgin_account_editor_proxy_type_expression_cb(GObject *self, + G_GNUC_UNUSED gpointer data) + const gchar *text = ""; + const gchar *value = NULL; + value = gtk_string_object_get_string(GTK_STRING_OBJECT(self)); + if(purple_strequal(value, "global")) { + text = _("Use Global Proxy Settings"); + } else if(purple_strequal(value, "none")) { + } else if(purple_strequal(value, "socks4")) { + } else if(purple_strequal(value, "socks5")) { + } else if(purple_strequal(value, "tor")) { + text = _("Tor/Privacy (SOCKS 5)"); + } else if(purple_strequal(value, "http")) { + } else if(purple_strequal(value, "envvar")) { + text = _("Use Environmental Settings"); +pidgin_account_editor_proxy_type_changed_cb(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + PidginAccountEditor *editor = data; + GObject *selected = NULL; + gboolean show_options = TRUE; + const gchar *value = NULL; + selected = adw_combo_row_get_selected_item(ADW_COMBO_ROW(editor->proxy_type)); + value = gtk_string_object_get_string(GTK_STRING_OBJECT(selected)); + if(purple_strequal(value, "global") || purple_strequal(value, "none") || + purple_strequal(value, "envvar")) + gtk_widget_set_visible(editor->proxy_options, show_options); /******************************************************************************
*****************************************************************************/
@@ -154,15 +1114,60 @@
PidginAccountEditor *editor = PIDGIN_ACCOUNT_EDITOR(obj);
g_clear_object(&editor->account);
+ g_clear_object(&editor->avatar_dialog); + g_clear_object(&editor->avatar_pixbuf); G_OBJECT_CLASS(pidgin_account_editor_parent_class)->dispose(obj);
-pidgin_account_editor_init(PidginAccountEditor *account_editor) {
- GtkWidget *widget = GTK_WIDGET(account_editor);
+pidgin_account_editor_init(PidginAccountEditor *editor) { + GtkCssProvider *css_provider = NULL; + GtkStyleContext *context = NULL; + GtkWidget *widget = GTK_WIDGET(editor); gtk_widget_init_template(widget);
+ css_provider = gtk_css_provider_new(); + gtk_css_provider_load_from_resource(css_provider, + "/im/pidgin/Pidgin3/Accounts/entry.css"); + context = gtk_widget_get_style_context(GTK_WIDGET(editor)); + gtk_style_context_add_provider(context, + GTK_STYLE_PROVIDER(css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_clear_object(&css_provider); + pidgin_account_editor_proxy_type_changed_cb(NULL, NULL, editor); +pidgin_account_editor_constructed(GObject *obj) { + PidginAccountEditor *editor = PIDGIN_ACCOUNT_EDITOR(obj); + G_OBJECT_CLASS(pidgin_account_editor_parent_class)->constructed(obj); + if(PURPLE_IS_ACCOUNT(editor->account)) { + pidgin_protocol_chooser_set_protocol(PIDGIN_PROTOCOL_CHOOSER(editor->protocol), + purple_account_get_protocol(editor->account)); + pidgin_account_editor_update(editor); + pidgin_account_editor_login_options_update_editable(editor); +pidgin_account_editor_finalize(GObject *obj) { + PidginAccountEditor *editor = PIDGIN_ACCOUNT_EDITOR(obj); + g_clear_pointer(&editor->user_split_entries, g_list_free); + g_clear_pointer(&editor->user_split_rows, g_list_free); + g_clear_pointer(&editor->advanced_entries, g_list_free); + g_clear_pointer(&editor->advanced_rows, g_list_free); + G_OBJECT_CLASS(pidgin_account_editor_parent_class)->finalize(obj); @@ -172,7 +1177,9 @@
obj_class->get_property = pidgin_account_editor_get_property;
obj_class->set_property = pidgin_account_editor_set_property;
+ obj_class->constructed = pidgin_account_editor_constructed; obj_class->dispose = pidgin_account_editor_dispose;
+ obj_class->finalize = pidgin_account_editor_finalize; * PidginAccountEditor::account:
@@ -192,13 +1199,62 @@
gtk_widget_class_set_template_from_resource(widget_class,
"/im/pidgin/Pidgin3/Accounts/editor.ui");
+ gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_editor_response_cb); gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
+ gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_editor_protocol_changed_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_editor_username_changed_cb); + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_editor_avatar_set_clicked_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_editor_avatar_remove_clicked_cb); + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
+ gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, + gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor, gtk_widget_class_bind_template_callback(widget_class,
- pidgin_account_editor_response_cb);
+ pidgin_account_editor_proxy_type_expression_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_editor_proxy_type_changed_cb); /******************************************************************************
--- a/pidgin/resources/Accounts/editor.ui Thu Sep 15 20:59:06 2022 -0500
+++ b/pidgin/resources/Accounts/editor.ui Thu Sep 15 22:32:05 2022 -0500
@@ -19,6 +19,7 @@
+ <requires lib="Adw" version="1.0"/> <requires lib="gtk" version="4.0"/>
<requires lib="pidgin" version="3.0"/>
<!-- interface-license-type gplv2 -->
@@ -26,35 +27,230 @@
<!-- interface-description Internet Messenger -->
<!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
<template class="PidginAccountEditor" parent="GtkDialog">
+ <property name="resizable">0</property> + <property name="default-height">600</property> + <property name="default-width">400</property> + <property name="title" translatable="1">Edit Account</property> <signal name="response" handler="pidgin_account_editor_response_cb" swapped="no"/>
<child internal-child="content_area">
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
+ <property name="vexpand">1</property> - <object class="GtkNotebook" id="notebook">
- <property name="focusable">1</property>
- <object class="GtkNotebookPage">
- <property name="position">0</property>
- <property name="child">
- <object class="PidginProxyOptions" id="proxy_options">
- <property name="margin-start">12</property>
- <property name="margin-end">12</property>
- <property name="margin-top">12</property>
- <property name="margin-bottom">12</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
+ <object class="GtkScrolledWindow"> + <property name="vexpand">1</property> + <property name="hexpand">1</property> + <property name="child"> + <object class="AdwPreferencesPage"> + <property name="vexpand">1</property> + <property name="hexpand">1</property> + <object class="AdwPreferencesGroup" id="login_options"> + <property name="title" translatable="1">Login Options</property> + <object class="AdwActionRow"> + <property name="activatable-widget">protocol</property> + <property name="title" translatable="1">Pro_tocol</property> + <property name="use-underline">1</property> + <object class="PidginProtocolChooser" id="protocol"> + <property name="hexpand">1</property> + <property name="valign">center</property> + <signal name="notify::protocol" handler="pidgin_account_editor_protocol_changed_cb" object="PidginAccountEditor" swapped="no"/> + <object class="AdwActionRow"> + <property name="activatable-widget">username</property> + <property name="title" translatable="1">_Username</property> + <property name="use-underline">1</property> + <object class="GtkEntry" id="username"> + <property name="hexpand">1</property> + <property name="valign">center</property> + <property name="truncate-multiline">1</property> + <signal name="changed" handler="pidgin_account_editor_username_changed_cb" object="PidginAccountEditor" swapped="no"/> + <object class="AdwPreferencesGroup"> + <property name="title" translatable="1">User Options</property> + <object class="AdwActionRow"> + <property name="activatable-widget">alias</property> + <property name="title" translatable="1">_Local alias</property> + <property name="use-underline">1</property> + <object class="GtkEntry" id="alias"> + <property name="truncate-multiline">1</property> + <property name="hexpand">1</property> + <property name="valign">center</property> + <object class="AdwActionRow" id="avatar_row"> + <property name="activatable-widget">avatar</property> + <property name="title" translatable="1">Use custom _avatar</property> + <property name="use-underline">1</property> + <property name="visible">0</property> + <object class="GtkSwitch" id="use_custom_avatar"> + <property name="valign">center</property> + <object class="GtkButton"> + <property name="css-classes">flat</property> + <property name="valign">center</property> + <binding name="sensitive"> + <lookup name="active">use_custom_avatar</lookup> + <object class="GtkImage" id="avatar"> + <property name="icon-name">select-avatar</property> + <property name="icon-size">large</property> + <signal name="clicked" handler="pidgin_account_editor_avatar_set_clicked_cb" object="PidginAccountEditor" swapped="no"/> + <object class="GtkButton" id="remove"> + <property name="label" translatable="1">_Remove</property> + <property name="use-underline">1</property> + <property name="valign">center</property> + <binding name="sensitive"> + <lookup name="active">use_custom_avatar</lookup> + <signal name="clicked" handler="pidgin_account_editor_avatar_remove_clicked_cb" object="PidginAccountEditor" swapped="no"/>
- <object class="GtkLabel">
- <property name="label" translatable="1">_Proxy</property>
- <property name="use-underline">1</property>
+ <object class="AdwPreferencesGroup" id="advanced_group"> + <property name="title" translatable="1">Advanced Options</property> + <property name="description" translatable="1">Additional options for this account.</property> + <property name="visible">0</property> + <child type="header-suffix"> + <object class="GtkSwitch" id="advanced_toggle"> + <property name="active">0</property> + <property name="valign">center</property>
+ <object class="AdwPreferencesGroup"> + <property name="title" translatable="1">Proxy</property> + <object class="AdwComboRow" id="proxy_type"> + <property name="title" translatable="1">Proxy t_ype</property> + <property name="use-underline">1</property> + <property name="use-subtitle">1</property> + <property name="model"> + <object class="GtkStringList"> + <property name="expression"> + <closure type="gchararray" function="pidgin_account_editor_proxy_type_expression_cb"/> + <signal name="notify::selected" handler="pidgin_account_editor_proxy_type_changed_cb" object="PidginAccountEditor" swapped="no"/> + <object class="AdwPreferencesRow" id="proxy_options"> + <property name="visible">0</property> + <object class="GtkListBox"> + <object class="AdwActionRow"> + <property name="activatable-widget">proxy_host</property> + <property name="title" translatable="1">_Host</property> + <property name="use-underline">1</property> + <object class="GtkEntry" id="proxy_host"> + <property name="hexpand">1</property> + <property name="valign">center</property> + <object class="AdwActionRow"> + <property name="activatable-widget">proxy_port</property> + <property name="title" translatable="1">P_ort</property> + <property name="use-underline">1</property> + <object class="GtkSpinButton" id="proxy_port"> + <property name="text" translatable="1">0</property> + <property name="adjustment"> + <object class="GtkAdjustment"> + <property name="upper">65535</property> + <property name="step-increment">1</property> + <property name="page-increment">10</property> + <property name="numeric">1</property> + <property name="hexpand">1</property> + <property name="valign">center</property> + <object class="AdwActionRow"> + <property name="activatable-widget">proxy_username</property> + <property name="title" translatable="1">User_name</property> + <property name="use-underline">1</property> + <object class="GtkEntry" id="proxy_username"> + <property name="hexpand">1</property> + <property name="valign">center</property> + <object class="AdwActionRow"> + <property name="activatable-widget">proxy_password</property> + <property name="title" translatable="1">Pa_ssword</property> + <property name="use-underline">1</property> + <object class="GtkPasswordEntry" id="proxy_password"> + <property name="hexpand">1</property> + <property name="show-peek-icon">1</property> + <property name="valign">center</property>
@@ -62,7 +258,6 @@
<object class="GtkButton" id="button1">
<property name="label" translatable="1">_Cancel</property>
- <property name="focusable">1</property>
<property name="receives-default">1</property>
<property name="use-underline">1</property>
@@ -70,8 +265,8 @@
<object class="GtkButton" id="button2">
<property name="label" translatable="1">_Save</property>
- <property name="focusable">1</property>
<property name="receives-default">1</property>
+ <property name="sensitive">0</property> <property name="use-underline">1</property>