# HG changeset patch
# Node ID a132f9405a74387793a29a013d46f7baec8f468b
# Parent  d2aa432ab723cebf65134f84647dceeb619fe1cd
diff --git a/libpurple/purpleconversation.c b/libpurple/purpleconversation.c
--- a/libpurple/purpleconversation.c
+++ b/libpurple/purpleconversation.c
@@ -42,17 +42,25 @@
 	PurpleConversationType type;
 	PurpleAccount *account;
 
+	gboolean avatar_editable;
 	PurpleAvatar *avatar;
+
 	char *name;
 	char *alias;
+
 	char *title;
+	gboolean title_editable;
 	gboolean title_generated;
 
 	gboolean age_restricted;
+	gboolean description_editable;
 	char *description;
+
+	gboolean topic_editable;
 	char *topic;
 	PurpleContactInfo *topic_author;
 	GDateTime *topic_updated;
+
 	char *user_nickname;
 	gboolean favorite;
 	GDateTime *created_on;
@@ -82,14 +90,18 @@
 	PROP_GLOBAL_ID,
 	PROP_TYPE,
 	PROP_ACCOUNT,
+	PROP_AVATAR_EDITABLE,
 	PROP_AVATAR,
 	PROP_NAME,
 	PROP_ALIAS,
+	PROP_TITLE_EDITABLE,
 	PROP_TITLE,
 	PROP_TITLE_FOR_DISPLAY,
 	PROP_TITLE_GENERATED,
 	PROP_AGE_RESTRICTED,
+	PROP_DESCRIPTION_EDITABLE,
 	PROP_DESCRIPTION,
+	PROP_TOPIC_EDITABLE,
 	PROP_TOPIC,
 	PROP_TOPIC_AUTHOR,
 	PROP_TOPIC_UPDATED,
@@ -508,6 +520,10 @@
 		purple_conversation_set_account(conversation,
 		                                g_value_get_object(value));
 		break;
+	case PROP_AVATAR_EDITABLE:
+		purple_conversation_set_avatar_editable(conversation,
+		                                        g_value_get_boolean(value));
+		break;
 	case PROP_AVATAR:
 		purple_conversation_set_avatar(conversation,
 		                               g_value_get_object(value));
@@ -518,6 +534,10 @@
 	case PROP_ALIAS:
 		purple_conversation_set_alias(conversation, g_value_get_string(value));
 		break;
+	case PROP_TITLE_EDITABLE:
+		purple_conversation_set_title_editable(conversation,
+		                                       g_value_get_boolean(value));
+		break;
 	case PROP_TITLE:
 		purple_conversation_set_title(conversation, g_value_get_string(value));
 		break;
@@ -525,10 +545,18 @@
 		purple_conversation_set_age_restricted(conversation,
 		                                       g_value_get_boolean(value));
 		break;
+	case PROP_DESCRIPTION_EDITABLE:
+		purple_conversation_set_description_editable(conversation,
+		                                             g_value_get_boolean(value));
+		break;
 	case PROP_DESCRIPTION:
 		purple_conversation_set_description(conversation,
 		                                    g_value_get_string(value));
 		break;
+	case PROP_TOPIC_EDITABLE:
+		purple_conversation_set_topic_editable(conversation,
+		                                       g_value_get_boolean(value));
+		break;
 	case PROP_TOPIC:
 		purple_conversation_set_topic(conversation, g_value_get_string(value));
 		break;
@@ -611,6 +639,10 @@
 		g_value_set_object(value,
 		                   purple_conversation_get_account(conversation));
 		break;
+	case PROP_AVATAR_EDITABLE:
+		g_value_set_boolean(value,
+		                    purple_conversation_get_avatar_editable(conversation));
+		break;
 	case PROP_AVATAR:
 		g_value_set_object(value,
 		                   purple_conversation_get_avatar(conversation));
@@ -621,6 +653,10 @@
 	case PROP_ALIAS:
 		g_value_set_string(value, purple_conversation_get_alias(conversation));
 		break;
+	case PROP_TITLE_EDITABLE:
+		g_value_set_boolean(value,
+		                    purple_conversation_get_title_editable(conversation));
+		break;
 	case PROP_TITLE:
 		g_value_set_string(value, purple_conversation_get_title(conversation));
 		break;
@@ -636,10 +672,18 @@
 		g_value_set_boolean(value,
 		                    purple_conversation_get_age_restricted(conversation));
 		break;
