--- a/pidgin/meson.build Tue Jan 17 02:27:41 2023 -0600
+++ b/pidgin/meson.build Tue Jan 17 02:29:19 2023 -0600
@@ -24,6 +24,7 @@
'pidginaccountfilterconnected.c',
'pidginaccountfilterprotocol.c',
'pidginaccountmanager.c',
'pidginaccountsdisabledmenu.c',
'pidginaccountsenabledmenu.c',
@@ -90,6 +91,7 @@
'pidginaccountfilterconnected.h',
'pidginaccountfilterprotocol.h',
'pidginaccountmanager.h',
'pidginaccountsdisabledmenu.h',
'pidginaccountsenabledmenu.h',
--- a/pidgin/pidginaccountmanager.c Tue Jan 17 02:27:41 2023 -0600
+++ b/pidgin/pidginaccountmanager.c Tue Jan 17 02:29:19 2023 -0600
@@ -29,167 +29,33 @@
#include "pidginaccounteditor.h"
+#include "pidginaccountrow.h" struct _PidginAccountManager {
- GtkTreeSelection *selection;
- GtkWidget *modify_button;
- GtkWidget *remove_button;
G_DEFINE_TYPE(PidginAccountManager, pidgin_account_manager, GTK_TYPE_DIALOG)
-static void pidgin_account_manager_account_notify_cb(GObject *obj, GParamSpec *pspec, gpointer data);
/******************************************************************************
*****************************************************************************/
-pidgin_account_manager_find_account(PidginAccountManager *manager,
- PurpleAccount *account, GtkTreeIter *iter)
+pidgin_account_manager_create_widget(gpointer item, + G_GNUC_UNUSED gpointer data) - GtkTreeModel *model = GTK_TREE_MODEL(manager->model);
- if(!gtk_tree_model_get_iter_first(model, iter)) {
- PurpleAccount *current = NULL;
- gtk_tree_model_get(model, iter,
- COLUMN_ACCOUNT, ¤t,
- if(current == account) {
- g_clear_object(¤t);
- g_clear_object(¤t);
- } while(gtk_tree_model_iter_next(model, iter));
-pidgin_account_manager_get_selected_account(PidginAccountManager *manager) {
- PurpleAccount *account = NULL;
- if(gtk_tree_selection_count_selected_rows(manager->selection) == 0) {
+ if(!PURPLE_IS_ACCOUNT(item)) { - gtk_tree_selection_get_selected(manager->selection, NULL, &iter);
- gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
- COLUMN_ACCOUNT, &account,
-pidgin_account_manager_refresh_account(PidginAccountManager *manager,
- PurpleAccount *account,
- PurpleContactInfo *info = NULL;
- PurpleImage *image = NULL;
- PurpleProtocol *protocol = NULL;
- GdkPixbuf *avatar = NULL;
- const gchar *protocol_icon = NULL, *protocol_name = NULL;
- /* Try to find the avatar for the account. */
- image = purple_buddy_icons_find_account_icon(account);
- raw = purple_gdk_pixbuf_from_image(image);
- avatar = gdk_pixbuf_scale_simple(raw, 22, 22, GDK_INTERP_HYPER);
- /* Get the protocol fields. */
- protocol = purple_account_get_protocol(account);
- if(PURPLE_IS_PROTOCOL(protocol)) {
- protocol_name = purple_protocol_get_name(protocol);
- protocol_icon = purple_protocol_get_icon_name(protocol);
- protocol_name = _("Unknown");
- info = PURPLE_CONTACT_INFO(account);
- gtk_list_store_set(manager->model, iter,
- COLUMN_ENABLED, purple_account_get_enabled(account),
- COLUMN_USERNAME, purple_contact_info_get_username(info),
- COLUMN_PROTOCOL_ICON, protocol_icon,
- COLUMN_PROTOCOL_NAME, protocol_name,
- COLUMN_ACCOUNT, account,
- g_clear_object(&avatar);
-pidgin_account_manager_update_account(PidginAccountManager *manager,
- PurpleAccount *account)
- if(pidgin_account_manager_find_account(manager, account, &iter)) {
- pidgin_account_manager_refresh_account(manager, account, &iter);
-pidgin_account_manager_add_account(PidginAccountManager *manager,
- PurpleAccount *account)
- gtk_list_store_append(manager->model, &iter);
- pidgin_account_manager_refresh_account(manager, account, &iter);
- g_signal_connect_object(account, "notify",
- G_CALLBACK(pidgin_account_manager_account_notify_cb),
-pidgin_account_manager_populate_helper(PurpleAccount *account, gpointer data) {
- pidgin_account_manager_add_account(PIDGIN_ACCOUNT_MANAGER(data), account);
-pidgin_account_manager_populate(PidginAccountManager *manager) {
- purple_account_manager_foreach(purple_account_manager_get_default(),
- pidgin_account_manager_populate_helper,
+ return pidgin_account_row_new(PURPLE_ACCOUNT(item)); @@ -200,51 +66,22 @@
gtk_window_present_with_time(GTK_WINDOW(editor), GDK_CURRENT_TIME);
-pidgin_account_manager_edit_selected_account(PidginAccountManager *manager) {
- PurpleAccount *account = NULL;
- GtkWidget *editor = NULL;
- account = pidgin_account_manager_get_selected_account(manager);
- editor = pidgin_account_editor_new(account);
- gtk_window_set_transient_for(GTK_WINDOW(editor),
- gtk_window_present_with_time(GTK_WINDOW(editor), GDK_CURRENT_TIME);
- g_clear_object(&account);
-pidgin_account_manager_remove_selected_account(PidginAccountManager *manager) {
- PurpleAccount *account = NULL;
- PurpleNotificationManager *notification_manager = NULL;
- account = pidgin_account_manager_get_selected_account(manager);
- /* Remove all notifications including connection errors for the account. */
- notification_manager = purple_notification_manager_get_default();
- purple_notification_manager_remove_with_account(notification_manager,
- /* Delete the account. */
- purple_accounts_delete(account);
- g_clear_object(&account);
/******************************************************************************
*****************************************************************************/
-pidgin_account_manager_account_notify_cb(GObject *obj,
- G_GNUC_UNUSED GParamSpec *pspec,
+pidgin_account_manager_refresh_add_cb(GListModel *list, + G_GNUC_UNUSED guint position, + G_GNUC_UNUSED guint removed, + G_GNUC_UNUSED guint added, - PidginAccountManager *manager = PIDGIN_ACCOUNT_MANAGER(data);
- PurpleAccount *account = PURPLE_ACCOUNT(obj);
+ PidginAccountManager *manager = data; - pidgin_account_manager_update_account(manager, account);
+ /* If there are no accounts, the placeholder is shown, which includes an + * Add button. So hide the one in the button box if that's the case. */ + gtk_widget_set_visible(manager->add, g_list_model_get_n_items(list) != 0); @@ -257,12 +94,6 @@
pidgin_account_manager_create_account(manager);
- pidgin_account_manager_edit_selected_account(manager);
- pidgin_account_manager_remove_selected_account(manager);
case GTK_RESPONSE_DELETE_EVENT:
gtk_window_destroy(GTK_WINDOW(dialog));
@@ -273,100 +104,16 @@
-pidgin_account_manager_selection_changed_cb(GtkTreeSelection *selection,
- PidginAccountManager *manager = data;
- gboolean sensitive = TRUE;
- if(gtk_tree_selection_count_selected_rows(selection) == 0) {
- gtk_widget_set_sensitive(manager->modify_button, sensitive);
- gtk_widget_set_sensitive(manager->remove_button, sensitive);
-pidgin_account_manager_row_activated_cb(G_GNUC_UNUSED GtkTreeView *tree_view,
- G_GNUC_UNUSED GtkTreeViewColumn *column,
+pidgin_account_manager_row_activated_cb(G_GNUC_UNUSED GtkListBox *box, + G_GNUC_UNUSED gpointer data) - PidginAccountManager *manager = data;
- if(gtk_tree_model_get_iter(GTK_TREE_MODEL(manager->model), &iter, path)) {
- GtkWidget *editor = NULL;
- PurpleAccount *account = NULL;
- gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
- COLUMN_ACCOUNT, &account,
- editor = pidgin_account_editor_new(account);
- gtk_widget_show(editor);
- g_clear_object(&account);
-pidgin_account_manager_enable_toggled_cb(G_GNUC_UNUSED GtkCellRendererToggle *renderer,
- gchar *path, gpointer data)
- PidginAccountManager *manager = data;
- GtkTreeModel *model = GTK_TREE_MODEL(manager->model);
+ GtkWidget *editor = NULL; + PurpleAccount *account = NULL; - if(gtk_tree_model_get_iter_from_string(model, &iter, path)) {
- PurpleAccount *account = NULL;
- gboolean enabled = FALSE;
- /* The value of enabled in the model is the old value, so if enabled
- * is currently set to TRUE, we are disabling the account and vice
- gtk_tree_model_get(model, &iter,
- COLUMN_ENABLED, &enabled,
- COLUMN_ACCOUNT, &account,
- /* The account was just enabled, so set its status. */
- PurpleSavedStatus *status = purple_savedstatus_get_current();
- purple_savedstatus_activate_for_account(status, account);
- purple_account_set_enabled(account, !enabled);
- /* We don't update the model here, as it's updated via the notify
-pidgin_account_manager_account_added_cb(G_GNUC_UNUSED PurpleAccountManager *purple_manager,
- PurpleAccount *account,
- PidginAccountManager *manager = data;
- pidgin_account_manager_add_account(manager, account);
-pidgin_account_manager_account_removed_cb(G_GNUC_UNUSED PurpleAccountManager *purple_manager,
- PurpleAccount *account,
- PidginAccountManager *manager = data;
- if(pidgin_account_manager_find_account(manager, account, &iter)) {
- gtk_list_store_remove(manager->model, &iter);
+ account = pidgin_account_row_get_account(PIDGIN_ACCOUNT_ROW(row)); + editor = pidgin_account_editor_new(account); + gtk_widget_show(editor); /******************************************************************************
@@ -374,19 +121,17 @@
*****************************************************************************/
pidgin_account_manager_init(PidginAccountManager *manager) {
- PurpleAccountManager *purple_manager = NULL;
+ GListModel *purple_manager = NULL; gtk_widget_init_template(GTK_WIDGET(manager));
- pidgin_account_manager_populate(manager);
- purple_manager = purple_account_manager_get_default();
- g_signal_connect_object(purple_manager, "added",
- G_CALLBACK(pidgin_account_manager_account_added_cb),
+ purple_manager = purple_account_manager_get_default_as_model(); + gtk_list_box_bind_model(manager->list_box, purple_manager, + pidgin_account_manager_create_widget, NULL, NULL); + g_signal_connect_object(purple_manager, "items-changed", + G_CALLBACK(pidgin_account_manager_refresh_add_cb), - g_signal_connect_object(purple_manager, "removed",
- G_CALLBACK(pidgin_account_manager_account_removed_cb),
+ pidgin_account_manager_refresh_add_cb(purple_manager, 0, 0, 0, manager); @@ -399,23 +144,16 @@
gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
- gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
- gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
gtk_widget_class_bind_template_callback(widget_class,
- pidgin_account_manager_enable_toggled_cb);
- gtk_widget_class_bind_template_callback(widget_class,
pidgin_account_manager_response_cb);
gtk_widget_class_bind_template_callback(widget_class,
pidgin_account_manager_row_activated_cb);
gtk_widget_class_bind_template_callback(widget_class,
- pidgin_account_manager_selection_changed_cb);
+ pidgin_account_manager_create_account); /******************************************************************************
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginaccountrow.c Tue Jan 17 02:29:19 2023 -0600
@@ -0,0 +1,447 @@
+ * 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 <glib/gi18n-lib.h> +#include "pidgin/pidginaccountrow.h" +struct _PidginAccountRow { + PurpleAccount *account; +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; +/****************************************************************************** + *****************************************************************************/ +pidgin_account_row_refresh_buddy_icon(PidginAccountRow *row) { + PurpleImage *image = NULL; +#warning FIX call this in the right place when buddy icons are better and can autorefresh + if(!PURPLE_IS_ACCOUNT(row->account)) { + image = purple_buddy_icons_find_account_icon(row->account); + if(PURPLE_IS_IMAGE(image)) { + GdkTexture *texture = NULL; + bytes = purple_image_get_contents(image); + texture = gdk_texture_new_from_bytes(bytes, NULL); + if(GDK_IS_TEXTURE(texture)) { + adw_avatar_set_custom_image(row->avatar, GDK_PAINTABLE(texture)); + g_object_unref(texture); +pidgin_account_row_refresh_status(PidginAccountRow *row) { + const char *status = NULL; + gboolean connected = FALSE; + gboolean error = FALSE; + if(PURPLE_IS_ACCOUNT(row->account)) { + if(!purple_account_get_enabled(row->account)) { + status = _("Disabled"); + const PurpleConnectionErrorInfo *error_info = NULL; + error_info = purple_account_get_current_error(row->account); + if(error_info != NULL) { + status = error_info->description; + PurpleConnection *connection = NULL; + connection = purple_account_get_connection(row->account); + if(PURPLE_IS_CONNECTION(connection)) { + switch(purple_connection_get_state(connection)) { + case PURPLE_CONNECTION_STATE_DISCONNECTED: + status = _("Disconnected"); + case PURPLE_CONNECTION_STATE_DISCONNECTING: + status = _("Disconnecting..."); + case PURPLE_CONNECTION_STATE_CONNECTED: + status = _("Connected"); + case PURPLE_CONNECTION_STATE_CONNECTING: + status = _("Connecting..."); + gtk_switch_set_state(row->enabled, connected); + gtk_label_set_text(row->status, status); + gtk_widget_add_css_class(GTK_WIDGET(row->status), "error"); + gtk_widget_remove_css_class(GTK_WIDGET(row->status), "error"); +/****************************************************************************** + *****************************************************************************/ +pidgin_account_row_state_changed_cb(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + PidginAccountRow *row = data; + pidgin_account_row_refresh_status(row); +pidgin_account_row_connection_changed_cb(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + PidginAccountRow *row = data; + PurpleConnection *connection = NULL; + connection = purple_account_get_connection(row->account); + if(PURPLE_IS_CONNECTION(connection)) { + g_signal_connect_object(connection, "notify::state", + G_CALLBACK(pidgin_account_row_state_changed_cb), + pidgin_account_row_refresh_status(row); +pidgin_account_row_enable_state_set_cb(G_GNUC_UNUSED GtkSwitch *sw, + gboolean state, gpointer data) + PidginAccountRow *row = data; + PurpleAccount *account = row->account; + if(purple_account_get_enabled(account) == state) { + /* The account was just enabled, so set its status. */ + PurpleSavedStatus *status = purple_savedstatus_get_current(); + purple_savedstatus_activate_for_account(status, account); + purple_account_set_enabled(account, state); +pidgin_account_row_buddyicon_cb(G_GNUC_UNUSED GObject *self, + PurpleAccount *account, + G_GNUC_UNUSED gpointer data) + const char *buddy_icon_path = NULL; + if(!PURPLE_IS_ACCOUNT(account)) { + buddy_icon_path = purple_account_get_buddy_icon_path(account); + if(buddy_icon_path != NULL) { + path = g_strdup_printf("file://%s", buddy_icon_path); +pidgin_account_row_protocol_name_cb(G_GNUC_UNUSED GObject *self, + PurpleAccount *account, + G_GNUC_UNUSED gpointer data) + const char *name = _("Unknown"); + if(PURPLE_IS_ACCOUNT(account)) { + PurpleProtocol *protocol = purple_account_get_protocol(account); + if(PURPLE_IS_PROTOCOL(protocol)) { + name = purple_protocol_get_name(protocol); +pidgin_account_row_protocol_icon_cb(G_GNUC_UNUSED GObject *self, + PurpleAccount *account, + G_GNUC_UNUSED gpointer data) + const char *icon_name = NULL; + if(PURPLE_IS_ACCOUNT(account)) { + PurpleProtocol *protocol = purple_account_get_protocol(account); + if(PURPLE_IS_PROTOCOL(protocol)) { + icon_name = purple_protocol_get_icon_name(protocol); + return g_strdup(icon_name); +pidgin_account_manager_remove_account_cb(G_GNUC_UNUSED AdwMessageDialog *self, + char *response, gpointer data) + PurpleAccount *account = data; + PurpleNotificationManager *notification_manager = NULL; + if(!purple_strequal(response, "remove")) { + /* Remove all notifications including connection errors for the account. */ + notification_manager = purple_notification_manager_get_default(); + purple_notification_manager_remove_with_account(notification_manager, + /* Delete the account. */ + purple_accounts_delete(account); +pidgin_account_row_remove_cb(G_GNUC_UNUSED GtkButton *self, gpointer data) { + PidginAccountRow *row = data; + AdwMessageDialog *dialog = NULL; + PurpleContactInfo *info = NULL; + const char *name = NULL; + char *protocol_name = NULL; + info = PURPLE_CONTACT_INFO(row->account); + name = purple_contact_info_get_name_for_display(info); + protocol_name = pidgin_account_row_protocol_name_cb(NULL, row->account, + root = gtk_widget_get_root(GTK_WIDGET(row)); + dialog = ADW_MESSAGE_DIALOG(adw_message_dialog_new(GTK_WINDOW(root), + adw_message_dialog_format_body(dialog, + _("Do you want to remove the %s (%s) " + "account from Pidgin?"), + adw_message_dialog_add_responses(dialog, "cancel", _("Cancel"), + "remove", _("Remove"), NULL); + adw_message_dialog_set_response_appearance(dialog, "remove", + ADW_RESPONSE_DESTRUCTIVE); + adw_message_dialog_set_default_response(dialog, "cancel"); + adw_message_dialog_set_close_response(dialog, "cancel"); + g_signal_connect_object(dialog, "response", + G_CALLBACK(pidgin_account_manager_remove_account_cb), + gtk_window_present(GTK_WINDOW(dialog)); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +G_DEFINE_TYPE(PidginAccountRow, pidgin_account_row, GTK_TYPE_LIST_BOX_ROW) +pidgin_account_row_get_property(GObject *obj, guint param_id, GValue *value, + PidginAccountRow *row = PIDGIN_ACCOUNT_ROW(obj); + g_value_set_object(value, pidgin_account_row_get_account(row)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +pidgin_account_row_set_property(GObject *obj, guint param_id, + const GValue *value, GParamSpec *pspec) + PidginAccountRow *row = PIDGIN_ACCOUNT_ROW(obj); + pidgin_account_row_set_account(row, g_value_get_object(value)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); +pidgin_account_row_finalize(GObject *obj) { + PidginAccountRow *row = PIDGIN_ACCOUNT_ROW(obj); + g_clear_object(&row->account); + G_OBJECT_CLASS(pidgin_account_row_parent_class)->finalize(obj); +pidgin_account_row_init(PidginAccountRow *row) { + gtk_widget_init_template(GTK_WIDGET(row)); + pidgin_account_row_refresh_buddy_icon(row); + pidgin_account_row_refresh_status(row); +pidgin_account_row_class_init(PidginAccountRowClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + obj_class->finalize = pidgin_account_row_finalize; + obj_class->get_property = pidgin_account_row_get_property; + obj_class->set_property = pidgin_account_row_set_property; + * PidginAccountRow:account: + * The account that this row will be representing. + properties[PROP_ACCOUNT] = g_param_spec_object( + "The account that this row is representing", + 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/Pidgin3/Accounts/account-row.ui" + gtk_widget_class_bind_template_child(widget_class, PidginAccountRow, + gtk_widget_class_bind_template_child(widget_class, PidginAccountRow, + gtk_widget_class_bind_template_child(widget_class, PidginAccountRow, + gtk_widget_class_bind_template_child(widget_class, PidginAccountRow, + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_row_enable_state_set_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_row_buddyicon_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_row_protocol_name_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_row_protocol_icon_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_row_remove_cb); +/****************************************************************************** + *****************************************************************************/ +pidgin_account_row_new(PurpleAccount *account) { + return g_object_new(PIDGIN_TYPE_ACCOUNT_ROW, "account", account, NULL); +pidgin_account_row_get_account(PidginAccountRow *row) { + g_return_val_if_fail(PIDGIN_IS_ACCOUNT_ROW(row), NULL); +pidgin_account_row_set_account(PidginAccountRow *row, PurpleAccount *account) { + PurpleAccount *old = NULL; + g_return_if_fail(PIDGIN_IS_ACCOUNT_ROW(row)); + old = (row->account != NULL) ? g_object_ref(row->account) : NULL; + if(g_set_object(&row->account, account)) { + if(PURPLE_IS_ACCOUNT(old)) { + PurpleConnection *connection = purple_account_get_connection(old); + if(PURPLE_IS_CONNECTION(connection)) { + g_signal_handlers_disconnect_by_func(connection, + pidgin_account_row_state_changed_cb, + g_signal_handlers_disconnect_by_func(old, + pidgin_account_row_connection_changed_cb, + if(PURPLE_IS_ACCOUNT(account)) { + g_signal_connect_object(account, "notify::connection", + G_CALLBACK(pidgin_account_row_connection_changed_cb), + g_signal_connect_object(account, "notify::error", + G_CALLBACK(pidgin_account_row_state_changed_cb), + pidgin_account_row_refresh_buddy_icon(row); + pidgin_account_row_refresh_status(row); + g_object_notify_by_pspec(G_OBJECT(row), properties[PROP_ACCOUNT]); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginaccountrow.h Tue Jan 17 02:29:19 2023 -0600
@@ -0,0 +1,85 @@
+ * 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_ACCOUNT_ROW_H +#define PIDGIN_ACCOUNT_ROW_H + * A [class@Gtk.ListBoxRow] subclass to display a [class@Purple.Account]. +#define PIDGIN_TYPE_ACCOUNT_ROW (pidgin_account_row_get_type()) +G_DECLARE_FINAL_TYPE(PidginAccountRow, pidgin_account_row, PIDGIN, ACCOUNT_ROW, + * pidgin_account_row_new: + * @account: (nullable): The account to represent. + * Creates a new #PidginAccountRow to display a [class@Purple.Account]. + * Returns: (transfer full): The new account row. +GtkWidget *pidgin_account_row_new(PurpleAccount *account); + * pidgin_account_row_get_account: + * Gets the [class@Purple.Account] that @row is displaying. + * Returns: (transfer none): The account if set otherwise %NULL. +PurpleAccount *pidgin_account_row_get_account(PidginAccountRow *row); + * pidgin_account_row_set_account: + * @account: (nullable): The new account to set, or %NULL to unset. + * Sets the [class@Purple.Account] for @row to display. +void pidgin_account_row_set_account(PidginAccountRow *row, PurpleAccount *account); +#endif /* PIDGIN_ACCOUNT_ROW_H */ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Accounts/account-row.ui Tue Jan 17 02:29:19 2023 -0600
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?> +Pidgin - Internet Messenger +Copyright (C) Pidgin Developers <devel@pidgin.im> +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 library; if not, see <https://www.gnu.org/licenses/>. + <requires lib="gtk" version="4.0"/> + <!-- interface-license-type gplv2 --> + <!-- interface-name Pidgin --> + <!-- interface-description Internet Messenger --> + <!-- interface-copyright Pidgin Developers <devel@pidgin.im> --> + <template class="PidginAccountRow" parent="GtkListBoxRow"> + <property name="selectable">0</property> + <property name="child"> + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="spacing">6</property> + <object class="GtkSwitch" id="enabled"> + <binding name="active"> + <lookup name="enabled" type="PurpleAccount"> + <lookup name="account">PidginAccountRow</lookup> + <property name="valign">center</property> + <signal name="state-set" handler="pidgin_account_row_enable_state_set_cb" swapped="no"/> + <object class="AdwAvatar" id="avatar"> + <property name="size">48</property> + <lookup name="name-for-display" type="PurpleContactInfo"> + <lookup name="account">PidginAccountRow</lookup> + <object class="GtkBox"> + <property name="hexpand">1</property> + <property name="orientation">vertical</property> + <property name="valign">center</property> + <object class="GtkLabel" id="name"> + <property name="xalign">0</property> + <lookup name="name-for-display" type="PurpleContactInfo"> + <lookup name="account">PidginAccountRow</lookup> + <object class="GtkLabel" id="status"> + <property name="css-classes">dim-label</property> + <property name="wrap">1</property> + <property name="xalign">0</property> + <object class="GtkBox"> + <property name="orientation">vertical</property> + <property name="valign">center</property> + <object class="GtkImage"> + <binding name="icon-name"> + <closure type="gchararray" function="pidgin_account_row_protocol_icon_cb"> + <lookup name="account">PidginAccountRow</lookup> + <object class="GtkLabel"> + <closure type="gchararray" function="pidgin_account_row_protocol_name_cb"> + <lookup name="account">PidginAccountRow</lookup> + <property name="css-classes">dim-label</property> + <object class="GtkButton"> + <!-- This button is not really an error, but the destructive-action styling is far too bold to use in a list box. --> + <property name="css-classes">circular + <property name="icon-name">list-remove-symbolic</property> + <property name="valign">center</property> + <signal name="clicked" handler="pidgin_account_row_remove_cb" swapped="no"/> + <object class="GtkImage"> + <property name="icon-name">go-next-symbolic</property> --- a/pidgin/resources/Accounts/manager.ui Tue Jan 17 02:27:41 2023 -0600
+++ b/pidgin/resources/Accounts/manager.ui Tue Jan 17 02:29:19 2023 -0600
@@ -24,118 +24,89 @@
<!-- interface-name Pidgin -->
<!-- interface-description Internet Messenger -->
<!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
- <object class="GtkListStore" id="model">
- <!-- column-name enabled -->
- <column type="gboolean"/>
- <!-- column-name avatar -->
- <column type="GdkPixbuf"/>
- <!-- column-name username -->
- <column type="gchararray"/>
- <!-- column-name protocol-icon -->
- <column type="gchararray"/>
- <!-- column-name protocol-name -->
- <column type="gchararray"/>
- <!-- column-name account -->
- <column type="GObject"/>
<template class="PidginAccountManager" parent="GtkDialog">
<property name="title" translatable="1">Accounts</property>
- <property name="default-width">500</property>
- <property name="default-height">300</property>
+ <property name="default-width">640</property> + <property name="default-height">480</property> <signal name="response" handler="pidgin_account_manager_response_cb" swapped="no"/>
<child internal-child="content_area">
- <object class="GtkScrolledWindow">
- <property name="vexpand">1</property>
- <property name="focusable">1</property>
+ <object class="GtkBox"> - <object class="GtkTreeView">
- <property name="focusable">1</property>
- <property name="model">model</property>
- <signal name="row-activated" handler="pidgin_account_manager_row_activated_cb" object="PidginAccountManager" swapped="no"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="selection">
- <signal name="changed" handler="pidgin_account_manager_selection_changed_cb" object="PidginAccountManager" swapped="no"/>
- <object class="GtkTreeViewColumn">
- <property name="title" translatable="1">Enabled</property>
+ <object class="GtkScrolledWindow"> + <property name="vexpand">1</property> + <property name="child"> + <object class="AdwClamp"> + <property name="margin-bottom">24</property> + <property name="margin-end">24</property> + <property name="margin-start">24</property> + <property name="margin-top">24</property> + <property name="orientation">horizontal</property> + <property name="valign">center</property> - <object class="GtkCellRendererToggle">
- <signal name="toggled" handler="pidgin_account_manager_enable_toggled_cb" object="PidginAccountManager" swapped="no"/>
+ <object class="GtkListBox" id="list_box"> + <property name="css-classes">boxed-list + <property name="selection-mode">none</property> + <property name="show-separators">1</property> + <signal name="row-activated" handler="pidgin_account_manager_row_activated_cb" swapped="no"/> + <child type="placeholder"> + <object class="GtkBox"> + <property name="margin-bottom">48</property> + <property name="margin-top">48</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <property name="valign">center</property> + <property name="vexpand">1</property> + <object class="GtkImage"> + <property name="css-classes">dim-label</property> + <property name="icon-name">view-list-symbolic</property> + <property name="pixel-size">128</property> + <object class="GtkLabel"> + <property name="css-classes">title-1</property> + <property name="label" translatable="1">No Accounts</property> + <object class="GtkButton"> + <property name="css-classes">pill +suggested-action</property> + <property name="halign">center</property> + <signal name="clicked" handler="pidgin_account_manager_create_account" swapped="yes"/> + <property name="child"> + <object class="AdwButtonContent"> + <property name="icon-name">list-add-symbolic</property> + <property name="label" translatable="1">_Add…</property> + <property name="use-underline">1</property>
- <attribute name="active">0</attribute>
- <object class="GtkTreeViewColumn">
- <property name="resizable">1</property>
- <property name="title" translatable="1">Username</property>
- <object class="GtkCellRendererPixbuf"/>
- <attribute name="pixbuf">1</attribute>
- <object class="GtkCellRendererText"/>
- <attribute name="markup">2</attribute>
- <object class="GtkTreeViewColumn">
- <property name="resizable">1</property>
- <property name="title" translatable="1">Protocol</property>
- <object class="GtkCellRendererPixbuf"/>
- <attribute name="icon-name">3</attribute>
- <object class="GtkCellRendererText"/>
- <attribute name="markup">4</attribute>
- <object class="GtkButton" id="button3">
- <property name="label" translatable="1">_Add...</property>
+ <object class="GtkButton" id="add"> + <property name="css-classes">suggested-action</property> <property name="focusable">1</property>
<property name="receives-default">1</property>
- <property name="use-underline">1</property>
- <object class="GtkButton" id="modify_button">
- <property name="label" translatable="1">_Modify...</property>
- <property name="sensitive">0</property>
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="use-underline">1</property>
- <object class="GtkButton" id="remove_button">
- <property name="label" translatable="1">_Remove</property>
- <property name="sensitive">0</property>
- <property name="focusable">1</property>
- <property name="receives-default">1</property>
- <property name="use-underline">1</property>
+ <property name="child"> + <object class="AdwButtonContent"> + <property name="icon-name">list-add-symbolic</property> + <property name="label" translatable="1">_Add…</property> + <property name="use-underline">1</property> @@ -147,9 +118,7 @@
- <action-widget response="0">button3</action-widget>
- <action-widget response="1">modify_button</action-widget>
- <action-widget response="2">remove_button</action-widget>
+ <action-widget response="0">add</action-widget> <action-widget response="close">button2</action-widget>
--- a/pidgin/resources/pidgin.gresource.xml Tue Jan 17 02:27:41 2023 -0600
+++ b/pidgin/resources/pidgin.gresource.xml Tue Jan 17 02:29:19 2023 -0600
@@ -4,6 +4,7 @@
<file compressed="true" preprocess="xml-stripblanks">About/about.ui</file>
<file compressed="true">About/about.md</file>
<file compressed="true" preprocess="json-stripblanks">About/credits.json</file>
+ <file compressed="true" preprocess="xml-stripblanks">Accounts/account-row.ui</file> <file compressed="true" preprocess="xml-stripblanks">Accounts/chooser.ui</file>
<file compressed="true" preprocess="xml-stripblanks">Accounts/editor.ui</file>
<file compressed="true" preprocess="xml-stripblanks">Accounts/manager.ui</file>
--- a/po/POTFILES.in Tue Jan 17 02:27:41 2023 -0600
+++ b/po/POTFILES.in Tue Jan 17 02:29:19 2023 -0600
@@ -263,6 +263,7 @@
pidgin/pidginaccountfilterconnected.c
pidgin/pidginaccountfilterprotocol.c
pidgin/pidginaccountmanager.c
+pidgin/pidginaccountrow.c pidgin/pidginaccountsdisabledmenu.c
pidgin/pidginaccountsenabledmenu.c
pidgin/pidginactiongroup.c
@@ -315,6 +316,7 @@
pidgin/prefs/pidginproxyprefs.c
pidgin/prefs/pidginvvprefs.c
pidgin/resources/About/about.ui
+pidgin/resources/Accounts/account-row.ui pidgin/resources/Accounts/chooser.ui
pidgin/resources/Accounts/editor.ui
pidgin/resources/Accounts/manager.ui