Mon, 06 Apr 2020 22:27:40 -0500
Migrate the accounts menu to its own set of widgets
--- a/pidgin/glade/pidgin3.xml.in Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/glade/pidgin3.xml.in Mon Apr 06 22:27:40 2020 -0500 @@ -2,6 +2,7 @@ <glade-catalog name="pidgin" version="@PURPLE_MAJOR_VERSION@.@PURPLE_MINOR_VERSION@" library="pidgin3"> <glade-widget-classes> <glade-widget-class name="PidginAccountChooser" generic-name="account_chooser" title="AccountChooser"/> + <glade-widget-class name="PidginAccountsMenu" generic-name="accounts_menu" title="AccountsMenu"/> <glade-widget-class name="PidginInviteDialog" generic-name="invite_dialog" title="InviteDialog"/> <glade-widget-class name="PidginMenuTray" generic-name="menu_tray" title="MenuTray"/> <glade-widget-class name="PidginPluginsMenu" generic-name="plugins_menu" title="PluginsMenu"/> @@ -9,6 +10,7 @@ </glade-widget-classes> <glade-widget-group name="pidgin" title="Pidgin"> <glade-widget-class-ref name="PidginAccountChooser"/> + <glade-widget-class-ref name="PidginAccountsMenu"/> <glade-widget-class-ref name="PidginInviteDialog"/> <glade-widget-class-ref name="PidginMenuTray"/> <glade-widget-class-ref name="PidginPluginsMenu"/>
--- a/pidgin/gtkblist.c Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/gtkblist.c Mon Apr 06 22:27:40 2020 -0500 @@ -137,8 +137,6 @@ (gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(x))) & \ GDK_WINDOW_STATE_MAXIMIZED) -static GtkWidget *accountmenu = NULL; - static guint visibility_manager_count = 0; static GdkVisibilityState gtk_blist_visibility = GDK_VISIBILITY_UNOBSCURED; static gboolean gtk_blist_focused = FALSE; @@ -3517,13 +3515,7 @@ /*************************************************** * Crap * ***************************************************/ -/* TODO: fill out tooltips... */ static const GtkActionEntry blist_menu_entries[] = { -/* NOTE: Do not set any accelerator to Control+O. It is mapped by - gtk_blist_key_press_cb to "Get User Info" on the selected buddy. */ - /* Accounts menu */ - { "AccountsMenu", NULL, N_("_Accounts"), NULL, NULL, NULL }, - /* Tools */ { "ToolsMenu", NULL, N_("_Tools"), NULL, NULL, NULL }, { "SetMood", NULL, N_("Set _Mood"), "<control>D", NULL, set_mood_show }, @@ -3532,8 +3524,6 @@ static const char *blist_menu = "<ui>" "<menubar name='BList'>" - "<menu action='AccountsMenu'>" - "</menu>" "<menu action='ToolsMenu'>" "<menuitem action='SetMood'/>" "</menu>" @@ -4358,8 +4348,6 @@ g_return_if_fail(gtkblist != NULL); - pidgin_blist_update_accounts_menu(); - sensitive = (purple_connections_get_all() != NULL); for (i = 0; i < require_connection_size; i++) @@ -4735,12 +4723,6 @@ } static void -account_actions_changed(PurpleAccount *account, gpointer data) -{ - pidgin_blist_update_accounts_menu(); -} - -static void account_status_changed(PurpleAccount *account, PurpleStatus *old, PurpleStatus *new, PidginBuddyList *gtkblist) { @@ -5592,9 +5574,6 @@ gtk_widget_show(menu); gtk_box_pack_start(GTK_BOX(gtkblist->main_vbox), menu, FALSE, FALSE, 0); - menu = gtk_ui_manager_get_widget(gtkblist->ui, "/BList/AccountsMenu"); - accountmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu)); - gtkblist->menu = pidgin_buddy_list_menu_new(); gtk_box_pack_start(GTK_BOX(gtkblist->main_vbox), gtkblist->menu, FALSE, FALSE, 0); @@ -5813,8 +5792,6 @@ purple_signal_connect(handle, "account-error-changed", gtkblist, PURPLE_CALLBACK(update_account_error_state), gtkblist); - purple_signal_connect(handle, "account-actions-changed", gtkblist, - PURPLE_CALLBACK(account_actions_changed), NULL); handle = pidgin_accounts_get_handle(); purple_signal_connect(handle, "account-modified", gtkblist, @@ -7254,7 +7231,6 @@ purple_signals_unregister_by_instance(pidgin_blist_get_handle()); purple_signals_disconnect_by_handle(pidgin_blist_get_handle()); - accountmenu = NULL; gtkblist = NULL; } @@ -7645,224 +7621,6 @@ } } -static void -modify_account_cb(GtkWidget *widget, gpointer data) -{ - pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, data); -} - -static void -enable_account_cb(GtkCheckMenuItem *widget, gpointer data) -{ - PurpleAccount *account = data; - const PurpleSavedStatus *saved_status; - - saved_status = purple_savedstatus_get_current(); - purple_savedstatus_activate_for_account(saved_status, account); - - purple_account_set_enabled(account, PIDGIN_UI, TRUE); -} - -static void -disable_account_cb(GtkCheckMenuItem *widget, gpointer data) -{ - PurpleAccount *account = data; - - purple_account_set_enabled(account, PIDGIN_UI, FALSE); -} - -static void -protocol_act(GtkWidget *obj, PurpleProtocolAction *pam) -{ - if (pam && pam->callback) - pam->callback(pam); -} - -void -pidgin_blist_update_accounts_menu(void) -{ - GtkWidget *menuitem, *submenu; - GtkAccelGroup *accel_group; - GList *l, *accounts; - gboolean disabled_accounts = FALSE; - gboolean enabled_accounts = FALSE; - - if (accountmenu == NULL) - return; - - /* Clear the old Accounts menu */ - for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) { - menuitem = l->data; - - if (menuitem != gtk_ui_manager_get_widget(gtkblist->ui, "/BList/AccountsMenu/ManageAccounts")) - gtk_widget_destroy(menuitem); - } - - accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu)); - - for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) { - char *buf = NULL; - GtkWidget *image = NULL; - PurpleAccount *account = NULL; - GdkPixbuf *pixbuf = NULL; - - account = accounts->data; - - if (!purple_account_get_enabled(account, PIDGIN_UI)) { - if (!disabled_accounts) { - menuitem = gtk_menu_item_new_with_label(_("Enable Account")); - gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem); - - submenu = gtk_menu_new(); - gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); - gtk_menu_set_accel_path(GTK_MENU(submenu), "<Actions>/BListActions/EnableAccount"); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - - disabled_accounts = TRUE; - } - - buf = g_strconcat(purple_account_get_username(account), " (", - purple_account_get_protocol_name(account), ")", NULL); - menuitem = gtk_image_menu_item_new_with_label(buf); - g_free(buf); - - pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); - if (pixbuf != NULL) { - if (!purple_account_is_connected(account)) - gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); - image = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(G_OBJECT(pixbuf)); - gtk_widget_show(image); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); - } - - g_signal_connect(G_OBJECT(menuitem), "activate", - G_CALLBACK(enable_account_cb), account); - gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); - - } else { - enabled_accounts = TRUE; - } - } - - if (!enabled_accounts) { - gtk_widget_show_all(accountmenu); - return; - } - - pidgin_separator(accountmenu); - - for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) { - char *buf = NULL; - char *accel_path_buf = NULL; - GtkWidget *image = NULL; - PurpleConnection *gc = NULL; - PurpleAccount *account = NULL; - GdkPixbuf *pixbuf = NULL; - PurpleProtocol *protocol; - - account = accounts->data; - - if (!purple_account_get_enabled(account, PIDGIN_UI)) - continue; - - buf = g_strconcat(purple_account_get_username(account), " (", - purple_account_get_protocol_name(account), ")", NULL); - menuitem = gtk_image_menu_item_new_with_label(buf); - accel_path_buf = g_strconcat("<Actions>/AccountActions/", buf, NULL); - g_free(buf); - - pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL); - if (pixbuf != NULL) { - if (!purple_account_is_connected(account)) - gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, - 0.0, FALSE); - image = gtk_image_new_from_pixbuf(pixbuf); - g_object_unref(G_OBJECT(pixbuf)); - gtk_widget_show(image); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem); - - submenu = gtk_menu_new(); - gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group); - gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf); - g_free(accel_path_buf); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); - - menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account")); - g_signal_connect(G_OBJECT(menuitem), "activate", - G_CALLBACK(modify_account_cb), account); - gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); - - pidgin_separator(submenu); - - gc = purple_account_get_connection(account); - protocol = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? - purple_connection_get_protocol(gc) : NULL; - - if (protocol && - (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods) || - PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions))) { - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods) && - (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) { - - if (purple_account_get_status(account, "mood")) { - menuitem = gtk_menu_item_new_with_mnemonic(_("Set _Mood...")); - g_signal_connect(G_OBJECT(menuitem), "activate", - G_CALLBACK(set_mood_cb), account); - gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); - } - } - - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) { - GtkWidget *menuitem; - PurpleProtocolAction *action = NULL; - GList *actions, *l; - - actions = purple_protocol_client_iface_get_actions(protocol, gc); - - for (l = actions; l != NULL; l = l->next) - { - if (l->data) - { - action = (PurpleProtocolAction *) l->data; - action->connection = gc; - - menuitem = gtk_menu_item_new_with_label(action->label); - gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); - - g_signal_connect(G_OBJECT(menuitem), "activate", - G_CALLBACK(protocol_act), action); - g_object_set_data_full(G_OBJECT(menuitem), "protocol_action", - action, - (GDestroyNotify)purple_protocol_action_free); - gtk_widget_show(menuitem); - } - else - pidgin_separator(submenu); - } - - g_list_free(actions); - } - } else { - menuitem = gtk_menu_item_new_with_label(_("No actions available")); - gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); - gtk_widget_set_sensitive(menuitem, FALSE); - } - - pidgin_separator(submenu); - - menuitem = gtk_menu_item_new_with_mnemonic(_("_Disable")); - g_signal_connect(G_OBJECT(menuitem), "activate", - G_CALLBACK(disable_account_cb), account); - gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); - } - - gtk_widget_show_all(accountmenu); -} - void pidgin_blist_update_sort_methods(void) {
--- a/pidgin/meson.build Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/meson.build Mon Apr 06 22:27:40 2020 -0500 @@ -34,7 +34,9 @@ 'libpidgin.c', 'minidialog.c', 'pidginabout.c', + 'pidginaccountactionsmenu.c', 'pidginaccountchooser.c', + 'pidginaccountsmenu.c', 'pidginactiongroup.c', 'pidginbuddylistmenu.c', 'pidgincontactcompletion.c', @@ -90,7 +92,9 @@ 'gtkxfer.h', 'minidialog.h', 'pidginabout.h', + 'pidginaccountactionsmenu.c', 'pidginaccountchooser.h', + 'pidginaccountsmenu.c', 'pidginactiongroup.h', 'pidginbuddylistmenu.h', 'pidgincontactcompletion.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidginaccountactionsmenu.c Mon Apr 06 22:27:40 2020 -0500 @@ -0,0 +1,284 @@ +/* + * pidgin + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "pidginaccountactionsmenu.h" + +#include <purple.h> + +#include "internal.h" + +#include "pidgin/pidgin.h" +#include "pidgin/gtkaccount.h" + +struct _PidginAccountActionsMenu { + GtkMenu parent; + + GtkWidget *separator; + + PurpleAccount *account; +}; + +enum { + PROP_ZERO, + PROP_ACCOUNT, + N_PROPERTIES +}; +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; + +/****************************************************************************** + * GSignal Handlers + *****************************************************************************/ +static void +pidgin_account_actions_menu_edit_cb(GtkMenuItem *item, gpointer data) { + PidginAccountActionsMenu *menu = PIDGIN_ACCOUNT_ACTIONS_MENU(data); + + pidgin_account_dialog_show(PIDGIN_MODIFY_ACCOUNT_DIALOG, menu->account); +} + +static void +pidgin_account_actions_menu_disable_cb(GtkMenuItem *item, gpointer data) { + PidginAccountActionsMenu *menu = PIDGIN_ACCOUNT_ACTIONS_MENU(data); + + purple_account_set_enabled(menu->account, PIDGIN_UI, FALSE); +} + +static void +pidgin_account_actions_menu_action(GtkMenuItem *item, gpointer data) { + PurpleProtocolAction *action = (PurpleProtocolAction *)data; + + if(action && action->callback) { + action->callback(action); + } +} + +/****************************************************************************** + * Helpers + *****************************************************************************/ +static void +pidgin_account_actions_menu_set_account(PidginAccountActionsMenu *menu, + PurpleAccount *account) +{ + PurpleConnection *connection = NULL; + PurpleProtocol *protocol = NULL; + GList *children = NULL, *l = NULL; + gboolean items_added = FALSE; + gint position = 0; + + g_clear_object(&menu->account); + + if(!PURPLE_IS_ACCOUNT(account)) { + return; + } + + menu->account = PURPLE_ACCOUNT(g_object_ref(G_OBJECT(account))); + + connection = purple_account_get_connection(account); + if(connection == NULL) { + return; + } + + if(!PURPLE_CONNECTION_IS_CONNECTED(connection)) { + return; + } + + /* we're pretty sure we're going to insert some items into the menu, so we + * need to figure out where to put them. GtkMenu stores its children in + * order, so we just need to walk them to find the proper position. + */ + children = gtk_container_get_children(GTK_CONTAINER(menu)); + for(l = children, position = 0; l != NULL; l = l->next, position++) { + /* check if the widget is our separator and if so, bail out of the + * loop. + */ + if(l->data == menu->separator) { + /* and push position past the separator */ + position++; + + break; + } + } + g_list_free(children); + + protocol = purple_connection_get_protocol(connection); + if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) { + GtkWidget *item = NULL; + GList *actions = NULL, *l = NULL; + + actions = purple_protocol_client_iface_get_actions(protocol, + connection); + + for(l = actions; l; l = l->next) { + PurpleProtocolAction *action = (PurpleProtocolAction *)l->data; + + if(action == NULL) { + item = gtk_separator_menu_item_new(); + gtk_menu_shell_insert(GTK_MENU_SHELL(menu), item, position++); + gtk_widget_show(item); + + continue; + } + + if(action->label == NULL) { + purple_protocol_action_free(action); + + continue; + } + + /* now add the action */ + item = gtk_menu_item_new_with_label(action->label); + g_signal_connect_data(G_OBJECT(item), "activate", + G_CALLBACK(pidgin_account_actions_menu_action), + action, + (GClosureNotify)purple_protocol_action_free, + 0); + gtk_menu_shell_insert(GTK_MENU_SHELL(menu), item, position++); + gtk_widget_show(item); + + /* since we added an item, make sure items_added is true */ + items_added = TRUE; + } + + g_list_free(actions); + } + + /* if we added any items, make our separator visible. */ + if(items_added) { + gtk_widget_show(menu->separator); + } +} + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +G_DEFINE_TYPE(PidginAccountActionsMenu, pidgin_account_actions_menu, + GTK_TYPE_MENU) + +static void +pidgin_account_actions_menu_get_property(GObject *obj, guint param_id, + GValue *value, GParamSpec *pspec) +{ + PidginAccountActionsMenu *menu = PIDGIN_ACCOUNT_ACTIONS_MENU(obj); + + switch(param_id) { + case PROP_ACCOUNT: + g_value_set_object(value, + pidgin_account_actions_menu_get_account(menu)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +static void +pidgin_account_actions_menu_set_property(GObject *obj, guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + PidginAccountActionsMenu *menu = PIDGIN_ACCOUNT_ACTIONS_MENU(obj); + + switch(param_id) { + case PROP_ACCOUNT: + pidgin_account_actions_menu_set_account(menu, + g_value_get_object(value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; + } +} + +static void +pidgin_account_actions_menu_finalize(GObject *obj) { + PidginAccountActionsMenu *menu = PIDGIN_ACCOUNT_ACTIONS_MENU(obj); + + g_clear_object(&menu->account); + + G_OBJECT_CLASS(pidgin_account_actions_menu_parent_class)->finalize(obj); +} + +static void +pidgin_account_actions_menu_init(PidginAccountActionsMenu *menu) { + /* initialize our template */ + gtk_widget_init_template(GTK_WIDGET(menu)); +}; + +static void +pidgin_account_actions_menu_class_init(PidginAccountActionsMenuClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + gtk_widget_class_set_template_from_resource( + widget_class, + "/im/pidgin/Pidgin/Accounts/actionsmenu.ui" + ); + + obj_class->get_property = pidgin_account_actions_menu_get_property; + obj_class->set_property = pidgin_account_actions_menu_set_property; + obj_class->finalize = pidgin_account_actions_menu_finalize; + + /** + * PidginAccountActionsMenu::account: + * + * The #PurpleAccount that this menu was created for. + */ + properties[PROP_ACCOUNT] = + g_param_spec_object("account", "account", + "The account this menu is for", + PURPLE_TYPE_ACCOUNT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); + + gtk_widget_class_bind_template_child(widget_class, PidginAccountActionsMenu, + separator); + + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_actions_menu_edit_cb); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_account_actions_menu_disable_cb); +} + +/****************************************************************************** + * Public API + *****************************************************************************/ +GtkWidget * +pidgin_account_actions_menu_new(PurpleAccount *account) { + GObject *obj = NULL; + + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); + + obj = g_object_new(PIDGIN_TYPE_ACCOUNT_ACTIONS_MENU, + "account", account, + NULL); + + return GTK_WIDGET(obj); +} + +PurpleAccount * +pidgin_account_actions_menu_get_account(PidginAccountActionsMenu *menu) { + g_return_val_if_fail(PIDGIN_IS_ACCOUNT_ACTIONS_MENU(menu), NULL); + + return menu->account; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidginaccountactionsmenu.h Mon Apr 06 22:27:40 2020 -0500 @@ -0,0 +1,67 @@ +/* + * pidgin + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef PIDGIN_ACCOUNT_ACTIONS_MENU_H +#define PIDGIN_ACCOUNT_ACTIONS_MENU_H + +/** + * SECTION:pidginaccountactionsmenu + * @section_id: pidgin-account-actions-menu + * @short_description: A menu for managing account actions + * @title: Accounts Actions Menu + * + * #PidginAccountActionsMenu is a #GtkMenu that provides an interface to users + * edit accounts and activate its actions. + */ + +#include <gtk/gtk.h> + +#include <purple.h> + +G_BEGIN_DECLS + +#define PIDGIN_TYPE_ACCOUNT_ACTIONS_MENU (pidgin_account_actions_menu_get_type()) +G_DECLARE_FINAL_TYPE(PidginAccountActionsMenu, pidgin_account_actions_menu, + PIDGIN, ACCOUNT_ACTIONS_MENU, GtkMenu) + +/** + * pidgin_account_actions_menu_new: + * @account: The #PurpleAccount that this menu is for. + * + * Creates a new #PidginAccountActionsMenu for @account. + * + * Returns: (transfer full): The new #PidginAccountActionsMenu instance. + */ +GtkWidget *pidgin_account_actions_menu_new(PurpleAccount *account); + +/** + * pidgin_account_actions_menu_get_account: + * @menu: The #PidginAccountActionsMenu instance. + * + * Gets the #PurpleAccount associated with @menu. + * + * Returns: (transfer none): The #PurpleAccount associated with @menu. + */ +PurpleAccount *pidgin_account_actions_menu_get_account(PidginAccountActionsMenu *menu); + +G_END_DECLS + +#endif /* PIDGIN_ACCOUNT_ACTIONS_MENU_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidginaccountsmenu.c Mon Apr 06 22:27:40 2020 -0500 @@ -0,0 +1,248 @@ +/* + * pidgin + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "pidginaccountsmenu.h" + +#include <purple.h> + +#include "internal.h" + +#include "pidgin/gtkaccount.h" +#include "pidgin/pidgin.h" +#include "pidgin/pidginaccountactionsmenu.h" + +struct _PidginAccountsMenu { + GtkMenu parent; + + GtkWidget *disabled_menu; + GtkWidget *separator; + + GHashTable *account_items; + GHashTable *disabled_items; +}; + +/****************************************************************************** + * GSignal Handlers + *****************************************************************************/ +static void +pidgin_accounts_menu_open_manager_cb(GtkMenuItem *item, gpointer data) { + pidgin_accounts_window_show(); +} + +static void +pidgin_accounts_menu_enable_account(GtkMenuItem *item, gpointer data) { + PurpleAccount *account = PURPLE_ACCOUNT(data); + + purple_account_set_enabled(account, PIDGIN_UI, TRUE); +} + +/****************************************************************************** + * Helpers + *****************************************************************************/ +static GtkWidget * +pidgin_accounts_menu_create_account_menu_item(PidginAccountsMenu *menu, + PurpleAccount *account) +{ + GtkWidget *item = NULL; + const gchar *account_name = purple_account_get_username(account); + const gchar *protocol_name = purple_account_get_protocol_name(account); + gchar *label = g_strdup_printf("%s (%s)", account_name, protocol_name); + + item = gtk_menu_item_new_with_label(label); + g_free(label); + gtk_widget_show(item); + + return item; +} + +static void +pidgin_accounts_menu_add_enabled_account(PidginAccountsMenu *menu, + PurpleAccount *account) +{ + GtkWidget *item = NULL, *submenu = NULL; + gpointer data = NULL; + + /* if the account is in the disabled list, delete its widget */ + data = g_hash_table_lookup(menu->disabled_items, account); + if(data != NULL) { + gtk_widget_destroy(GTK_WIDGET(data)); + } + + item = pidgin_accounts_menu_create_account_menu_item(menu, account); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + g_hash_table_insert(menu->account_items, + g_object_ref(G_OBJECT(account)), + item); + + /* create the submenu and attach it to item right away, this allows us to + * reuse item for the submenu items. + */ + submenu = pidgin_account_actions_menu_new(account); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu); +} + +static void +pidgin_accounts_menu_add_disabled_account(PidginAccountsMenu *menu, + PurpleAccount *account) +{ + GtkWidget *item = NULL; + gpointer data = NULL; + + /* if the account is in the enabled list, delete its widget */ + data = g_hash_table_lookup(menu->account_items, account); + if(data != NULL) { + gtk_widget_destroy(GTK_WIDGET(data)); + } + + item = pidgin_accounts_menu_create_account_menu_item(menu, account); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(pidgin_accounts_menu_enable_account), account); + gtk_menu_shell_append(GTK_MENU_SHELL(menu->disabled_menu), item); + + g_hash_table_insert(menu->disabled_items, + g_object_ref(G_OBJECT(account)), + item); +} + +static void +pidgin_accounts_menu_add_current(PidginAccountsMenu *menu) { + GList *accounts = NULL, *l = NULL; + + accounts = purple_accounts_get_all(); + for(l = accounts; l != NULL; l = l->next) { + PurpleAccount *account = PURPLE_ACCOUNT(l->data); + + if(purple_account_get_enabled(account, PIDGIN_UI)) { + pidgin_accounts_menu_add_enabled_account(menu, account); + } else { + pidgin_accounts_menu_add_disabled_account(menu, account); + } + } +} + +/****************************************************************************** + * Purple Signal Callbacks + *****************************************************************************/ +static void +pidgin_accounts_menu_account_status_changed(PurpleAccount *account, + gpointer d) +{ + PidginAccountsMenu *menu = PIDGIN_ACCOUNTS_MENU(d); + gpointer data = NULL; + + data = g_hash_table_lookup(menu->account_items, account); + if(GTK_IS_WIDGET(data)) { + gtk_menu_item_set_submenu(GTK_MENU_ITEM(data), + pidgin_account_actions_menu_new(account)); + } +} + +static void +pidgin_accounts_menu_account_enabled(PurpleAccount *account, gpointer data) { + pidgin_accounts_menu_add_enabled_account(PIDGIN_ACCOUNTS_MENU(data), + account); +} + +static void +pidgin_accounts_menu_account_disabled(PurpleAccount *account, gpointer data) { + pidgin_accounts_menu_add_disabled_account(PIDGIN_ACCOUNTS_MENU(data), + account); +} + +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ +G_DEFINE_TYPE(PidginAccountsMenu, pidgin_accounts_menu, GTK_TYPE_MENU) + +static void +pidgin_accounts_menu_init(PidginAccountsMenu *menu) { + gpointer handle; + + /* initialize our template */ + gtk_widget_init_template(GTK_WIDGET(menu)); + + /* create our storage for the items */ + menu->account_items = g_hash_table_new_full(g_direct_hash, g_direct_equal, + g_object_unref, + (GDestroyNotify)gtk_widget_destroy); + menu->disabled_items = g_hash_table_new_full(g_direct_hash, g_direct_equal, + g_object_unref, + (GDestroyNotify)gtk_widget_destroy); + + /* add all of the existing accounts */ + pidgin_accounts_menu_add_current(menu); + + /* finally connect to the purple signals to stay up to date */ + handle = purple_accounts_get_handle(); + purple_signal_connect(handle, "account-signed-on", menu, + G_CALLBACK(pidgin_accounts_menu_account_status_changed), + menu); + purple_signal_connect(handle, "account-signed-off", menu, + G_CALLBACK(pidgin_accounts_menu_account_status_changed), + menu); + purple_signal_connect(handle, "account-actions-changed", menu, + G_CALLBACK(pidgin_accounts_menu_account_status_changed), + menu); + purple_signal_connect(handle, "account-enabled", menu, + G_CALLBACK(pidgin_accounts_menu_account_enabled), + menu); + purple_signal_connect(handle, "account-disabled", menu, + G_CALLBACK(pidgin_accounts_menu_account_disabled), + menu); +}; + +static void +pidgin_accounts_menu_finalize(GObject *obj) { + purple_signals_disconnect_by_handle(obj); + + G_OBJECT_CLASS(pidgin_accounts_menu_parent_class)->finalize(obj); +} + +static void +pidgin_accounts_menu_class_init(PidginAccountsMenuClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + obj_class->finalize = pidgin_accounts_menu_finalize; + + gtk_widget_class_set_template_from_resource( + widget_class, + "/im/pidgin/Pidgin/Accounts/menu.ui" + ); + + gtk_widget_class_bind_template_child(widget_class, PidginAccountsMenu, + disabled_menu); + gtk_widget_class_bind_template_child(widget_class, PidginAccountsMenu, + separator); + + gtk_widget_class_bind_template_callback(widget_class, + pidgin_accounts_menu_open_manager_cb); +} + +/****************************************************************************** + * Public API + *****************************************************************************/ +GtkWidget * +pidgin_accounts_menu_new(void) { + return GTK_WIDGET(g_object_new(PIDGIN_TYPE_ACCOUNTS_MENU, NULL)); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pidginaccountsmenu.h Mon Apr 06 22:27:40 2020 -0500 @@ -0,0 +1,57 @@ +/* + * pidgin + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef PIDGIN_ACCOUNTS_MENU_H +#define PIDGIN_ACCOUNTS_MENU_H + +/** + * SECTION:pidginaccountsmenu + * @section_id: pidgin-accounts-menu + * @short_description: A menu for managing accounts and their actions + * @title: Accounts Menu + * + * #PidginAccountMenu is a #GtkMenu that provides an interface to users to open + * the account manager as well as activate account actions. + * + * It manages itself as accounts are created/deleted and enabled/disabled and + * can be added as a submenu to any #GtkMenuItem. + */ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define PIDGIN_TYPE_ACCOUNTS_MENU (pidgin_accounts_menu_get_type()) +G_DECLARE_FINAL_TYPE(PidginAccountsMenu, pidgin_accounts_menu, PIDGIN, + ACCOUNTS_MENU, GtkMenu) + +/** + * pidgin_accounts_menu_new: + * + * Creates a new #PidginAccountsMenu instance that keeps itself up to date. + * + * Returns: (transfer full): The new #PidginAccountsMenu instance. + */ +GtkWidget *pidgin_accounts_menu_new(void); + +G_END_DECLS + +#endif /* PIDGIN_ACCOUNTS_MENU_H */
--- a/pidgin/pidginactiongroup.c Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/pidginactiongroup.c Mon Apr 06 22:27:40 2020 -0500 @@ -26,7 +26,6 @@ #include "internal.h" -#include "pidgin/gtkaccount.h" #include "pidgin/gtkblist.h" #include "pidgin/gtkdialogs.h" #include "pidgin/gtkpounce.h" @@ -408,13 +407,6 @@ } static void -pidgin_action_group_manage_accounts(GSimpleAction *simple, GVariant *parameter, - gpointer data) -{ - pidgin_accounts_window_show(); -} - -static void pidgin_action_group_mute_sounds(GSimpleAction *action, GVariant *value, gpointer data) { @@ -570,9 +562,6 @@ .name = PIDGIN_ACTION_JOIN_CHAT, .activate = pidgin_action_group_join_chat, }, { - .name = PIDGIN_ACTION_MANAGE_ACCOUNTS, - .activate = pidgin_action_group_manage_accounts, - }, { .name = PIDGIN_ACTION_MUTE_SOUNDS, .state = "false", .change_state = pidgin_action_group_mute_sounds,
--- a/pidgin/pidginactiongroup.h Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/pidginactiongroup.h Mon Apr 06 22:27:40 2020 -0500 @@ -103,14 +103,6 @@ #define PIDGIN_ACTION_JOIN_CHAT ("join-chat") /** - * PIDGIN_ACTION_MANAGE_ACCOUNTS: - * - * A constatnt that represents the manage-accounts action to displays the - * manage accounts window. - */ -#define PIDGIN_ACTION_MANAGE_ACCOUNTS ("manage-accounts") - -/** * PIDGIN_ACTION_MUTE_SOUNDS: * * A constatnt that represents the mute-sounds action.
--- a/pidgin/pidginbuddylistmenu.c Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/pidginbuddylistmenu.c Mon Apr 06 22:27:40 2020 -0500 @@ -26,6 +26,10 @@ GtkMenuBar parent; GtkWidget *sort_buddies; + + GtkWidget *accounts; + GtkWidget *accounts_menu; + GtkWidget *plugins; GtkWidget *plugins_menu; }; @@ -39,6 +43,8 @@ pidgin_buddy_list_menu_init(PidginBuddyListMenu *menu) { gtk_widget_init_template(GTK_WIDGET(menu)); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->accounts), + menu->accounts_menu); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->plugins), menu->plugins_menu); } @@ -55,6 +61,10 @@ gtk_widget_class_bind_template_child(widget_class, PidginBuddyListMenu, sort_buddies); gtk_widget_class_bind_template_child(widget_class, PidginBuddyListMenu, + accounts); + gtk_widget_class_bind_template_child(widget_class, PidginBuddyListMenu, + accounts_menu); + gtk_widget_class_bind_template_child(widget_class, PidginBuddyListMenu, plugins); gtk_widget_class_bind_template_child(widget_class, PidginBuddyListMenu, plugins_menu);
--- a/pidgin/pidginpluginsmenu.h Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/pidginpluginsmenu.h Mon Apr 06 22:27:40 2020 -0500 @@ -35,7 +35,6 @@ * submenu to any #GtkMenuItem. */ - #include <gtk/gtk.h> G_BEGIN_DECLS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/resources/Accounts/actionsmenu.ui Mon Apr 06 22:27:40 2020 -0500 @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +--> +<interface> + <requires lib="gtk+" version="3.20"/> + <!-- interface-license-type gplv2 --> + <!-- interface-name Pidgin --> + <!-- interface-description Internet Messenger --> + <!-- interface-copyright Pidgin Developers <devel@pidgin.im> --> + <template class="PidginAccountActionsMenu" parent="GtkMenu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Edit Account</property> + <property name="use_underline">True</property> + <signal name="activate" handler="pidgin_account_actions_menu_edit_cb" object="PidginAccountActionsMenu" swapped="no"/> + </object> + </child> + <child> + <object class="GtkSeparatorMenuItem" id="separator"> + <property name="can_focus">False</property> + </object> + </child> + <child> + <object class="GtkSeparatorMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Disable</property> + <property name="use_underline">True</property> + <signal name="activate" handler="pidgin_account_actions_menu_disable_cb" object="PidginAccountActionsMenu" swapped="no"/> + </object> + </child> + </template> +</interface>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/resources/Accounts/menu.ui Mon Apr 06 22:27:40 2020 -0500 @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +--> +<interface> + <requires lib="gtk+" version="3.20"/> + <!-- interface-license-type gplv2 --> + <!-- interface-name Pidgin --> + <!-- interface-description Internet Messenger --> + <!-- interface-copyright Pidgin Developers <devel@pidgin.im> --> + <template class="PidginAccountsMenu" parent="GtkMenu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="menu_type_hint">dnd</property> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Account _Manager</property> + <property name="use_underline">True</property> + <signal name="activate" handler="pidgin_accounts_menu_open_manager_cb" object="PidginAccountsMenu" swapped="no"/> + <accelerator key="a" signal="activate" modifiers="GDK_CONTROL_MASK"/> + </object> + </child> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Enable Account</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="disabled_menu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + </child> + <child> + <object class="GtkSeparatorMenuItem" id="separator"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </template> +</interface>
--- a/pidgin/resources/BuddyList/menu.ui Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/resources/BuddyList/menu.ui Mon Apr 06 22:27:40 2020 -0500 @@ -184,27 +184,11 @@ </object> </child> <child> - <object class="GtkMenuItem"> + <object class="GtkMenuItem" id="accounts"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">_Accounts</property> <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuItem" id="manage_accounts"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="action_name">blist.manage-accounts</property> - <property name="label" translatable="yes">Manage Accounts</property> - <property name="use_underline">True</property> - <accelerator key="a" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - </object> - </child> </object> </child> <child> @@ -369,6 +353,10 @@ </object> </child> </template> + <object class="PidginAccountsMenu" id="accounts_menu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> <object class="PidginPluginsMenu" id="plugins_menu"> <property name="visible">True</property> <property name="can_focus">False</property>
--- a/pidgin/resources/pidgin.gresource.xml Wed Apr 01 13:08:57 2020 -0500 +++ b/pidgin/resources/pidgin.gresource.xml Mon Apr 06 22:27:40 2020 -0500 @@ -6,6 +6,8 @@ <file compressed="true">About/about.md</file> <file compressed="true">About/credits.json</file> <file compressed="true">Accounts/chooser.ui</file> + <file compressed="true">Accounts/actionsmenu.ui</file> + <file compressed="true">Accounts/menu.ui</file> <file compressed="true">BuddyList/menu.ui</file> <file compressed="true">Conversations/invite_dialog.ui</file> <file compressed="true">Debug/debug.ui</file>