libpurple/purpleconversation.c

changeset 42114
d3fe2b899c89
parent 42070
5d5363c55331
child 42258
a4053066d6ca
--- a/libpurple/purpleconversation.c	Fri Mar 03 01:23:31 2023 -0600
+++ b/libpurple/purpleconversation.c	Fri Mar 03 01:24:36 2023 -0600
@@ -1,53 +1,45 @@
 /*
- * purple
- *
- * Purple 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.
+ * Purple - Internet Messaging Library
+ * 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 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 program is distributed in the hope that it will be useful,
+ * 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 General Public License for more details.
+ * 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 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
+ * 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 <glib/gi18n-lib.h>
 
-#include "buddylist.h"
-#include "cmds.h"
+#include "purpleconversation.h"
+
 #include "conversations.h"
 #include "debug.h"
-#include "notify.h"
-#include "prefs.h"
-#include "purpleconversation.h"
 #include "purpleconversationmanager.h"
+#include "purpleconversationmember.h"
 #include "purpleenums.h"
 #include "purplehistorymanager.h"
 #include "purplemarkup.h"
 #include "purpleprivate.h"
-#include "purpleprotocol.h"
-#include "purpleprotocolclient.h"
-#include "request.h"
-#include "signals.h"
 
 typedef struct {
-	PurpleAccount *account;           /* The user using this conversation. */
+	PurpleAccount *account;
+
+	char *name;
+	char *title;
 
-	char *name;                       /* The name of the conversation.     */
-	char *title;                      /* The window title.                 */
+	PurpleConversationUiOps *ui_ops;
 
-	PurpleConversationUiOps *ui_ops;  /* UI-specific operations.           */
+	PurpleConnectionFlags features;
 
-	PurpleConnectionFlags features;   /* The supported features            */
+	GListStore *members;
 } PurpleConversationPrivate;
 
 enum {
@@ -56,17 +48,37 @@
 	PROP_NAME,
 	PROP_TITLE,
 	PROP_FEATURES,
+	PROP_MEMBERS,
 	N_PROPERTIES
 };
-
 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
 
