pidgin/pidgin

Merged in default (pull request #680)

2020-05-11, Gary Kramlich
924a47d2652d
Merged in default (pull request #680)

Replace GtkUIManager in GtkBlist with non-deprecated api

Approved-by: Elliott Sales de Andrade
Approved-by: John Bailey
--- a/doc/reference/libpurple/signals_connection.xml Sun May 10 13:43:52 2020 +0000
+++ b/doc/reference/libpurple/signals_connection.xml Mon May 11 02:40:30 2020 +0000
@@ -8,6 +8,8 @@
<refsect1 id="connections.signals" role="signal_proto">
<title role="signal_proto.title">List of signals</title>
<synopsis>
+ &quot;<link linkend="connections-online">online</link>&quot;
+ &quot;<link linkend="connections-offline">offline</link>&quot;
&quot;<link linkend="connections-signing-on">signing-on</link>&quot;
&quot;<link linkend="connections-signed-on">signed-on</link>&quot;
&quot;<link linkend="connections-autojoin">autojoin</link>&quot;
@@ -20,6 +22,39 @@
<refsect1 id="connections.signal-details" role="signals">
<title role="signals.title">Signal details</title>
+<refsect2 id="connections-online" role="signal">
+ <title>The <literal>&quot;online&quot;</literal> signal</title>
+<programlisting>
+void user_function (gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the first connection has connected when all connections were
+previously not connected.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
+<refsect2 id="connections-offline" role="signal">
+ <title>The <literal>&quot;online&quot;</literal> signal</title>
+<programlisting>
+void user_function (gpointer user_data)
+</programlisting>
+ <para>
+Emitted when the last connected connection has disconnected.
+ </para>
+ <variablelist role="params">
+ <varlistentry>
+ <term><parameter>user_data</parameter>&#160;:</term>
+ <listitem><simpara>user data set when the signal handler was connected.</simpara></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect2>
+
<refsect2 id="connections-signing-on" role="signal">
<title>The <literal>&quot;signing-on&quot;</literal> signal</title>
<programlisting>
--- a/doc/reference/pidgin/pidgin-docs.xml Sun May 10 13:43:52 2020 +0000
+++ b/doc/reference/pidgin/pidgin-docs.xml Mon May 11 02:40:30 2020 +0000
@@ -56,6 +56,10 @@
<xi:include href="xml/pidgin.xml" />
<xi:include href="xml/pidginabout.xml" />
<xi:include href="xml/pidginaccountchooser.xml" />
+ <xi:include href="xml/pidginaccountactionsmenu.xml" />
+ <xi:include href="xml/pidginaccountsmenu.xml" />
+ <xi:include href="xml/pidginactiongroup.xml" />
+ <xi:include href="xml/pidginbuddylistmenu.xml" />
<xi:include href="xml/pidgincontactcompletion.xml" />
<xi:include href="xml/pidgindebug.xml" />
<xi:include href="xml/pidgingdkpixbuf.xml" />
@@ -64,8 +68,10 @@
<xi:include href="xml/pidginlog.xml" />
<xi:include href="xml/pidginmenutray.xml" />
<xi:include href="xml/pidginmessage.xml" />
+ <xi:include href="xml/pidginmooddialog.xml" />
<xi:include href="xml/pidginplugininfo.xml" />
<xi:include href="xml/pidginpluginsdialog.xml" />
+ <xi:include href="xml/pidginpluginsmenu.xml" />
<xi:include href="xml/pidginstock.xml" />
<xi:include href="xml/pidgintalkatu.xml" />
<xi:include href="xml/pidgintooltip.xml" />
--- a/libpurple/connection.c Sun May 10 13:43:52 2020 +0000
+++ b/libpurple/connection.c Mon May 11 02:40:30 2020 +0000
@@ -102,6 +102,7 @@
static GList *connections = NULL;
static GList *connections_connecting = NULL;
+static GList *connections_connected = NULL;
static PurpleConnectionUiOps *connection_ui_ops = NULL;
static int connections_handle;
@@ -185,9 +186,12 @@
if (priv->state == PURPLE_CONNECTION_CONNECTED) {
PurpleAccount *account;
PurplePresence *presence;
+ gboolean emit_online = FALSE;
+ gpointer handle = NULL;
account = purple_connection_get_account(gc);
presence = purple_account_get_presence(account);
+ handle = purple_connections_get_handle();
/* Set the time the account came online */
purple_presence_set_login_time(presence, time(NULL));
@@ -214,12 +218,25 @@
purple_blist_add_account(account);
- purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
- purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
+ purple_signal_emit(handle, "signed-on", gc);
+ purple_signal_emit_return_1(handle, "autojoin", gc);
purple_serv_set_permit_deny(gc);
update_keepalive(gc, TRUE);
+
+ /* check if connections_connected is NULL, if so we need to emit the
+ * online signal.
+ */
+ if(connections_connected == NULL) {
+ emit_online = TRUE;
+ }
+
+ connections_connected = g_list_append(connections_connected, gc);
+
+ if(emit_online) {
+ purple_signal_emit(handle, "online");
+ }
}
else if (priv->state == PURPLE_CONNECTION_DISCONNECTED) {
PurpleAccount *account = purple_connection_get_account(gc);
@@ -846,17 +863,19 @@
PurpleAccount *account;
GSList *buddies;
gboolean remove = FALSE;
+ gpointer handle;
priv->is_finalizing = TRUE;
account = purple_connection_get_account(gc);
+ handle = purple_connections_get_handle();
purple_debug_info("connection", "Disconnecting connection %p\n", gc);
if (purple_connection_get_state(gc) != PURPLE_CONNECTION_CONNECTING)
remove = TRUE;
- purple_signal_emit(purple_connections_get_handle(), "signing-off", gc);
+ purple_signal_emit(handle, "signing-off", gc);
g_slist_free_full(priv->active_chats, (GDestroyNotify)purple_chat_conversation_leave);
@@ -881,12 +900,17 @@
if (remove)
purple_blist_remove_account(account);
- purple_signal_emit(purple_connections_get_handle(), "signed-off", gc);
+ purple_signal_emit(handle, "signed-off", gc);
purple_account_request_close_with_account(account);
purple_request_close_with_handle(gc);
purple_notify_close_with_handle(gc);
+ connections_connected = g_list_remove(connections_connected, gc);
+ if(connections_connected == NULL) {
+ purple_signal_emit(handle, "offline");
+ }
+
purple_debug_info("connection", "Destroying connection %p\n", gc);
purple_account_set_connection(account, NULL);
@@ -1122,6 +1146,11 @@
return connections_connecting;
}
+gboolean
+purple_connections_is_online(void) {
+ return (connections_connected != NULL);
+}
+
void
purple_connections_set_ui_ops(PurpleConnectionUiOps *ops)
{
@@ -1139,6 +1168,12 @@
{
void *handle = purple_connections_get_handle();
+ purple_signal_register(handle, "online", purple_marshal_VOID, G_TYPE_NONE,
+ 0);
+
+ purple_signal_register(handle, "offline", purple_marshal_VOID, G_TYPE_NONE,
+ 0);
+
purple_signal_register(handle, "signing-on",
purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
PURPLE_TYPE_CONNECTION);
--- a/libpurple/connection.h Sun May 10 13:43:52 2020 +0000
+++ b/libpurple/connection.h Mon May 11 02:40:30 2020 +0000
@@ -613,6 +613,15 @@
*/
GList *purple_connections_get_connecting(void);
+/**
+ * purple_connections_is_online:
+ *
+ * Checks if at least one account is online.
+ *
+ * Returns: %TRUE if at least one account is online.
+ */
+gboolean purple_connections_is_online(void);
+
/**************************************************************************/
/* UI Registration Functions */
/**************************************************************************/
--- a/pidgin/glade/pidgin3.xml.in Sun May 10 13:43:52 2020 +0000
+++ b/pidgin/glade/pidgin3.xml.in Mon May 11 02:40:30 2020 +0000
@@ -2,14 +2,18 @@
<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"/>
<glade-widget-class name="PidginScrollBook" generic-name="scroll_book" title="ScrollBook"/>
</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"/>
<glade-widget-class-ref name="PidginScrollBook"/>
</glade-widget-group>
</glade-catalog>
--- a/pidgin/gtkblist.c Sun May 10 13:43:52 2020 +0000
+++ b/pidgin/gtkblist.c Mon May 11 02:40:30 2020 +0000
@@ -31,24 +31,23 @@
#include "gtkdialogs.h"
#include "gtkxfer.h"
#include "gtkpounce.h"
-#include "gtkprefs.h"
#include "gtkprivacy.h"
#include "gtkroomlist.h"
#include "gtkstatusbox.h"
#include "gtkscrollbook.h"
-#include "gtksmiley-manager.h"
#include "gtkstyle.h"
#include "gtkblist-theme.h"
#include "gtkblist-theme-loader.h"
#include "gtkutils.h"
#include "pidgin/minidialog.h"
-#include "pidgin/pidginabout.h"
#include "pidgin/pidginaccountchooser.h"
+#include "pidgin/pidginactiongroup.h"
+#include "pidgin/pidginbuddylistmenu.h"
#include "pidgin/pidgindebug.h"
#include "pidgin/pidgingdkpixbuf.h"
#include "pidgin/pidginlog.h"
+#include "pidgin/pidginmooddialog.h"
#include "pidgin/pidginplugininfo.h"
-#include "pidgin/pidginpluginsdialog.h"
#include "pidgin/pidgintooltip.h"
#include "pidginmenutray.h"
#include "pidginstock.h"
@@ -126,8 +125,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;
@@ -140,8 +137,6 @@
static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
-static guint sort_merge_id;
-static GtkActionGroup *sort_action_group = NULL;
static PidginBuddyList *gtkblist = NULL;
@@ -779,16 +774,6 @@
}
}
-static void gtk_blist_show_systemlog_cb(void)
-{
- pidgin_syslog_show();
-}
-
-static void gtk_blist_show_onlinehelp_cb(void)
-{
- purple_notify_uri(NULL, PURPLE_WEBSITE "documentation");
-}
-
static void
do_join_chat(PidginChatData *data)
{
@@ -2008,84 +1993,6 @@
return handled;
}
-static void gtk_blist_show_xfer_dialog_cb(GtkAction *item, gpointer data)
-{
- pidgin_xfer_dialog_show(NULL);
-}
-
-static void pidgin_blist_buddy_details_cb(GtkToggleAction *item, gpointer data)
-{
- pidgin_set_cursor(gtkblist->window, GDK_WATCH);
-
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons",
- gtk_toggle_action_get_active(item));
-
- pidgin_clear_cursor(gtkblist->window);
-}
-
-static void pidgin_blist_show_idle_time_cb(GtkToggleAction *item, gpointer data)
-{
- pidgin_set_cursor(gtkblist->window, GDK_WATCH);
-
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time",
- gtk_toggle_action_get_active(item));
-
- pidgin_clear_cursor(gtkblist->window);
-}
-
-static void pidgin_blist_show_protocol_icons_cb(GtkToggleAction *item, gpointer data)
-{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons",
- gtk_toggle_action_get_active(item));
-}
-
-static void pidgin_blist_show_empty_groups_cb(GtkToggleAction *item, gpointer data)
-{
- pidgin_set_cursor(gtkblist->window, GDK_WATCH);
-
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups",
- gtk_toggle_action_get_active(item));
-
- pidgin_clear_cursor(gtkblist->window);
-}
-
-static void pidgin_blist_edit_mode_cb(GtkToggleAction *checkitem, gpointer data)
-{
- pidgin_set_cursor(gtkblist->window, GDK_WATCH);
-
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies",
- gtk_toggle_action_get_active(checkitem));
-
- pidgin_clear_cursor(gtkblist->window);
-}
-
-static void pidgin_blist_mute_sounds_cb(GtkToggleAction *item, gpointer data)
-{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute",
- gtk_toggle_action_get_active(item));
-}
-
-
-static void
-pidgin_blist_mute_pref_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
-{
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui,
- "/BList/ToolsMenu/MuteSounds")), (gboolean)GPOINTER_TO_INT(value));
-}
-
-static void
-pidgin_blist_sound_method_pref_cb(const char *name, PurplePrefType type,
- gconstpointer value, gpointer data)
-{
- gboolean sensitive = TRUE;
-
- if(purple_strequal(value, "none"))
- sensitive = FALSE;
-
- gtk_action_set_sensitive(gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/MuteSounds"), sensitive);
-}
-
static void
add_buddies_from_vcard(const char *protocol_id, PurpleGroup *group, GList *list,
const char *alias)
@@ -3330,402 +3237,6 @@
return FALSE;
}
-static void
-toggle_debug(void)
-{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled",
- !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"));
-}
-
-static char *get_mood_icon_path(const char *mood)
-{
- char *path;
-
- if (purple_strequal(mood, "busy")) {
- path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons",
- "hicolor", "16x16", "status", "user-busy.png", NULL);
- } else if (purple_strequal(mood, "hiptop")) {
- path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons",
- "hicolor", "16x16", "emblems", "emblem-hiptop.png",
- NULL);
- } else {
- char *filename = g_strdup_printf("%s.png", mood);
- path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
- "emotes", "small", filename, NULL);
- g_free(filename);
- }
- return path;
-}
-
-static void
-update_status_with_mood(PurpleAccount *account, const gchar *mood,
- const gchar *text)
-{
- if (mood && *mood) {
- if (text) {
- purple_account_set_status(account, "mood", TRUE,
- PURPLE_MOOD_NAME, mood,
- PURPLE_MOOD_COMMENT, text,
- NULL);
- } else {
- purple_account_set_status(account, "mood", TRUE,
- PURPLE_MOOD_NAME, mood,
- NULL);
- }
- } else {
- purple_account_set_status(account, "mood", FALSE, NULL);
- }
-}
-
-static void
-edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields)
-{
- PurpleRequestField *mood_field;
- GList *l;
-
- mood_field = purple_request_fields_get_field(fields, "mood");
- l = purple_request_field_list_get_selected(mood_field);
-
- if (l) {
- const char *mood = purple_request_field_list_get_data(mood_field, l->data);
-
- if (gc) {
- const char *text;
- PurpleAccount *account = purple_connection_get_account(gc);
-
- if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES) {
- PurpleRequestField *text_field;
- text_field = purple_request_fields_get_field(fields, "text");
- text = purple_request_field_string_get_value(text_field);
- } else {
- text = NULL;
- }
-
- update_status_with_mood(account, mood, text);
- } else {
- GList *accounts = purple_accounts_get_all_active();
-
- for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
- PurpleAccount *account = (PurpleAccount *) accounts->data;
- PurpleConnection *gc = purple_account_get_connection(account);
-
- if (gc && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) {
- update_status_with_mood(account, mood, NULL);
- }
- }
- }
- }
-}
-
-static void
-global_moods_for_each(gpointer key, gpointer value, gpointer user_data)
-{
- GList **out_moods = (GList **) user_data;
- PurpleMood *mood = (PurpleMood *) value;
-
- *out_moods = g_list_append(*out_moods, mood);
-}
-
-static PurpleMood *
-get_global_moods(void)
-{
- GHashTable *global_moods =
- g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
- GHashTable *mood_counts =
- g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
- GList *accounts = purple_accounts_get_all_active();
- PurpleMood *result = NULL;
- GList *out_moods = NULL;
- int i = 0;
- int num_accounts = 0;
-
- for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
- PurpleAccount *account = (PurpleAccount *) accounts->data;
- if (purple_account_is_connected(account)) {
- PurpleConnection *gc = purple_account_get_connection(account);
-
- if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS) {
- PurpleProtocol *protocol = purple_connection_get_protocol(gc);
- PurpleMood *mood = NULL;
-
- for (mood = purple_protocol_client_iface_get_moods(protocol, account) ;
- mood->mood != NULL ; mood++) {
- int mood_count =
- GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood));
-
- if (!g_hash_table_lookup(global_moods, mood->mood)) {
- g_hash_table_insert(global_moods, (gpointer)mood->mood, mood);
- }
- g_hash_table_insert(mood_counts, (gpointer)mood->mood,
- GINT_TO_POINTER(mood_count + 1));
- }
-
- num_accounts++;
- }
- }
- }
-
- g_hash_table_foreach(global_moods, global_moods_for_each, &out_moods);
- result = g_new0(PurpleMood, g_hash_table_size(global_moods) + 1);
-
- while (out_moods) {
- PurpleMood *mood = (PurpleMood *) out_moods->data;
- int in_num_accounts =
- GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood));
-
- if (in_num_accounts == num_accounts) {
- /* mood is present in all accounts supporting moods */
- result[i].mood = mood->mood;
- result[i].description = mood->description;
- i++;
- }
- out_moods = g_list_delete_link(out_moods, out_moods);
- }
-
- g_hash_table_destroy(global_moods);
- g_hash_table_destroy(mood_counts);
-
- return result;
-}
-
-/* get current set mood for all mood-supporting accounts, or NULL if not set
- or not set to the same on all */
-static const gchar *
-get_global_mood_status(void)
-{
- GList *accounts = purple_accounts_get_all_active();
- const gchar *found_mood = NULL;
-
- for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
- PurpleAccount *account = (PurpleAccount *) accounts->data;
-
- if (purple_account_is_connected(account) &&
- (purple_connection_get_flags(purple_account_get_connection(account)) &
- PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) {
- PurplePresence *presence = purple_account_get_presence(account);
- PurpleStatus *status = purple_presence_get_status(presence, "mood");
- const gchar *curr_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
-
- if (found_mood != NULL && !purple_strequal(curr_mood, found_mood)) {
- /* found a different mood */
- found_mood = NULL;
- break;
- } else {
- found_mood = curr_mood;
- }
- }
- }
-
- return found_mood;
-}
-
-static void
-set_mood_cb(GtkWidget *widget, PurpleAccount *account)
-{
- const char *current_mood;
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *g;
- PurpleRequestField *f;
- PurpleConnection *gc = NULL;
- PurpleProtocol *protocol = NULL;
- PurpleMood *mood;
- PurpleMood *global_moods = NULL;
-
- if (account) {
- PurplePresence *presence = purple_account_get_presence(account);
- PurpleStatus *status = purple_presence_get_status(presence, "mood");
- gc = purple_account_get_connection(account);
- g_return_if_fail(purple_connection_get_protocol(gc) != NULL);
- protocol = purple_connection_get_protocol(gc);
- current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
- } else {
- current_mood = get_global_mood_status();
- }
-
- fields = purple_request_fields_new();
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
-
- purple_request_field_list_add_icon(f, _("None"), NULL, "");
- if (current_mood == NULL)
- purple_request_field_list_add_selected(f, _("None"));
-
- /* TODO: rlaager wants this sorted. */
- /* TODO: darkrain wants it sorted post-translation */
- if (account && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods)) {
- mood = purple_protocol_client_iface_get_moods(protocol, account);
- } else {
- mood = global_moods = get_global_moods();
- }
- for ( ; mood->mood != NULL ; mood++) {
- char *path;
-
- if (mood->description == NULL) {
- continue;
- }
-
- path = get_mood_icon_path(mood->mood);
- purple_request_field_list_add_icon(f, _(mood->description),
- path, (gpointer)mood->mood);
- g_free(path);
-
- if (current_mood && purple_strequal(current_mood, mood->mood))
- purple_request_field_list_add_selected(f, _(mood->description));
- }
- purple_request_field_group_add_field(g, f);
-
- purple_request_fields_add_group(fields, g);
-
- /* if the connection allows setting a mood message */
- if (gc && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES)) {
- g = purple_request_field_group_new(NULL);
- f = purple_request_field_string_new("text",
- _("Message (optional)"), NULL, FALSE);
- purple_request_field_group_add_field(g, f);
- purple_request_fields_add_group(fields, g);
- }
-
- purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"),
- NULL, fields,
- _("OK"), G_CALLBACK(edit_mood_cb),
- _("Cancel"), NULL,
- purple_request_cpar_from_connection(gc), gc);
-
- g_free(global_moods);
-}
-
-static void
-set_mood_show(void)
-{
- set_mood_cb(NULL, NULL);
-}
-
-/***************************************************
- * Crap *
- ***************************************************/
-static void
-pidgin_blist_plugins_dialog_cb(GtkAction *action, GtkWidget *window) {
- GtkWidget *dialog = pidgin_plugins_dialog_new();
-
- gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
-
- gtk_widget_show_all(dialog);
-}
-
-static void
-_pidgin_about_cb(GtkAction *action, GtkWidget *window) {
- GtkWidget *about = pidgin_about_dialog_new();
-
- gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(window));
-
- gtk_widget_show_all(about);
-}
-
-/* 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. */
- /* Buddies menu */
- { "BuddiesMenu", NULL, N_("_Buddies"), NULL, NULL, NULL },
- { "NewInstantMessage", PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("New Instant _Message..."), "<control>M", NULL, pidgin_dialogs_im },
- { "JoinAChat", PIDGIN_STOCK_CHAT, N_("Join a _Chat..."), "<control>C", NULL, pidgin_blist_joinchat_show },
- { "GetUserInfo", PIDGIN_STOCK_TOOLBAR_USER_INFO, N_("Get User _Info..."), "<control>I", NULL, pidgin_dialogs_info },
- { "ViewUserLog", NULL, N_("View User _Log..."), "<control>L", NULL, pidgin_dialogs_log },
- { "ShowMenu", NULL, N_("Sh_ow"), NULL, NULL, NULL },
- { "SortMenu", NULL, N_("_Sort Buddies"), NULL, NULL, NULL },
- { "AddBuddy", GTK_STOCK_ADD, N_("_Add Buddy..."), "<control>B", NULL, pidgin_blist_add_buddy_cb },
- { "AddChat", GTK_STOCK_ADD, N_("Add C_hat..."), NULL, NULL, pidgin_blist_add_chat_cb },
- { "AddGroup", GTK_STOCK_ADD, N_("Add _Group..."), NULL, NULL, purple_blist_request_add_group },
- { "Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", NULL, purple_core_quit },
-
- /* Accounts menu */
- { "AccountsMenu", NULL, N_("_Accounts"), NULL, NULL, NULL },
- { "ManageAccounts", NULL, N_("Manage Accounts"), "<control>A", NULL, pidgin_accounts_window_show },
-
- /* Tools */
- { "ToolsMenu", NULL, N_("_Tools"), NULL, NULL, NULL },
- { "BuddyPounces", NULL, N_("Buddy _Pounces"), NULL, NULL, pidgin_pounces_manager_show },
- { "CustomSmileys", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("Custom Smile_ys"), "<control>Y", NULL, pidgin_smiley_manager_show },
- { "Plugins", PIDGIN_STOCK_TOOLBAR_PLUGINS, N_("Plu_gins"), "<control>U", NULL, pidgin_blist_plugins_dialog_cb },
- { "Preferences", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), "<control>P", NULL, pidgin_prefs_show },
- { "Privacy", NULL, N_("Pr_ivacy"), NULL, NULL, pidgin_privacy_dialog_show },
- { "SetMood", NULL, N_("Set _Mood"), "<control>D", NULL, set_mood_show },
- { "FileTransfers", PIDGIN_STOCK_TOOLBAR_TRANSFER, N_("_File Transfers"), "<control>T", NULL, G_CALLBACK(gtk_blist_show_xfer_dialog_cb) },
- { "RoomList", NULL, N_("R_oom List"), NULL, NULL, pidgin_roomlist_dialog_show },
- { "SystemLog", NULL, N_("System _Log"), NULL, NULL, gtk_blist_show_systemlog_cb },
-
- /* Help */
- { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
- { "OnlineHelp", GTK_STOCK_HELP, N_("Online _Help"), "F1", NULL, gtk_blist_show_onlinehelp_cb },
- { "DebugWindow", NULL, N_("_Debug Window"), NULL, NULL, toggle_debug },
- { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, G_CALLBACK(_pidgin_about_cb) },
-};
-
-/* Toggle items */
-static const GtkToggleActionEntry blist_menu_toggle_entries[] = {
- /* Buddies->Show menu */
- { "ShowOffline", NULL, N_("_Offline Buddies"), NULL, NULL, G_CALLBACK(pidgin_blist_edit_mode_cb), FALSE },
- { "ShowEmptyGroups", NULL, N_("_Empty Groups"), NULL, NULL, G_CALLBACK(pidgin_blist_show_empty_groups_cb), FALSE },
- { "ShowBuddyDetails", NULL, N_("Buddy _Details"), NULL, NULL, G_CALLBACK(pidgin_blist_buddy_details_cb), FALSE },
- { "ShowIdleTimes", NULL, N_("Idle _Times"), NULL, NULL, G_CALLBACK(pidgin_blist_show_idle_time_cb), FALSE },
- { "ShowProtocolIcons", NULL, N_("_Protocol Icons"), NULL, NULL, G_CALLBACK(pidgin_blist_show_protocol_icons_cb), FALSE },
-
- /* Tools menu */
- { "MuteSounds", NULL, N_("Mute _Sounds"), NULL, NULL, G_CALLBACK(pidgin_blist_mute_sounds_cb), FALSE },
-};
-
-static const char *blist_menu =
-"<ui>"
- "<menubar name='BList'>"
- "<menu action='BuddiesMenu'>"
- "<menuitem action='NewInstantMessage'/>"
- "<menuitem action='JoinAChat'/>"
- "<menuitem action='GetUserInfo'/>"
- "<menuitem action='ViewUserLog'/>"
- "<separator/>"
- "<menu action='ShowMenu'>"
- "<menuitem action='ShowOffline'/>"
- "<menuitem action='ShowEmptyGroups'/>"
- "<menuitem action='ShowBuddyDetails'/>"
- "<menuitem action='ShowIdleTimes'/>"
- "<menuitem action='ShowProtocolIcons'/>"
- "</menu>"
- "<menu action='SortMenu'/>"
- "<separator/>"
- "<menuitem action='AddBuddy'/>"
- "<menuitem action='AddChat'/>"
- "<menuitem action='AddGroup'/>"
- "<separator/>"
- "<menuitem action='Quit'/>"
- "</menu>"
- "<menu action='AccountsMenu'>"
- "<menuitem action='ManageAccounts'/>"
- "</menu>"
- "<menu action='ToolsMenu'>"
- "<menuitem action='BuddyPounces'/>"
- "<menuitem action='CustomSmileys'/>"
- "<menuitem action='Plugins'/>"
- "<menuitem action='Preferences'/>"
- "<menuitem action='Privacy'/>"
- "<menuitem action='SetMood'/>"
- "<separator/>"
- "<menuitem action='FileTransfers'/>"
- "<menuitem action='RoomList'/>"
- "<menuitem action='SystemLog'/>"
- "<separator/>"
- "<menuitem action='MuteSounds'/>"
- "<placeholder name='PluginActions'/>"
- "</menu>"
- "<menu action='HelpMenu'>"
- "<menuitem action='OnlineHelp'/>"
- "<separator/>"
- "<menuitem action='DebugWindow'/>"
- "<separator/>"
- "<menuitem action='About'/>"
- "</menu>"
- "</menubar>"
-"</ui>";
-
/*********************************************************
* Private Utility functions *
*********************************************************/
@@ -4124,7 +3635,7 @@
if (!(name && *name))
return NULL;
- path = get_mood_icon_path(name);
+ path = pidgin_mood_get_icon_path(name);
} else {
filename = g_strdup_printf("emblem-%s.png", name);
path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons",
@@ -4523,67 +4034,6 @@
gtknode->row = NULL;
}
-static const char *require_connection[] =
-{
- "/BList/BuddiesMenu/NewInstantMessage",
- "/BList/BuddiesMenu/JoinAChat",
- "/BList/BuddiesMenu/GetUserInfo",
- "/BList/BuddiesMenu/AddBuddy",
- "/BList/BuddiesMenu/AddChat",
- "/BList/BuddiesMenu/AddGroup",
- "/BList/ToolsMenu/Privacy",
-};
-
-static const int require_connection_size = sizeof(require_connection)
- / sizeof(*require_connection);
-
-/*
- * Rebuild dynamic menus and make menu items sensitive/insensitive
- * where appropriate.
- */
-static void
-update_menu_bar(PidginBuddyList *gtkblist)
-{
- GtkAction *action;
- gboolean sensitive;
- int i;
-
- 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++)
- {
- action = gtk_ui_manager_get_action(gtkblist->ui, require_connection[i]);
- gtk_action_set_sensitive(action, sensitive);
- }
-
- action = gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/JoinAChat");
- gtk_action_set_sensitive(action, pidgin_blist_joinchat_is_showable());
-
- action = gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/AddChat");
- gtk_action_set_sensitive(action, pidgin_blist_joinchat_is_showable());
-
- action = gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/RoomList");
- gtk_action_set_sensitive(action, pidgin_roomlist_is_showable());
-}
-
-static void
-sign_on_off_cb(PurpleConnection *gc, PurpleBuddyList *blist)
-{
- PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(blist);
-
- update_menu_bar(gtkblist);
-}
-
-static void
-plugin_changed_cb(PurplePlugin *p, gpointer data)
-{
- pidgin_blist_update_plugin_actions();
-}
-
static void
unseen_conv_menu(GdkEvent *event)
{
@@ -4929,13 +4379,6 @@
return;
pidgin_blist_select_notebook_page(gtkblist);
- update_menu_bar(gtkblist);
-}
-
-static void
-account_actions_changed(PurpleAccount *account, gpointer data)
-{
- pidgin_blist_update_accounts_menu();
}
static void
@@ -5691,8 +5134,8 @@
{
PidginBuddyListPrivate *priv;
void *handle;
+ GSimpleActionGroup *actions;
GtkTreeViewColumn *column;
- GtkWidget *menu;
GtkWidget *sep;
GtkWidget *infobar;
GtkWidget *content_area;
@@ -5700,9 +5143,6 @@
GtkWidget *close;
gchar *text;
const char *theme_name;
- GtkActionGroup *action_group;
- GError *error;
- GtkAccelGroup *accel_group;
GtkTreeSelection *selection;
GtkTargetEntry dte[] = {{"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW},
{"application/x-im-contact", 0, DRAG_BUDDY},
@@ -5755,42 +5195,15 @@
gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK);
/******************************* Menu bar *************************************/
- action_group = gtk_action_group_new("BListActions");
- gtk_action_group_set_translation_domain(action_group, PACKAGE);
- gtk_action_group_add_actions(action_group,
- blist_menu_entries,
- G_N_ELEMENTS(blist_menu_entries),
- GTK_WINDOW(gtkblist->window));
- gtk_action_group_add_toggle_actions(action_group,
- blist_menu_toggle_entries,
- G_N_ELEMENTS(blist_menu_toggle_entries),
- GTK_WINDOW(gtkblist->window));
-
- gtkblist->ui = gtk_ui_manager_new();
- gtk_ui_manager_insert_action_group(gtkblist->ui, action_group, 0);
-
- accel_group = gtk_ui_manager_get_accel_group(gtkblist->ui);
- gtk_window_add_accel_group(GTK_WINDOW(gtkblist->window), accel_group);
- pidgin_load_accels();
- g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(pidgin_save_accels_cb), NULL);
-
- error = NULL;
- if (!gtk_ui_manager_add_ui_from_string(gtkblist->ui, blist_menu, -1, &error))
- {
- g_message("building menus failed: %s", error->message);
- g_error_free(error);
- exit(EXIT_FAILURE);
- }
-
- menu = gtk_ui_manager_get_widget(gtkblist->ui, "/BList");
- gtkblist->menutray = pidgin_menu_tray_new();
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtkblist->menutray);
- gtk_widget_show(gtkblist->menutray);
- 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));
+ actions = pidgin_action_group_new();
+ gtk_widget_insert_action_group(gtkblist->window, "blist",
+ G_ACTION_GROUP(actions));
+
+ gtkblist->menu = pidgin_buddy_list_menu_new();
+ gtk_box_pack_start(GTK_BOX(gtkblist->main_vbox), gtkblist->menu, FALSE,
+ FALSE, 0);
+
+ gtkblist->menutray = pidgin_buddy_list_menu_get_menu_tray(PIDGIN_BUDDY_LIST_MENU(gtkblist->menu));
/****************************** Notebook *************************************/
gtkblist->notebook = gtk_notebook_new();
@@ -5959,33 +5372,7 @@
gtk_widget_set_name(gtkblist->statusbox, "pidgin_blist_statusbox");
gtk_widget_show(gtkblist->statusbox);
- /* set the Show Offline Buddies option. must be done
- * after the treeview or faceprint gets mad. -Robot101
- */
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowOffline")),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies"));
-
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowEmptyGroups")),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups"));
-
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/MuteSounds")),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/mute"));
-
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowBuddyDetails")),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"));
-
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowIdleTimes")),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time"));
-
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_ui_manager_get_action(gtkblist->ui, "/BList/BuddiesMenu/ShowMenu/ShowProtocolIcons")),
- purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"));
-
- if(purple_strequal(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none"))
- gtk_action_set_sensitive(gtk_ui_manager_get_action(gtkblist->ui, "/BList/ToolsMenu/MuteSounds"), FALSE);
-
/* Update some dynamic things */
- update_menu_bar(gtkblist);
- pidgin_blist_update_plugin_actions();
pidgin_blist_update_sort_methods();
/* OK... let's show this bad boy. */
@@ -6016,12 +5403,6 @@
purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/blist/sort_type",
_prefs_change_sort_method, NULL);
- /* menus */
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/sound/mute",
- pidgin_blist_mute_pref_cb, NULL);
- purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/sound/method",
- pidgin_blist_sound_method_pref_cb, NULL);
-
/* Setup some purple signal handlers. */
handle = purple_accounts_get_handle();
@@ -6037,25 +5418,11 @@
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,
PURPLE_CALLBACK(account_modified), gtkblist);
- handle = purple_connections_get_handle();
- purple_signal_connect(handle, "signed-on", gtkblist,
- PURPLE_CALLBACK(sign_on_off_cb), list);
- purple_signal_connect(handle, "signed-off", gtkblist,
- PURPLE_CALLBACK(sign_on_off_cb), list);
-
- handle = purple_plugins_get_handle();
- purple_signal_connect(handle, "plugin-load", gtkblist,
- PURPLE_CALLBACK(plugin_changed_cb), NULL);
- purple_signal_connect(handle, "plugin-unload", gtkblist,
- PURPLE_CALLBACK(plugin_changed_cb), NULL);
-
handle = purple_conversations_get_handle();
purple_signal_connect(handle, "conversation-updated", gtkblist,
PURPLE_CALLBACK(conversation_updated_cb),
@@ -7484,7 +6851,6 @@
purple_signals_unregister_by_instance(pidgin_blist_get_handle());
purple_signals_disconnect_by_handle(pidgin_blist_get_handle());
- accountmenu = NULL;
gtkblist = NULL;
}
@@ -7524,7 +6890,6 @@
gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL;
g_clear_object(&gtkblist->treemodel);
- g_object_unref(G_OBJECT(gtkblist->ui));
g_object_unref(G_OBJECT(gtkblist->empty_avatar));
g_clear_object(&priv->current_theme);
@@ -7875,438 +7240,38 @@
}
}
-static void
-plugin_act(GSimpleAction *action, GVariant *param, PurplePluginAction *pam)
-{
- if (pam && pam->callback)
- pam->callback(pam);
-}
-
-static GtkWidget *
-build_plugin_actions(GActionMap *action_map, const gchar *parent,
- PurplePlugin *plugin)
-{
- GMenu *menu = NULL;
- GMenu *section = NULL;
- PurplePluginActionsCb actions_cb;
- PurplePluginAction *action = NULL;
- GList *actions, *l;
- char *name;
- int count = 0;
- GtkWidget *ret;
-
- actions_cb =
- purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin));
-
- actions = actions_cb(plugin);
-
- if (actions == NULL) {
- return NULL;
- }
-
- menu = g_menu_new();
-
- for (l = actions; l != NULL; l = l->next) {
- GAction *menuaction;
-
- action = (PurplePluginAction *)l->data;
-
- if (action == NULL) {
- if (section != NULL) {
- /* Close and append section if any */
- g_menu_append_section(menu, NULL,
- G_MENU_MODEL(section));
- g_clear_object(&section);
- }
-
- continue;
- }
-
- action->plugin = plugin;
-
- name = g_strdup_printf("plugin.%s-action-%d", parent, count++);
- /* +7 to skip "plugin." prefix */
- menuaction = G_ACTION(g_simple_action_new(name + 7, NULL));
- g_signal_connect_data(G_OBJECT(menuaction), "activate",
- G_CALLBACK(plugin_act), action,
- (GClosureNotify)purple_plugin_action_free, 0);
- g_action_map_add_action(action_map, menuaction);
- g_object_unref(menuaction);
-
- if (section == NULL) {
- section = g_menu_new();
- }
-
- g_menu_append(section, action->label, name);
- g_free(name);
- }
-
- if (section != NULL) {
- /* Close and append final section if any */
- g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
- g_clear_object(&section);
- }
-
- g_list_free(actions);
-
- ret = gtk_menu_new_from_model(G_MENU_MODEL(menu));
- g_object_unref(menu);
- return ret;
-}
-
-
-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);
-}
-
-static GSList *plugin_menu_items;
-
-void
-pidgin_blist_update_plugin_actions(void)
-{
- GtkWidget *toolsmenu;
- GSimpleActionGroup *action_group;
- PurplePlugin *plugin = NULL;
- PurplePluginInfo *info;
- GList *l;
-
- int count = 0;
-
- if ((gtkblist == NULL) || (gtkblist->ui == NULL))
- return;
-
- /* Clear the old menu */
- g_slist_free_full(plugin_menu_items,
- (GDestroyNotify)gtk_widget_destroy);
- plugin_menu_items = NULL;
-
- toolsmenu = gtk_ui_manager_get_widget(gtkblist->ui,
- "/BList/ToolsMenu");
- toolsmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(toolsmenu));
-
- action_group = g_simple_action_group_new();
-
- /* Add a submenu for each plugin with custom actions */
- for (l = purple_plugins_get_loaded(); l; l = l->next) {
- char *name;
- GtkWidget *submenu;
- GtkWidget *menuitem;
-
- plugin = (PurplePlugin *)l->data;
- info = purple_plugin_get_info(plugin);
-
- if (!purple_plugin_info_get_actions_cb(info))
- continue;
-
- name = g_strdup_printf("plugin%d", count);
- submenu = build_plugin_actions(G_ACTION_MAP(action_group),
- name, plugin);
- g_free(name);
-
- if (submenu == NULL) {
- continue;
- }
-
- menuitem = gtk_menu_item_new_with_mnemonic(
- _(gplugin_plugin_info_get_name(
- GPLUGIN_PLUGIN_INFO(info))));
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
- gtk_widget_show(menuitem);
- plugin_menu_items = g_slist_prepend(plugin_menu_items,
- menuitem);
- gtk_menu_shell_append(GTK_MENU_SHELL(toolsmenu), menuitem);
-
- count++;
- }
-
- /* Replaces existing "plugin" group if any */
- gtk_widget_insert_action_group(toolsmenu, "plugin",
- G_ACTION_GROUP(action_group));
- g_object_unref(action_group);
-}
-
-static void
-sortmethod_act(GtkRadioAction *action, GtkRadioAction *current, char *id)
-{
- if (action == current)
- {
- pidgin_set_cursor(gtkblist->window, GDK_WATCH);
- /* This is redundant. I think. */
- /* pidgin_blist_sort_method_set(id); */
- purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/sort_type", id);
-
- pidgin_clear_cursor(gtkblist->window);
- }
-}
-
void
pidgin_blist_update_sort_methods(void)
{
- PidginBlistSortMethod *method = NULL;
+ GtkWidget *sort_item = NULL;
+ GMenu *menu = NULL;
GList *l;
- GSList *sl = NULL;
- const char *m = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type");
-
- GtkRadioAction *action;
- GString *ui_string;
-
- if ((gtkblist == NULL) || (gtkblist->ui == NULL))
+
+ if(gtkblist == NULL)
return;
- /* Clear the old menu */
- if (sort_action_group) {
- gtk_ui_manager_remove_ui(gtkblist->ui, sort_merge_id);
- gtk_ui_manager_remove_action_group(gtkblist->ui, sort_action_group);
- g_object_unref(G_OBJECT(sort_action_group));
- }
-
- sort_action_group = gtk_action_group_new("SortMethods");
- gtk_action_group_set_translation_domain(sort_action_group, PACKAGE);
-
- ui_string = g_string_new("<ui><menubar name='BList'>"
- "<menu action='BuddiesMenu'><menu action='SortMenu'>");
-
+ /* create the gmenu */
+ menu = g_menu_new();
+
+ /* walk through the sort methods and update all the things */
for (l = pidgin_blist_sort_methods; l; l = l->next) {
+ PidginBlistSortMethod *method = NULL;
+ GMenuItem *item = NULL;
+ gchar *action = NULL;
+
method = (PidginBlistSortMethod *)l->data;
- g_string_append_printf(ui_string, "<menuitem action='%s'/>", method->id);
- action = gtk_radio_action_new(method->id,
- method->name,
- NULL,
- NULL,
- 0);
- gtk_action_group_add_action_with_accel(sort_action_group, GTK_ACTION(action), NULL);
-
- gtk_radio_action_set_group(action, sl);
- sl = gtk_radio_action_get_group(action);
-
- if (purple_strequal(m, method->id))
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
- else
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), FALSE);
-
- g_signal_connect(G_OBJECT(action), "changed",
- G_CALLBACK(sortmethod_act), method->id);
- }
-
- g_string_append(ui_string, "</menu></menu></menubar></ui>");
- gtk_ui_manager_insert_action_group(gtkblist->ui, sort_action_group, 1);
- sort_merge_id = gtk_ui_manager_add_ui_from_string(gtkblist->ui, ui_string->str, -1, NULL);
-
- g_string_free(ui_string, TRUE);
-}
-
+ action = g_action_print_detailed_name("blist.sort-method",
+ g_variant_new_string(method->id));
+ item = g_menu_item_new(method->name, action);
+ g_free(action);
+
+ g_menu_append_item(menu, item);
+ }
+
+ /* replace the old submenu with a new one */
+ sort_item = pidgin_buddy_list_menu_get_sort_item(PIDGIN_BUDDY_LIST_MENU(gtkblist->menu));
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(sort_item),
+ gtk_menu_new_from_model(G_MENU_MODEL(menu)));
+ g_object_unref(G_OBJECT(menu));
+}
--- a/pidgin/gtkblist.h Sun May 10 13:43:52 2020 +0000
+++ b/pidgin/gtkblist.h Mon May 11 02:40:30 2020 +0000
@@ -120,7 +120,7 @@
GtkCellRenderer *text_rend;
- GtkUIManager *ui;
+ GtkWidget *menu;
GtkWidget *menutray;
GtkWidget *menutrayicon;
--- a/pidgin/meson.build Sun May 10 13:43:52 2020 +0000
+++ b/pidgin/meson.build Mon May 11 02:40:30 2020 +0000
@@ -34,7 +34,11 @@
'libpidgin.c',
'minidialog.c',
'pidginabout.c',
+ 'pidginaccountactionsmenu.c',
'pidginaccountchooser.c',
+ 'pidginaccountsmenu.c',
+ 'pidginactiongroup.c',
+ 'pidginbuddylistmenu.c',
'pidgincontactcompletion.c',
'pidgindebug.c',
'pidgingdkpixbuf.c',
@@ -42,8 +46,10 @@
'pidginlog.c',
'pidginmenutray.c',
'pidginmessage.c',
+ 'pidginmooddialog.c',
'pidginplugininfo.c',
'pidginpluginsdialog.c',
+ 'pidginpluginsmenu.c',
'pidginprotocolchooser.c',
'pidginprotocolstore.c',
'pidgintalkatu.c',
@@ -87,7 +93,11 @@
'gtkxfer.h',
'minidialog.h',
'pidginabout.h',
+ 'pidginaccountactionsmenu.c',
'pidginaccountchooser.h',
+ 'pidginaccountsmenu.c',
+ 'pidginactiongroup.h',
+ 'pidginbuddylistmenu.h',
'pidgincontactcompletion.h',
'pidgindebug.h',
'pidgingdkpixbuf.h',
@@ -96,8 +106,10 @@
'pidginlog.h',
'pidginmenutray.h',
'pidginmessage.h',
+ 'pidginmooddialog.h',
'pidginplugininfo.h',
'pidginpluginsdialog.h',
+ 'pidginpluginsmenu.h',
'pidginprotocolchooser.h',
'pidginprotocolstore.h',
'pidgintalkatu.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginaccountactionsmenu.c Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,296 @@
+/*
+ * 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/gtkaccount.h"
+#include "pidgin/pidgin.h"
+#include "pidgin/pidginmooddialog.h"
+
+struct _PidginAccountActionsMenu {
+ GtkMenu parent;
+
+ GtkWidget *separator;
+ GtkWidget *set_mood;
+
+ 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_set_mood_cb(GtkMenuItem *item, gpointer data) {
+ PidginAccountActionsMenu *menu = PIDGIN_ACCOUNT_ACTIONS_MENU(data);
+
+ pidgin_mood_dialog_show(menu->account);
+}
+
+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;
+ gboolean show_separator = FALSE;
+ gint position = 0;
+
+ if(g_set_object(&menu->account, account)) {
+ if(!PURPLE_IS_ACCOUNT(menu->account)) {
+ return;
+ }
+ }
+
+ 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 get the index of the set_mood item to find the
+ * proper position.
+ */
+ children = gtk_container_get_children(GTK_CONTAINER(menu));
+ position = g_list_index(children, menu->set_mood) + 1;
+ g_list_free(children);
+
+ protocol = purple_connection_get_protocol(connection);
+
+ if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods)) {
+ gtk_widget_show(menu->set_mood);
+
+ show_separator = TRUE;
+ }
+
+ if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) {
+ GtkWidget *item = NULL;
+ GList *actions = NULL;
+
+ actions = purple_protocol_client_iface_get_actions(protocol,
+ connection);
+
+ while(actions != NULL) {
+ PurpleProtocolAction *action = (PurpleProtocolAction *)actions->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 */
+ show_separator = TRUE;
+
+ /* Iterate to the next item while deleting the one we just
+ * processed.
+ */
+ actions = g_list_delete_link(actions, actions);
+ }
+ }
+
+ /* if we added any items, make our separator visible. */
+ if(show_separator) {
+ 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_child(widget_class, PidginAccountActionsMenu,
+ set_mood);
+
+ 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);
+ gtk_widget_class_bind_template_callback(widget_class,
+ pidgin_account_actions_menu_set_mood_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 May 11 02:40:30 2020 +0000
@@ -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 for users
+ * to edit accounts and activate the account's 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 May 11 02:40:30 2020 +0000
@@ -0,0 +1,245 @@
+/*
+ * 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;
+
+ /* if the account is in the disabled list, delete its widget */
+ g_hash_table_remove(menu->disabled_items, account);
+
+ 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;
+
+ /* if the account is in the enabled list, delete its widget */
+ g_hash_table_remove(menu->account_items, account);
+
+ 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) {
+ PidginAccountsMenu *menu = PIDGIN_ACCOUNTS_MENU(obj);
+
+ purple_signals_disconnect_by_handle(obj);
+
+ g_hash_table_destroy(menu->account_items);
+ g_hash_table_destroy(menu->disabled_items);
+
+ 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 May 11 02:40:30 2020 +0000
@@ -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
+ *
+ * #PidginAccountsMenu 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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginactiongroup.c Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,859 @@
+/*
+ * 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 "pidginactiongroup.h"
+
+#include <purple.h>
+
+#include "internal.h"
+
+#include "pidgin/gtkblist.h"
+#include "pidgin/gtkdialogs.h"
+#include "pidgin/gtkpounce.h"
+#include "pidgin/gtkprefs.h"
+#include "pidgin/gtkprivacy.h"
+#include "pidgin/gtkroomlist.h"
+#include "pidgin/gtksmiley-manager.h"
+#include "pidgin/gtkxfer.h"
+#include "pidgin/pidginabout.h"
+#include "pidgin/pidginlog.h"
+#include "pidgin/pidginmooddialog.h"
+
+struct _PidginActionGroup {
+ GSimpleActionGroup parent;
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+/**< private >
+ * online_actions:
+ *
+ * This list keeps track of which actions should only be enabled while online.
+ */
+static const gchar *pidgin_action_group_online_actions[] = {
+ PIDGIN_ACTION_ADD_BUDDY,
+ PIDGIN_ACTION_ADD_GROUP,
+ PIDGIN_ACTION_GET_USER_INFO,
+ PIDGIN_ACTION_NEW_MESSAGE,
+ PIDGIN_ACTION_PRIVACY,
+ PIDGIN_ACTION_SET_MOOD,
+};
+
+static const gchar *pidgin_action_group_chat_actions[] = {
+ PIDGIN_ACTION_ADD_CHAT,
+ PIDGIN_ACTION_JOIN_CHAT,
+};
+
+static const gchar *pidgin_action_group_room_list_actions[] = {
+ PIDGIN_ACTION_ROOM_LIST,
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+
+/*< private >
+ * pidgin_action_group_bool_pref_handler:
+ * @group: The #PidginActionGroup instance.
+ * @action_name: The name of the action to update.
+ * @value: The value of the preference.
+ *
+ * Changes the state of the action named @action_name to match @value.
+ *
+ * This function is meant to be called from a #PurplePrefCallback function as
+ * there isn't a good way to have a #PurplePrefCallback with multiple items in
+ * the data parameter without leaking them forever.
+ */
+static void
+pidgin_action_group_bool_pref_handler(PidginActionGroup *group,
+ const gchar *action_name,
+ gboolean value)
+{
+ GAction *action = NULL;
+
+ action = g_action_map_lookup_action(G_ACTION_MAP(group), action_name);
+ if(action != NULL) {
+ g_simple_action_set_state(G_SIMPLE_ACTION(action),
+ g_variant_new_boolean(value));
+ }
+}
+
+/*< private >
+ * pidgin_action_group_setup_bool:
+ * @group: The #PidginActionGroup instance.
+ * @action_name: The name of the action to setup.
+ * @pref_name: The name of the preference that @action_name is tied to.
+ * @callback: (scope notified): A #PurplePrefCallback to call when the
+ * preference is changed.
+ *
+ * Initializes the boolean action named @action_name to the value of @pref_name
+ * and setups up a preference change callback to @callback to maintain the
+ * state of the action.
+ */
+static void
+pidgin_action_group_setup_bool(PidginActionGroup *group,
+ const gchar *action_name,
+ const gchar *pref_name,
+ PurplePrefCallback callback)
+{
+ GAction *action = NULL;
+ gboolean value = FALSE;
+
+ /* find the action, if we can't find it, bail */
+ action = g_action_map_lookup_action(G_ACTION_MAP(group), action_name);
+ g_return_if_fail(action != NULL);
+
+ /* get the value of the preference */
+ value = purple_prefs_get_bool(pref_name);
+
+ /* change the state of the action to match the preference value. */
+ g_simple_action_set_state(G_SIMPLE_ACTION(action),
+ g_variant_new_boolean(value));
+
+ /* finally add a preference callback to update the state based on the
+ * preference.
+ */
+ purple_prefs_connect_callback(group, pref_name, callback, group);
+}
+
+/*< private >
+ * pidgin_action_group_string_pref_handler:
+ * @group: The #PidginActionGroup instance.
+ * @action_name: The name of the action to update.
+ * @value: The value of the preference.
+ *
+ * Changes the state of the action named @action_name to match @value.
+ *
+ * This function is meant to be called from a #PurplePrefCallback function as
+ * there isn't a good way to have a #PurplePrefCallback with multiple items in
+ * the data parameter without leaking them forever.
+ */
+static void
+pidgin_action_group_string_pref_handler(PidginActionGroup *group,
+ const gchar *action_name,
+ const gchar *value)
+{
+ GAction *action = NULL;
+
+ action = g_action_map_lookup_action(G_ACTION_MAP(group), action_name);
+ if(action != NULL) {
+ g_simple_action_set_state(G_SIMPLE_ACTION(action),
+ g_variant_new_string(value));
+ }
+}
+
+/*< private >
+ * pidgin_action_group_setup_string:
+ * @group: The #PidginActionGroup instance.
+ * @action_name: The name of the action to setup.
+ * @pref_name: The name of the preference that @action_name is tied to.
+ * @callback: (scope notified): A #PurplePrefCallback to call when the
+ * preference is changed.
+ *
+ * Initializes the string action named @action_name to the value of @pref_name
+ * and setups up a preference change callback to @callback to maintain the
+ * state of the action.
+ */
+static void
+pidgin_action_group_setup_string(PidginActionGroup *group,
+ const gchar *action_name,
+ const gchar *pref_name,
+ PurplePrefCallback callback)
+{
+ GAction *action = NULL;
+ const gchar *value = NULL;
+
+ /* find the action, if we can't find it, bail */
+ action = g_action_map_lookup_action(G_ACTION_MAP(group), action_name);
+ g_return_if_fail(action != NULL);
+
+ /* change the state of the action to match the preference value. */
+ value = purple_prefs_get_string(pref_name);
+ g_simple_action_set_state(G_SIMPLE_ACTION(action),
+ g_variant_new_string(value));
+
+ /* finally add a preference callback to update the state based on the
+ * preference.
+ */
+ purple_prefs_connect_callback(group, pref_name, callback, group);
+}
+
+/*< private >
+ * pidgin_action_group_actions_set_enable:
+ * @group: The #PidginActionGroup instance.
+ * @actions: The action names.
+ * @n_actions: The number of @actions.
+ * @enabled: Whether or not to enable the actions.
+ *
+ * Sets the enabled property of the named actions to @enabled.
+ */
+static void
+pidgin_action_group_actions_set_enable(PidginActionGroup *group,
+ const gchar *const *actions,
+ gint n_actions,
+ gboolean enabled)
+{
+ gint i = 0;
+
+ for(i = 0; i < n_actions; i++) {
+ GAction *action = NULL;
+ const gchar *name = actions[i];
+
+ action = g_action_map_lookup_action(G_ACTION_MAP(group), name);
+
+ if(action != NULL) {
+ g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
+ } else {
+ g_warning("Failed to find action named %s", name);
+ }
+ }
+}
+
+/*< private >
+ * pidgin_action_group_online_actions_set_enable:
+ * @group: The #PidginActionGroup instance.
+ * @enabled: %TRUE to enable the actions, %FALSE to disable them.
+ *
+ * Enables/disables the actions that require being online to use.
+ */
+static void
+pidgin_action_group_online_actions_set_enable(PidginActionGroup *group,
+ gboolean enabled)
+{
+ gint n_actions = G_N_ELEMENTS(pidgin_action_group_online_actions);
+
+ pidgin_action_group_actions_set_enable(group,
+ pidgin_action_group_online_actions,
+ n_actions,
+ enabled);
+}
+
+/*< private >
+ * pidgin_action_group_chat_actions_set_enable:
+ * @group: The #PidginActionGroup instance.
+ * @enabled: Whether or not to enable/disable the actions.
+ *
+ * Sets the enabled state of the chat specific actions to the value of @enabled.
+ */
+static void
+pidgin_action_group_chat_actions_set_enable(PidginActionGroup *group,
+ gboolean enabled)
+{
+ gint n_actions = G_N_ELEMENTS(pidgin_action_group_chat_actions);
+
+ pidgin_action_group_actions_set_enable(group,
+ pidgin_action_group_chat_actions,
+ n_actions,
+ enabled);
+}
+
+/*< private >
+ * pidgin_action_group_room_list_actions_set_enable:
+ * @group: The #PidginActionGroup instance.
+ * @enabled: Whether or not to enable/disable the actions.
+ *
+ * Sets the enabled state of the room list specific actions to the value of
+ * @enabled.
+ */
+static void
+pidgin_action_group_room_list_actions_set_enable(PidginActionGroup *group,
+ gboolean enabled)
+{
+ gint n_actions = G_N_ELEMENTS(pidgin_action_group_room_list_actions);
+
+ pidgin_action_group_actions_set_enable(group,
+ pidgin_action_group_room_list_actions,
+ n_actions,
+ enabled);
+}
+
+/******************************************************************************
+ * Purple Signal Callbacks
+ *****************************************************************************/
+static void
+pidgin_action_group_online_cb(gpointer data) {
+ pidgin_action_group_online_actions_set_enable(PIDGIN_ACTION_GROUP(data),
+ TRUE);
+}
+
+static void
+pidgin_action_group_offline_cb(gpointer data) {
+ pidgin_action_group_online_actions_set_enable(PIDGIN_ACTION_GROUP(data),
+ FALSE);
+}
+
+static void
+pidgin_action_group_signed_on_cb(PurpleAccount *account, gpointer data) {
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+ PurpleProtocol *protocol = NULL;
+ const gchar *protocol_id = NULL;
+ gboolean should_enable_chat = FALSE, should_enable_room_list = FALSE;
+
+ protocol_id = purple_account_get_protocol_id(account);
+ protocol = purple_protocols_find(protocol_id);
+
+ /* We assume that the current state is correct, so we don't bother changing
+ * state unless the newly connected account implements the chat interface,
+ * which would cause a state change.
+ */
+ should_enable_chat = PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info);
+ if(should_enable_chat) {
+ pidgin_action_group_chat_actions_set_enable(group, TRUE);
+ }
+
+ /* likewise, for the room list, we only care about enabling in this
+ * handler.
+ */
+ should_enable_room_list = PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST,
+ get_list);
+ if(should_enable_room_list) {
+ pidgin_action_group_room_list_actions_set_enable(group, TRUE);
+ }
+}
+
+static void
+pidgin_action_group_signed_off_cb(PurpleAccount *account, gpointer data) {
+ gboolean should_disable_chat = TRUE, should_disable_room_list = TRUE;
+ GList *connections = NULL, *l = NULL;
+
+ /* walk through all the connections, looking for online ones that implement
+ * the chat interface. We don't bother checking the account that this
+ * signal was emitted for, because it's already offline and will be
+ * filtered out by the online check.
+ */
+ connections = purple_connections_get_all();
+ for(l = connections; l != NULL; l = l->next) {
+ PurpleConnection *connection = PURPLE_CONNECTION(l->data);
+ PurpleProtocol *protocol = NULL;
+
+ /* if the connection isn't online, we don't care about it */
+ if(!PURPLE_CONNECTION_IS_CONNECTED(connection)) {
+ continue;
+ }
+
+ protocol = purple_connection_get_protocol(connection);
+
+ /* check if the protocol implements the chat interface */
+ if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info)) {
+ should_disable_chat = FALSE;
+ }
+
+ /* check if the protocol implement the room list interface */
+ if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, get_list)) {
+ should_disable_room_list = FALSE;
+ }
+
+ /* if we can't disable both, we can bail out of the loop */
+ if(!should_disable_chat && !should_disable_room_list) {
+ break;
+ }
+ }
+
+ if(should_disable_chat) {
+ pidgin_action_group_chat_actions_set_enable(PIDGIN_ACTION_GROUP(data),
+ FALSE);
+ }
+
+ if(should_disable_room_list) {
+ pidgin_action_group_room_list_actions_set_enable(PIDGIN_ACTION_GROUP(data),
+ FALSE);
+ }
+}
+
+/******************************************************************************
+ * Preference Callbacks
+ *****************************************************************************/
+static void
+pidgin_action_group_mute_sounds_callback(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+
+ pidgin_action_group_bool_pref_handler(group, PIDGIN_ACTION_MUTE_SOUNDS,
+ (gboolean)GPOINTER_TO_INT(value));
+}
+
+static void
+pidgin_action_group_show_buddy_icons_callback(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+
+ pidgin_action_group_bool_pref_handler(group,
+ PIDGIN_ACTION_SHOW_BUDDY_ICONS,
+ (gboolean)GPOINTER_TO_INT(value));
+}
+
+static void
+pidgin_action_group_show_empty_groups_callback(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+
+ pidgin_action_group_bool_pref_handler(group,
+ PIDGIN_ACTION_SHOW_EMPTY_GROUPS,
+ (gboolean)GPOINTER_TO_INT(value));
+}
+
+static void
+pidgin_action_group_show_idle_times_callback(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+
+ pidgin_action_group_bool_pref_handler(group,
+ PIDGIN_ACTION_SHOW_IDLE_TIMES,
+ (gboolean)GPOINTER_TO_INT(value));
+}
+
+static void
+pidgin_action_group_show_offline_buddies_callback(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+
+ pidgin_action_group_bool_pref_handler(group,
+ PIDGIN_ACTION_SHOW_OFFLINE_BUDDIES,
+ (gboolean)GPOINTER_TO_INT(value));
+}
+
+static void
+pidgin_action_group_show_protocol_icons_callback(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+
+ pidgin_action_group_bool_pref_handler(group,
+ PIDGIN_ACTION_SHOW_PROTOCOL_ICONS,
+ (gboolean)GPOINTER_TO_INT(value));
+}
+
+static void
+pidgin_action_group_sort_method_callback(const gchar *name,
+ PurplePrefType type,
+ gconstpointer value,
+ gpointer data)
+{
+ PidginActionGroup *group = PIDGIN_ACTION_GROUP(data);
+
+ pidgin_action_group_string_pref_handler(group,
+ PIDGIN_ACTION_SORT_METHOD,
+ value);
+}
+
+/******************************************************************************
+ * Action Callbacks
+ *****************************************************************************/
+static void
+pidgin_action_group_about(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ GtkWidget *about = pidgin_about_dialog_new();
+
+ /* fix me? */
+#if 0
+ gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(window));
+#endif
+
+ gtk_widget_show_all(about);
+}
+
+static void
+pidgin_action_group_add_buddy(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ purple_blist_request_add_buddy(NULL, NULL, NULL, NULL);
+}
+
+static void
+pidgin_action_group_add_chat(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ purple_blist_request_add_chat(NULL, NULL, NULL, NULL);
+}
+
+static void
+pidgin_action_group_add_group(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ purple_blist_request_add_group();
+}
+
+static void
+pidgin_action_group_buddy_pounces(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_pounces_manager_show();
+}
+
+static void
+pidgin_action_group_custom_smiley(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_smiley_manager_show();
+}
+
+static void
+pidgin_action_group_debug(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ gboolean old = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled");
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/enabled", !old);
+}
+
+static void
+pidgin_action_group_file_transfers(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_xfer_dialog_show(NULL);
+}
+
+static void
+pidgin_action_group_get_user_info(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_dialogs_info();
+}
+
+static void
+pidgin_action_group_join_chat(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_blist_joinchat_show();
+}
+
+static void
+pidgin_action_group_mute_sounds(GSimpleAction *action, GVariant *value,
+ gpointer data)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute",
+ g_variant_get_boolean(value));
+}
+
+static void
+pidgin_action_group_new_message(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_dialogs_im();
+}
+
+static void
+pidgin_action_group_online_help(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ purple_notify_uri(NULL, PURPLE_WEBSITE "help");
+}
+
+static void
+pidgin_action_group_preferences(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_prefs_show();
+}
+
+static void
+pidgin_action_group_privacy(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_privacy_dialog_show();
+}
+
+static void
+pidgin_action_group_quit(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ purple_core_quit();
+}
+
+static void
+pidgin_action_group_room_list(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_roomlist_dialog_show();
+}
+
+static void
+pidgin_action_group_set_mood(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_mood_dialog_show(NULL);
+}
+
+static void
+pidgin_action_group_show_buddy_icons(GSimpleAction *action, GVariant *value,
+ gpointer data)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons",
+ g_variant_get_boolean(value));
+}
+
+static void
+pidgin_action_group_show_empty_groups(GSimpleAction *action, GVariant *value,
+ gpointer data)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_empty_groups",
+ g_variant_get_boolean(value));
+}
+
+static void
+pidgin_action_group_show_idle_times(GSimpleAction *action,
+ GVariant *value,
+ gpointer data)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_idle_time",
+ g_variant_get_boolean(value));
+}
+
+static void
+pidgin_action_group_show_offline_buddies(GSimpleAction *action,
+ GVariant *value,
+ gpointer data)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_offline_buddies",
+ g_variant_get_boolean(value));
+}
+
+static void
+pidgin_action_group_show_protocol_icons(GSimpleAction *action,
+ GVariant *value,
+ gpointer data)
+{
+ purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons",
+ g_variant_get_boolean(value));
+}
+
+static void
+pidgin_action_group_sort_method(GSimpleAction *action, GVariant *value,
+ gpointer data)
+{
+ purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/sort_type",
+ g_variant_get_string(value, NULL));
+}
+
+static void
+pidgin_action_group_system_log(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_syslog_show();
+}
+
+static void
+pidgin_action_group_view_user_log(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ pidgin_dialogs_log();
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginActionGroup, pidgin_action_group,
+ G_TYPE_SIMPLE_ACTION_GROUP)
+
+static void
+pidgin_action_group_init(PidginActionGroup *group) {
+ gpointer handle = NULL;
+ GActionEntry entries[] = {
+ {
+ .name = PIDGIN_ACTION_ABOUT,
+ .activate = pidgin_action_group_about,
+ }, {
+ .name = PIDGIN_ACTION_ADD_BUDDY,
+ .activate = pidgin_action_group_add_buddy,
+ }, {
+ .name = PIDGIN_ACTION_ADD_CHAT,
+ .activate = pidgin_action_group_add_chat,
+ }, {
+ .name = PIDGIN_ACTION_ADD_GROUP,
+ .activate = pidgin_action_group_add_group,
+ }, {
+ .name = PIDGIN_ACTION_BUDDY_POUNCES,
+ .activate = pidgin_action_group_buddy_pounces,
+ }, {
+ .name = PIDGIN_ACTION_CUSTOM_SMILEY,
+ .activate = pidgin_action_group_custom_smiley,
+ }, {
+ .name = PIDGIN_ACTION_DEBUG,
+ .activate = pidgin_action_group_debug,
+ }, {
+ .name = PIDGIN_ACTION_FILE_TRANSFERS,
+ .activate = pidgin_action_group_file_transfers,
+ }, {
+ .name = PIDGIN_ACTION_GET_USER_INFO,
+ .activate = pidgin_action_group_get_user_info,
+ }, {
+ .name = PIDGIN_ACTION_JOIN_CHAT,
+ .activate = pidgin_action_group_join_chat,
+ }, {
+ .name = PIDGIN_ACTION_MUTE_SOUNDS,
+ .state = "false",
+ .change_state = pidgin_action_group_mute_sounds,
+ }, {
+ .name = PIDGIN_ACTION_NEW_MESSAGE,
+ .activate = pidgin_action_group_new_message,
+ }, {
+ .name = PIDGIN_ACTION_ONLINE_HELP,
+ .activate = pidgin_action_group_online_help,
+ }, {
+ .name = PIDGIN_ACTION_PREFERENCES,
+ .activate = pidgin_action_group_preferences,
+ }, {
+ .name = PIDGIN_ACTION_PRIVACY,
+ .activate = pidgin_action_group_privacy,
+ }, {
+ .name = PIDGIN_ACTION_QUIT,
+ .activate = pidgin_action_group_quit,
+ }, {
+ .name = PIDGIN_ACTION_ROOM_LIST,
+ .activate = pidgin_action_group_room_list,
+ }, {
+ .name = PIDGIN_ACTION_SET_MOOD,
+ .activate = pidgin_action_group_set_mood,
+ }, {
+ .name = PIDGIN_ACTION_SHOW_BUDDY_ICONS,
+ .state = "false",
+ .change_state = pidgin_action_group_show_buddy_icons,
+ }, {
+ .name = PIDGIN_ACTION_SHOW_EMPTY_GROUPS,
+ .state = "false",
+ .change_state = pidgin_action_group_show_empty_groups,
+ }, {
+ .name = PIDGIN_ACTION_SHOW_IDLE_TIMES,
+ .state = "false",
+ .change_state = pidgin_action_group_show_idle_times,
+ }, {
+ .name = PIDGIN_ACTION_SHOW_OFFLINE_BUDDIES,
+ .state = "false",
+ .change_state = pidgin_action_group_show_offline_buddies,
+ }, {
+ .name = PIDGIN_ACTION_SHOW_PROTOCOL_ICONS,
+ .state = "false",
+ .change_state = pidgin_action_group_show_protocol_icons,
+ }, {
+ .name = PIDGIN_ACTION_SORT_METHOD,
+ .parameter_type = "s",
+ .state = "'none'",
+ .change_state = pidgin_action_group_sort_method,
+ }, {
+ .name = PIDGIN_ACTION_SYSTEM_LOG,
+ .activate = pidgin_action_group_system_log,
+ }, {
+ .name = PIDGIN_ACTION_VIEW_USER_LOG,
+ .activate = pidgin_action_group_view_user_log,
+ },
+ };
+
+ g_action_map_add_action_entries(G_ACTION_MAP(group), entries,
+ G_N_ELEMENTS(entries), NULL);
+
+ /* now add some handlers for preference changes and set actions to the
+ * correct value.
+ */
+ pidgin_action_group_setup_bool(group, PIDGIN_ACTION_MUTE_SOUNDS,
+ PIDGIN_PREFS_ROOT "/sound/mute",
+ pidgin_action_group_mute_sounds_callback);
+ pidgin_action_group_setup_bool(group, PIDGIN_ACTION_SHOW_BUDDY_ICONS,
+ PIDGIN_PREFS_ROOT "/blist/show_buddy_icons",
+ pidgin_action_group_show_buddy_icons_callback);
+ pidgin_action_group_setup_bool(group, PIDGIN_ACTION_SHOW_EMPTY_GROUPS,
+ PIDGIN_PREFS_ROOT "/blist/show_empty_groups",
+ pidgin_action_group_show_empty_groups_callback);
+ pidgin_action_group_setup_bool(group, PIDGIN_ACTION_SHOW_IDLE_TIMES,
+ PIDGIN_PREFS_ROOT "/blist/show_idle_time",
+ pidgin_action_group_show_idle_times_callback);
+ pidgin_action_group_setup_bool(group, PIDGIN_ACTION_SHOW_OFFLINE_BUDDIES,
+ PIDGIN_PREFS_ROOT "/blist/show_offline_buddies",
+ pidgin_action_group_show_offline_buddies_callback);
+ pidgin_action_group_setup_bool(group, PIDGIN_ACTION_SHOW_PROTOCOL_ICONS,
+ PIDGIN_PREFS_ROOT "/blist/show_protocol_icons",
+ pidgin_action_group_show_protocol_icons_callback);
+
+ pidgin_action_group_setup_string(group, PIDGIN_ACTION_SORT_METHOD,
+ PIDGIN_PREFS_ROOT "/blist/sort_type",
+ pidgin_action_group_sort_method_callback);
+
+ /* assume we are offline and disable all of the actions that require us to
+ * be online.
+ */
+ pidgin_action_group_online_actions_set_enable(group, FALSE);
+ pidgin_action_group_chat_actions_set_enable(group, FALSE);
+ pidgin_action_group_room_list_actions_set_enable(group, FALSE);
+
+ /* connect to the online and offline signals in purple connections. This
+ * is used to toggle states of actions that require being online.
+ */
+ handle = purple_connections_get_handle();
+ purple_signal_connect(handle, "online", group,
+ PURPLE_CALLBACK(pidgin_action_group_online_cb),
+ group);
+ purple_signal_connect(handle, "offline", group,
+ PURPLE_CALLBACK(pidgin_action_group_offline_cb),
+ group);
+
+ /* connect to account-signed-on and account-signed-off to toggle actions
+ * that depend on specific interfaces in accounts.
+ */
+ handle = purple_accounts_get_handle();
+ purple_signal_connect(handle, "account-signed-on", group,
+ PURPLE_CALLBACK(pidgin_action_group_signed_on_cb),
+ group);
+ purple_signal_connect(handle, "account-signed-off", group,
+ PURPLE_CALLBACK(pidgin_action_group_signed_off_cb),
+ group);
+};
+
+static void
+pidgin_action_group_finalize(GObject *obj) {
+ purple_signals_disconnect_by_handle(obj);
+
+ G_OBJECT_CLASS(pidgin_action_group_parent_class)->finalize(obj);
+}
+
+static void
+pidgin_action_group_class_init(PidginActionGroupClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ obj_class->finalize = pidgin_action_group_finalize;
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GSimpleActionGroup *
+pidgin_action_group_new(void) {
+ return G_SIMPLE_ACTION_GROUP(g_object_new(PIDGIN_TYPE_ACTION_GROUP, NULL));
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginactiongroup.h Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,247 @@
+/*
+ * 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_ACTION_GROUP_H
+#define PIDGIN_ACTION_GROUP_H
+
+/**
+ * SECTION:pidginactiongroup
+ * @section_id: pidgin-action-group
+ * @short_description: An action group for Pidgin
+ * @title: Action Group
+ *
+ * A #GSimpleActionGroup containing most of our actions. A lot of this will
+ * need to be added to the #GtkApplication, but I didn't want to do that part
+ * quite yet, so I created this instead.
+ */
+
+#include <glib.h>
+
+#include <gio/gio.h>
+
+/**
+ * PIDGIN_ACTION_ABOUT:
+ *
+ * A constant that represents the about action that shows the about window.
+ */
+#define PIDGIN_ACTION_ABOUT ("about")
+
+/**
+ * PIDGIN_ACTION_ADD_BUDDY:
+ *
+ * A constant that represents the add-buddy action to add a buddy to the
+ * contact list.
+ */
+#define PIDGIN_ACTION_ADD_BUDDY ("add-buddy")
+
+/**
+ * PIDGIN_ACTION_ADD_CHAT:
+ *
+ * A constant that represents the add-chat action to add a chat to the
+ * contact list.
+ */
+#define PIDGIN_ACTION_ADD_CHAT ("add-chat")
+
+/**
+ * PIDGIN_ACTION_ADD_GROUP:
+ *
+ * A constant that represents the add-group action to add a group to the
+ * contact list.
+ */
+#define PIDGIN_ACTION_ADD_GROUP ("add-group")
+
+/**
+ * PIDGIN_ACTION_BUDDY_POUNCES:
+ *
+ * A constant that represents the pounces action.
+ */
+#define PIDGIN_ACTION_BUDDY_POUNCES ("buddy-pounces")
+
+/**
+ * PIDGIN_ACTION_CUSTOM_SMILEY:
+ *
+ * A constant that represents the custom-smiley action to toggle the visibility
+ * of the smiley manager.
+ */
+#define PIDGIN_ACTION_CUSTOM_SMILEY ("custom-smiley")
+
+/**
+ * PIDGIN_ACTION_DEBUG:
+ *
+ * A constant that represents the debug action to toggle the visibility of the
+ * debug window.
+ */
+#define PIDGIN_ACTION_DEBUG ("debug")
+
+/**
+ * PIDGIN_ACTION_FILE_TRANSFERS:
+ *
+ * A constant that represents the file-transfers action to toggle the
+ * visibility of the file transfers window.
+ */
+#define PIDGIN_ACTION_FILE_TRANSFERS ("file-transfers")
+
+/**
+ * PIDGIN_ACTION_GET_USER_INFO:
+ *
+ * A constant that represents the get-user-info action.
+ */
+#define PIDGIN_ACTION_GET_USER_INFO ("get-user-info")
+
+/**
+ * PIDGIN_ACTION_JOIN_CHAT:
+ *
+ * A constant that represents the join-chat action.
+ */
+#define PIDGIN_ACTION_JOIN_CHAT ("join-chat")
+
+/**
+ * PIDGIN_ACTION_MUTE_SOUNDS:
+ *
+ * A constant that represents the mute-sounds action.
+ */
+#define PIDGIN_ACTION_MUTE_SOUNDS ("mute-sounds")
+
+/**
+ * PIDGIN_ACTION_NEW_MESSAGE:
+ *
+ * A constant that represents the new-message action.
+ */
+#define PIDGIN_ACTION_NEW_MESSAGE ("new-message")
+
+/**
+ * PIDGIN_ACTION_ONLINE_HELP:
+ *
+ * A constant that represents the online-help action.
+ */
+#define PIDGIN_ACTION_ONLINE_HELP ("online-help")
+
+/**
+ * PIDGIN_ACTION_PREFERENCES:
+ *
+ * A constant that represents the preferences action.
+ */
+#define PIDGIN_ACTION_PREFERENCES ("preferences")
+
+/**
+ * PIDGIN_ACTION_PRIVACY:
+ *
+ * A constant that represents the privacy action.
+ */
+#define PIDGIN_ACTION_PRIVACY ("privacy")
+
+/**
+ * PIDGIN_ACTION_QUIT:
+ *
+ * A constant that represents the quit action.
+ */
+#define PIDGIN_ACTION_QUIT ("quit")
+
+/**
+ * PIDGIN_ACTION_ROOM_LIST:
+ *
+ * A constant that represents the room-list action.
+ */
+#define PIDGIN_ACTION_ROOM_LIST ("room-list")
+
+/**
+ * PIDGIN_ACTION_SET_MOOD:
+ *
+ * A constant that represents the set-mood action.
+ */
+#define PIDGIN_ACTION_SET_MOOD ("set-mood")
+
+/**
+ * PIDGIN_ACTION_SHOW_BUDDY_ICONS:
+ *
+ * A constant that represents the show-buddy-icons action.
+ */
+#define PIDGIN_ACTION_SHOW_BUDDY_ICONS ("show-buddy-icons")
+
+/**
+ * PIDGIN_ACTION_SHOW_EMPTY_GROUPS:
+ *
+ * A constant that represents the show-empty-groups action.
+ */
+#define PIDGIN_ACTION_SHOW_EMPTY_GROUPS ("show-empty-groups")
+
+/**
+ * PIDGIN_ACTION_SHOW_IDLE_TIMES:
+ *
+ * A constant that represents the show-idle-times action.
+ */
+#define PIDGIN_ACTION_SHOW_IDLE_TIMES ("show-idle-times")
+
+/**
+ * PIDGIN_ACTION_SHOW_OFFLINE_BUDDIES:
+ *
+ * A constant that represents the show-offline-buddies action.
+ */
+#define PIDGIN_ACTION_SHOW_OFFLINE_BUDDIES ("show-offline-buddies")
+
+/**
+ * PIDGIN_ACTION_SHOW_PROTOCOL_ICONS:
+ *
+ * A constant that represents the show-protocol-icons action.
+ */
+#define PIDGIN_ACTION_SHOW_PROTOCOL_ICONS ("show-protocol-icons")
+
+/**
+ * PIDGIN_ACTION_SORT_METHOD:
+ *
+ * A constant that represents the sort-method action to change the sorting
+ * method of the buddy list.
+ */
+#define PIDGIN_ACTION_SORT_METHOD ("sort-method")
+
+/**
+ * PIDGIN_ACTION_SYSTEM_LOG:
+ *
+ * A constant that represents the system-log action.
+ */
+#define PIDGIN_ACTION_SYSTEM_LOG ("system-log")
+
+/**
+ * PIDGIN_ACTION_VIEW_USER_LOG:
+ *
+ * A constant that represents the view-user-log action.
+ */
+#define PIDGIN_ACTION_VIEW_USER_LOG ("view-user-log")
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_ACTION_GROUP (pidgin_action_group_get_type())
+G_DECLARE_FINAL_TYPE(PidginActionGroup, pidgin_action_group, PIDGIN,
+ ACTION_GROUP, GSimpleActionGroup)
+
+/**
+ * pidgin_action_group_new:
+ *
+ * Creates a new #PidginActionGroup instance that contains all of the
+ * #GAction's in Pidgin.
+ *
+ * Returns: (transfer full): The new #PidginActionGroup instance.
+ */
+GSimpleActionGroup *pidgin_action_group_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_ACTION_GROUP_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginbuddylistmenu.c Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,99 @@
+/*
+ * 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 "pidginbuddylistmenu.h"
+
+struct _PidginBuddyListMenu {
+ GtkMenuBar parent;
+
+ GtkWidget *sort_buddies;
+
+ GtkWidget *accounts;
+ GtkWidget *accounts_menu;
+
+ GtkWidget *plugins;
+ GtkWidget *plugins_menu;
+
+ GtkWidget *menu_tray;
+};
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginBuddyListMenu, pidgin_buddy_list_menu, GTK_TYPE_MENU_BAR)
+
+static void
+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);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu->menu_tray);
+}
+
+static void
+pidgin_buddy_list_menu_class_init(PidginBuddyListMenuClass *klass) {
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+ gtk_widget_class_set_template_from_resource(
+ widget_class,
+ "/im/pidgin/Pidgin/BuddyList/menu.ui"
+ );
+
+ 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);
+ gtk_widget_class_bind_template_child(widget_class, PidginBuddyListMenu,
+ menu_tray);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GtkWidget *
+pidgin_buddy_list_menu_new(void) {
+ return GTK_WIDGET(g_object_new(PIDGIN_TYPE_BUDDY_LIST_MENU, NULL));
+}
+
+GtkWidget *
+pidgin_buddy_list_menu_get_sort_item(PidginBuddyListMenu *menu) {
+ g_return_val_if_fail(PIDGIN_IS_BUDDY_LIST_MENU(menu), NULL);
+
+ return menu->sort_buddies;
+}
+
+GtkWidget *
+pidgin_buddy_list_menu_get_menu_tray(PidginBuddyListMenu *menu) {
+ g_return_val_if_fail(PIDGIN_IS_BUDDY_LIST_MENU(menu), NULL);
+
+ return menu->menu_tray;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginbuddylistmenu.h Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,76 @@
+/*
+ * 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_BUDDY_LIST_MENU_H
+#define PIDGIN_BUDDY_LIST_MENU_H
+
+/**
+ * SECTION:pidginbuddylistmenu
+ * @section_id: pidgin-buddylist-menu
+ * @short_description: A widget to display the menubar in the buddy list window.
+ * @title: Buddylist Menu
+ *
+ * #PidginBuddyListMenu is a transitional widget as we slowly migrate the
+ * buddylist window to glade.
+ */
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_BUDDY_LIST_MENU (pidgin_buddy_list_menu_get_type())
+G_DECLARE_FINAL_TYPE(PidginBuddyListMenu, pidgin_buddy_list_menu, PIDGIN,
+ BUDDY_LIST_MENU, GtkMenuBar)
+
+/**
+ * pidgin_buddy_list_menu_new:
+ *
+ * Creates a new #PidginBuddyListMenu instance.
+ *
+ * Returns: (transfer full): The new #PidginBuddyListMenu instance.
+ */
+GtkWidget *pidgin_buddy_list_menu_new(void);
+
+/**
+ * pidgin_buddy_list_menu_get_sort_item:
+ * @menu: The #PidginBuddyList instance.
+ *
+ * Returns the sort menu item from the buddies menu.
+ *
+ * Returns: (transfer none): The sort menu item from the buddies menu.
+ */
+GtkWidget *pidgin_buddy_list_menu_get_sort_item(PidginBuddyListMenu *menu);
+
+/**
+ * pidgin_buddy_list_menu_get_menu_tray:
+ * @menu: The #PidginBuddyList instance.
+ *
+ * Gets the #PidginMenuTray instance from @menu.
+ *
+ * Returns: (transfer none): The #PidginMenuTray from @menu.
+ */
+GtkWidget *pidgin_buddy_list_menu_get_menu_tray(PidginBuddyListMenu *menu);
+
+G_END_DECLS
+
+#endif /* PIDGIN_BUDDY_LIST_MENU_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginmooddialog.c Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,326 @@
+/*
+ * 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 "pidgin/pidginmooddialog.h"
+
+#include "internal.h"
+
+/*< private >
+ * pidgin_mood_update_status:
+ * @account: The #PurpleAccount instance.
+ * @mood: The id of the new mood.
+ * @text: The new status text.
+ *
+ * Updates the current status for @account with the given @mood and @text.
+ */
+static void
+update_status_with_mood(PurpleAccount *account, const gchar *mood,
+ const gchar *text)
+{
+ if (mood && *mood) {
+ if (text) {
+ purple_account_set_status(account, "mood", TRUE,
+ PURPLE_MOOD_NAME, mood,
+ PURPLE_MOOD_COMMENT, text,
+ NULL);
+ } else {
+ purple_account_set_status(account, "mood", TRUE,
+ PURPLE_MOOD_NAME, mood,
+ NULL);
+ }
+ } else {
+ purple_account_set_status(account, "mood", FALSE, NULL);
+ }
+}
+
+/*< private
+ * pidgin_mood_edit_cb:
+ * @connection: The #PurpleConnection instance.
+ * @fields: The #PurpleRequestFields
+ *
+ * This a callback function for when the request dialog has been accepted.
+ */
+static void
+pidgin_mood_dialog_edit_cb(PurpleConnection *connection,
+ PurpleRequestFields *fields)
+{
+ PurpleRequestField *mood_field = NULL;
+ GList *l = NULL;
+ const gchar *mood = NULL;
+
+ mood_field = purple_request_fields_get_field(fields, "mood");
+ l = purple_request_field_list_get_selected(mood_field);
+
+ if(l == NULL) {
+ return;
+ }
+
+ mood = purple_request_field_list_get_data(mood_field, l->data);
+
+ if(connection != NULL) {
+ PurpleAccount *account = purple_connection_get_account(connection);
+ PurpleConnectionFlags flags;
+ const gchar *text = NULL;
+
+ flags = purple_connection_get_flags(connection);
+ if (flags & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES) {
+ PurpleRequestField *text_field = NULL;
+
+ text_field = purple_request_fields_get_field(fields, "text");
+ text = purple_request_field_string_get_value(text_field);
+ } else {
+ text = NULL;
+ }
+
+ update_status_with_mood(account, mood, text);
+ } else {
+ GList *accounts = purple_accounts_get_all_active();
+
+ for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
+ PurpleAccount *account = (PurpleAccount *) accounts->data;
+
+ connection = purple_account_get_connection(account);
+ if(PURPLE_IS_CONNECTION(connection)) {
+ PurpleConnectionFlags flags;
+
+ flags = purple_connection_get_flags(connection);
+ if(flags & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS) {
+ update_status_with_mood(account, mood, NULL);
+ }
+ }
+ }
+ }
+}
+
+/*< private >
+ * pidgin_mood_get_global_moods:
+ *
+ * Returns an array of all global moods.
+ *
+ * This function should be in libpurple, and it needs a lot of cleanup. It
+ * should probably also be returning a GList of moods as that's easier to deal
+ * with.
+ *
+ * Also, there is non-deterministic behavior here that the order of the
+ * returned moods depends purely on the order that the accounts where connected
+ * in. This is probably okay, but we should look at fixing that somehow.
+ *
+ * Returns: (transfer full): A list of all global moods.
+ */
+static PurpleMood *
+pidgin_mood_get_global_moods(void) {
+ GHashTable *global_moods = NULL;
+ GHashTable *mood_counts = NULL;
+ GList *accounts = NULL;
+ PurpleMood *result = NULL;
+ GList *out_moods = NULL;
+ int i = 0;
+ int num_accounts = 0;
+
+ global_moods = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+ mood_counts = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+
+ accounts = purple_accounts_get_all_active();
+ for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
+ PurpleAccount *account = (PurpleAccount *) accounts->data;
+ if (purple_account_is_connected(account)) {
+ PurpleConnection *gc = purple_account_get_connection(account);
+
+ if (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS) {
+ PurpleProtocol *protocol = purple_connection_get_protocol(gc);
+ PurpleMood *mood = NULL;
+
+ for (mood = purple_protocol_client_iface_get_moods(protocol, account) ;
+ mood->mood != NULL ; mood++) {
+ int mood_count =
+ GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood));
+
+ if (!g_hash_table_contains(global_moods, mood->mood)) {
+ g_hash_table_insert(global_moods, (gpointer)mood->mood, mood);
+ }
+ g_hash_table_insert(mood_counts, (gpointer)mood->mood,
+ GINT_TO_POINTER(mood_count + 1));
+ }
+
+ num_accounts++;
+ }
+ }
+ }
+
+ result = g_new0(PurpleMood, g_hash_table_size(global_moods) + 1);
+
+ out_moods = g_hash_table_get_values(global_moods);
+ while (out_moods) {
+ PurpleMood *mood = (PurpleMood *) out_moods->data;
+ int in_num_accounts =
+ GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood));
+
+ if (in_num_accounts == num_accounts) {
+ /* mood is present in all accounts supporting moods */
+ result[i].mood = mood->mood;
+ result[i].description = mood->description;
+ i++;
+ }
+ out_moods = g_list_delete_link(out_moods, out_moods);
+ }
+
+ g_hash_table_destroy(global_moods);
+ g_hash_table_destroy(mood_counts);
+
+ return result;
+}
+
+/*< private >
+ * pidgin_mood_get_global_status:
+ *
+ * Get the currently selected mood name for all mood support accounts. If no
+ * mood is set, or accounts have different moods then %NULL is returned.
+ *
+ * Returns: The currently selected mood name or %NULL if a mood is not set, or
+ * accounts are using different moods.
+ */
+static const gchar *
+pidgin_mood_get_global_status(void) {
+ GList *accounts = purple_accounts_get_all_active();
+ const gchar *found_mood = NULL;
+
+ for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
+ PurpleAccount *account = (PurpleAccount *) accounts->data;
+
+ if (purple_account_is_connected(account) &&
+ (purple_connection_get_flags(purple_account_get_connection(account)) &
+ PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) {
+ PurplePresence *presence = purple_account_get_presence(account);
+ PurpleStatus *status = purple_presence_get_status(presence, "mood");
+ const gchar *curr_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+
+ if (found_mood != NULL && !purple_strequal(curr_mood, found_mood)) {
+ /* found a different mood */
+ found_mood = NULL;
+ break;
+ } else {
+ found_mood = curr_mood;
+ }
+ }
+ }
+
+ return found_mood;
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+void
+pidgin_mood_dialog_show(PurpleAccount *account) {
+ const gchar *current_mood;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *g;
+ PurpleRequestField *f;
+ PurpleConnection *gc = NULL;
+ PurpleProtocol *protocol = NULL;
+ PurpleMood *mood;
+ PurpleMood *global_moods = NULL;
+
+ if (account) {
+ PurplePresence *presence = purple_account_get_presence(account);
+ PurpleStatus *status = purple_presence_get_status(presence, "mood");
+ gc = purple_account_get_connection(account);
+ g_return_if_fail(purple_connection_get_protocol(gc) != NULL);
+ protocol = purple_connection_get_protocol(gc);
+ current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+ } else {
+ current_mood = pidgin_mood_get_global_status();
+ }
+
+ fields = purple_request_fields_new();
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
+
+ purple_request_field_list_add_icon(f, _("None"), NULL, "");
+ if (current_mood == NULL)
+ purple_request_field_list_add_selected(f, _("None"));
+
+ /* TODO: rlaager wants this sorted. */
+ /* TODO: darkrain wants it sorted post-translation */
+ if (account && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods)) {
+ mood = purple_protocol_client_iface_get_moods(protocol, account);
+ } else {
+ mood = global_moods = pidgin_mood_get_global_moods();
+ }
+ for ( ; mood->mood != NULL ; mood++) {
+ char *path;
+
+ if (mood->description == NULL) {
+ continue;
+ }
+
+ path = pidgin_mood_get_icon_path(mood->mood);
+ purple_request_field_list_add_icon(f, _(mood->description),
+ path, (gpointer)mood->mood);
+ g_free(path);
+
+ if (current_mood && purple_strequal(current_mood, mood->mood))
+ purple_request_field_list_add_selected(f, _(mood->description));
+ }
+ purple_request_field_group_add_field(g, f);
+
+ purple_request_fields_add_group(fields, g);
+
+ /* if the connection allows setting a mood message */
+ if (gc && (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOOD_MESSAGES)) {
+ g = purple_request_field_group_new(NULL);
+ f = purple_request_field_string_new("text",
+ _("Message (optional)"), NULL, FALSE);
+ purple_request_field_group_add_field(g, f);
+ purple_request_fields_add_group(fields, g);
+ }
+
+ purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"),
+ NULL, fields,
+ _("OK"), G_CALLBACK(pidgin_mood_dialog_edit_cb),
+ _("Cancel"), NULL,
+ purple_request_cpar_from_connection(gc), gc);
+
+ g_free(global_moods);
+}
+
+gchar *
+pidgin_mood_get_icon_path(const gchar *mood) {
+ gchar *path;
+
+ if(purple_strequal(mood, "busy")) {
+ path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons",
+ "hicolor", "16x16", "status", "user-busy.png", NULL);
+ } else if(purple_strequal(mood, "hiptop")) {
+ path = g_build_filename(PURPLE_DATADIR, "pidgin", "icons",
+ "hicolor", "16x16", "emblems", "emblem-hiptop.png",
+ NULL);
+ } else {
+ gchar *filename = g_strdup_printf("%s.png", mood);
+ path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
+ "emotes", "small", filename, NULL);
+ g_free(filename);
+ }
+
+ return path;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginmooddialog.h Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,66 @@
+/*
+ * 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_MOOD_DIALOG_H
+#define PIDGIN_MOOD_DIALOG_H
+
+/**
+ * SECTION:pidginmooddialog
+ * @section_id: pidgin-mood-dialog
+ * @short_description: A dialog for selecting moods
+ * @title: Mood Dialog
+ *
+ * A simple dialog for selecting a global mood as well as a per account mood.
+ */
+
+#include <glib.h>
+
+#include <gtk/gtk.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+/**
+ * pidgin_mood_dialog_show:
+ * @account: (nullable): The #PurpleAccount whose mood to set, or %NULL for the
+ * global mood.
+ *
+ * Presents a dialog to select the mood for @account or the global mood if
+ * @account is %NULL.
+ */
+void pidgin_mood_dialog_show(PurpleAccount *account);
+
+/**
+ * pidgin_mood_get_icon_path:
+ * @mood: The id of the mood.
+ *
+ * Gets the path to the icon for @mood.
+ *
+ * Returns: (transfer full): The location of the icon for @mood.
+ */
+gchar *pidgin_mood_get_icon_path(const gchar *mood);
+
+G_END_DECLS
+
+#endif /* PIDGIN_MOOD_DIALOG_H */
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginpluginsmenu.c Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,307 @@
+/*
+ * 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 "pidginpluginsmenu.h"
+
+#include <gplugin.h>
+
+#include <purple.h>
+
+#include "internal.h"
+
+#include "pidgin/pidginpluginsdialog.h"
+
+struct _PidginPluginsMenu {
+ GtkMenu parent;
+
+ GtkWidget *separator;
+
+ GSimpleActionGroup *action_group;
+
+ GHashTable *plugin_items;
+};
+
+#define PIDGIN_PLUGINS_MENU_ACTION_PREFIX "plugins-menu"
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_plugins_menu_action_activated(GSimpleAction *simple, GVariant *parameter,
+ gpointer data)
+{
+ PurplePluginAction *action = (PurplePluginAction *)data;
+
+ if(action != NULL && action->callback != NULL) {
+ action->callback(action);
+ }
+}
+
+static void
+pidgin_plugins_menu_add_plugin_actions(PidginPluginsMenu *menu,
+ PurplePlugin *plugin)
+{
+ GPluginPluginInfo *info = NULL;
+ PurplePluginActionsCb actions_cb = NULL;
+ GList *actions = NULL;
+ GtkWidget *submenu = NULL, *item = NULL;
+ gint i = 0;
+
+ 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));
+
+ return;
+ }
+
+ actions = actions_cb(plugin);
+ if(actions == NULL) {
+ g_object_unref(G_OBJECT(info));
+
+ return;
+ }
+
+ submenu = gtk_menu_new();
+
+ for(i = 0; actions != NULL; i++) {
+ PurplePluginAction *action = (PurplePluginAction *)actions->data;
+ GSimpleAction *gaction = NULL;
+ GtkWidget *action_item = NULL;
+ gchar *action_base_name = NULL;
+ gchar *action_full_name = NULL;
+
+ if(action->label == NULL) {
+ continue;
+ }
+
+ action_base_name = g_strdup_printf("%s-%d",
+ gplugin_plugin_info_get_id(info),
+ i);
+ action_full_name = g_strdup_printf("%s.%s",
+ PIDGIN_PLUGINS_MENU_ACTION_PREFIX,
+ action_base_name);
+
+ /* 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),
+ action_full_name);
+ 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);
+
+ /* 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),
+ action,
+ (GClosureNotify)purple_plugin_action_free,
+ 0);
+
+ /* finally add the action to the action group and remove our ref */
+ g_action_map_add_action(G_ACTION_MAP(menu->action_group),
+ G_ACTION(gaction));
+ 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_widget_show(item);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+ g_hash_table_insert(menu->plugin_items,
+ g_object_ref(G_OBJECT(plugin)),
+ item);
+
+ g_object_unref(G_OBJECT(info));
+
+ /* make sure that our separator is visible */
+ gtk_widget_show(menu->separator);
+}
+
+static void
+pidgin_plugins_menu_remove_plugin_actions(PidginPluginsMenu *menu,
+ PurplePlugin *plugin)
+{
+ GPluginPluginInfo *info = NULL;
+ PurplePluginActionsCb actions_cb = NULL;
+ GList *actions = NULL;
+ gint i = 0;
+
+ /* 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)) {
+ return;
+ }
+
+ 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));
+
+ return;
+ }
+
+ actions = actions_cb(plugin);
+ if(actions == NULL) {
+ g_object_unref(G_OBJECT(info));
+
+ return;
+ }
+
+ /* now walk through the actions and remove them from the action group. */
+ for(i = 0; actions != NULL; i++) {
+ gchar *name = NULL;
+
+ name = g_strdup_printf("%s-%d", gplugin_plugin_info_get_id(info), i);
+
+ g_action_map_remove_action(G_ACTION_MAP(menu->action_group), name);
+ g_free(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);
+ }
+}
+
+/******************************************************************************
+ * Purple Signal Callbacks
+ *****************************************************************************/
+static void
+pidgin_plugins_menu_plugin_load_cb(PurplePlugin *plugin, gpointer data) {
+ pidgin_plugins_menu_add_plugin_actions(PIDGIN_PLUGINS_MENU(data), plugin);
+}
+
+static void
+pidgin_plugins_menu_plugin_unload_cb(PurplePlugin *plugin, gpointer data) {
+ pidgin_plugins_menu_remove_plugin_actions(PIDGIN_PLUGINS_MENU(data),
+ plugin);
+}
+
+/******************************************************************************
+ * Static Actions
+ *****************************************************************************/
+static void
+pidgin_plugins_menu_show_manager(GSimpleAction *action, GVariant *parameter,
+ gpointer data)
+{
+ GtkWidget *dialog = pidgin_plugins_dialog_new();
+
+ /* fixme? */
+#if 0
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
+#endif
+
+ gtk_widget_show_all(dialog);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(PidginPluginsMenu, pidgin_plugins_menu, GTK_TYPE_MENU)
+
+static void
+pidgin_plugins_menu_init(PidginPluginsMenu *menu) {
+ GActionEntry actions[] = {
+ {
+ .name = "manager",
+ .activate = pidgin_plugins_menu_show_manager,
+ }
+ };
+ gpointer handle;
+
+ /* 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();
+ g_action_map_add_action_entries(G_ACTION_MAP(menu->action_group), actions,
+ G_N_ELEMENTS(actions), NULL);
+ 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,
+ g_object_unref,
+ (GDestroyNotify)gtk_widget_destroy);
+
+ /* finally connect to the purple signals to stay up to date */
+ handle = purple_plugins_get_handle();
+ purple_signal_connect(handle, "plugin-load", menu,
+ PURPLE_CALLBACK(pidgin_plugins_menu_plugin_load_cb),
+ menu);
+ purple_signal_connect(handle, "plugin-unload", menu,
+ PURPLE_CALLBACK(pidgin_plugins_menu_plugin_unload_cb),
+ menu);
+};
+
+static void
+pidgin_plugins_menu_finalize(GObject *obj) {
+ purple_signals_disconnect_by_handle(obj);
+
+ G_OBJECT_CLASS(pidgin_plugins_menu_parent_class)->finalize(obj);
+}
+
+static void
+pidgin_plugins_menu_class_init(PidginPluginsMenuClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+ obj_class->finalize = pidgin_plugins_menu_finalize;
+
+ gtk_widget_class_set_template_from_resource(
+ widget_class,
+ "/im/pidgin/Pidgin/Plugins/menu.ui"
+ );
+
+ gtk_widget_class_bind_template_child(widget_class, PidginPluginsMenu,
+ separator);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GtkWidget *
+pidgin_plugins_menu_new(void) {
+ return GTK_WIDGET(g_object_new(PIDGIN_TYPE_PLUGINS_MENU, NULL));
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidginpluginsmenu.h Mon May 11 02:40:30 2020 +0000
@@ -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_PLUGINS_MENU_H
+#define PIDGIN_PLUGINS_MENU_H
+
+/**
+ * SECTION:pidginpluginsmenu
+ * @section_id: pidgin-plugins-menu
+ * @short_description: A menu for managing plugins and their actions
+ * @title: Plugins Menu
+ *
+ * #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.
+ */
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PIDGIN_TYPE_PLUGINS_MENU (pidgin_plugins_menu_get_type())
+G_DECLARE_FINAL_TYPE(PidginPluginsMenu, pidgin_plugins_menu, PIDGIN,
+ PLUGINS_MENU, GtkMenu)
+
+/**
+ * pidgin_plugins_menu_new:
+ *
+ * Creates a new #PidginPluginsMenu instance that keeps itself up to date.
+ *
+ * Returns: (transfer full): The new #PidginPluginsMenu instance.
+ */
+GtkWidget *pidgin_plugins_menu_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_PLUGINS_MENU_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Accounts/actionsmenu.ui Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,69 @@
+<?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="GtkMenuItem" id="set_mood">
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Set _Mood...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="pidgin_account_actions_menu_set_mood_cb" object="PidginAccountActionsMenu" swapped="no"/>
+ </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 May 11 02:40:30 2020 +0000
@@ -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>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/BuddyList/menu.ui Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,369 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <requires lib="pidgin" version="3.0"/>
+ <template class="PidginBuddyListMenu" parent="GtkMenuBar">
+ <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">_Buddies</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="new_message">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.new-message</property>
+ <property name="label" translatable="yes">New Instant _Message...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="m" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="join_chat">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.join-chat</property>
+ <property name="label" translatable="yes">Join a _Chat...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="c" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="get_user_info">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.get-user-info</property>
+ <property name="label" translatable="yes">Get User _Info...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="i" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="view_user_log">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.view-user-log</property>
+ <property name="label" translatable="yes">View User _Log...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="l" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </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">Sh_ow</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="GtkCheckMenuItem" id="show_offline_buddies">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.show-offline-buddies</property>
+ <property name="label" translatable="yes">_Offline Buddies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="show_empty_groups">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.show-empty-groups</property>
+ <property name="label" translatable="yes">_Empty Groups</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="show_buddy_icons">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.show-buddy-icons</property>
+ <property name="label" translatable="yes">Buddy _Icons</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="show_idle_times">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.show-idle-times</property>
+ <property name="label" translatable="yes">Idle _Times</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="show_protocol_icons">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.show-protocol-icons</property>
+ <property name="label" translatable="yes">_Protocol Icons</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="sort_buddies">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Sort Buddies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="add_buddy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.add-buddy</property>
+ <property name="label" translatable="yes">_Add Buddy...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="b" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="add_chat">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.add-chat</property>
+ <property name="label" translatable="yes">Add C_hat...</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="add_group">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.add-group</property>
+ <property name="label" translatable="yes">Add _Group...</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="quit">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.quit</property>
+ <property name="label" translatable="yes">_Quit</property>
+ <property name="use_underline">True</property>
+ <accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <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>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Tools</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="buddy_pounces">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.buddy-pounces</property>
+ <property name="label" translatable="yes">Buddy _Pounces</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="custom_smileys">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.custom-smiley</property>
+ <property name="label" translatable="yes">Custom Smile_ys</property>
+ <property name="use_underline">True</property>
+ <accelerator key="y" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="preferences">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.preferences</property>
+ <property name="label" translatable="yes">Pr_eferences</property>
+ <property name="use_underline">True</property>
+ <accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="privacy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.privacy</property>
+ <property name="label" translatable="yes">Pr_ivacy</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="set_mood">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.set-mood</property>
+ <property name="label" translatable="yes">Set _Mood</property>
+ <property name="use_underline">True</property>
+ <accelerator key="d" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="file_transfers">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.file-transfers</property>
+ <property name="label" translatable="yes">_File Transfers</property>
+ <property name="use_underline">True</property>
+ <accelerator key="t" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="room_list">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.room-list</property>
+ <property name="label" translatable="yes">R_oom List</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="system_log">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.system-log</property>
+ <property name="label" translatable="yes">System _Log</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="mute_sounds">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.mute-sounds</property>
+ <property name="label" translatable="yes">Mute _Sounds</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <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>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Help</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="online_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.online-help</property>
+ <property name="label" translatable="yes">Online _Help</property>
+ <property name="use_underline">True</property>
+ <accelerator key="F1" signal="activate"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="debug">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.debug</property>
+ <property name="label" translatable="yes">_Debug Window</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="about">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action_name">blist.about</property>
+ <property name="label" translatable="yes">_About</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="PidginAccountsMenu" id="accounts_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <object class="PidginMenuTray" id="menu_tray">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_justified">True</property>
+ </object>
+ <object class="PidginPluginsMenu" id="plugins_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+</interface>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Plugins/menu.ui Mon May 11 02:40:30 2020 +0000
@@ -0,0 +1,47 @@
+<?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.22"/>
+ <!-- interface-license-type gplv2 -->
+ <!-- interface-name Pidgin -->
+ <!-- interface-description Internet Messenger -->
+ <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+ <template class="PidginPluginsMenu" 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="action_name">plugins-menu.manager</property>
+ <property name="label" translatable="yes">Manage Plu_gins</property>
+ <property name="use_underline">True</property>
+ <accelerator key="u" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separator">
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </template>
+</interface>
--- a/pidgin/resources/pidgin.gresource.xml Sun May 10 13:43:52 2020 +0000
+++ b/pidgin/resources/pidgin.gresource.xml Mon May 11 02:40:30 2020 +0000
@@ -6,11 +6,15 @@
<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>
<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/prefs.ui</file>
<file compressed="true">Prefs/vv.ui</file>
<file compressed="true">Privacy/dialog.ui</file>