+	case PROP_DESCRIPTION_EDITABLE:
+		g_value_set_boolean(value,
+		                    purple_conversation_get_description_editable(conversation));
+		break;
 	case PROP_DESCRIPTION:
 		g_value_set_string(value,
 		                   purple_conversation_get_description(conversation));
 		break;
+	case PROP_TOPIC_EDITABLE:
+		g_value_set_boolean(value,
+		                    purple_conversation_get_topic_editable(conversation));
+		break;
 	case PROP_TOPIC:
 		g_value_set_string(value, purple_conversation_get_topic(conversation));
 		break;
@@ -856,6 +900,25 @@
 		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
 	/**
+	 * PurpleConversation:avatar-editable:
+	 *
+	 * Whether or not the user can edit the avatar for this conversation.
+	 *
+	 * This property should typically only be set by protocols and should be
+	 * changed if the user's permissions in the conversation change.
+	 *
+	 * User interfaces should check
+	 * [method@ProtocolConversation.implements_set_avatar] before making this
+	 * action available to users.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_AVATAR_EDITABLE] = g_param_spec_boolean(
+		"avatar-editable", NULL, NULL,
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * PurpleConversation:avatar:
 	 *
 	 * The [class@Avatar] for the conversation.
@@ -895,6 +958,25 @@
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
+	 * PurpleConversation:title-editable:
+	 *
+	 * Whether or not the user can edit the title.
+	 *
+	 * This property should typically only be set by protocols and should be
+	 * changed if the user's permissions in the conversation change.
+	 *
+	 * User interfaces should check
+	 * [method@ProtocolConversation.implements_set_title] before making this
+	 * action available to users.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_TITLE_EDITABLE] = g_param_spec_boolean(
+		"title-editable", NULL, NULL,
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * PurpleConversation:title:
 	 *
 	 * The title of the conversation.
@@ -961,6 +1043,25 @@
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
+	 * PurpleConversation:description-editable:
+	 *
+	 * Whether or not the user can edit the title.
+	 *
+	 * This property should typically only be set by protocols and should be
+	 * changed if the user's permissions in the conversation change.
+	 *
+	 * User interfaces should check
+	 * [method@ProtocolConversation.implements_set_description] before making
+	 * this action available to users.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_DESCRIPTION_EDITABLE] = g_param_spec_boolean(
+		"description-editable", NULL, NULL,
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * PurpleConversation:description:
 	 *
 	 * Sets the description of the conversation. This field is typically used
@@ -977,6 +1078,25 @@
 		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
 	/**
+	 * PurpleConversation:topic-editable:
+	 *
+	 * Whether or not the user can edit the title.
+	 *
+	 * This property should typically only be set by protocols and should be
+	 * changed if the user's permissions in the conversation change.
+	 *
+	 * User interfaces should check
+	 * [method@ProtocolConversation.implements_set_topic] before making this
+	 * action available to users.
+	 *
+	 * Since: 3.0
+	 */
+	properties[PROP_TOPIC_EDITABLE] = g_param_spec_boolean(
+		"topic-editable", NULL, NULL,
+		FALSE,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	/**
 	 * PurpleConversation:topic:
 	 *
 	 * The topic of the conversation.
@@ -1459,6 +1579,27 @@
 	return conversation->title_generated;
 }
 
+gboolean
+purple_conversation_get_title_editable(PurpleConversation *conversation) {
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return conversation->title_editable;
+}
+
+void
+purple_conversation_set_title_editable(PurpleConversation *conversation,
+                                       gboolean editable)
+{
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	if(conversation->title_editable != editable) {
+		conversation->title_editable = editable;
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_TITLE_EDITABLE]);
+	}
+}
+
 void
 purple_conversation_set_name(PurpleConversation *conversation,
                              const char *name)
@@ -1636,6 +1777,28 @@
 	}
 }
 
+gboolean
+purple_conversation_get_description_editable(PurpleConversation *conversation)
+{
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return conversation->description_editable;
+}
+
+void
+purple_conversation_set_description_editable(PurpleConversation *conversation,
+                                             gboolean editable)
+{
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	if(conversation->description_editable != editable) {
+		conversation->description_editable = editable;
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_DESCRIPTION_EDITABLE]);
+	}
+}
+
 const char *
 purple_conversation_get_topic(PurpleConversation *conversation) {
 	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL);
@@ -1655,6 +1818,27 @@
 	}
 }
 
+gboolean
+purple_conversation_get_topic_editable(PurpleConversation *conversation) {
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return conversation->topic_editable;
+}
+
+void
+purple_conversation_set_topic_editable(PurpleConversation *conversation,
+                                       gboolean editable)
+{
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	if(conversation->topic_editable != editable) {
+		conversation->topic_editable = editable;
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_TOPIC_EDITABLE]);
+	}
+}
+
 void
 purple_conversation_set_topic_full(PurpleConversation *conversation,
                                    const char *topic,
@@ -1864,6 +2048,27 @@
 	}
 }
 
+gboolean
+purple_conversation_get_avatar_editable(PurpleConversation *conversation) {
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
+
+	return conversation->avatar_editable;
+}
+
+void
+purple_conversation_set_avatar_editable(PurpleConversation *conversation,
+                                        gboolean editable)
+{
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
+
+	if(conversation->avatar_editable != editable) {
+		conversation->avatar_editable = editable;
+
+		g_object_notify_by_pspec(G_OBJECT(conversation),
+		                         properties[PROP_AVATAR_EDITABLE]);
+	}
+}
+
 const char *
 purple_conversation_get_title_for_display(PurpleConversation *conversation) {
 	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), NULL);
diff --git a/libpurple/purpleconversation.h b/libpurple/purpleconversation.h
--- a/libpurple/purpleconversation.h
+++ b/libpurple/purpleconversation.h
@@ -312,6 +312,32 @@
 gboolean purple_conversation_get_title_generated(PurpleConversation *conversation);
 
 /**
+ * purple_conversation_get_title_editable:
+ *
+ * Gets whether or not the conversation title is editable.
+ *
+ * Returns: true if the user can edit the title; otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_conversation_get_title_editable(PurpleConversation *conversation);
+
+/**
+ * purple_conversation_set_title_editable:
+ * @editable: the new value
+ *
+ * Sets whether or not a conversation's title is editable.
+ *
+ * This should primarily be called by a protocol when a user's permissions
+ * change in the conversation.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_conversation_set_title_editable(PurpleConversation *conversation, gboolean editable);
+
+/**
  * purple_conversation_set_name:
  * @conversation: The conversation.
  * @name: The conversation's name.
@@ -453,6 +479,32 @@
 void purple_conversation_set_description(PurpleConversation *conversation, const char *description);
 
 /**
+ * purple_conversation_get_description_editable:
+ *
+ * Gets whether or not the conversation description is editable.
+ *
+ * Returns: true if the user can edit the description; otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_conversation_get_description_editable(PurpleConversation *conversation);
+
+/**
+ * purple_conversation_set_description_editable:
+ * @editable: the new value
+ *
+ * Sets whether or not a conversation's description is editable.
+ *
+ * This should primarily be called by a protocol when a user's permissions
+ * change in the conversation.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_conversation_set_description_editable(PurpleConversation *conversation, gboolean editable);
+
+/**
  * purple_conversation_get_topic:
  * @conversation: The instance.
  *
@@ -478,6 +530,32 @@
 void purple_conversation_set_topic(PurpleConversation *conversation, const char *topic);
 
 /**
+ * purple_conversation_get_topic_editable:
+ *
+ * Gets whether or not the conversation topic is editable.
+ *
+ * Returns: true if the user can edit the topic; otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_conversation_get_topic_editable(PurpleConversation *conversation);
+
+/**
+ * purple_conversation_set_topic_editable:
+ * @editable: the new value
+ *
+ * Sets whether or not a conversation's topic is editable.
+ *
+ * This should primarily be called by a protocol when a user's permissions
+ * change in the conversation.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_conversation_set_topic_editable(PurpleConversation *conversation, gboolean editable);
+
+/**
  * purple_conversation_set_topic_full:
  * @conversation: The instance:
  * @topic: (nullable): The new topic.
@@ -765,6 +843,32 @@
 void purple_conversation_set_avatar(PurpleConversation *conversation, PurpleAvatar *avatar);
 
 /**
+ * purple_conversation_get_avatar_editable:
+ *
+ * Gets whether or not the conversation avatar is editable.
+ *
+ * Returns: true if the user can edit the avatar; otherwise false.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+gboolean purple_conversation_get_avatar_editable(PurpleConversation *conversation);
+
+/**
+ * purple_conversation_set_avatar_editable:
+ * @editable: the new value
+ *
+ * Sets whether or not a conversation's avatar is editable.
+ *
+ * This should primarily be called by a protocol when a user's permissions
+ * change in the conversation.
+ *
+ * Since: 3.0
+ */
+PURPLE_AVAILABLE_IN_3_0
+void purple_conversation_set_avatar_editable(PurpleConversation *conversation, gboolean editable);
+
+/**
  * purple_conversation_get_title_for_display:
  * @conversation: The instance.
  *
diff --git a/libpurple/tests/test_conversation.c b/libpurple/tests/test_conversation.c
--- a/libpurple/tests/test_conversation.c
+++ b/libpurple/tests/test_conversation.c
@@ -71,10 +71,14 @@
 	char *topic = NULL;
 	char *user_nickname = NULL;
 	gboolean age_restricted = FALSE;
+	gboolean avatar_editable = FALSE;
+	gboolean description_editable = FALSE;
+	gboolean drafting = FALSE;
 	gboolean favorite = FALSE;
+	gboolean logging = FALSE;
 	gboolean needs_attention = FALSE;
-	gboolean logging = FALSE;
-	gboolean drafting = FALSE;
+	gboolean title_editable = FALSE;
+	gboolean topic_editable = FALSE;
 
 	account = g_object_new(
 		PURPLE_TYPE_ACCOUNT,
@@ -100,9 +104,11 @@
 		"age-restricted", TRUE,
 		"alias", "talky talk",
 		"avatar", avatar,
+		"avatar-editable", TRUE,
 		"created-on", created_on,
 		"creator", creator,
 		"description", "to describe or not to describe...",
+		"description-editable", TRUE,
 		"drafting", TRUE,
 		"error", error,
 		"favorite", TRUE,
@@ -111,8 +117,10 @@
 		"name", "name1",
 		"needs-attention", TRUE,
 		"title", "test conversation",
+		"title-editable", TRUE,
 		"topic", "the topic...",
 		"topic-author", topic_author,
+		"topic-editable", TRUE,
 		"topic-updated", topic_updated,
 		"type", PURPLE_CONVERSATION_TYPE_THREAD,
 		"typing-state", PURPLE_TYPING_STATE_TYPING,
@@ -125,9 +133,11 @@
 		"age-restricted", &age_restricted,
 		"alias", &alias,
 		"avatar", &avatar1,
+		"avatar-editable", &avatar_editable,
 		"created-on", &created_on1,
 		"creator", &creator1,
 		"description", &description,
+		"description-editable", &description_editable,
 		"drafting", &drafting,
 		"error", &error1,
 		"favorite", &favorite,
@@ -139,9 +149,11 @@
 		"needs-attention", &needs_attention,
 		"tags", &tags,
 		"title", &title,
+		"title-editable", &title_editable,
 		"title-for-display", &title_for_display,
 		"topic", &topic,
 		"topic-author", &topic_author1,
+		"topic-editable", &topic_editable,
 		"topic-updated", &topic_updated1,
 		"type", &type,
 		"typing-state", &typing_state,
@@ -160,6 +172,8 @@
 	g_assert_true(avatar1 == avatar);
 	g_clear_object(&avatar1);
 
+	g_assert_true(avatar_editable);
+
 	g_assert_nonnull(created_on1);
 	g_assert_true(g_date_time_equal(created_on1, created_on));
 	g_clear_pointer(&created_on1, g_date_time_unref);
@@ -170,6 +184,8 @@
 	g_assert_cmpstr(description, ==, "to describe or not to describe...");
 	g_clear_pointer(&description, g_free);
 
+	g_assert_true(description_editable);
+
 	g_assert_true(drafting);
 
 	g_assert_error(error1, error->domain, error->code);
@@ -201,6 +217,8 @@
 	g_assert_cmpstr(title, ==, "test conversation");
 	g_clear_pointer(&title, g_free);
 
+	g_assert_true(title_editable);
+
 	g_assert_cmpstr(title_for_display, ==, "talky talk");
 	g_clear_pointer(&title_for_display, g_free);
 
@@ -210,6 +228,8 @@
 	g_assert_true(topic_author == topic_author1);
 	g_clear_object(&topic_author1);
 
+	g_assert_true(topic_editable);
+
 	g_assert_nonnull(topic_updated1);
 	g_assert_true(g_date_time_equal(topic_updated1, topic_updated));
 	g_clear_pointer(&topic_updated1, g_date_time_unref);
