--- 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',
@@ -87,6 +88,7 @@
'pidginaccountchooser.h',
'pidginaccountfilterconnected.h',
'pidginaccountfilterprotocol.h',
+ 'pidginaccountmanager.h', 'pidginaccountsdisabledmenu.h',
'pidginaccountsenabledmenu.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 + * 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 "pidginaccountmanager.h" +#include "pidgingdkpixbuf.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) + 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) { + 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, + 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 = pidgin_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"); + gtk_list_store_set(manager->model, iter, + COLUMN_ENABLED, purple_account_get_enabled(account, PIDGIN_UI), + COLUMN_USERNAME, purple_account_get_username(account), + 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, +/****************************************************************************** + *****************************************************************************/ +pidgin_account_manager_account_notify_cb(GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + PidginAccountManager *manager = PIDGIN_ACCOUNT_MANAGER(data); + PurpleAccount *account = PURPLE_ACCOUNT(obj); + pidgin_account_manager_update_account(manager, account); +pidgin_account_manager_response_cb(GtkDialog *dialog, gint response_id, + G_GNUC_UNUSED gpointer data) + PidginAccountManager *manager = PIDGIN_ACCOUNT_MANAGER(dialog); + PurpleAccount *account = NULL; + pidgin_account_dialog_show(PIDGIN_ADD_ACCOUNT_DIALOG, NULL); + account = pidgin_account_manager_get_selected_account(manager); + pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account); + g_clear_object(&account); + account = pidgin_account_manager_get_selected_account(manager); + purple_accounts_delete(account); + g_clear_object(&account); + case GTK_RESPONSE_CLOSE: + gtk_widget_destroy(GTK_WIDGET(dialog)); + g_warning("not sure how you got here..."); +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, + PidginAccountManager *manager = data; + 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, + pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, account); + 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); + 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, PIDGIN_UI, !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); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +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), + g_signal_connect_object(purple_manager, "removed", + G_CALLBACK(pidgin_account_manager_account_removed_cb), +pidgin_account_manager_class_init(PidginAccountManagerClass *klass) { + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + gtk_widget_class_set_template_from_resource( + "/im/pidgin/Pidgin3/Accounts/manager.ui" + 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_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 + * 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_MANAGER_H +#define PIDGIN_ACCOUNT_MANAGER_H + * PidginAccountManager: + * A singleton dialog for managing accounts. +#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. +GtkWidget *pidgin_account_manager_new(void); +#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 "pidginaccountmanager.h" #include "pidginaccountsdisabledmenu.h"
#include "pidginaccountsenabledmenu.h"
#include "pidginconversationwindow.h"
@@ -231,7 +232,15 @@
pidgin_application_accounts(GSimpleAction *simple, GVariant *parameter,
- 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); @@ -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); 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/>. + <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"> + <!-- 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="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> + <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> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + <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> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + <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> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + <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> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="shadow-type">in</property> + <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 class="GtkTreeViewColumn"> + <property name="title" translatable="yes">Enabled</property> + <object class="GtkCellRendererToggle"> + <signal name="toggled" handler="pidgin_account_manager_enable_toggled_cb" object="PidginAccountManager" swapped="no"/> + <attribute name="active">0</attribute> + <object class="GtkTreeViewColumn"> + <property name="resizable">True</property> + <property name="title" translatable="yes">Username</property> + <object class="GtkCellRendererPixbuf"/> + <attribute name="pixbuf">1</attribute> + <object class="GtkCellRendererText"/> + <attribute name="markup">2</attribute> + <object class="GtkTreeViewColumn"> + <property name="resizable">True</property> + <property name="title" translatable="yes">Protocol</property> + <object class="GtkCellRendererPixbuf"/> + <attribute name="icon-name">3</attribute> + <object class="GtkCellRendererText"/> + <attribute name="markup">4</attribute> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + <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> --- 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