pidgin/pidgin

Create a manager for conversations.

2021-06-14, Gary Kramlich
2c312f084d84
Parents 4169f8090a0e
Children 1472618b493d
Create a manager for conversations.

I Skipped unit tests because need a connection and full protocol implementation to do it properly.

Testing Done:
Compiled and ran with an IRC account.

Reviewed at https://reviews.imfreedom.org/r/677/
--- a/doc/reference/libpurple/libpurple-docs.xml Sat Jun 12 19:10:06 2021 -0500
+++ b/doc/reference/libpurple/libpurple-docs.xml Mon Jun 14 04:07:45 2021 -0500
@@ -72,6 +72,7 @@
<xi:include href="xml/purplechatconversation.xml" />
<xi:include href="xml/purplechatuser.xml" />
<xi:include href="xml/purpleconversation.xml" />
+ <xi:include href="xml/purpleconversationmanager.xml" />
<xi:include href="xml/purpleconversationuiops.xml" />
<xi:include href="xml/purplecredentialmanager.xml" />
<xi:include href="xml/purplecredentialprovider.xml" />
--- a/finch/gntconv.c Sat Jun 12 19:10:06 2021 -0500
+++ b/finch/gntconv.c Mon Jun 14 04:07:45 2021 -0500
@@ -281,55 +281,88 @@
static void
account_signed_on_off(PurpleConnection *gc, gpointer null)
{
- GList *list = purple_conversations_get_ims();
- while (list) {
- PurpleConversation *conv = list->data;
- PurpleConversation *cc = find_im_with_contact(
- purple_conversation_get_account(conv), purple_conversation_get_name(conv));
- if (cc)
+ GList *list, *l;
+
+ list = purple_conversations_get_all();
+
+ for(l = list; l != NULL; l = l->next) {
+ PurpleConversation *conv = NULL;
+ PurpleConversation *cc = NULL;
+
+ if(!PURPLE_IS_IM_CONVERSATION(l->data)) {
+ continue;
+ }
+
+ conv = PURPLE_CONVERSATION(l->data);
+ cc = find_im_with_contact(purple_conversation_get_account(conv),
+ purple_conversation_get_name(conv));
+
+ if(cc) {
generate_send_to_menu(FINCH_CONV(cc));
- list = list->next;
+ }
+ }
+
+ /* If we disconnected we're done for now. */
+ if(!PURPLE_CONNECTION_IS_CONNECTED(gc)) {
+ g_list_free(list);
+
+ return;
}
- if (PURPLE_CONNECTION_IS_CONNECTED(gc)) {
- /* We just signed on. Let's see if there's any chat that we have open,
- * and hadn't left before the disconnect. */
- list = purple_conversations_get_chats();
- while (list) {
- PurpleConversation *conv = list->data;
- PurpleChat *chat;
- GHashTable *comps = NULL;
+ /* Since we just signed on. Let's see if there's any chat that we have open,
+ * and hadn't left before the disconnect.
+ */
+ for(l = list; l != NULL; l = l->next) {
+ PurpleConversation *conv = NULL;
+ PurpleChat *chat;
+ GHashTable *comps = NULL;
+
+ conv = PURPLE_CONVERSATION(l->data);
+
+ if(!PURPLE_IS_CHAT_CONVERSATION(conv)) {
+ continue;
+ }
- list = list->next;
- if (purple_conversation_get_account(conv) != purple_connection_get_account(gc) ||
- !g_object_get_data(G_OBJECT(conv), "want-to-rejoin"))
- continue;
+ if (purple_conversation_get_account(conv) != purple_connection_get_account(gc) ||
+ !g_object_get_data(G_OBJECT(conv), "want-to-rejoin"))
+ {
+ continue;
+ }
- chat = find_chat_for_conversation(conv);
- if (chat == NULL) {
- PurpleProtocol *protocol = purple_connection_get_protocol(gc);
- comps = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc,
- purple_conversation_get_name(conv));
- } else {
- comps = purple_chat_get_components(chat);
- }
- purple_serv_join_chat(gc, comps);
- if (chat == NULL && comps != NULL)
- g_hash_table_destroy(comps);
+ chat = find_chat_for_conversation(conv);
+ if (chat == NULL) {
+ PurpleProtocol *protocol = purple_connection_get_protocol(gc);
+ comps = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc,
+ purple_conversation_get_name(conv));
+ } else {
+ comps = purple_chat_get_components(chat);
+ }
+ purple_serv_join_chat(gc, comps);
+ if(chat == NULL && comps != NULL) {
+ g_hash_table_destroy(comps);
}
}
+
+ g_list_free(list);
}
static void
account_signing_off(PurpleConnection *gc)
{
- GList *list = purple_conversations_get_chats();
+ GList *list = purple_conversations_get_all();
PurpleAccount *account = purple_connection_get_account(gc);
/* We are about to sign off. See which chats we are currently in, and mark
* them for rejoin on reconnect. */
- while (list) {
- PurpleConversation *conv = list->data;
+ while(list != NULL) {
+ PurpleConversation *conv = NULL;
+
+ if(!PURPLE_IS_CHAT_CONVERSATION(list->data)) {
+ continue;
+ }
+
+ conv = PURPLE_CONVERSATION(list->data);
+
if (!purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)) &&
purple_conversation_get_account(conv) == account) {
g_object_set_data(G_OBJECT(conv), "want-to-rejoin", GINT_TO_POINTER(TRUE));
@@ -339,7 +372,8 @@
"the account reconnects."),
PURPLE_MESSAGE_NO_LOG);
}
- list = list->next;
+
+ list = g_list_delete_link(list, list);
}
}
@@ -1001,7 +1035,7 @@
/* Print the list of users in the room */
GString *string = g_string_new(NULL);
GList *iter;
- int count = g_list_length(users);
+ gint count = g_list_length(users);
g_string_printf(string,
ngettext("List of %d user:\n", "List of %d users:\n", count), count);
--- a/libpurple/conversations.c Sat Jun 12 19:10:06 2021 -0500
+++ b/libpurple/conversations.c Mon Jun 14 04:07:45 2021 -0500
@@ -22,219 +22,91 @@
#include "internal.h"
#include "purpleprivate.h"
-#include "conversations.h"
+#include "purpleconversationmanager.h"
-static GList *conversations = NULL;
-static GList *ims = NULL;
-static GList *chats = NULL;
static PurpleConversationUiOps *default_ops = NULL;
-/*
- * A hash table used for efficient lookups of conversations by name.
- * struct _purple_hconv => PurpleConversation*
- */
-static GHashTable *conversation_cache = NULL;
-
-struct _purple_hconv {
- gboolean im;
- char *name;
- PurpleAccount *account;
-};
-
-static guint
-_purple_conversations_hconv_hash(struct _purple_hconv *hc)
-{
- return g_str_hash(hc->name) ^ hc->im ^ g_direct_hash(hc->account);
-}
-
-static guint
-_purple_conversations_hconv_equal(struct _purple_hconv *hc1, struct _purple_hconv *hc2)
-{
- return (hc1->im == hc2->im &&
- hc1->account == hc2->account &&
- g_str_equal(hc1->name, hc2->name));
-}
-
-static void
-_purple_conversations_hconv_free_key(struct _purple_hconv *hc)
-{
- g_free(hc->name);
- g_free(hc);
-}
-
void
-purple_conversations_add(PurpleConversation *conv)
-{
- PurpleAccount *account;
- struct _purple_hconv *hc;
+purple_conversations_add(PurpleConversation *conv) {
+ PurpleConversationManager *manager = NULL;
g_return_if_fail(conv != NULL);
- if (g_list_find(conversations, conv) != NULL)
- return;
-
- conversations = g_list_prepend(conversations, conv);
-
- if (PURPLE_IS_IM_CONVERSATION(conv))
- ims = g_list_prepend(ims, conv);
- else
- chats = g_list_prepend(chats, conv);
+ manager = purple_conversation_manager_get_default();
- account = purple_conversation_get_account(conv);
-
- hc = g_new(struct _purple_hconv, 1);
- hc->name = g_strdup(purple_normalize(account,
- purple_conversation_get_name(conv)));
- hc->account = account;
- hc->im = PURPLE_IS_IM_CONVERSATION(conv);
-
- g_hash_table_insert(conversation_cache, hc, conv);
+ purple_conversation_manager_register(manager, conv);
}
void
-purple_conversations_remove(PurpleConversation *conv)
-{
- PurpleAccount *account;
- struct _purple_hconv hc;
+purple_conversations_remove(PurpleConversation *conv) {
+ PurpleConversationManager *manager = NULL;
g_return_if_fail(conv != NULL);
- conversations = g_list_remove(conversations, conv);
-
- if (PURPLE_IS_IM_CONVERSATION(conv))
- ims = g_list_remove(ims, conv);
- else
- chats = g_list_remove(chats, conv);
-
- account = purple_conversation_get_account(conv);
-
- hc.name = (gchar *)purple_normalize(account,
- purple_conversation_get_name(conv));
- hc.account = account;
- hc.im = PURPLE_IS_IM_CONVERSATION(conv);
-
- g_hash_table_remove(conversation_cache, &hc);
-}
+ manager = purple_conversation_manager_get_default();
-void
-_purple_conversations_update_cache(PurpleConversation *conv, const char *name,
- PurpleAccount *account)
-{
- PurpleAccount *old_account;
- struct _purple_hconv *hc;
-
- g_return_if_fail(conv != NULL);
- g_return_if_fail(account != NULL || name != NULL);
-
- old_account = purple_conversation_get_account(conv);
-
- hc = g_new(struct _purple_hconv, 1);
- hc->im = PURPLE_IS_IM_CONVERSATION(conv);
- hc->account = old_account;
- hc->name = (gchar *)purple_normalize(old_account,
- purple_conversation_get_name(conv));
-
- g_hash_table_remove(conversation_cache, hc);
-
- if (account)
- hc->account = account;
- if (name)
- hc->name = g_strdup(purple_normalize(hc->account, name));
-
- g_hash_table_insert(conversation_cache, hc, conv);
+ purple_conversation_manager_unregister(manager, conv);
}
GList *
-purple_conversations_get_all(void)
-{
- return conversations;
-}
+purple_conversations_get_all(void) {
+ PurpleConversationManager *manager = NULL;
-GList *
-purple_conversations_get_ims(void)
-{
- return ims;
-}
+ manager = purple_conversation_manager_get_default();
-GList *
-purple_conversations_get_chats(void)
-{
- return chats;
+ return purple_conversation_manager_get_all(manager);
}
PurpleConversation *
purple_conversations_find_with_account(const char *name,
- PurpleAccount *account)
+ PurpleAccount *account)
{
- PurpleConversation *c = NULL;
- struct _purple_hconv hc;
+ PurpleConversationManager *manager = NULL;
g_return_val_if_fail(name != NULL, NULL);
- hc.name = (gchar *)purple_normalize(account, name);
- hc.account = account;
+ manager = purple_conversation_manager_get_default();
- hc.im = TRUE;
- c = g_hash_table_lookup(conversation_cache, &hc);
- if (!c) {
- hc.im = FALSE;
- c = g_hash_table_lookup(conversation_cache, &hc);
- }
-
- return c;
+ return purple_conversation_manager_find(manager, account, name);
}
PurpleConversation *
-purple_conversations_find_im_with_account(const char *name,
- PurpleAccount *account)
+purple_conversations_find_im_with_account(const gchar *name,
+ PurpleAccount *account)
{
- PurpleConversation *im = NULL;
- struct _purple_hconv hc;
+ PurpleConversationManager *manager = NULL;
g_return_val_if_fail(name != NULL, NULL);
- hc.name = (gchar *)purple_normalize(account, name);
- hc.account = account;
- hc.im = TRUE;
+ manager = purple_conversation_manager_get_default();
- im = g_hash_table_lookup(conversation_cache, &hc);
-
- return im;
+ return purple_conversation_manager_find_im(manager, account, name);
}
PurpleConversation *
-purple_conversations_find_chat_with_account(const char *name,
- PurpleAccount *account)
+purple_conversations_find_chat_with_account(const gchar *name,
+ PurpleAccount *account)
{
- PurpleConversation *c = NULL;
- struct _purple_hconv hc;
+ PurpleConversationManager *manager = NULL;
g_return_val_if_fail(name != NULL, NULL);
- hc.name = (gchar *)purple_normalize(account, name);
- hc.account = account;
- hc.im = FALSE;
+ manager = purple_conversation_manager_get_default();
- c = g_hash_table_lookup(conversation_cache, &hc);
-
- return c;
+ return purple_conversation_manager_find_chat(manager, account, name);
}
PurpleConversation *
-purple_conversations_find_chat(const PurpleConnection *gc, int id)
-{
- GList *l;
- PurpleConversation *chat;
+purple_conversations_find_chat(PurpleConnection *gc, gint id) {
+ PurpleAccount *account = NULL;
+ PurpleConversationManager *manager = NULL;
- for (l = purple_conversations_get_chats(); l != NULL; l = l->next) {
- chat = (PurpleConversation *)l->data;
+ g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
- if (purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)) == id &&
- purple_conversation_get_connection(chat) == gc)
- return chat;
- }
+ account = purple_connection_get_account(gc);
+ manager = purple_conversation_manager_get_default();
- return NULL;
+ return purple_conversation_manager_find_chat_by_id(manager, account, id);
}
void
@@ -262,10 +134,6 @@
{
void *handle = purple_conversations_get_handle();
- conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
- (GEqualFunc)_purple_conversations_hconv_equal,
- (GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
-
/**********************************************************************
* Register preferences
**********************************************************************/
@@ -458,9 +326,5 @@
void
purple_conversations_uninit(void)
{
- while (conversations)
- g_object_unref(G_OBJECT(conversations->data));
-
- g_hash_table_destroy(conversation_cache);
purple_signals_unregister_by_instance(purple_conversations_get_handle());
}
--- a/libpurple/conversations.h Sat Jun 12 19:10:06 2021 -0500
+++ b/libpurple/conversations.h Mon Jun 14 04:07:45 2021 -0500
@@ -48,6 +48,7 @@
*
* Adds a conversation to the list of conversations.
*/
+G_DEPRECATED_FOR(purple_conversation_manager_register)
void purple_conversations_add(PurpleConversation *conv);
/**
@@ -56,6 +57,7 @@
*
* Removes a conversation from the list of conversations.
*/
+G_DEPRECATED_FOR(purple_conversation_manager_unregister)
void purple_conversations_remove(PurpleConversation *conv);
/**
@@ -67,27 +69,10 @@
*
* Returns: (element-type PurpleConversation) (transfer none): A GList of all conversations.
*/
+G_DEPRECATED_FOR(purple_conversation_manager_get_all)
GList *purple_conversations_get_all(void);
/**
- * purple_conversations_get_ims:
- *
- * Returns a list of all IMs.
- *
- * Returns: (element-type PurpleIMConversation) (transfer none): All IMs.
- */
-GList *purple_conversations_get_ims(void);
-
-/**
- * purple_conversations_get_chats:
- *
- * Returns a list of all chats.
- *
- * Returns: (element-type PurpleChatConversation) (transfer none): All chats.
- */
-GList *purple_conversations_get_chats(void);
-
-/**
* purple_conversations_find_with_account:
* @name: The name of the conversation.
* @account: The account associated with the conversation.
@@ -96,6 +81,7 @@
*
* Returns: (transfer none): The conversation if found, or %NULL otherwise.
*/
+G_DEPRECATED_FOR(purple_conversation_manager_find)
PurpleConversation *purple_conversations_find_with_account(const char *name,
PurpleAccount *account);
@@ -108,6 +94,7 @@
*
* Returns: (transfer none): The conversation if found, or %NULL otherwise.
*/
+G_DEPRECATED_FOR(purple_conversation_manager_find_im)
PurpleConversation *purple_conversations_find_im_with_account(const char *name, PurpleAccount *account);
/**
@@ -119,6 +106,7 @@
*
* Returns: (transfer none): The conversation if found, or %NULL otherwise.
*/
+G_DEPRECATED_FOR(purple_conversation_manager_find_chat)
PurpleConversation *purple_conversations_find_chat_with_account(const char *name, PurpleAccount *account);
/**
@@ -130,7 +118,8 @@
*
* Returns: (transfer none): The chat conversation.
*/
-PurpleConversation *purple_conversations_find_chat(const PurpleConnection *gc, int id);
+G_DEPRECATED_FOR(purple_conversation_manager_find_chat_by_id)
+PurpleConversation *purple_conversations_find_chat(PurpleConnection *gc, int id);
/**
* purple_conversations_set_ui_ops:
--- a/libpurple/core.c Sat Jun 12 19:10:06 2021 -0500
+++ b/libpurple/core.c Mon Jun 14 04:07:45 2021 -0500
@@ -172,6 +172,7 @@
purple_savedstatuses_init();
purple_notify_init();
purple_conversations_init();
+ purple_conversation_manager_startup();
purple_blist_init();
purple_log_init();
purple_network_init();
@@ -226,6 +227,7 @@
_purple_smiley_custom_uninit();
_purple_smiley_parser_uninit();
purple_idle_uninit();
+ purple_conversation_manager_shutdown();
purple_conversations_uninit();
purple_blist_uninit();
purple_notify_uninit();
--- a/libpurple/meson.build Sat Jun 12 19:10:06 2021 -0500
+++ b/libpurple/meson.build Mon Jun 14 04:07:45 2021 -0500
@@ -48,6 +48,7 @@
'purplechatconversation.c',
'purplechatuser.c',
'purpleconversation.c',
+ 'purpleconversationmanager.c',
'purpleconversationuiops.c',
'purplecredentialmanager.c',
'purplecredentialprovider.c',
@@ -139,6 +140,7 @@
'purplechatconversation.h',
'purplechatuser.h',
'purpleconversation.h',
+ 'purpleconversationmanager.h',
'purpleconversationuiops.h',
'purplecredentialmanager.h',
'purplecredentialprovider.h',
--- a/libpurple/protocols/null/nullprpl.c Sat Jun 12 19:10:06 2021 -0500
+++ b/libpurple/protocols/null/nullprpl.c Mon Jun 14 04:07:45 2021 -0500
@@ -1036,11 +1036,19 @@
/* add each chat room. the chat ids are cached in seen_ids so that each room
* is only returned once, even if multiple users are in it. */
- for (chats = purple_conversations_get_chats(); chats; chats = g_list_next(chats)) {
- PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(chats->data);
- PurpleRoomlistRoom *room;
- const char *name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
- int id = purple_chat_conversation_get_id(chat);
+ for (chats = purple_conversations_get_all(); chats; chats = g_list_next(chats)) {
+ PurpleChatConversation *chat = NULL;
+ PurpleRoomlistRoom *room = NULL;
+ const gchar *name = NULL;
+ gint id = 0;
+
+ if(!PURPLE_IS_CHAT_CONVERSATION(chats->data)) {
+ continue;
+ }
+
+ chat = PURPLE_CHAT_CONVERSATION(chats->data);
+ name = purple_conversation_get_name(PURPLE_CONVERSATION(chat));
+ id = purple_chat_conversation_get_id(chat);
/* have we already added this room? */
if (g_list_find_custom(seen_ids, name, (GCompareFunc)strcmp))
--- a/libpurple/purpleconversation.c Sat Jun 12 19:10:06 2021 -0500
+++ b/libpurple/purpleconversation.c Mon Jun 14 04:07:45 2021 -0500
@@ -483,8 +483,6 @@
priv = purple_conversation_get_instance_private(conv);
if(g_set_object(&priv->account, account)) {
- _purple_conversations_update_cache(conv, NULL, account);
-
g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_ACCOUNT]);
purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ACCOUNT);
@@ -585,8 +583,6 @@
g_free(priv->name);
priv->name = g_strdup(name);
- _purple_conversations_update_cache(conv, name, NULL);
-
g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_NAME]);
purple_conversation_autoset_title(conv);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleconversationmanager.c Mon Jun 14 04:07:45 2021 -0500
@@ -0,0 +1,238 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <purpleconversationmanager.h>
+
+#include <purplechatconversation.h>
+#include <purpleimconversation.h>
+#include <purpleprivate.h>
+
+struct _PurpleConversationManager {
+ GObject parent;
+
+ GHashTable *conversations;
+};
+
+static PurpleConversationManager *default_manager = NULL;
+
+G_DEFINE_TYPE(PurpleConversationManager, purple_conversation_manager,
+ G_TYPE_OBJECT)
+
+typedef gboolean (*PurpleConversationManagerCompareFunc)(PurpleConversation *conversation, gpointer userdata);
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static gboolean
+purple_conversation_is_im(PurpleConversation *conversation,
+ G_GNUC_UNUSED gpointer userdata)
+{
+ return PURPLE_IS_IM_CONVERSATION(conversation);
+}
+
+static gboolean
+purple_conversation_is_chat(PurpleConversation *conversation,
+ G_GNUC_UNUSED gpointer userdata)
+{
+ return PURPLE_IS_CHAT_CONVERSATION(conversation);
+}
+
+static gboolean
+purple_conversation_chat_has_id(PurpleConversation *conversation,
+ gpointer userdata)
+{
+ PurpleChatConversation *chat = NULL;
+ gint id = GPOINTER_TO_INT(userdata);
+
+
+ if(!PURPLE_IS_CHAT_CONVERSATION(conversation)) {
+ return FALSE;
+ }
+
+ chat = PURPLE_CHAT_CONVERSATION(conversation);
+
+ return (purple_chat_conversation_get_id(chat) == id);
+}
+
+static PurpleConversation *
+purple_conversation_manager_find_internal(PurpleConversationManager *manager,
+ PurpleAccount *account,
+ const gchar *name,
+ PurpleConversationManagerCompareFunc func,
+ gpointer userdata)
+{
+ GHashTableIter iter;
+ gpointer key;
+
+ g_hash_table_iter_init(&iter, manager->conversations);
+ while(g_hash_table_iter_next(&iter, &key, NULL)) {
+ PurpleConversation *conversation = PURPLE_CONVERSATION(key);
+
+ if(purple_strequal(purple_conversation_get_name(conversation), name)) {
+ if(purple_conversation_get_account(conversation) == account) {
+ if(func != NULL && !func(conversation, userdata)) {
+ continue;
+ }
+
+ return conversation;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_conversation_manager_init(PurpleConversationManager *manager) {
+ manager->conversations = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ g_object_unref, NULL);
+}
+
+static void
+purple_conversation_manager_finalize(GObject *obj) {
+ PurpleConversationManager *manager = PURPLE_CONVERSATION_MANAGER(obj);
+
+ g_hash_table_destroy(manager->conversations);
+
+ G_OBJECT_CLASS(purple_conversation_manager_parent_class)->finalize(obj);
+}
+
+static void
+purple_conversation_manager_class_init(PurpleConversationManagerClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ obj_class->finalize = purple_conversation_manager_finalize;
+}
+
+/******************************************************************************
+ * Private API
+ *****************************************************************************/
+void
+purple_conversation_manager_startup(void) {
+ if(default_manager == NULL) {
+ default_manager = g_object_new(PURPLE_TYPE_CONVERSATION_MANAGER, NULL);
+ }
+}
+
+void
+purple_conversation_manager_shutdown(void) {
+ g_clear_object(&default_manager);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleConversationManager *
+purple_conversation_manager_get_default(void) {
+ return default_manager;
+}
+
+gboolean
+purple_conversation_manager_register(PurpleConversationManager *manager,
+ PurpleConversation *conversation)
+{
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE);
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+ return g_hash_table_insert(manager->conversations,
+ g_object_ref(conversation), NULL);
+}
+
+gboolean
+purple_conversation_manager_unregister(PurpleConversationManager *manager,
+ PurpleConversation *conversation)
+{
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE);
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+ return g_hash_table_remove(manager->conversations, conversation);
+}
+
+gboolean
+purple_conversation_manager_is_registered(PurpleConversationManager *manager,
+ PurpleConversation *conversation)
+{
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), FALSE);
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+ return g_hash_table_lookup_extended(manager->conversations, conversation,
+ NULL, NULL);
+}
+
+GList *
+purple_conversation_manager_get_all(PurpleConversationManager *manager) {
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+
+ return g_hash_table_get_keys(manager->conversations);
+}
+
+
+PurpleConversation *
+purple_conversation_manager_find(PurpleConversationManager *manager,
+ PurpleAccount *account, const gchar *name)
+{
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return purple_conversation_manager_find_internal(manager, account, name,
+ NULL, NULL);
+}
+
+PurpleConversation *
+purple_conversation_manager_find_im(PurpleConversationManager *manager,
+ PurpleAccount *account, const gchar *name)
+{
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return purple_conversation_manager_find_internal(manager, account, name,
+ purple_conversation_is_im,
+ NULL);
+}
+
+PurpleConversation *
+purple_conversation_manager_find_chat(PurpleConversationManager *manager,
+ PurpleAccount *account,
+ const gchar *name)
+{
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return purple_conversation_manager_find_internal(manager, account, name,
+ purple_conversation_is_chat,
+ NULL);
+}
+
+PurpleConversation *
+purple_conversation_manager_find_chat_by_id(PurpleConversationManager *manager,
+ PurpleAccount *account, gint id)
+{
+ g_return_val_if_fail(PURPLE_IS_CONVERSATION_MANAGER(manager), NULL);
+ g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+ return purple_conversation_manager_find_internal(manager, account, NULL,
+ purple_conversation_chat_has_id,
+ GINT_TO_POINTER(id));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpleconversationmanager.h Mon Jun 14 04:07:45 2021 -0500
@@ -0,0 +1,177 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <purple.h> may be included directly"
+#endif
+
+#ifndef PURPLE_CONVERSATION_MANAGER_H
+#define PURPLE_CONVERSATION_MANAGER_H
+
+#include <glib.h>
+
+#include <purpleconversation.h>
+
+/**
+ * SECTION:purpleconversationmanager
+ * @section_id: libpurple-purpleconversationmanager
+ * @title: Management of conversations
+ *
+ * #PurpleConversationManager keeps track of all #PurpleConversation's inside of
+ * libpurple and allows searching of them.
+ */
+
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_CONVERSATION_MANAGER (purple_conversation_manager_get_type())
+G_DECLARE_FINAL_TYPE(PurpleConversationManager, purple_conversation_manager,
+ PURPLE, CONVERSATION_MANAGER, GObject)
+
+/**
+ * purple_conversation_manager_get_default:
+ *
+ * Gets the default instance of #PurpleConversationManager. This instance
+ * can be used for any of the API including connecting to signals.
+ *
+ * Returns: (transfer none): The default #PurpleConversationManager instance.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversationManager *purple_conversation_manager_get_default(void);
+
+/**
+ * purple_conversation_manager_register:
+ * @manager: The #PurpleConversationManager instance.
+ * @conversation: The #PurpleConversation to register.
+ *
+ * Registers @conversation with @manager.
+ *
+ * Returns: %TRUE if @conversation was not yet registered.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_manager_register(PurpleConversationManager *manager, PurpleConversation *conversation);
+
+/**
+ * purple_conversation_manager_unregister:
+ * @manager: The #PurpleConversationManager instance.
+ * @conversation: The #PurpleConversation to unregister.
+ *
+ * Unregisters @conversation with @manager.
+ *
+ * Returns: %TRUE if @conversation was found and unregistered.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_manager_unregister(PurpleConversationManager *manager, PurpleConversation *conversation);
+
+/**
+ * purple_conversation_manager_is_registered:
+ * @manager: The #PurpleConversationManager instance.
+ * @conversation: The #PurpleConversation instance.
+ *
+ * Checks if @conversation is registered with @manager.
+ *
+ * Returns: %TRUE if @conversation is registered with @manager, %FALSE
+ * otherwise.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_conversation_manager_is_registered(PurpleConversationManager *manager, PurpleConversation *conversation);
+
+/**
+ * purple_conversation_manager_get_all:
+ * @manager: The #PurpleConversationManager instance.
+ *
+ * Gets a list of all conversations that are registered with @manager.
+ *
+ * Returns: (transfer container) (element-type PurpleConversation): A list of
+ * all of the registered conversations.
+ *
+ * Since: 3.0.0
+ */
+GList *purple_conversation_manager_get_all(PurpleConversationManager *manager);
+
+/**
+ * purple_conversation_manager_find:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @name: The name of the conversation.
+ *
+ * Looks for a registered conversation belonging to @account and named @named.
+ * This function will return the first one matching the given criteria. If you
+ * specifically need an im or chat see purple_conversation_manager_find_im()
+ * or purple_conversation_manager_find_chat().
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find(PurpleConversationManager *manager, PurpleAccount *account, const gchar *name);
+
+/**
+ * purple_conversation_manager_find_im:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @name: The name of the conversation.
+ *
+ * Looks for a registered im conversation belonging to @account and named
+ * @name.
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find_im(PurpleConversationManager *manager, PurpleAccount *account, const gchar *name);
+
+/**
+ * purple_conversation_manager_find_chat:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @name: The name of the conversation.
+ *
+ * Looks for a registered chat conversation belonging to @account and named
+ * @name.
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find_chat(PurpleConversationManager *manager, PurpleAccount *account, const gchar *name);
+
+/**
+ * purple_conversation_manager_find_chat_by_id:
+ * @manager: The #PurpleConversationManager instance.
+ * @account: The #PurpleAccount instance whose conversation to find.
+ * @id: The id of the conversation.
+ *
+ * Looks for a registered chat conversation belonging to @account with an id of
+ * @id.
+ *
+ * This is typically only called by protocols.
+ *
+ * Returns: (transfer none): The #PurpleConversation if found, otherwise %NULL.
+ *
+ * Since: 3.0.0
+ */
+PurpleConversation *purple_conversation_manager_find_chat_by_id(PurpleConversationManager *manager, PurpleAccount *account, gint id);
+
+G_END_DECLS
+
+#endif /* PURPLE_CONVERSATION_MANAGER_H */
--- a/libpurple/purpleprivate.h Sat Jun 12 19:10:06 2021 -0500
+++ b/libpurple/purpleprivate.h Mon Jun 14 04:07:45 2021 -0500
@@ -153,22 +153,6 @@
PurpleChatConversation *chat);
/**
- * _purple_conversations_update_cache:
- * @conv: The conversation.
- * @name: The new name. If no change, use %NULL.
- * @account: The new account. If no change, use %NULL.
- *
- * Updates the conversation cache to use a new conversation name and/or
- * account. This function only updates the conversation cache. It is the
- * caller's responsibility to actually update the conversation.
- *
- * Note: This function should only be called by purple_conversation_set_name()
- * and purple_conversation_set_account() in conversation.c.
- */
-void _purple_conversations_update_cache(PurpleConversation *conv,
- const char *name, PurpleAccount *account);
-
-/**
* _purple_statuses_get_primitive_scores:
*
* Note: This function should only be called by
@@ -197,6 +181,24 @@
_purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *msg);
/**
+ * purple_conversation_manager_startup:
+ *
+ * Starts up the conversation manager by creating the default instance.
+ *
+ * Since: 3.0.0
+ */
+void purple_conversation_manager_startup(void);
+
+/**
+ * purple_conversation_manager_shutdown:
+ *
+ * Shuts down the conversation manager by destroying the default instance.
+ *
+ * Since: 3.0.0
+ */
+void purple_conversation_manager_shutdown(void);
+
+/**
* purple_credential_manager_startup:
*
* Starts up the credential manager by creating the default instance.
--- a/pidgin/gtkblist.c Sat Jun 12 19:10:06 2021 -0500
+++ b/pidgin/gtkblist.c Mon Jun 14 04:07:45 2021 -0500
@@ -54,7 +54,6 @@
#include "pidgin/pidginstylecontext.h"
#include "pidgin/pidgintooltip.h"
#include "pidgin/pidginwindow.h"
-#include "pidginmenutray.h"
#include "pidginstock.h"
#include <gdk/gdkkeysyms.h>
@@ -4014,70 +4013,10 @@
}
static void
-unseen_conv_menu(GdkEvent *event)
-{
- static GtkWidget *menu = NULL;
- GList *convs = NULL;
- GList *chats, *ims;
-
- if (menu) {
- gtk_widget_destroy(menu);
- menu = NULL;
- }
-
- ims = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 0);
-
- chats = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 0);
-
- if(ims && chats)
- convs = g_list_concat(ims, chats);
- else if(ims && !chats)
- convs = ims;
- else if(!ims && chats)
- convs = chats;
-
- if (!convs)
- /* no conversations added, don't show the menu */
- return;
-
- menu = gtk_menu_new();
-
- pidgin_conversations_fill_menu(menu, convs);
- g_list_free(convs);
- gtk_widget_show_all(menu);
- gtk_menu_popup_at_pointer(GTK_MENU(menu), event);
-}
-
-static gboolean
-menutray_press_cb(GtkWidget *widget, GdkEventButton *event)
-{
- GList *convs;
-
- if (event->button == GDK_BUTTON_PRIMARY) {
- convs = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 1);
- if(!convs)
- convs = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 1);
-
- if (convs) {
- pidgin_conv_present_conversation((PurpleConversation*)convs->data);
- g_list_free(convs);
- }
-
- } else if (gdk_event_triggers_context_menu((GdkEvent *)event)) {
- unseen_conv_menu((GdkEvent *)event);
- }
-
- return TRUE;
-}
-
-static void
conversation_updated_cb(PurpleConversation *conv, PurpleConversationUpdateType type,
PidginBuddyList *gtkblist)
{
PurpleAccount *account = purple_conversation_get_account(conv);
- GList *convs = NULL;
- GList *ims, *chats;
- GList *l = NULL;
if (type != PURPLE_CONVERSATION_UPDATE_UNSEEN)
return;
@@ -4087,60 +4026,6 @@
if(buddy != NULL)
pidgin_blist_update_buddy(NULL, PURPLE_BLIST_NODE(buddy), TRUE);
}
-
- if (gtkblist->menutrayicon) {
- gtk_widget_destroy(gtkblist->menutrayicon);
- gtkblist->menutrayicon = NULL;
- }
-
- ims = pidgin_conversations_get_unseen_ims(PIDGIN_UNSEEN_TEXT, FALSE, 0);
-
- chats = pidgin_conversations_get_unseen_chats(PIDGIN_UNSEEN_NICK, FALSE, 0);
-
- if(ims && chats)
- convs = g_list_concat(ims, chats);
- else if(ims && !chats)
- convs = ims;
- else if(!ims && chats)
- convs = chats;
-
- if (convs) {
- GtkWidget *img = NULL;
- GString *tooltip_text = NULL;
-
- tooltip_text = g_string_new("");
- l = convs;
- while (l != NULL) {
- int count = 0;
- PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data);
-
- if(gtkconv)
- count = gtkconv->unseen_count;
- else if(g_object_get_data(G_OBJECT(l->data), "unseen-count"))
- count = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(l->data), "unseen-count"));
-
- g_string_append_printf(tooltip_text,
- ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count),
- count, purple_conversation_get_title(l->data));
- l = l->next;
- }
- if(tooltip_text->len > 0) {
- /* get rid of the last newline */
- g_string_truncate(tooltip_text, tooltip_text->len -1);
- img = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_PENDING,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
-
- gtkblist->menutrayicon = gtk_event_box_new();
- gtk_container_add(GTK_CONTAINER(gtkblist->menutrayicon), img);
- gtk_widget_show(img);
- gtk_widget_show(gtkblist->menutrayicon);
- g_signal_connect(G_OBJECT(gtkblist->menutrayicon), "button-press-event", G_CALLBACK(menutray_press_cb), NULL);
-
- pidgin_menu_tray_append(PIDGIN_MENU_TRAY(gtkblist->menutray), gtkblist->menutrayicon, tooltip_text->str);
- }
- g_string_free(tooltip_text, TRUE);
- g_list_free(convs);
- }
}
static void
@@ -5160,9 +5045,6 @@
g_signal_connect(G_OBJECT(gtkblist->window), "key_press_event", G_CALLBACK(gtk_blist_window_key_press_cb), gtkblist);
gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK);
- /******************************* Menu bar *************************************/
- gtkblist->menutray = pidgin_contact_list_get_menu_tray(PIDGIN_CONTACT_LIST(gtkblist->window));
-
/****************************** Notebook *************************************/
gtkblist->notebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gtkblist->notebook), FALSE);
--- a/pidgin/gtkblist.h Sat Jun 12 19:10:06 2021 -0500
+++ b/pidgin/gtkblist.h Mon Jun 14 04:07:45 2021 -0500
@@ -55,10 +55,8 @@
* into. Your plugin might want to pack something in it
* itself. Go, plugins!
* @treeview: It's a treeview... d'uh.
- * @treemodel: This is the treemodel.
+ * @treemodel: This is the treemodel.
* @text_column: Column
- * @menutray: The menu tray widget.
- * @menutrayicon: The menu tray icon.
* @refresh_timer: The timer for refreshing every 30 seconds
* @drag_timeout: The timeout for expanding contacts on drags
* @drag_rect: This is the bounding rectangle of the cell we're
@@ -97,8 +95,6 @@
GtkCellRenderer *text_rend;
GtkWidget *menu;
- GtkWidget *menutray;
- GtkWidget *menutrayicon;
guint refresh_timer;
--- a/pidgin/gtkconv.c Sat Jun 12 19:10:06 2021 -0500
+++ b/pidgin/gtkconv.c Mon Jun 14 04:07:45 2021 -0500
@@ -1783,24 +1783,6 @@
min_state, hidden_only, max_count);
}
-GList *
-pidgin_conversations_get_unseen_ims(PidginUnseenState min_state,
- gboolean hidden_only,
- guint max_count)
-{
- return pidgin_conversations_get_unseen(purple_conversations_get_ims(),
- min_state, hidden_only, max_count);
-}
-
-GList *
-pidgin_conversations_get_unseen_chats(PidginUnseenState min_state,
- gboolean hidden_only,
- guint max_count)
-{
- return pidgin_conversations_get_unseen(purple_conversations_get_chats(),
- min_state, hidden_only, max_count);
-}
-
static void
unseen_conv_menu_cb(GtkMenuItem *item, PurpleConversation *conv)
{
@@ -4537,13 +4519,19 @@
static void
account_signing_off(PurpleConnection *gc)
{
- GList *list = purple_conversations_get_chats();
+ GList *list = purple_conversations_get_all();
PurpleAccount *account = purple_connection_get_account(gc);
/* We are about to sign off. See which chats we are currently in, and mark
* them for rejoin on reconnect. */
- while (list) {
- PurpleConversation *conv = list->data;
+ while(list != NULL) {
+ PurpleConversation *conv = NULL;
+
+ if(!PURPLE_IS_CHAT_CONVERSATION(list->data)) {
+ continue;
+ }
+
+ conv = PURPLE_CONVERSATION(list->data);
if (!purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)) &&
purple_conversation_get_account(conv) == account) {
g_object_set_data(G_OBJECT(conv), "want-to-rejoin", GINT_TO_POINTER(TRUE));
@@ -4553,7 +4541,8 @@
"rejoin the chat when the account reconnects."),
PURPLE_MESSAGE_NO_LOG);
}
- list = list->next;
+
+ list = g_list_delete_link(list, list);
}
}
@@ -4784,12 +4773,16 @@
if (PURPLE_IS_IM_CONVERSATION(conv)) {
GList *convs;
list = g_list_copy(list);
- for (convs = purple_conversations_get_ims(); convs; convs = convs->next)
+ for (convs = purple_conversations_get_all(); convs; convs = convs->next) {
+ if(!PURPLE_IS_IM_CONVERSATION(convs->data)) {
+ continue;
+ }
if (convs->data != conv &&
pidgin_conv_find_gtkconv(convs->data) == gtkconv) {
pidgin_conv_attach(convs->data);
list = g_list_concat(list, g_list_copy(purple_conversation_get_message_history(convs->data)));
}
+ }
list = g_list_sort(list, (GCompareFunc)message_compare);
gtkconv->attach_current = list;
list = g_list_last(list);
--- a/pidgin/gtkconv.h Sat Jun 12 19:10:06 2021 -0500
+++ b/pidgin/gtkconv.h Mon Jun 14 04:07:45 2021 -0500
@@ -188,48 +188,6 @@
guint max_count);
/**
- * pidgin_conversations_get_unseen_ims:
- * @min_state: The minimum unseen state.
- * @hidden_only: If %TRUE, only consider hidden conversations.
- * @max_count: Maximum number of conversations to return, or 0 for
- * no maximum.
- *
- * Returns a list of IM conversations which have an unseen state greater
- * than or equal to the specified minimum state. Using the hidden_only
- * parameter, this search can be limited to hidden IM conversations. The
- * max_count parameter will limit the total number of IM converations
- * returned if greater than zero. The returned list should be freed by the
- * caller.
- *
- * Returns: (transfer container) (element-type PurpleConversation): List of PurpleIMConversation matching criteria, or %NULL.
- */
-GList *
-pidgin_conversations_get_unseen_ims(PidginUnseenState min_state,
- gboolean hidden_only,
- guint max_count);
-
-/**
- * pidgin_conversations_get_unseen_chats:
- * @min_state: The minimum unseen state.
- * @hidden_only: If %TRUE, only consider hidden conversations.
- * @max_count: Maximum number of conversations to return, or 0 for
- * no maximum.
- *
- * Returns a list of chat conversations which have an unseen state greater
- * than or equal to the specified minimum state. Using the hidden_only
- * parameter, this search can be limited to hidden chat conversations. The
- * max_count parameter will limit the total number of chat converations
- * returned if greater than zero. The returned list should be freed by the
- * caller.
- *
- * Returns: (transfer container) (element-type PurpleConversation): List of PurpleChatConversation matching criteria, or %NULL.
- */
-GList *
-pidgin_conversations_get_unseen_chats(PidginUnseenState min_state,
- gboolean hidden_only,
- guint max_count);
-
-/**
* pidgin_conversations_fill_menu:
* @menu: Menu widget to add items to.
* @convs: (element-type PurpleConversation): List of PurpleConversation to add to menu.
--- a/po/POTFILES.in Sat Jun 12 19:10:06 2021 -0500
+++ b/po/POTFILES.in Mon Jun 14 04:07:45 2021 -0500
@@ -243,6 +243,7 @@
libpurple/purplechatconversation.c
libpurple/purplechatuser.c
libpurple/purpleconversation.c
+libpurple/purpleconversationmanager.c
libpurple/purplecredentialmanager.c
libpurple/purplecredentialprovider.c
libpurple/purple-gio.c