pidgin/pidgin

Add some new methods to purple tags
default tip
12 hours ago, Gary Kramlich
b45add2a840c
Add some new methods to purple tags

* purple_tags_exists is a simplier version of purple_tags_lookup.
* purple_tags_contains makes it easier to find multiple matching tags.

Testing Done:
Ran the unit tests under valgrind and had the turtles check in on things too.

Reviewed at https://reviews.imfreedom.org/r/3143/
/*
* Pidgin - Internet Messenger
* Copyright (C) Pidgin Developers <devel@pidgin.im>
*
* Pidgin is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <glib/gi18n-lib.h>
#include <adwaita.h>
#include "pidginaccounteditor.h"
#include "pidginprotocolchooser.h"
struct _PidginAccountEditor {
AdwPreferencesPage parent;
PurpleAccount *account;
gboolean valid;
/* Login Options */
GtkWidget *login_options;
GtkWidget *protocol;
GtkWidget *username;
GtkWidget *user_splits;
GtkWidget *require_password_row;
GtkWidget *require_password;
GList *user_split_rows;
/* User Options */
GtkWidget *alias;
GtkFileDialog *avatar_dialog;
GdkPixbuf *avatar_pixbuf;
GtkWidget *avatar_row;
GtkWidget *use_custom_avatar;
GtkWidget *avatar;
/* Advanced Options */
GtkWidget *advanced_group;
GtkWidget *advanced_toggle;
GList *advanced_entries;
GList *advanced_rows;
/* Proxy Options */
GtkWidget *proxy_type;
GtkWidget *proxy_options;
GtkWidget *proxy_host;
GtkWidget *proxy_port;
GtkWidget *proxy_username;
GtkWidget *proxy_password;
};
enum {
PROP_0,
PROP_ACCOUNT,
PROP_VALID,
N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
/******************************************************************************
* Prototypes
*****************************************************************************/
static void pidgin_account_editor_connection_changed_cb(GObject *obj,
GParamSpec *pspec,
gpointer data);
/******************************************************************************
* Helpers
*****************************************************************************/
static void
pidgin_account_editor_set_valid(PidginAccountEditor *editor, gboolean valid) {
g_return_if_fail(PIDGIN_IS_ACCOUNT_EDITOR(editor));
if(editor->valid != valid) {
editor->valid = valid;
g_object_notify_by_pspec(G_OBJECT(editor), properties[PROP_VALID]);
}
}
static void
pidgin_account_editor_add_user_split(gpointer data, gpointer user_data) {
PurpleAccountUserSplit *split = data;
PidginAccountEditor *editor = user_data;
if(!purple_account_user_split_is_constant(split)) {
GtkWidget *row = NULL;
gboolean sensitive = TRUE;
if(PURPLE_IS_ACCOUNT(editor->account)) {
if(purple_account_is_connected(editor->account)) {
sensitive = FALSE;
}
}
row = adw_entry_row_new();
editor->user_split_rows = g_list_append(editor->user_split_rows, row);
gtk_list_box_append(GTK_LIST_BOX(editor->user_splits), row);
gtk_widget_set_focusable(row, FALSE);
if(!sensitive) {
gtk_widget_set_sensitive(row, sensitive);
}
adw_preferences_row_set_title(ADW_PREFERENCES_ROW(row),
purple_account_user_split_get_text(split));
}
}
static gboolean
pidgin_account_editor_update_login_options(PidginAccountEditor *editor,
PurpleProtocol *protocol)
{
PurpleProtocolOptions options;
GList *user_splits = NULL;
GList *split_item = NULL;
GList *row_item = NULL;
gchar *username = NULL;
gboolean require_password = FALSE;
gboolean ret = FALSE;
/* Make the username field sensitive as it may have previously be made
* insensitive.
*/
gtk_widget_set_sensitive(editor->username, TRUE);
/* 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.
*/
PurpleContactInfo *info = PURPLE_CONTACT_INFO(editor->account);
username = g_strdup(purple_contact_info_get_username(info));
require_password = purple_account_get_require_password(editor->account);
if(purple_account_is_connected(editor->account)) {
gtk_widget_set_sensitive(editor->username, FALSE);
}
}
/* Now remove the rows we added to the preference group for each non
* constant user split.
*/
while(editor->user_split_rows != NULL) {
gtk_list_box_remove(GTK_LIST_BOX(editor->user_splits),
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);
/* 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
* the username.
*/
split_item = g_list_last(user_splits);
row_item = g_list_last(editor->user_split_rows);
while(split_item != NULL && row_item != NULL) {
GtkWidget *row = row_item->data;
PurpleAccountUserSplit *split = split_item->data;
gchar *ptr = NULL;
const gchar *value = NULL;
if(username != NULL) {
gchar sep = purple_account_user_split_get_separator(split);
if(purple_account_user_split_get_reverse(split)) {
ptr = strrchr(username, sep);
} else {
ptr = strchr(username, sep);
}
if(ptr != NULL) {
/* Insert a null terminator in place of the separator. */
*ptr = '\0';
/* Set the value to the first byte after the separator. */
value = ptr + 1;
}
}
if(value == NULL) {
value = purple_account_user_split_get_default_value(split);
}
if(value != NULL && GTK_IS_EDITABLE(row)) {
gtk_editable_set_text(GTK_EDITABLE(row), value);
}
split_item = split_item->prev;
row_item = row_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.
*/
if(username != NULL) {
gtk_editable_set_text(GTK_EDITABLE(editor->username), username);
g_free(username);
ret = TRUE;
}
options = purple_protocol_get_options(protocol);
gtk_widget_set_visible(editor->require_password_row,
options & OPT_PROTO_PASSWORD_OPTIONAL);
gtk_switch_set_active(GTK_SWITCH(editor->require_password),
require_password);
return ret;
}
static void
pidgin_account_editor_update_user_options(PidginAccountEditor *editor,
G_GNUC_UNUSED PurpleProtocol *protocol)
{
PurpleImage *image = NULL;
gboolean show_avatar_opts = TRUE;
const gchar *svalue = "";
gboolean use_global = TRUE;
/* Check if the protocol supports avatars. */
show_avatar_opts = FALSE;
gtk_widget_set_visible(editor->avatar_row, show_avatar_opts);
/* Determine our values. */
if(editor->account != NULL) {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(editor->account);
svalue = purple_contact_info_get_alias(info);
use_global = purple_account_get_bool(editor->account,
"use-global-buddyicon", TRUE);
}
if(svalue == NULL) {
svalue = "";
}
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);
} else {
gtk_image_set_from_icon_name(GTK_IMAGE(editor->avatar),
"select-avatar");
}
}
static gboolean
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)) {
return TRUE;
}
/* 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)) {
return TRUE;
}
return FALSE;
}
static GtkWidget *
pidgin_account_editor_add_advanced_boolean(PidginAccountEditor *editor,
PurpleAccountOption *option)
{
GtkWidget *row = NULL;
GtkWidget *toggle = NULL;
gboolean value = FALSE;
gchar *title = NULL;
if(pidgin_account_editor_advanced_option_use_default(editor)) {
value = purple_account_option_get_default_bool(option);
} else {
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);
gtk_widget_set_focusable(row, FALSE);
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);
g_free(title);
adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group),
row);
/* 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);
return toggle;
}
static GtkWidget *
pidgin_account_editor_add_advanced_int(PidginAccountEditor *editor,
PurpleAccountOption *option)
{
GtkWidget *row = NULL;
gint value = 0;
gchar *title = NULL;
gchar *svalue = NULL;
if(pidgin_account_editor_advanced_option_use_default(editor)) {
value = purple_account_option_get_default_int(option);
} else {
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_entry_row_new();
g_object_bind_property(editor->advanced_toggle, "active", row, "visible",
G_BINDING_SYNC_CREATE);
gtk_widget_set_focusable(row, FALSE);
adw_preferences_row_set_use_underline(ADW_PREFERENCES_ROW(row), TRUE);
adw_entry_row_set_input_purpose(ADW_ENTRY_ROW(row),
GTK_INPUT_PURPOSE_NUMBER);
title = g_strdup_printf("_%s", purple_account_option_get_text(option));
adw_preferences_row_set_title(ADW_PREFERENCES_ROW(row), title);
g_free(title);
adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group),
row);
/* Add the row to the editor's list of advanced rows. */
editor->advanced_rows = g_list_append(editor->advanced_rows, row);
/* Set the default value. */
svalue = g_strdup_printf("%d", value);
gtk_editable_set_text(GTK_EDITABLE(row), svalue);
g_free(svalue);
return row;
}
static GtkWidget *
pidgin_account_editor_add_advanced_string(PidginAccountEditor *editor,
PurpleAccountOption *option)
{
GtkWidget *row = NULL;
gchar *title = NULL;
const gchar *value = NULL;
if(pidgin_account_editor_advanced_option_use_default(editor)) {
value = purple_account_option_get_default_string(option);
} else {
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 depending on the masked hint. */
if(purple_account_option_string_get_masked(option)) {
row = adw_password_entry_row_new();
} else {
row = adw_entry_row_new();
}
g_object_bind_property(editor->advanced_toggle, "active", row, "visible",
G_BINDING_SYNC_CREATE);
gtk_widget_set_focusable(row, FALSE);
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);
g_free(title);
adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group),
row);
/* Add the row to the editor's list of advanced rows. */
editor->advanced_rows = g_list_append(editor->advanced_rows, row);
if(value != NULL) {
gtk_editable_set_text(GTK_EDITABLE(row), value);
}
return row;
}
static GtkWidget *
pidgin_account_editor_add_advanced_list(PidginAccountEditor *editor,
PurpleAccountOption *option)
{
GtkWidget *row = NULL;
GtkStringList *model = NULL;
GList *data = NULL;
GList *items = NULL;
gchar *title = NULL;
const gchar *value = FALSE;
guint index = 0;
guint position = 0;
if(pidgin_account_editor_advanced_option_use_default(editor)) {
value = purple_account_option_get_default_list_value(option);
} else {
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);
g_free(title);
adw_preferences_group_add(ADW_PREFERENCES_GROUP(editor->advanced_group),
row);
/* 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(kvp != NULL) {
if(purple_strequal(kvp->value, value)) {
position = index;
}
data = g_list_append(data, kvp->value);
gtk_string_list_append(model, kvp->key);
}
index++;
}
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);
return row;
}
static void
pidgin_account_editor_add_advanced_option(PidginAccountEditor *editor,
PurpleAccountOption *option)
{
PurplePrefType type;
GtkWidget *widget = NULL;
type = purple_account_option_get_pref_type(option);
switch(type) {
case PURPLE_PREF_BOOLEAN:
widget = pidgin_account_editor_add_advanced_boolean(editor, option);
break;
case PURPLE_PREF_INT:
widget = pidgin_account_editor_add_advanced_int(editor, option);
break;
case PURPLE_PREF_STRING:
widget = pidgin_account_editor_add_advanced_string(editor, option);
break;
case PURPLE_PREF_STRING_LIST:
widget = pidgin_account_editor_add_advanced_list(editor, option);
break;
default:
purple_debug_error("PidginAccountEditor",
"Invalid Account Option pref type (%d)", type);
break;
}
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,
widget);
} else {
purple_account_option_destroy(option);
}
}
static void
pidgin_account_editor_update_advanced_options(PidginAccountEditor *editor,
PurpleProtocol *protocol)
{
GList *options = NULL;
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);
return;
}
options = purple_protocol_get_account_options(protocol);
if(options == NULL) {
gtk_widget_set_visible(editor->advanced_group, FALSE);
return;
}
/* 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);
}
static void
pidgin_account_editor_update_proxy_options(PidginAccountEditor *editor) {
PurpleProxyInfo *info = NULL;
GListModel *model = NULL;
char *str = NULL;
const char *type = "global";
const char *hostname = NULL;
const char *username = NULL;
const char *password = NULL;
int port = 8080;
guint position = 0;
if(!PURPLE_IS_ACCOUNT(editor->account)) {
return;
}
info = purple_account_get_proxy_info(editor->account);
if(PURPLE_IS_PROXY_INFO(info)) {
switch(purple_proxy_info_get_proxy_type(info)) {
case PURPLE_PROXY_TYPE_USE_GLOBAL:
type = "global";
break;
case PURPLE_PROXY_TYPE_NONE:
type = "none";
break;
case PURPLE_PROXY_TYPE_SOCKS4:
type = "socks4";
break;
case PURPLE_PROXY_TYPE_SOCKS5:
type = "socks5";
break;
case PURPLE_PROXY_TYPE_TOR:
type = "tor";
break;
case PURPLE_PROXY_TYPE_HTTP:
type = "http";
break;
case PURPLE_PROXY_TYPE_USE_ENVVAR:
type = "envvar";
break;
}
hostname = purple_proxy_info_get_hostname(info);
port = purple_proxy_info_get_port(info);
username = purple_proxy_info_get_username(info);
password = purple_proxy_info_get_password(info);
}
model = adw_combo_row_get_model(ADW_COMBO_ROW(editor->proxy_type));
for(guint index = 0; index < g_list_model_get_n_items(model); index++) {
const char *value = gtk_string_list_get_string(GTK_STRING_LIST(model),
index);
if(purple_strequal(type, value)) {
position = index;
break;
}
}
adw_combo_row_set_selected(ADW_COMBO_ROW(editor->proxy_type), position);
if(hostname == NULL) {
hostname = "";
}
gtk_editable_set_text(GTK_EDITABLE(editor->proxy_host), hostname);
str = g_strdup_printf("%d", port);
gtk_editable_set_text(GTK_EDITABLE(editor->proxy_port), str);
g_free(str);
if(username == NULL) {
username = "";
}
gtk_editable_set_text(GTK_EDITABLE(editor->proxy_username), username);
if(password == NULL) {
password = "";
}
gtk_editable_set_text(GTK_EDITABLE(editor->proxy_password), password);
}
static void
pidgin_account_editor_update(PidginAccountEditor *editor) {
PurpleProtocol *protocol = NULL;
gboolean sensitive = FALSE;
/* Reset the sensitivity of the protocol chooser to sensitive. */
gtk_widget_set_sensitive(editor->protocol, TRUE);
if(PURPLE_IS_ACCOUNT(editor->account)) {
if(purple_account_is_connected(editor->account)) {
/* If the account is connected, disable the protocol chooser. */
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);
pidgin_account_editor_set_valid(editor, sensitive);
}
static void
pidgin_account_editor_login_options_update_editable(PidginAccountEditor *editor)
{
PidginProtocolChooser *chooser = NULL;
PurpleProtocol *selected_protocol = NULL;
PurpleProtocolOptions options;
gboolean editable = TRUE;
chooser = PIDGIN_PROTOCOL_CHOOSER(editor->protocol);
selected_protocol = pidgin_protocol_chooser_get_protocol(chooser);
if(PURPLE_IS_ACCOUNT(editor->account)) {
PurpleConnection *connection = NULL;
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)) {
PurpleProtocol *connected_protocol = NULL;
editable = FALSE;
/* 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);
if(connected_protocol != selected_protocol) {
pidgin_protocol_chooser_set_protocol(chooser, connected_protocol);
pidgin_account_editor_update(editor);
selected_protocol = connected_protocol;
}
}
}
options = purple_protocol_get_options(selected_protocol);
gtk_widget_set_visible(editor->require_password_row,
options & OPT_PROTO_PASSWORD_OPTIONAL);
gtk_widget_set_sensitive(editor->protocol, editable);
gtk_editable_set_editable(GTK_EDITABLE(editor->username), editable);
for(GList *l = editor->user_split_rows; l != NULL; l = l->next) {
GtkWidget *row = l->data;
gtk_editable_set_editable(GTK_EDITABLE(row), editable);
}
gtk_widget_set_sensitive(editor->require_password, editable);
}
static gboolean
pidgin_account_editor_save_login_options(PidginAccountEditor *editor) {
PurpleProtocol *protocol = NULL;
GList *split_item = NULL;
GList *row_item = NULL;
GString *username = NULL;
const gchar *protocol_id = NULL;
gboolean new_account = FALSE;
gboolean require_password = FALSE;
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);
row_item = editor->user_split_rows;
while(split_item != NULL && row_item != NULL) {
PurpleAccountUserSplit *split = split_item->data;
GtkWidget *row = row_item->data;
const gchar *value = "";
gchar sep = '\0';
sep = purple_account_user_split_get_separator(split);
g_string_append_c(username, sep);
if(GTK_IS_EDITABLE(row)) {
value = gtk_editable_get_text(GTK_EDITABLE(row));
}
if(value == NULL || *value == '\0') {
value = purple_account_user_split_get_default_value(split);
}
g_string_append(username, value);
split_item = split_item->next;
row_item = row_item->next;
}
if(!PURPLE_IS_ACCOUNT(editor->account)) {
editor->account = purple_account_new(username->str, protocol_id);
new_account = TRUE;
} else {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(editor->account);
purple_contact_info_set_username(info, username->str);
purple_account_set_protocol_id(editor->account, protocol_id);
}
g_string_free(username, TRUE);
require_password = gtk_switch_get_active(GTK_SWITCH(editor->require_password));
purple_account_set_require_password(editor->account, require_password);
return new_account;
}
static void
pidgin_account_editor_save_user_options(PidginAccountEditor *editor) {
PurpleContactInfo *info = PURPLE_CONTACT_INFO(editor->account);
const gchar *svalue = NULL;
gboolean bvalue = FALSE;
purple_account_freeze_notify_settings(editor->account);
/* Set the alias. */
svalue = gtk_editable_get_text(GTK_EDITABLE(editor->alias));
if(*svalue == '\0') {
svalue = NULL;
}
purple_contact_info_set_alias(info, 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);
#pragma message("implement this when buddy icons do not suck so bad.")
purple_account_thaw_notify_settings(editor->account);
}
static void
pidgin_account_editor_save_advanced_options(PidginAccountEditor *editor) {
purple_account_freeze_notify_settings(editor->account);
for(GList *l = editor->advanced_entries; l != NULL; l = l->next) {
GtkWidget *widget = l->data;
PurpleAccountOption *option = NULL;
GList *keys = NULL;
const gchar *setting = NULL;
const gchar *svalue = NULL;
gboolean bvalue = FALSE;
gint ivalue = 0;
guint selected = 0;
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);
break;
case PURPLE_PREF_INT:
svalue = gtk_editable_get_text(GTK_EDITABLE(widget));
ivalue = atoi(svalue);
purple_account_set_int(editor->account, setting, ivalue);
break;
case PURPLE_PREF_BOOLEAN:
bvalue = gtk_switch_get_active(GTK_SWITCH(widget));
purple_account_set_bool(editor->account, setting, bvalue);
break;
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);
break;
default:
break;
}
}
purple_account_thaw_notify_settings(editor->account);
}
static void
pidgin_account_editor_save_proxy(PidginAccountEditor *editor,
gboolean new_account)
{
PurpleProxyInfo *info = NULL;
PurpleProxyType type = PURPLE_PROXY_TYPE_NONE;
GObject *item = NULL;
const gchar *svalue = NULL;
/* Build the ProxyInfo object */
if(!new_account) {
info = purple_account_get_proxy_info(editor->account);
}
/* If this is a new account, or the account's proxy info is null, create a
* new instance, and set it on the account.
*/
if(new_account || !PURPLE_IS_PROXY_INFO(info)) {
info = purple_proxy_info_new();
purple_account_set_proxy_info(editor->account, info);
}
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);
svalue = gtk_editable_get_text(GTK_EDITABLE(editor->proxy_port));
purple_proxy_info_set_port(info, atoi(svalue));
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);
}
/******************************************************************************
* Callbacks
*****************************************************************************/
static void
pidgin_account_editor_connection_changed_cb(G_GNUC_UNUSED GObject *obj,
G_GNUC_UNUSED GParamSpec *pspec,
gpointer data)
{
PidginAccountEditor *editor = data;
pidgin_account_editor_login_options_update_editable(editor);
}
static void
pidgin_account_editor_protocol_changed_cb(G_GNUC_UNUSED GObject *obj,
G_GNUC_UNUSED GParamSpec *pspec,
gpointer data)
{
pidgin_account_editor_update(data);
}
static void
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') {
sensitive = TRUE;
}
pidgin_account_editor_set_valid(editor, sensitive);
}
static void
pidgin_account_editor_avatar_response_cb(GObject *obj, GAsyncResult *result,
gpointer data)
{
PidginAccountEditor *editor = data;
GFile *file = NULL;
file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(obj), result, NULL);
if(file != NULL) {
GError *error = NULL;
char *filename = NULL;
filename = g_file_get_path(file);
g_clear_object(&editor->avatar_pixbuf);
editor->avatar_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
if(error != NULL) {
g_warning("Failed to create pixbuf from file %s: %s", filename,
error->message);
g_clear_error(&error);
} else {
gtk_image_set_from_pixbuf(GTK_IMAGE(editor->avatar),
editor->avatar_pixbuf);
}
g_free(filename);
g_object_unref(file);
}
g_clear_object(&editor->avatar_dialog);
}
static void
pidgin_account_editor_avatar_set_clicked_cb(G_GNUC_UNUSED GtkButton *self,
gpointer data)
{
PidginAccountEditor *editor = data;
GtkRoot *root = NULL;
root = gtk_widget_get_root(GTK_WIDGET(editor));
editor->avatar_dialog = gtk_file_dialog_new();
gtk_file_dialog_set_title(editor->avatar_dialog, _("Buddy Icon"));
gtk_file_dialog_set_modal(editor->avatar_dialog, TRUE);
gtk_file_dialog_open(editor->avatar_dialog, GTK_WINDOW(root), NULL,
pidgin_account_editor_avatar_response_cb, editor);
}
static void
pidgin_account_editor_avatar_remove_clicked_cb(G_GNUC_UNUSED GtkButton *self,
gpointer data)
{
PidginAccountEditor *editor = data;
gtk_image_set_from_icon_name(GTK_IMAGE(editor->avatar), "select-avatar");
g_clear_object(&editor->avatar_pixbuf);
}
static gchar *
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")) {
text = _("No proxy");
} else if(purple_strequal(value, "socks4")) {
text = _("SOCKS 4");
} else if(purple_strequal(value, "socks5")) {
text = _("SOCKS 5");
} else if(purple_strequal(value, "tor")) {
text = _("Tor/Privacy (SOCKS 5)");
} else if(purple_strequal(value, "http")) {
text = _("HTTP");
} else if(purple_strequal(value, "envvar")) {
text = _("Use Environmental Settings");
}
return g_strdup(text);
}
static void
pidgin_account_editor_proxy_type_changed_cb(G_GNUC_UNUSED GObject *obj,
G_GNUC_UNUSED GParamSpec *pspec,
gpointer data)
{
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"))
{
show_options = FALSE;
}
gtk_widget_set_visible(editor->proxy_options, show_options);
}
/******************************************************************************
* GObject Implementation
*****************************************************************************/
G_DEFINE_FINAL_TYPE(PidginAccountEditor, pidgin_account_editor,
ADW_TYPE_PREFERENCES_PAGE)
static void
pidgin_account_editor_get_property(GObject *obj, guint param_id, GValue *value,
GParamSpec *pspec)
{
PidginAccountEditor *editor = PIDGIN_ACCOUNT_EDITOR(obj);
switch(param_id) {
case PROP_ACCOUNT:
g_value_set_object(value,
pidgin_account_editor_get_account(editor));
break;
case PROP_VALID:
g_value_set_boolean(value,
pidgin_account_editor_get_is_valid(editor));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
pidgin_account_editor_set_property(GObject *obj, guint param_id,
const GValue *value, GParamSpec *pspec)
{
PidginAccountEditor *editor = PIDGIN_ACCOUNT_EDITOR(obj);
switch(param_id) {
case PROP_ACCOUNT:
pidgin_account_editor_set_account(editor,
g_value_get_object(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
pidgin_account_editor_dispose(GObject *obj) {
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);
}
static void
pidgin_account_editor_init(PidginAccountEditor *editor) {
GtkWidget *widget = GTK_WIDGET(editor);
gtk_widget_init_template(widget);
pidgin_account_editor_proxy_type_changed_cb(NULL, NULL, editor);
}
static void
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);
}
static void
pidgin_account_editor_finalize(GObject *obj) {
PidginAccountEditor *editor = PIDGIN_ACCOUNT_EDITOR(obj);
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);
}
static void
pidgin_account_editor_class_init(PidginAccountEditorClass *klass) {
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
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:
*
* The account that this editor is modifying.
*
* Since: 3.0
*/
properties[PROP_ACCOUNT] = g_param_spec_object(
"account", "account",
"The account to modify",
PURPLE_TYPE_ACCOUNT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* PidginAccountEditor:valid:
*
* Whether or not the account settings are valid and it is okay to save the
* account.
*
* Since: 3.0
*/
properties[PROP_VALID] = g_param_spec_boolean(
"valid", "valid",
"Whether or not the account settings are valid",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
gtk_widget_class_set_template_from_resource(widget_class,
"/im/pidgin/Pidgin3/Accounts/editor.ui");
/* Login Options */
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
login_options);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
protocol);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
username);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
user_splits);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
require_password_row);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
require_password);
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);
/* User Options */
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
alias);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
avatar_row);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
use_custom_avatar);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
avatar);
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);
/* Advanced Options */
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
advanced_group);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
advanced_toggle);
/* Proxy Options */
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
proxy_type);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
proxy_options);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
proxy_host);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
proxy_port);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
proxy_username);
gtk_widget_class_bind_template_child(widget_class, PidginAccountEditor,
proxy_password);
gtk_widget_class_bind_template_callback(widget_class,
pidgin_account_editor_proxy_type_expression_cb);
gtk_widget_class_bind_template_callback(widget_class,
pidgin_account_editor_proxy_type_changed_cb);
}
/******************************************************************************
* API
*****************************************************************************/
GtkWidget *
pidgin_account_editor_new(PurpleAccount *account) {
return g_object_new(PIDGIN_TYPE_ACCOUNT_EDITOR, "account", account, NULL);
}
PurpleAccount *
pidgin_account_editor_get_account(PidginAccountEditor *editor) {
g_return_val_if_fail(PIDGIN_IS_ACCOUNT_EDITOR(editor), NULL);
return editor->account;
}
void
pidgin_account_editor_set_account(PidginAccountEditor *editor,
PurpleAccount *account)
{
g_return_if_fail(PIDGIN_IS_ACCOUNT_EDITOR(editor));
/* Disconnect the notify handler from the previous account. */
if(PURPLE_IS_ACCOUNT(editor->account)) {
g_signal_handlers_disconnect_by_func(editor->account,
pidgin_account_editor_connection_changed_cb,
editor);
}
if(g_set_object(&editor->account, account)) {
if(PURPLE_IS_ACCOUNT(account)) {
pidgin_protocol_chooser_set_protocol(PIDGIN_PROTOCOL_CHOOSER(editor->protocol),
purple_account_get_protocol(editor->account));
g_signal_connect_object(account, "notify::connection",
G_CALLBACK(pidgin_account_editor_connection_changed_cb),
editor, 0);
} else {
/* If we don't have an account reset the fields that are static. */
pidgin_protocol_chooser_set_protocol(PIDGIN_PROTOCOL_CHOOSER(editor->protocol),
NULL);
gtk_editable_set_text(GTK_EDITABLE(editor->username), "");
}
g_object_notify_by_pspec(G_OBJECT(editor), properties[PROP_ACCOUNT]);
}
pidgin_account_editor_update(editor);
}
gboolean
pidgin_account_editor_get_is_valid(PidginAccountEditor *editor) {
g_return_val_if_fail(PIDGIN_IS_ACCOUNT_EDITOR(editor), FALSE);
return editor->valid;
}
void
pidgin_account_editor_save(PidginAccountEditor *editor) {
gboolean new_account = FALSE;
g_return_if_fail(PIDGIN_IS_ACCOUNT_EDITOR(editor));
g_return_if_fail(editor->valid == TRUE);
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, enable it, and add it to the account manager.
*/
if(new_account) {
PurpleAccountManager *manager = NULL;
purple_account_set_enabled(editor->account, TRUE);
manager = purple_account_manager_get_default();
purple_account_manager_add(manager, editor->account);
}
}