--- a/libpurple/plugins/idle.c Fri May 27 04:11:45 2022 -0500
+++ b/libpurple/plugins/idle.c Fri May 27 14:52:34 2022 -0500
@@ -132,9 +132,22 @@
idled_accts = g_list_remove(idled_accts, acct);
-idle_action(PurplePluginAction *action)
+signing_off_cb(PurpleConnection *gc, void *data) + PurpleAccount *account; + account = purple_connection_get_account(gc); + idled_accts = g_list_remove(idled_accts, account); +/****************************************************************************** + *****************************************************************************/ +purple_idle_set_account_idle_time(G_GNUC_UNUSED GSimpleAction *action, + G_GNUC_UNUSED GVariant *parameter, /* Use the super fancy request API */
@@ -155,7 +168,7 @@
request = purple_request_fields_new();
purple_request_fields_add_group(request, group);
- purple_request_fields(action->plugin,
+ purple_request_fields(data, _("Set Account Idle Time"),
@@ -166,7 +179,9 @@
-unidle_action(PurplePluginAction *action)
+purple_idle_unset_account_idle_time(G_GNUC_UNUSED GSimpleAction *action, + G_GNUC_UNUSED GVariant *parameter, PurpleRequestFields *request;
PurpleRequestFieldGroup *group;
@@ -188,7 +203,7 @@
request = purple_request_fields_new();
purple_request_fields_add_group(request, group);
- purple_request_fields(action->plugin,
+ purple_request_fields(data, _("Unset Account Idle Time"),
@@ -199,7 +214,9 @@
-idle_all_action(PurplePluginAction *action)
+purple_idle_set_all_accounts_idle_time(G_GNUC_UNUSED GSimpleAction *action, + G_GNUC_UNUSED GVariant *parameter, PurpleRequestFields *request;
PurpleRequestFieldGroup *group;
@@ -213,7 +230,7 @@
request = purple_request_fields_new();
purple_request_fields_add_group(request, group);
- purple_request_fields(action->plugin,
+ purple_request_fields(data, _("Set Idle Time for All Accounts"),
@@ -224,7 +241,9 @@
-unidle_all_action(PurplePluginAction *action)
+purple_idle_unset_all_accounts_idle_time(G_GNUC_UNUSED GSimpleAction *action, + G_GNUC_UNUSED GVariant *parameter, + G_GNUC_UNUSED gpointer data) /* freeing the list here will cause segfaults if the user idles an account
* after the list is freed */
@@ -233,48 +252,47 @@
-actions(PurplePlugin *plugin)
- PurplePluginAction *act = NULL;
- act = purple_plugin_action_new(_("Set Account Idle Time"),
- l = g_list_append(l, act);
- act = purple_plugin_action_new(_("Unset Account Idle Time"),
- l = g_list_append(l, act);
- act = purple_plugin_action_new(_("Set Idle Time for All Accounts"),
- l = g_list_append(l, act);
- act = purple_plugin_action_new(
- _("Unset Idle Time for All Idled Accounts"), unidle_all_action);
- l = g_list_append(l, act);
-signing_off_cb(PurpleConnection *gc, void *data)
- PurpleAccount *account;
- account = purple_connection_get_account(gc);
- idled_accts = g_list_remove(idled_accts, account);
+/****************************************************************************** + *****************************************************************************/ static GPluginPluginInfo *
idle_query(GError **error)
+ GSimpleActionGroup *group = NULL; + GActionEntry entries[] = { + .name = "set-account-idle-time", + .activate = purple_idle_set_account_idle_time, + .name = "unset-account-idle-time", + .activate = purple_idle_unset_account_idle_time, + .name = "set-all-accounts-idle-time", + .activate = purple_idle_set_all_accounts_idle_time, + .name = "unset-all-accounts-idle-time", + .activate = purple_idle_unset_all_accounts_idle_time, const gchar * const authors[] = {
"Eric Warmenhoven <eric@warmenhoven.org>",
+ group = g_simple_action_group_new(); + g_action_map_add_action_entries(G_ACTION_MAP(group), entries, + G_N_ELEMENTS(entries), NULL); + g_menu_append(menu, _("Set Account Idle Time"), "set-account-idle-time"); + g_menu_append(menu, _("Unset Account Idle Time"), + "unset-account-idle-time"); + g_menu_append(menu, _("Set Idle Time for All Accounts"), + "set-all-accounts-idle-time"); + g_menu_append(menu, _("Unset Idle Time for All Idled Accounts"), + "unset-all-accounts-idle-time"); return purple_plugin_info_new(
/* This is a cultural reference. Dy'er Mak'er is a song by Led Zeppelin.
@@ -287,7 +305,8 @@
"website", PURPLE_WEBSITE,
"abi-version", PURPLE_ABI_VERSION,
@@ -305,7 +324,7 @@
idle_unload(GPluginPlugin *plugin, gboolean shutdown, GError **error)
- unidle_all_action(NULL);
+ purple_idle_unset_all_accounts_idle_time(NULL, NULL, NULL); --- a/libpurple/purpleplugininfo.c Fri May 27 04:11:45 2022 -0500
+++ b/libpurple/purpleplugininfo.c Fri May 27 14:52:34 2022 -0500
@@ -46,6 +46,9 @@
/* TRUE if a plugin has been unloaded at least once. Auto-load
* plugins that have been unloaded once will not be auto-loaded again. */
+ GActionGroup *action_group; + GMenuModel *menu_model; } PurplePluginInfoPrivate;
@@ -55,6 +58,8 @@
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
@@ -63,6 +68,39 @@
GPLUGIN_TYPE_PLUGIN_INFO);
/**************************************************************************
+ **************************************************************************/ +purple_plugin_info_set_action_group(PurplePluginInfo *info, + PurplePluginInfoPrivate *priv = NULL; + g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info)); + priv = purple_plugin_info_get_instance_private(info); + if(g_set_object(&priv->action_group, group)) { + g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_ACTION_GROUP]); +purple_plugin_info_set_action_menu(PurplePluginInfo *info, + GMenuModel *menu_model) + PurplePluginInfoPrivate *priv = NULL; + g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info)); + priv = purple_plugin_info_get_instance_private(info); + if(g_set_object(&priv->menu_model, menu_model)) { + g_object_notify_by_pspec(G_OBJECT(info), properties[PROP_ACTION_MENU]); +/************************************************************************** **************************************************************************/
@@ -94,6 +132,14 @@
priv->flags = g_value_get_flags(value);
+ case PROP_ACTION_GROUP: + purple_plugin_info_set_action_group(info, + g_value_get_object(value)); + purple_plugin_info_set_action_menu(info, + g_value_get_object(value)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
@@ -126,6 +172,14 @@
g_value_set_flags(value, purple_plugin_info_get_flags(info));
+ case PROP_ACTION_GROUP: + g_value_take_object(value, + purple_plugin_info_get_action_group(info)); + g_value_take_object(value, + purple_plugin_info_get_action_menu(info)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
@@ -215,6 +269,32 @@
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ * PurplePluginInfo::action-group: + * A [class@Gio.ActionGroup] of actions that this plugin provides. + properties[PROP_ACTION_GROUP] = g_param_spec_object( + "action-group", "action-group", + "The action group for this plugin", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + * PurplePluginInfo::action-menu: + * A [class@Gio.MenuModel] for activating actions. + properties[PROP_ACTION_MENU] = g_param_spec_object( + "action-menu", "action-menu", + "The menu model for this plugin", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
@@ -326,3 +406,33 @@
priv->unloaded = unloaded;
+purple_plugin_info_get_action_group(PurplePluginInfo *info) { + PurplePluginInfoPrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL); + priv = purple_plugin_info_get_instance_private(info); + if(G_IS_ACTION_GROUP(priv->action_group)) { + return g_object_ref(priv->action_group); +purple_plugin_info_get_action_menu(PurplePluginInfo *info) { + PurplePluginInfoPrivate *priv = NULL; + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL); + priv = purple_plugin_info_get_instance_private(info); + if(G_IS_MENU_MODEL(priv->menu_model)) { + return g_object_ref(priv->menu_model); --- a/libpurple/purpleplugininfo.h Fri May 27 04:11:45 2022 -0500
+++ b/libpurple/purpleplugininfo.h Fri May 27 14:52:34 2022 -0500
@@ -25,6 +25,8 @@
#include <gplugin-native.h>
@@ -381,6 +383,30 @@
void purple_plugin_info_set_unloaded(PurplePluginInfo *info, gboolean unloaded);
+ * purple_plugin_info_get_action_group: + * Gets the [class:Gio.ActionGroup] from @info if one is set. + * Returns: (transfer full): The action group. +GActionGroup *purple_plugin_info_get_action_group(PurplePluginInfo *info); + * purple_plugin_info_get_action_menu: + * Gets the [class:Gio.MenuModel] from @info if one is set. + * Returns: (transfer full): The menu model. +GMenuModel *purple_plugin_info_get_action_menu(PurplePluginInfo *info); #endif /* PURPLE_PLUGIN_INFO_H */
--- a/pidgin/glade/pidgin3.xml.in Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/glade/pidgin3.xml.in Fri May 27 14:52:34 2022 -0500
@@ -13,7 +13,6 @@
<glade-widget-class name="PidginCredentialsPage" generic-name="credentials_page" title="CredentialsPage"/>
<glade-widget-class name="PidginDialog" generic-name="dialog" title="Dialog"/>
<glade-widget-class name="PidginInviteDialog" generic-name="invite_dialog" title="InviteDialog"/>
- <glade-widget-class name="PidginPluginsMenu" generic-name="plugins_menu" title="PluginsMenu"/>
<glade-widget-class name="PidginPresenceIcon" generic-name="presence_icon" title="PresenceIcon"/>
<glade-widget-class name="PidginProtocolChooser" generic-name="protocol_chooser" title="ProtocolChooser"/>
<glade-widget-class name="PidginProtocolStore" generic-name="protocol_store" title="ProtocolStore"/>
@@ -35,7 +34,6 @@
<glade-widget-class-ref name="PidginCredentialsPage"/>
<glade-widget-class-ref name="PidginDialog"/>
<glade-widget-class-ref name="PidginInviteDialog"/>
- <glade-widget-class-ref name="PidginPluginsMenu"/>
<glade-widget-class-ref name="PidginPresenceIcon"/>
<glade-widget-class-ref name="PidginProtocolChooser"/>
<glade-widget-class-ref name="PidginProtocolStore"/>
--- a/pidgin/pidginapplication.c Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/pidginapplication.c Fri May 27 14:52:34 2022 -0500
@@ -51,6 +51,7 @@
#include "pidgininactiveaccountsmenu.h"
#include "pidginmooddialog.h"
#include "pidginpluginsdialog.h"
+#include "pidginpluginsmenu.h" #include "pidginstatusmanager.h"
@@ -133,6 +134,12 @@
target = gtk_application_get_menu_by_id(GTK_APPLICATION(application),
g_menu_append_section(target, NULL, G_MENU_MODEL(source));
+ /* Link the PluginsMenu into its proper location. */ + model = pidgin_plugins_menu_new(); + target = gtk_application_get_menu_by_id(GTK_APPLICATION(application), + g_menu_append_section(target, NULL, model); /******************************************************************************
--- a/pidgin/pidgincontactlist.c Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/pidgincontactlist.c Fri May 27 14:52:34 2022 -0500
@@ -34,9 +34,6 @@
GtkWidget *accounts_menu;
- GtkWidget *plugins_menu;
G_DEFINE_TYPE(PidginContactList, pidgin_contact_list,
@@ -60,8 +57,6 @@
gtk_menu_item_set_submenu(GTK_MENU_ITEM(contact_list->accounts),
contact_list->accounts_menu);
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(contact_list->plugins),
- contact_list->plugins_menu);
@@ -83,10 +78,6 @@
gtk_widget_class_bind_template_child(widget_class, PidginContactList,
- gtk_widget_class_bind_template_child(widget_class, PidginContactList,
- gtk_widget_class_bind_template_child(widget_class, PidginContactList,
/******************************************************************************
--- a/pidgin/pidginpluginsmenu.c Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/pidginpluginsmenu.c Fri May 27 14:52:34 2022 -0500
@@ -1,5 +1,6 @@
+ * 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
@@ -16,243 +17,214 @@
* 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
+ * along with this program; if not, see <https://www.gnu.org/licenses/>. #include "pidginpluginsmenu.h"
+#include "pidginapplication.h" struct _PidginPluginsMenu {
- GSimpleActionGroup *action_group;
- GHashTable *plugin_items;
-#define PIDGIN_PLUGINS_MENU_ACTION_PREFIX "plugins-menu"
+G_DEFINE_TYPE(PidginPluginsMenu, pidgin_plugins_menu, G_TYPE_MENU_MODEL) /******************************************************************************
*****************************************************************************/
-pidgin_plugins_menu_action_activated(GSimpleAction *simple, GVariant *parameter,
- PurplePluginAction *action = (PurplePluginAction *)data;
+pidgin_plugins_menu_add_item(gpointer data, gpointer user_data) { + PidginPluginsMenu *menu = user_data; + GPluginPlugin *plugin = data; + GPluginPluginInfo *info = NULL; + info = gplugin_plugin_get_info(plugin); + if(PURPLE_IS_PLUGIN_INFO(info)) { + GActionGroup *group = NULL; - if(action != NULL && action->callback != NULL) {
- action->callback(action);
+ group = purple_plugin_info_get_action_group(PURPLE_PLUGIN_INFO(info)); + if(G_IS_ACTION_GROUP(group)) { + GApplication *application = g_application_get_default(); + prefix = gplugin_plugin_info_get_id(info); + pidgin_application_add_action_group(PIDGIN_APPLICATION(application), + g_queue_push_tail(menu->plugins, g_object_ref(plugin)); -pidgin_plugins_menu_add_plugin_actions(PidginPluginsMenu *menu,
- GPluginPluginInfo *info = NULL;
- PurplePluginActionsCb actions_cb = NULL;
- GtkWidget *submenu = NULL, *item = NULL;
- info = gplugin_plugin_get_info(GPLUGIN_PLUGIN(plugin));
- actions_cb = purple_plugin_info_get_actions_cb(PURPLE_PLUGIN_INFO(info));
- if(actions_cb == NULL) {
- g_object_unref(G_OBJECT(info));
- actions = actions_cb(plugin);
- g_object_unref(G_OBJECT(info));
- submenu = gtk_menu_new();
- for(i = 0; actions != NULL; i++) {
- PurplePluginAction *action = NULL;
- GSimpleAction *gaction = NULL;
- GtkWidget *action_item = NULL;
- gchar *action_base_name = NULL;
- gchar *action_full_name = NULL;
+pidgin_plugins_menu_refresh(PidginPluginsMenu *menu) { + GPluginManager *manager = NULL; + gint removed = 0, added = 0; - action = (PurplePluginAction *)actions->data;
- action_item = gtk_separator_menu_item_new();
- gtk_widget_show(action_item);
- gtk_menu_shell_append(GTK_MENU_SHELL(submenu), action_item);
- actions = g_list_delete_link(actions, actions);
- if(action->label == NULL) {
- actions = g_list_delete_link(actions, actions);
- action_base_name = g_strdup_printf("%s-%d",
- gplugin_plugin_info_get_id(info),
- action_full_name = g_strdup_printf("%s.%s",
- PIDGIN_PLUGINS_MENU_ACTION_PREFIX,
- /* create the menu item with the full action name */
- action_item = gtk_menu_item_new_with_label(action->label);
- gtk_actionable_set_action_name(GTK_ACTIONABLE(action_item),
- gtk_widget_show(action_item);
- g_free(action_full_name);
- /* add our action item to the menu */
- gtk_menu_shell_append(GTK_MENU_SHELL(submenu), action_item);
- /* now create the gaction with the base name */
- gaction = g_simple_action_new(action_base_name, NULL);
- g_free(action_base_name);
+ removed = g_queue_get_length(menu->plugins); + g_queue_clear_full(menu->plugins, g_object_unref); - /* now connect to the activate signal of the action using
- * g_signal_connect_data with a destroy notify to free the plugin action
- * when the signal handler is removed.
- g_signal_connect_data(G_OBJECT(gaction), "activate",
- G_CALLBACK(pidgin_plugins_menu_action_activated),
- (GClosureNotify)purple_plugin_action_free,
- /* finally add the action to the action group and remove our ref */
- g_action_map_add_action(G_ACTION_MAP(menu->action_group),
- g_object_unref(G_OBJECT(gaction));
- actions = g_list_delete_link(actions, actions);
- item = gtk_menu_item_new_with_label(gplugin_plugin_info_get_name(info));
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- g_hash_table_insert(menu->plugin_items,
- g_object_ref(G_OBJECT(plugin)),
- g_object_unref(G_OBJECT(info));
- /* make sure that our separator is visible */
- gtk_widget_show(menu->separator);
+ manager = gplugin_manager_get_default(); + loaded = gplugin_manager_find_plugins_with_state(manager, + GPLUGIN_PLUGIN_STATE_LOADED);
-pidgin_plugins_menu_remove_plugin_actions(PidginPluginsMenu *menu,
- GPluginPluginInfo *info = NULL;
- PurplePluginActionsCb actions_cb = NULL;
- /* try remove the menu item from plugin from the hash table. If we didn't
- * remove anything, we have nothing to do so bail.
- if(!g_hash_table_remove(menu->plugin_items, plugin)) {
- info = gplugin_plugin_get_info(GPLUGIN_PLUGIN(plugin));
- actions_cb = purple_plugin_info_get_actions_cb(PURPLE_PLUGIN_INFO(info));
- if(actions_cb == NULL) {
- g_object_unref(G_OBJECT(info));
+ g_slist_foreach(loaded, pidgin_plugins_menu_add_item, menu); - actions = actions_cb(plugin);
- g_object_unref(G_OBJECT(info));
- /* now walk through the actions and remove them from the action group. */
- for(i = 0; actions != NULL; i++) {
- name = g_strdup_printf("%s-%d", gplugin_plugin_info_get_id(info), i);
+ added = g_queue_get_length(menu->plugins); - g_action_map_remove_action(G_ACTION_MAP(menu->action_group), name);
- actions = g_list_delete_link(actions, actions);
- g_object_unref(G_OBJECT(info));
- /* finally, if this was the last item in the list, hide the separator. */
- if(g_hash_table_size(menu->plugin_items) == 0) {
- gtk_widget_hide(menu->separator);
+ /* Tell our listeners that our menu has changed. */ + g_menu_model_items_changed(G_MENU_MODEL(menu), 0, removed, added); /******************************************************************************
*****************************************************************************/
-pidgin_plugins_menu_plugin_loaded_cb(GObject *manager, GPluginPlugin *plugin,
+pidgin_plugins_menu_plugin_loaded_cb(G_GNUC_UNUSED GObject *manager, + G_GNUC_UNUSED GPluginPlugin *plugin, - pidgin_plugins_menu_add_plugin_actions(PIDGIN_PLUGINS_MENU(data), plugin);
+ pidgin_plugins_menu_refresh(PIDGIN_PLUGINS_MENU(data)); +pidgin_plugins_menu_plugin_unloaded_cb(G_GNUC_UNUSED GObject *manager, + GApplication *application = NULL; + GPluginPluginInfo *info = NULL; + /* Remove the action group that the plugin added. */ + info = gplugin_plugin_get_info(plugin); + if(GPLUGIN_IS_PLUGIN_INFO(info)) { + prefix = gplugin_plugin_info_get_id(info); + application = g_application_get_default(); + pidgin_application_add_action_group(PIDGIN_APPLICATION(application), + pidgin_plugins_menu_refresh(PIDGIN_PLUGINS_MENU(data)); +/****************************************************************************** + * GMenuModel Implementation + *****************************************************************************/ +pidgin_plugins_menu_is_mutable(GMenuModel *model) { +pidgin_plugins_menu_get_n_items(GMenuModel *model) { + PidginPluginsMenu *menu = PIDGIN_PLUGINS_MENU(model); + return g_queue_get_length(menu->plugins); -pidgin_plugins_menu_plugin_unloaded_cb(GObject *manager, GPluginPlugin *plugin,
+pidgin_plugins_menu_get_item_attributes(GMenuModel *model, gint index, + GHashTable **attributes) - pidgin_plugins_menu_remove_plugin_actions(PIDGIN_PLUGINS_MENU(data),
+ PidginPluginsMenu *menu = PIDGIN_PLUGINS_MENU(model); + GPluginPlugin *plugin = NULL; + GPluginPluginInfo *info = NULL; + GVariant *value = NULL; + /* Create our hash table of attributes to return. */ + *attributes = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + (GDestroyNotify)g_variant_unref); + /* Get the plugin the caller is interested in. */ + plugin = g_queue_peek_nth(menu->plugins, index); + /* Grab the plugin info and set the label attribute to the name of the + * plugin and set the action name space to the plugin's id. + info = gplugin_plugin_get_info(plugin); + value = g_variant_new_string(gplugin_plugin_info_get_name(info)); + g_hash_table_insert(*attributes, G_MENU_ATTRIBUTE_LABEL, + g_variant_ref_sink(value)); + value = g_variant_new_string(gplugin_plugin_info_get_id(info)); + g_hash_table_insert(*attributes, G_MENU_ATTRIBUTE_ACTION_NAMESPACE, + g_variant_ref_sink(value)); +pidgin_plugins_menu_get_item_links(GMenuModel *model, gint index, + PidginPluginsMenu *menu = PIDGIN_PLUGINS_MENU(model); + PurplePluginInfo *purple_info = NULL; + GPluginPlugin *plugin = NULL; + GPluginPluginInfo *info = NULL; + GMenuModel *actions_model = NULL; + *links = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + plugin = g_queue_peek_nth(menu->plugins, index); + if(!GPLUGIN_IS_PLUGIN(plugin)) { + info = gplugin_plugin_get_info(plugin); + purple_info = PURPLE_PLUGIN_INFO(info); + actions_model = purple_plugin_info_get_action_menu(purple_info); + if(G_IS_MENU_MODEL(actions_model)) { + g_hash_table_insert(*links, G_MENU_LINK_SUBMENU, actions_model); /******************************************************************************
*****************************************************************************/
-G_DEFINE_TYPE(PidginPluginsMenu, pidgin_plugins_menu, GTK_TYPE_MENU)
+pidgin_plugins_menu_finalize(GObject *obj) { + PidginPluginsMenu *menu = PIDGIN_PLUGINS_MENU(obj); + g_queue_free_full(menu->plugins, g_object_unref); + G_OBJECT_CLASS(pidgin_plugins_menu_parent_class)->finalize(obj); +pidgin_plugins_menu_constructed(GObject *obj) { + G_OBJECT_CLASS(pidgin_plugins_menu_parent_class)->constructed(obj); + pidgin_plugins_menu_refresh(PIDGIN_PLUGINS_MENU(obj)); pidgin_plugins_menu_init(PidginPluginsMenu *menu) {
GPluginManager *manager = NULL;
- /* initialize our template */
- gtk_widget_init_template(GTK_WIDGET(menu));
- /* create our internal action group and assign it to ourself */
- menu->action_group = g_simple_action_group_new();
- gtk_widget_insert_action_group(GTK_WIDGET(menu),
- PIDGIN_PLUGINS_MENU_ACTION_PREFIX,
- G_ACTION_GROUP(menu->action_group));
- /* create our storage for the items */
- menu->plugin_items = g_hash_table_new_full(g_direct_hash, g_direct_equal,
- (GDestroyNotify)gtk_widget_destroy);
+ menu->plugins = g_queue_new(); /* Connect to the plugin manager's signals so we can stay up to date. */
manager = gplugin_manager_get_default();
@@ -267,22 +239,22 @@
pidgin_plugins_menu_class_init(PidginPluginsMenuClass *klass) {
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GMenuModelClass *model_class = G_MENU_MODEL_CLASS(klass); - gtk_widget_class_set_template_from_resource(
- "/im/pidgin/Pidgin3/Plugins/menu.ui"
+ obj_class->finalize = pidgin_plugins_menu_finalize; + obj_class->constructed = pidgin_plugins_menu_constructed; - gtk_widget_class_bind_template_child(widget_class, PidginPluginsMenu,
+ model_class->is_mutable = pidgin_plugins_menu_is_mutable; + model_class->get_n_items = pidgin_plugins_menu_get_n_items; + model_class->get_item_attributes = pidgin_plugins_menu_get_item_attributes; + model_class->get_item_links = pidgin_plugins_menu_get_item_links; /******************************************************************************
*****************************************************************************/
pidgin_plugins_menu_new(void) {
- return GTK_WIDGET(g_object_new(PIDGIN_TYPE_PLUGINS_MENU, NULL));
+ return g_object_new(PIDGIN_TYPE_PLUGINS_MENU, NULL);
--- a/pidgin/pidginpluginsmenu.h Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/pidginpluginsmenu.h Fri May 27 14:52:34 2022 -0500
@@ -1,5 +1,6 @@
+ * 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
@@ -16,8 +17,7 @@
* 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
+ * along with this program; if not, see <https://www.gnu.org/licenses/>. #if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
@@ -34,18 +34,15 @@
- * #PidginPluginsMenu is a #GtkMenu that provides an interface to users to open
- * the plugin manager as well as activate plugin actions.
- * It manages itself as plugins are loaded and unloaded and can be added as a
- * submenu to any #GtkMenuItem.
+ * #PidginPluginsMenu is a [class@Gio.MenuModel] that automatically updates + * itself when plugins are loaded and unloaded. #define PIDGIN_TYPE_PLUGINS_MENU (pidgin_plugins_menu_get_type())
G_DECLARE_FINAL_TYPE(PidginPluginsMenu, pidgin_plugins_menu, PIDGIN,
+ PLUGINS_MENU, GMenuModel) * pidgin_plugins_menu_new:
@@ -54,7 +51,7 @@
* Returns: (transfer full): The new #PidginPluginsMenu instance.
-GtkWidget *pidgin_plugins_menu_new(void);
+GMenuModel *pidgin_plugins_menu_new(void); --- a/pidgin/resources/BuddyList/window.ui Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/resources/BuddyList/window.ui Fri May 27 14:52:34 2022 -0500
@@ -69,14 +69,6 @@
<property name="use-underline">True</property>
- <object class="GtkMenuItem" id="plugins">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="label" translatable="yes">_Plugins</property>
- <property name="use-underline">True</property>
<property name="expand">False</property>
@@ -91,8 +83,4 @@
<property name="visible">True</property>
<property name="can-focus">False</property>
- <object class="PidginPluginsMenu" id="plugins_menu">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
--- a/pidgin/resources/gtk/menus.ui Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/resources/gtk/menus.ui Fri May 27 14:52:34 2022 -0500
@@ -235,9 +235,7 @@
<attribute name="accel"><Primary>U</attribute>
- <attribute name="id">plugins-menu</attribute>
+ <section id="plugins-menu"/> <attribute name="label" translatable="yes">_Help</attribute>
--- a/pidgin/resources/pidgin.gresource.xml Fri May 27 04:11:45 2022 -0500
+++ b/pidgin/resources/pidgin.gresource.xml Fri May 27 14:52:34 2022 -0500
@@ -22,7 +22,6 @@
<file compressed="true">Debug/plugininfo.ui</file>
<file compressed="true">Log/log-viewer.ui</file>
<file compressed="true">Plugins/dialog.ui</file>
- <file compressed="true">Plugins/menu.ui</file>
<file compressed="true">Prefs/away.ui</file>
<file compressed="true">Prefs/conversation.ui</file>
<file compressed="true">Prefs/credentials.ui</file>