# HG changeset patch
# Node ID ec640af74e42
# Parent  1c4e7e4dec50
diff --git a/libpurple/purpleconversationmanager.c b/libpurple/purpleconversationmanager.c
--- a/libpurple/purpleconversationmanager.c
+++ b/libpurple/purpleconversationmanager.c
@@ -25,6 +25,7 @@
 enum {
 	SIG_REGISTERED,
 	SIG_UNREGISTERED,
+	SIG_CONVERSATION_CHANGED,
 	N_SIGNALS,
 };
 static guint signals[N_SIGNALS] = {0, };
@@ -129,6 +130,21 @@
 }
 
 /******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+
+/* This callback propagates the notify signal from conversations. */
+static void
+purple_conversation_manager_conversation_changed_cb(GObject *source,
+                                                    GParamSpec *pspec,
+                                                    gpointer data)
+{
+	g_signal_emit(data, signals[SIG_CONVERSATION_CHANGED],
+	              g_param_spec_get_name_quark(pspec),
+	              source, pspec);
+}
+
+/******************************************************************************
  * GObject Implementation
  *****************************************************************************/
 static void
@@ -194,6 +210,35 @@
 		G_TYPE_NONE,
 		1,
 		PURPLE_TYPE_CONVERSATION);
+
+	/**
+	 * PurpleConversationManager::conversation-changed:
+	 * @manager: The account manager instance.
+	 * @conversation: The conversation that was changed.
+	 * @pspec: The [class@GObject.ParamSpec] for the property that changed.
+	 *
+	 * This is a propagation of the notify signal from @conversation. This
+	 * means that your callback will be called for any conversation that
+	 * @manager knows about.
+	 *
+	 * This also supports details, so you can specify the signal name as
+	 * something like `conversation-changed::title` and your callback will only
+	 * be called when [property@Conversation:title] has been changed.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_CONVERSATION_CHANGED] = g_signal_new_class_handler(
+		"conversation-changed",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		2,
+		PURPLE_TYPE_CONVERSATION,
+		G_TYPE_PARAM);
 }
 
 /******************************************************************************
@@ -236,6 +281,12 @@
 	                              g_object_ref(conversation));
 
 	if(registered) {
+		/* Register our signals that need to be propagated. */
+		g_signal_connect_object(conversation, "notify",
+		                        G_CALLBACK(purple_conversation_manager_conversation_changed_cb),
+		                        manager, 0);
+
+		/* Tell everyone about the new conversation. */
 		g_signal_emit(manager, signals[SIG_REGISTERED], 0, conversation);
 	}
 
@@ -253,6 +304,12 @@
 
 	unregistered = g_hash_table_remove(manager->conversations, conversation);
 	if(unregistered) {
+		/* Disconnect all the signals we added for propagation. */
+		g_signal_handlers_disconnect_by_func(conversation,
+		                                     purple_conversation_manager_conversation_changed_cb,
+		                                     manager);
+
+		/* Tell everyone about the unregistered conversation. */
 		g_signal_emit(manager, signals[SIG_UNREGISTERED], 0, conversation);
 	}
 
diff --git a/libpurple/tests/test_conversation_manager.c b/libpurple/tests/test_conversation_manager.c
--- a/libpurple/tests/test_conversation_manager.c
+++ b/libpurple/tests/test_conversation_manager.c
@@ -94,6 +94,64 @@
 }
 
 /******************************************************************************
+ * Signal Tests
+ *****************************************************************************/
+static void
+test_purple_conversation_manager_signal_conversation_changed_cb(PurpleConversationManager *manager,
+                                                                PurpleConversation *conversation,
+                                                                GParamSpec *pspec,
+                                                                gpointer data)
+{
+	guint *counter = data;
+
+	g_assert_true(PURPLE_IS_CONVERSATION_MANAGER(manager));
+	g_assert_true(PURPLE_IS_CONVERSATION(conversation));
+	g_assert_true(G_IS_PARAM_SPEC(pspec));
+
+	*counter = *counter + 1;
+}
+
+static void
+test_purple_conversation_manager_signal_conversation_changed(void) {
+	PurpleAccount *account = NULL;
+	PurpleConversationManager *manager = NULL;
+	PurpleConversation *conversation = NULL;
+	guint counter = 0;
+	guint detailed_counter = 0;
+
+	manager = g_object_new(PURPLE_TYPE_CONVERSATION_MANAGER, NULL);
+	g_signal_connect(manager, "conversation-changed",
+	                 G_CALLBACK(test_purple_conversation_manager_signal_conversation_changed_cb),
+	                 &counter);
+	g_signal_connect(manager, "conversation-changed::title",
+	                 G_CALLBACK(test_purple_conversation_manager_signal_conversation_changed_cb),
+	                 &detailed_counter);
+
+	account = purple_account_new("test", "test");
+
+	conversation = g_object_new(
+		PURPLE_TYPE_CONVERSATION,
+		"account", account,
+		"name", "kool kats",
+		NULL);
+	purple_conversation_manager_register(manager, conversation);
+
+	/* Set the features and title of the conversation, and verify our counters. */
+	purple_conversation_set_features(conversation, PURPLE_CONNECTION_FLAG_NO_IMAGES);
+	purple_conversation_set_title(conversation, "A place for kool kats");
+
+	g_assert_cmpuint(counter, ==, 2);
+	g_assert_cmpuint(detailed_counter, ==, 1);
+
+	/* Clean up. */
+	purple_conversation_manager_unregister(manager, conversation);
+
+	g_clear_object(&conversation);
+	g_clear_object(&account);
+	g_clear_object(&manager);
+}
+
+/******************************************************************************
  * Main
  *****************************************************************************/
 gint
@@ -107,6 +165,9 @@
 	g_test_add_func("/conversation-manager/register-unregister",
 	                test_purple_conversation_manager_register_unregister);
 
+	g_test_add_func("/conversation-manager/signals/conversation-changed",
+	                test_purple_conversation_manager_signal_conversation_changed);
+
 	ret = g_test_run();
 
 	test_ui_purple_uninit();
