pidgin/pidgin

Parents f33f4713508b
Children fe56d627c33a
Create a new PidginAccountManager that replaces the manually built one.

The old code was left to keep this change as clean as possible and will be
removed afterwards.

Drag and drop as been removed as it's completely different in GTK4 and I didn't
want to waste time on it since it's going to be deleted when it's rewritten.

I also didn't implement the double click to modify account as that's different
in GTK4 and we don't yet have new Event Controller for it in GTK3.

Support for selecting multiple accounts was dropped and now only one account may be selected at a time.

Finally, I changed the title of the "Delete" button to "Remove" as "Remove"
seems more clear.

Testing Done:
Used the previous dialog to add, remove, and modify accounts and verified they were updated appropriately.
Also started pidgin without any accounts enabled, to verify that the new dialog was shown instead of the old.
Tested the add, modify, remove, and close buttons.
Tested that the modify and remove buttons are only sensitive when an account is selected.

Bugs closed: PIDGIN-17589

Reviewed at https://reviews.imfreedom.org/r/1318/
--- a/pidgin/meson.build Tue Mar 22 03:53:15 2022 -0500
+++ b/pidgin/meson.build Tue Mar 22 07:46:43 2022 -0500
@@ -23,6 +23,7 @@
'pidginaccountchooser.c',
'pidginaccountfilterconnected.c',
'pidginaccountfilterprotocol.c',
+ 'pidginaccountmanager.c',
'pidginaccountsdisabledmenu.c',
'pidginaccountsenabledmenu.c',
'pidginaccountsmenu.c',
@@ -87,6 +88,7 @@
'pidginaccountchooser.h',
'pidginaccountfilterconnected.h',
'pidginaccountfilterprotocol.h',
+ 'pidginaccountmanager.h',
'pidginaccountsdisabledmenu.h',
'pidginaccountsenabledmenu.h',
'pidginaccountsmenu.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginaccountmanager.c Tue Mar 22 07:46:43 2022 -0500
@@ -0,0 +1,392 @@
+/*
+ * 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.h>
+
+#include <purple.h>
+
+#include "pidginaccountmanager.h"
+
+#include "gtkaccount.h"
+#include "pidgincore.h"
+#include "pidgingdkpixbuf.h"
+
+struct _PidginAccountManager {
+ GtkDialog parent;
+
+ GtkListStore *model;
+ GtkTreeSelection *selection;
+
+ GtkWidget *modify_button;
+ GtkWidget *remove_button;
+};
+
+enum {
+ RESPONSE_ADD,
+ RESPONSE_MODIFY,
+ RESPONSE_REMOVE
+};
+
+enum {
+ COLUMN_ENABLED,
+ COLUMN_AVATAR,
+ COLUMN_USERNAME,
+ COLUMN_PROTOCOL_ICON,
+ COLUMN_PROTOCOL_NAME,
+ COLUMN_ACCOUNT
+};
+
+G_DEFINE_TYPE(PidginAccountManager, pidgin_account_manager, GTK_TYPE_DIALOG)
+
+static void pidgin_account_manager_account_notify_cb(GObject *obj, GParamSpec *pspec, gpointer data);
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static gboolean
+pidgin_account_manager_find_account(PidginAccountManager *manager,
+ PurpleAccount *account, GtkTreeIter *iter)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL(manager->model);
+
+ if(!gtk_tree_model_get_iter_first(model, iter)) {
+ return FALSE;
+ }
+
+ do {
+ PurpleAccount *current = NULL;
+
+ gtk_tree_model_get(model, iter,
+ COLUMN_ACCOUNT, &current,
+ -1);
+
+ if(current == account) {
+ g_clear_object(&current);
+
+ return TRUE;
+ }
+
+ g_clear_object(&current);
+ } while(gtk_tree_model_iter_next(model, iter));
+
+ return FALSE;
+}
+
+static PurpleAccount *
+pidgin_account_manager_get_selected_account(PidginAccountManager *manager) {
+ PurpleAccount *account = NULL;
+ GtkTreeIter iter;
+
+ if(gtk_tree_selection_count_selected_rows(manager->selection) == 0) {
+ return NULL;
+ }
+
+ gtk_tree_selection_get_selected(manager->selection, NULL, &iter);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+ COLUMN_ACCOUNT, &account,
+ -1);
+
+ return account;
+}
+
+static void
+pidgin_account_manager_refresh_account(PidginAccountManager *manager,
+ PurpleAccount *account,
+ GtkTreeIter *iter)
+{
+ 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);
+ if(image != NULL) {
+ GdkPixbuf *raw = NULL;
+
+ raw = pidgin_pixbuf_from_image(image);
+ g_object_unref(image);
+
+ avatar = gdk_pixbuf_scale_simple(raw, 22, 22, GDK_INTERP_HYPER);
+ g_clear_object(&raw);
+ }
+
+ /* 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);
+ } else {
+ protocol_name = _("Unknown");
+ }
+
+ gtk_list_store_set(manager->model, iter,
+ COLUMN_ENABLED, purple_account_get_enabled(account, PIDGIN_UI),
+ COLUMN_AVATAR, avatar,
+ COLUMN_USERNAME, purple_account_get_username(account),
+ COLUMN_PROTOCOL_ICON, protocol_icon,
+ COLUMN_PROTOCOL_NAME, protocol_name,
+ COLUMN_ACCOUNT, account,
+ -1);
+
+ g_clear_object(&avatar);
+}
+
+static void
+pidgin_account_manager_update_account(PidginAccountManager *manager,
+ PurpleAccount *account)
+{
+ GtkTreeIter iter;
+
+ if(pidgin_account_manager_find_account(manager, account, &iter)) {
+ pidgin_account_manager_refresh_account(manager, account, &iter);
+ }
+}
+
+static void
+pidgin_account_manager_add_account(PidginAccountManager *manager,
+ PurpleAccount *account)
+{
+ GtkTreeIter iter;
+
+ 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),
+ manager, 0);
+}
+
+static void
+pidgin_account_manager_populate_helper(PurpleAccount *account, gpointer data) {
+ pidgin_account_manager_add_account(PIDGIN_ACCOUNT_MANAGER(data), account);
+}
+
+static void
+pidgin_account_manager_populate(PidginAccountManager *manager) {
+ purple_account_manager_foreach(purple_account_manager_get_default(),
+ pidgin_account_manager_populate_helper,
+ manager);
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_account_manager_account_notify_cb(GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ gpointer data)
+{
+ PidginAccountManager *manager = PIDGIN_ACCOUNT_MANAGER(data);
+ PurpleAccount *account = PURPLE_ACCOUNT(obj);
+
+ pidgin_account_manager_update_account(manager, account);
+}
+
+static void
+pidgin_account_manager_response_cb(GtkDialog *dialog, gint response_id,
+ G_GNUC_UNUSED gpointer data)
+{
+ PidginAccountManager *manager = PIDGIN_ACCOUNT_MANAGER(dialog);
+ PurpleAccount *account = NULL;
+
+ switch(response_id) {
+ case RESPONSE_ADD:
+ pidgin_account_dialog_show(PIDGIN_ADD_ACCOUNT_DIALOG, NULL);
+ break;
+ case RESPONSE_MODIFY:
+ account = pidgin_account_manager_get_selected_account(manager);
+
+ pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account);
+
+ g_clear_object(&account);
+
+ break;
+ case RESPONSE_REMOVE:
+ account = pidgin_account_manager_get_selected_account(manager);
+
+ purple_accounts_delete(account);
+
+ g_clear_object(&account);
+
+ break;
+ case GTK_RESPONSE_CLOSE:
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ break;
+ default:
+ g_warning("not sure how you got here...");
+ }
+}
+
+static void
+pidgin_account_manager_selection_changed_cb(GtkTreeSelection *selection,
+ gpointer data)
+{
+ PidginAccountManager *manager = data;
+ gboolean sensitive = TRUE;
+
+ if(gtk_tree_selection_count_selected_rows(selection) == 0) {
+ sensitive = FALSE;
+ }
+
+ gtk_widget_set_sensitive(manager->modify_button, sensitive);
+ gtk_widget_set_sensitive(manager->remove_button, sensitive);
+}
+
+static void
+pidgin_account_manager_row_activated_cb(G_GNUC_UNUSED GtkTreeView *tree_view,
+ GtkTreePath *path,
+ G_GNUC_UNUSED GtkTreeViewColumn *column,
+ gpointer data)
+{
+ PidginAccountManager *manager = data;
+ GtkTreeIter iter;
+
+ if(gtk_tree_model_get_iter(GTK_TREE_MODEL(manager->model), &iter, path)) {
+ PurpleAccount *account = NULL;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(manager->model), &iter,
+ COLUMN_ACCOUNT, &account,
+ -1);
+
+ pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account);
+
+ g_clear_object(&account);
+ }
+}
+
+static void
+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);
+ GtkTreeIter iter;
+
+ 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
+ * versa.
+ */
+ gtk_tree_model_get(model, &iter,
+ COLUMN_ENABLED, &enabled,
+ COLUMN_ACCOUNT, &account,
+ -1);
+
+ /* The account was just enabled, so set its status. */
+ if(!enabled) {
+ PurpleSavedStatus *status = purple_savedstatus_get_current();
+ purple_savedstatus_activate_for_account(status, account);
+ }
+
+ purple_account_set_enabled(account, PIDGIN_UI, !enabled);
+
+ /* We don't update the model here, as it's updated via the notify
+ * signal.
+ */
+ }
+}
+
+static void
+pidgin_account_manager_account_added_cb(G_GNUC_UNUSED PurpleAccountManager *purple_manager,
+ PurpleAccount *account,
+ gpointer data)
+{
+ PidginAccountManager *manager = data;
+
+ pidgin_account_manager_add_account(manager, account);
+}
+
+static void
+pidgin_account_manager_account_removed_cb(G_GNUC_UNUSED PurpleAccountManager *purple_manager,
+ PurpleAccount *account,
+ gpointer data)
+{
+ PidginAccountManager *manager = data;
+ GtkTreeIter iter;
+
+ if(pidgin_account_manager_find_account(manager, account, &iter)) {
+ gtk_list_store_remove(manager->model, &iter);
+ }
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_account_manager_init(PidginAccountManager *manager) {
+ PurpleAccountManager *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),
+ manager, 0);
+ g_signal_connect_object(purple_manager, "removed",
+ G_CALLBACK(pidgin_account_manager_account_removed_cb),
+ manager, 0);
+}
+
+static void
+pidgin_account_manager_class_init(PidginAccountManagerClass *klass) {
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+ gtk_widget_class_set_template_from_resource(
+ widget_class,
+ "/im/pidgin/Pidgin3/Accounts/manager.ui"
+ );
+
+ gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
+ model);
+ gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
+ selection);
+
+ gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
+ modify_button);
+ gtk_widget_class_bind_template_child(widget_class, PidginAccountManager,
+ remove_button);
+
+ 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);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GtkWidget *
+pidgin_account_manager_new(void) {
+ return g_object_new(PIDGIN_TYPE_ACCOUNT_MANAGER, NULL);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginaccountmanager.h Tue Mar 22 07:46:43 2022 -0500
@@ -0,0 +1,59 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
+# error "only <pidgin.h> may be included directly"
+#endif
+
+#ifndef PIDGIN_ACCOUNT_MANAGER_H
+#define PIDGIN_ACCOUNT_MANAGER_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginAccountManager:
+ *
+ * A singleton dialog for managing accounts.
+ *
+ * Since: 3.0.0
+ */
+
+#define PIDGIN_TYPE_ACCOUNT_MANAGER (pidgin_account_manager_get_type())
+G_DECLARE_FINAL_TYPE(PidginAccountManager, pidgin_account_manager, PIDGIN,
+ ACCOUNT_MANAGER, GtkDialog)
+
+/**
+ * pidgin_account_manager_new:
+ *
+ * Creates a new account manager dialog.
+ *
+ * Returns: (transfer full): The dialog.
+ *
+ * Since: 3.0.0
+ */
+GtkWidget *pidgin_account_manager_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_ACCOUNT_MANAGER_H */
--- a/pidgin/pidginapplication.c Tue Mar 22 03:53:15 2022 -0500
+++ b/pidgin/pidginapplication.c Tue Mar 22 07:46:43 2022 -0500
@@ -42,6 +42,7 @@
#include "gtkroomlist.h"
#include "gtkxfer.h"
#include "pidginabout.h"
+#include "pidginaccountmanager.h"
#include "pidginaccountsdisabledmenu.h"
#include "pidginaccountsenabledmenu.h"
#include "pidginconversationwindow.h"
@@ -231,7 +232,15 @@
pidgin_application_accounts(GSimpleAction *simple, GVariant *parameter,
gpointer data)
{
- pidgin_accounts_window_show();
+ static GtkWidget *manager = NULL;
+
+ if(!GTK_IS_WIDGET(manager)) {
+ manager = pidgin_account_manager_new();
+ g_object_add_weak_pointer(G_OBJECT(manager), (gpointer)&manager);
+ }
+
+
+ gtk_window_present_with_time(GTK_WINDOW(manager), GDK_CURRENT_TIME);
}
static void
@@ -722,7 +731,8 @@
manager = purple_account_manager_get_default();
active_accounts = purple_account_manager_get_active(manager);
if(active_accounts == NULL) {
- pidgin_accounts_window_show();
+ g_action_group_activate_action(G_ACTION_GROUP(application),
+ "manage-accounts", NULL);
} else {
g_list_free(active_accounts);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Accounts/manager.ui Tue Mar 22 07:46:43 2022 -0500
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2
+
+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 program; if not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.24"/>
+ <!-- interface-license-type gplv2 -->
+ <!-- interface-name Pidgin -->
+ <!-- interface-description Internet Messenger -->
+ <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+ <object class="GtkListStore" id="model">
+ <columns>
+ <!-- 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"/>
+ </columns>
+ </object>
+ <template class="PidginAccountManager" parent="GtkDialog">
+ <property name="can-focus">False</property>
+ <property name="title" translatable="yes">Accounts</property>
+ <property name="default-width">500</property>
+ <property name="default-height">300</property>
+ <property name="type-hint">dialog</property>
+ <signal name="response" handler="pidgin_account_manager_response_cb" swapped="no"/>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="can-focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can-focus">False</property>
+ <property name="layout-style">end</property>
+ <child>
+ <object class="GtkButton" id="button3">
+ <property name="label" translatable="yes">_Add...</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="modify_button">
+ <property name="label" translatable="yes">_Modify...</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="remove_button">
+ <property name="label" translatable="yes">_Remove</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label" translatable="yes">_Close</property>
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="receives-default">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkTreeView">
+ <property name="visible">True</property>
+ <property name="can-focus">True</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>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="title" translatable="yes">Enabled</property>
+ <child>
+ <object class="GtkCellRendererToggle">
+ <signal name="toggled" handler="pidgin_account_manager_enable_toggled_cb" object="PidginAccountManager" swapped="no"/>
+ </object>
+ <attributes>
+ <attribute name="active">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Username</property>
+ <child>
+ <object class="GtkCellRendererPixbuf"/>
+ <attributes>
+ <attribute name="pixbuf">1</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="markup">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Protocol</property>
+ <child>
+ <object class="GtkCellRendererPixbuf"/>
+ <attributes>
+ <attribute name="icon-name">3</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="markup">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <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="-7">button2</action-widget>
+ </action-widgets>
+ </template>
+</interface>
--- a/pidgin/resources/pidgin.gresource.xml Tue Mar 22 03:53:15 2022 -0500
+++ b/pidgin/resources/pidgin.gresource.xml Tue Mar 22 07:46:43 2022 -0500
@@ -8,6 +8,7 @@
<file compressed="true">Accounts/actionsmenu.ui</file>
<file compressed="true">Accounts/chooser.ui</file>
<file compressed="true">Accounts/entry.css</file>
+ <file compressed="true">Accounts/manager.ui</file>
<file compressed="true">Accounts/menu.ui</file>
<file compressed="true">Avatar/avatar.ui</file>
<file compressed="true">Avatar/menu.ui</file>
--- a/po/POTFILES.in Tue Mar 22 03:53:15 2022 -0500
+++ b/po/POTFILES.in Tue Mar 22 07:46:43 2022 -0500
@@ -392,6 +392,7 @@
pidgin/resources/About/about.ui
pidgin/resources/Accounts/actionsmenu.ui
pidgin/resources/Accounts/chooser.ui
+pidgin/resources/Accounts/manager.ui
pidgin/resources/Accounts/menu.ui
pidgin/resources/Avatar/avatar.ui
pidgin/resources/Avatar/menu.ui