-G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PurpleConversation, purple_conversation,
-                                    G_TYPE_OBJECT);
+enum {
+	SIG_MEMBER_ADDED,
+	SIG_MEMBER_REMOVED,
+	N_SIGNALS,
+};
+static guint signals[N_SIGNALS] = {0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE(PurpleConversation, purple_conversation,
+                           G_TYPE_OBJECT);
 
 /**************************************************************************
  * Helpers
  **************************************************************************/
+static gboolean
+purple_conversation_check_member_equal(gconstpointer a, gconstpointer b) {
+	PurpleConversationMember *member_a = (PurpleConversationMember *)a;
+	PurpleConversationMember *member_b = (PurpleConversationMember *)b;
+	PurpleContactInfo *info_a = NULL;
+	PurpleContactInfo *info_b = NULL;
+
+	info_a = purple_conversation_member_get_contact_info(member_a);
+	info_b = purple_conversation_member_get_contact_info(member_b);
+
+	return (purple_contact_info_compare(info_a, info_b) == 0);
+}
+
 static void
 common_send(PurpleConversation *conv, const gchar *message,
             PurpleMessageFlags msgflags)
@@ -220,7 +232,7 @@
 
 	switch (param_id) {
 		case PROP_ACCOUNT:
-			priv->account = g_value_get_object(value);
+			purple_conversation_set_account(conv, g_value_get_object(value));
 			break;
 		case PROP_NAME:
 			g_free(priv->name);
@@ -258,6 +270,9 @@
 		case PROP_FEATURES:
 			g_value_set_flags(value, purple_conversation_get_features(conv));
 			break;
+		case PROP_MEMBERS:
+			g_value_set_object(value, purple_conversation_get_members(conv));
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
 			break;
@@ -265,7 +280,12 @@
 }
 
 static void
-purple_conversation_init(G_GNUC_UNUSED PurpleConversation *conv) {
+purple_conversation_init(PurpleConversation *conv) {
+	PurpleConversationPrivate *priv = NULL;
+
+	priv = purple_conversation_get_instance_private(conv);
+
+	priv->members = g_list_store_new(PURPLE_TYPE_CONVERSATION_MEMBER);
 }
 
 static void
@@ -340,6 +360,7 @@
 
 	g_clear_pointer(&priv->name, g_free);
 	g_clear_pointer(&priv->title, g_free);
+	g_clear_object(&priv->members);
 
 	G_OBJECT_CLASS(purple_conversation_parent_class)->finalize(object);
 }
@@ -348,11 +369,9 @@
 purple_conversation_class_init(PurpleConversationClass *klass) {
 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
 
+	obj_class->constructed = purple_conversation_constructed;
 	obj_class->dispose = purple_conversation_dispose;
 	obj_class->finalize = purple_conversation_finalize;
-	obj_class->constructed = purple_conversation_constructed;
-
-	/* Setup properties */
 	obj_class->get_property = purple_conversation_get_property;
 	obj_class->set_property = purple_conversation_set_property;
 
@@ -381,7 +400,70 @@
 		0,
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+	/**
+	 * PurpleConversation:members:
+	 *
+	 * The members that are currently in this conversation.
+	 *
+	 * Since: 3.0.0
+	 */
+	properties[PROP_MEMBERS] = g_param_spec_object(
+		"members", "members",
+		"The members that are currently in this conversation",
+		G_TYPE_LIST_MODEL,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+	/**
+	 * PurpleConversation::member-added:
+	 * @conversation: The instance.
+	 * @member: The [class@Purple.ConversationMember] instance.
+	 * @announce: Whether or not this addition should be announced.
+	 * @message: (nullable): An optional message to use in the announcement.
+	 *
+	 * Emitted when a new member is added to this conversation.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_MEMBER_ADDED] = g_signal_new_class_handler(
+		"member-added",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		3,
+		PURPLE_TYPE_CONVERSATION_MEMBER,
+		G_TYPE_BOOLEAN,
+		G_TYPE_STRING);
+
+	/**
+	 * PurpleConversation::member-removed:
+	 * @conversation: The instance.
+	 * @member: The [class@Purple.ConversationMember] instance.
+	 * @announce: Whether or not this removal should be announced.
+	 * @message: (nullable): An optional message to use in the announcement.
+	 *
+	 * Emitted when member is removed from this conversation.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_MEMBER_REMOVED] = g_signal_new_class_handler(
+		"member-removed",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		3,
+		PURPLE_TYPE_CONVERSATION_MEMBER,
+		G_TYPE_BOOLEAN,
+		G_TYPE_STRING);
 }
 
 /******************************************************************************
@@ -834,3 +916,124 @@
 
 	return menu;
 }
+
+GListModel *
+purple_conversation_get_members(PurpleConversation *conversation) {
+	PurpleConversationPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL);
+
+	priv = purple_conversation_get_instance_private(conversation);
+
+	return G_LIST_MODEL(priv->members);
+}
+
+gboolean
+purple_conversation_has_member(PurpleConversation *conversation,
+                               PurpleContactInfo *info, guint *position)
+{
+	PurpleConversationPrivate *priv = NULL;
+	PurpleConversationMember *needle = NULL;
+	gboolean found = FALSE;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(info), FALSE);
+
+	priv = purple_conversation_get_instance_private(conversation);
+
+	needle = purple_conversation_member_new(info);
+	found = g_list_store_find_with_equal_func(priv->members, needle,
+	                                          purple_conversation_check_member_equal,
+	                                          position);
+
+	g_clear_object(&needle);
+
+	return found;
+}
+
+PurpleConversationMember *
+purple_conversation_find_member(PurpleConversation *conversation,
+                                PurpleContactInfo *info)
+{
+	PurpleConversationMember *member = NULL;
+	guint position = 0;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL);
+	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(info), NULL);
+
+	if(purple_conversation_has_member(conversation, info, &position)) {
+		PurpleConversationPrivate *priv = NULL;
+
+		priv = purple_conversation_get_instance_private(conversation);
+
+		member = g_list_model_get_item(G_LIST_MODEL(priv->members), position);
+
+		/* We don't return a reference, but get_item does, so we need to get
+		 * rid of that.
+		 */
+		g_object_unref(member);
+	}
+
+	return member;
+}
+
+PurpleConversationMember *
+purple_conversation_add_member(PurpleConversation *conversation,
+                               PurpleContactInfo *info, gboolean announce,
+                               const char *message)
+{
+	PurpleConversationMember *member = NULL;
+	PurpleConversationPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL);
+	g_return_val_if_fail(PURPLE_IS_CONTACT_INFO(info), NULL);
+
+	priv = purple_conversation_get_instance_private(conversation);
+
+	member = purple_conversation_find_member(conversation, info);
+	if(PURPLE_IS_CONVERSATION_MEMBER(member)) {
+		return member;
+	}
+
+	member = purple_conversation_member_new(info);
+	g_list_store_append(priv->members, member);
+
+	g_signal_emit(conversation, signals[SIG_MEMBER_ADDED], 0, member, announce,
+	              message);
+
+	g_object_unref(member);
+
+	return member;
+}
+
+gboolean
+purple_conversation_remove_member(PurpleConversation *conversation,
+                                  PurpleConversationMember *member,
+                                  gboolean announce, const char *message)
+{
+	PurpleConversationPrivate *priv = NULL;
+	guint position = 0;
+
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION_MEMBER(member), FALSE);
+
+	priv = purple_conversation_get_instance_private(conversation);
+
+	if(!g_list_store_find(priv->members, member, &position)) {
+		return FALSE;
+	}
+
+	/* We need to ref member to make sure it stays around long enough for us
+	 * to emit the signal.
+	 */
+	g_object_ref(member);
+
+	g_list_store_remove(priv->members, position);
+
+	g_signal_emit(conversation, signals[SIG_MEMBER_REMOVED], 0, member,
+	              announce, message);
+
+	g_object_unref(member);
+
+	return TRUE;
+}

mercurial