pidgin/pidgin

7c16e3b1cce8
Parents cfa7c940ed62
Children 0d14fedafd4a
Create PurpleSavedPresence for saving the users presences

Testing Done:
Ran the unit tests.

Bugs closed: PIDGIN-17795

Reviewed at https://reviews.imfreedom.org/r/2440/
--- a/libpurple/meson.build Tue Apr 18 22:44:01 2023 -0500
+++ b/libpurple/meson.build Tue Apr 25 00:46:17 2023 -0500
@@ -88,6 +88,7 @@
'purplerequestgroup.c',
'purplerequestpage.c',
'purpleroomlistroom.c',
+ 'purplesavedpresence.c',
'purplesqlite3.c',
'purplesqlitehistoryadapter.c',
'purpletags.c',
@@ -205,6 +206,7 @@
'purplerequestgroup.h',
'purplerequestpage.h',
'purpleroomlistroom.h',
+ 'purplesavedpresence.h',
'purplesqlite3.h',
'purplesqlitehistoryadapter.h',
'purpletags.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplesavedpresence.c Tue Apr 25 00:46:17 2023 -0500
@@ -0,0 +1,376 @@
+/*
+ * purple
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * 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.
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include "purpleenums.h"
+#include "purplesavedpresence.h"
+#include "util.h"
+
+struct _PurpleSavedPresence {
+ GObject parent;
+
+ GDateTime *last_used;
+ guint use_count;
+
+ char *name;
+
+ PurplePresencePrimitive primitive;
+ char *message;
+ char *emoji;
+};
+
+enum {
+ PROP_0,
+ PROP_LAST_USED,
+ PROP_USE_COUNT,
+ PROP_NAME,
+ PROP_PRIMITIVE,
+ PROP_MESSAGE,
+ PROP_EMOJI,
+ N_PROPERTIES
+};
+static GParamSpec *properties[N_PROPERTIES];
+
+G_DEFINE_TYPE(PurpleSavedPresence, purple_saved_presence, G_TYPE_OBJECT)
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_saved_presence_set_property(GObject *obj, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ PurpleSavedPresence *presence = PURPLE_SAVED_PRESENCE(obj);
+
+ switch(param_id) {
+ case PROP_LAST_USED:
+ purple_saved_presence_set_last_used(presence,
+ g_value_get_boxed(value));
+ break;
+ case PROP_USE_COUNT:
+ purple_saved_presence_set_use_count(presence,
+ g_value_get_uint(value));
+ break;
+ case PROP_NAME:
+ purple_saved_presence_set_name(presence,
+ g_value_get_string(value));
+ break;
+ case PROP_PRIMITIVE:
+ purple_saved_presence_set_primitive(presence,
+ g_value_get_enum(value));
+ break;
+ case PROP_MESSAGE:
+ purple_saved_presence_set_message(presence,
+ g_value_get_string(value));
+ break;
+ case PROP_EMOJI:
+ purple_saved_presence_set_emoji(presence,
+ g_value_get_string(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_saved_presence_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleSavedPresence *presence = PURPLE_SAVED_PRESENCE(obj);
+
+ switch(param_id) {
+ case PROP_LAST_USED:
+ g_value_set_boxed(value,
+ purple_saved_presence_get_last_used(presence));
+ break;
+ case PROP_USE_COUNT:
+ g_value_set_uint(value,
+ purple_saved_presence_get_use_count(presence));
+ break;
+ case PROP_NAME:
+ g_value_set_string(value,
+ purple_saved_presence_get_name(presence));
+ break;
+ case PROP_PRIMITIVE:
+ g_value_set_enum(value,
+ purple_saved_presence_get_primitive(presence));
+ break;
+ case PROP_MESSAGE:
+ g_value_set_string(value,
+ purple_saved_presence_get_message(presence));
+ break;
+ case PROP_EMOJI:
+ g_value_set_string(value,
+ purple_saved_presence_get_emoji(presence));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+purple_saved_presence_init(G_GNUC_UNUSED PurpleSavedPresence *presence) {
+}
+
+static void
+purple_saved_presence_finalize(GObject *obj) {
+ PurpleSavedPresence *presence = PURPLE_SAVED_PRESENCE(obj);
+
+ g_clear_pointer(&presence->last_used, g_date_time_unref);
+
+ g_clear_pointer(&presence->name, g_free);
+ g_clear_pointer(&presence->message, g_free);
+ g_clear_pointer(&presence->emoji, g_free);
+
+ G_OBJECT_CLASS(purple_saved_presence_parent_class)->finalize(obj);
+}
+
+static void
+purple_saved_presence_class_init(PurpleSavedPresenceClass *klass) {
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ obj_class->get_property = purple_saved_presence_get_property;
+ obj_class->set_property = purple_saved_presence_set_property;
+ obj_class->finalize = purple_saved_presence_finalize;
+
+ /**
+ * PurpleSavedPresence:last-used:
+ *
+ * The [struct@GLib.DateTime] when this saved presence was last used.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_LAST_USED] = g_param_spec_boxed(
+ "last-used", "last-used",
+ "The time this presence was last used.",
+ G_TYPE_DATE_TIME,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * PurpleSavedPresence:use-count:
+ *
+ * The number of times this saved presence has been used.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_USE_COUNT] = g_param_spec_uint(
+ "use-count", "use-count",
+ "The number of times this saved presence has been used.",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * PurpleSavedPresence:name:
+ *
+ * The name of the saved presence.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_NAME] = g_param_spec_string(
+ "name", "name",
+ "The name of this saved presence.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * PurpleSavedPresence:primitive:
+ *
+ * The [enum@Purple.StatusPrimitive] for this saved presence.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_PRIMITIVE] = g_param_spec_enum(
+ "primitive", "primitive",
+ "The primitive for this saved presence.",
+ PURPLE_TYPE_PRESENCE_PRIMITIVE,
+ PURPLE_PRESENCE_PRIMITIVE_OFFLINE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * PurpleSavedPresence:message:
+ *
+ * The status message of this saved presence.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_MESSAGE] = g_param_spec_string(
+ "message", "message",
+ "The status message of this saved presence.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * PurpleSavedPresence:emoji:
+ *
+ * The emoji or mood of the presence.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_EMOJI] = g_param_spec_string(
+ "emoji", "emoji",
+ "The emoji for this saved presence.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+PurpleSavedPresence *
+purple_saved_presence_new(void) {
+ return g_object_new(PURPLE_TYPE_SAVED_PRESENCE, NULL);
+}
+
+GDateTime *
+purple_saved_presence_get_last_used(PurpleSavedPresence *presence) {
+ g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(presence), NULL);
+
+ return presence->last_used;
+}
+
+void
+purple_saved_presence_set_last_used(PurpleSavedPresence *presence,
+ GDateTime *last_used)
+{
+ g_return_if_fail(PURPLE_IS_SAVED_PRESENCE(presence));
+
+ if(presence->last_used != last_used) {
+ g_clear_pointer(&presence->last_used, g_date_time_unref);
+ if(last_used != NULL) {
+ presence->last_used = g_date_time_ref(last_used);
+ }
+
+ g_object_notify_by_pspec(G_OBJECT(presence),
+ properties[PROP_LAST_USED]);
+ }
+}
+
+guint
+purple_saved_presence_get_use_count(PurpleSavedPresence *presence) {
+ g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(presence), 0);
+
+ return presence->use_count;
+}
+
+void
+purple_saved_presence_set_use_count(PurpleSavedPresence *presence,
+ guint use_count)
+{
+ g_return_if_fail(PURPLE_IS_SAVED_PRESENCE(presence));
+
+ if(presence->use_count != use_count) {
+ presence->use_count = use_count;
+
+ g_object_notify_by_pspec(G_OBJECT(presence),
+ properties[PROP_USE_COUNT]);
+ }
+}
+
+const char *
+purple_saved_presence_get_name(PurpleSavedPresence *presence) {
+ g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(presence), NULL);
+
+ return presence->name;
+}
+
+void
+purple_saved_presence_set_name(PurpleSavedPresence *presence,
+ const char *name)
+{
+ g_return_if_fail(PURPLE_IS_SAVED_PRESENCE(presence));
+
+ if(!purple_strequal(presence->name, name)) {
+ g_free(presence->name);
+ presence->name = g_strdup(name);
+
+ g_object_notify_by_pspec(G_OBJECT(presence), properties[PROP_NAME]);
+ }
+}
+
+PurplePresencePrimitive
+purple_saved_presence_get_primitive(PurpleSavedPresence *presence) {
+ g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(presence),
+ PURPLE_PRESENCE_PRIMITIVE_OFFLINE);
+
+ return presence->primitive;
+}
+
+void
+purple_saved_presence_set_primitive(PurpleSavedPresence *presence,
+ PurplePresencePrimitive primitive)
+{
+ g_return_if_fail(PURPLE_IS_SAVED_PRESENCE(presence));
+
+ if(presence->primitive != primitive) {
+ presence->primitive = primitive;
+
+ g_object_notify_by_pspec(G_OBJECT(presence),
+ properties[PROP_PRIMITIVE]);
+ }
+}
+
+const char *
+purple_saved_presence_get_message(PurpleSavedPresence *presence) {
+ g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(presence), NULL);
+
+ return presence->message;
+}
+
+void
+purple_saved_presence_set_message(PurpleSavedPresence *presence,
+ const char *message)
+{
+ g_return_if_fail(PURPLE_IS_SAVED_PRESENCE(presence));
+
+ if(!purple_strequal(presence->message, message)) {
+ g_free(presence->message);
+ presence->message = g_strdup(message);
+
+ g_object_notify_by_pspec(G_OBJECT(presence), properties[PROP_MESSAGE]);
+ }
+}
+
+const char *
+purple_saved_presence_get_emoji(PurpleSavedPresence *presence) {
+ g_return_val_if_fail(PURPLE_IS_SAVED_PRESENCE(presence), NULL);
+
+ return presence->emoji;
+}
+
+void
+purple_saved_presence_set_emoji(PurpleSavedPresence *presence,
+ const char *emoji)
+{
+ g_return_if_fail(PURPLE_IS_SAVED_PRESENCE(presence));
+
+ if(!purple_strequal(presence->emoji, emoji)) {
+ g_free(presence->emoji);
+ presence->emoji = g_strdup(emoji);
+
+ g_object_notify_by_pspec(G_OBJECT(presence), properties[PROP_EMOJI]);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purplesavedpresence.h Tue Apr 25 00:46:17 2023 -0500
@@ -0,0 +1,197 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * 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.
+ *
+ * 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, 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_SAVED_PRESENCE_H
+#define PURPLE_SAVED_PRESENCE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libpurple/purplepresence.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_SAVED_PRESENCE purple_saved_presence_get_type()
+G_DECLARE_FINAL_TYPE(PurpleSavedPresence, purple_saved_presence, PURPLE,
+ SAVED_PRESENCE, GObject)
+
+/**
+ * purple_saved_presence_new:
+ *
+ * Creates a new saved_presence instance.
+ *
+ * Returns: (transfer full): The new instance.
+ *
+ * Since: 3.0.0
+ */
+PurpleSavedPresence *purple_saved_presence_new(void);
+
+/**
+ * purple_saved_presence_get_last_used:
+ * @presence: The instance.
+ *
+ * Gets the [struct@GLib.DateTime] that @presence was last used.
+ *
+ * Returns: (transfer none) (nullable): The time @presence was last used or
+ * %NULL.
+ *
+ * Since: 3.0.0
+ */
+GDateTime *purple_saved_presence_get_last_used(PurpleSavedPresence *presence);
+
+/**
+ * purple_saved_presence_set_last_used:
+ * @presence: The instance.
+ * @last_used: (nullable): The time this was last used.
+ *
+ * Sets the last time @presence was used to @last_used. If @last_used is %NULL,
+ * the time will be cleared.
+ *
+ * Since: 3.0.0
+ */
+void purple_saved_presence_set_last_used(PurpleSavedPresence *presence, GDateTime *last_used);
+
+/**
+ * purple_saved_presence_get_use_count:
+ * @presence: The instance.
+ *
+ * Gets the number of times @presence has been used.
+ *
+ * Returns: The number of times @presence has been used.
+ *
+ * Since: 3.0.0
+ */
+guint purple_saved_presence_get_use_count(PurpleSavedPresence *presence);
+
+/**
+ * purple_saved_presence_set_use_count:
+ * @presence: The instance.
+ * @use_count: The new use count.
+ *
+ * Sets the number of times @presence has been used to @use_count.
+ *
+ * Since: 3.0.0
+ */
+void purple_saved_presence_set_use_count(PurpleSavedPresence *presence, guint use_count);
+
+/**
+ * purple_saved_presence_get_name:
+ * @presence: The instance.
+ *
+ * Gets the name of @presence.
+ *
+ * Returns: (nullable): The name of @presence.
+ *
+ * Since: 3.0.0
+ */
+const char *purple_saved_presence_get_name(PurpleSavedPresence *presence);
+
+/**
+ * purple_saved_presence_set_name:
+ * @presence: The instance.
+ * @name: (nullable): The new name.
+ *
+ * Sets the name of @presence to @name. If @name is %NULL the name will be
+ * cleared.
+ *
+ * Since: 3.0.0
+ */
+void purple_saved_presence_set_name(PurpleSavedPresence *presence, const char *name);
+
+/**
+ * purple_saved_presence_get_primitive:
+ * @presence: The instance.
+ *
+ * Gets the [enum@PresencePrimitive] for @presence.
+ *
+ * Returns: The [enum@PresencePrimitive] for @presence.
+ *
+ * Since: 3.0.0
+ */
+PurplePresencePrimitive purple_saved_presence_get_primitive(PurpleSavedPresence *presence);
+
+/**
+ * purple_saved_presence_set_primitive:
+ * @presence: The instance.
+ * @primitive: The new primitive.
+ *
+ * Sets the [enum@PresencePrimitive] of @presence to @primitive.
+ *
+ * Since: 3.0.0
+ */
+void purple_saved_presence_set_primitive(PurpleSavedPresence *presence, PurplePresencePrimitive primitive);
+
+/**
+ * purple_saved_presence_get_message:
+ * @presence: The instance.
+ *
+ * Gets the message of @presence.
+ *
+ * Returns: (nullable): The message from @presence.
+ *
+ * Since: 3.0.0
+ */
+const char *purple_saved_presence_get_message(PurpleSavedPresence *presence);
+
+/**
+ * purple_saved_presence_set_message:
+ * @presence: The instance.
+ * @message: (nullable): The new message.
+ *
+ * Sets the message of @presence to @message. If @message is %NULL the message
+ * is cleared.
+ *
+ * Since: 3.0.0
+ */
+void purple_saved_presence_set_message(PurpleSavedPresence *presence, const char *message);
+
+/**
+ * purple_saved_presence_get_emoji:
+ * @presence: The instance.
+ *
+ * Gets the emoji for @presence.
+ *
+ * Returns: (nullable): The emoji for @presence.
+ *
+ * Since: 3.0.0
+ */
+const char *purple_saved_presence_get_emoji(PurpleSavedPresence *presence);
+
+/**
+ * purple_saved_presence_set_emoji:
+ * @presence: The instance.
+ * @emoji: (nullable): The new emoji.
+ *
+ * Sets the emoji of @presence to @emoji. If @emoji is %NULL, the emoji will be
+ * cleared.
+ *
+ * Since: 3.0.0
+ */
+void purple_saved_presence_set_emoji(PurpleSavedPresence *presence, const char *emoji);
+
+G_END_DECLS
+
+#endif /* PURPLE_SAVED_PRESENCE_H */
--- a/libpurple/tests/meson.build Tue Apr 18 22:44:01 2023 -0500
+++ b/libpurple/tests/meson.build Tue Apr 25 00:46:17 2023 -0500
@@ -29,6 +29,7 @@
'request_field',
'request_group',
'request_page',
+ 'saved_presence',
'str',
'tags',
'util',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_saved_presence.c Tue Apr 25 00:46:17 2023 -0500
@@ -0,0 +1,111 @@
+/*
+ * 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 <glib.h>
+
+#include <purple.h>
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_purple_saved_presence_new(void) {
+ PurpleSavedPresence *presence = NULL;
+
+ presence = purple_saved_presence_new();
+ g_assert_true(PURPLE_IS_SAVED_PRESENCE(presence));
+
+ g_clear_object(&presence);
+}
+
+static void
+test_purple_saved_presence_properties(void) {
+ PurpleSavedPresence *presence = NULL;
+ PurplePresencePrimitive primitive;
+ GDateTime *last_used = NULL;
+ GDateTime *last_used1 = NULL;
+ guint use_count;
+ char *name;
+ char *message;
+ char *emoji;
+
+ last_used = g_date_time_new_now_local();
+
+ /* Use g_object_new so we can test setting properties by name. All of them
+ * call the setter methods, so by doing it this way we exercise more of the
+ * code.
+ */
+ presence = g_object_new(
+ PURPLE_TYPE_SAVED_PRESENCE,
+ "last-used", last_used,
+ "use-count", 123,
+ "name", "my saved status",
+ "primitive", PURPLE_PRESENCE_PRIMITIVE_STREAMING,
+ "message", "I'm live on twitch at https://twitch.tv/rw_grim/",
+ "emoji", "💀",
+ NULL);
+
+ /* Now use g_object_get to read all of the properties. */
+ g_object_get(
+ presence,
+ "last-used", &last_used1,
+ "use-count", &use_count,
+ "name", &name,
+ "primitive", &primitive,
+ "message", &message,
+ "emoji", &emoji,
+ NULL);
+
+ /* Compare all the things. */
+ g_assert_nonnull(last_used1);
+ g_assert_true(g_date_time_equal(last_used, last_used1));
+ g_clear_pointer(&last_used1, g_date_time_unref);
+
+ g_assert_cmpuint(use_count, ==, 123);
+
+ g_assert_cmpstr(name, ==, "my saved status");
+ g_clear_pointer(&name, g_free);
+
+ g_assert_cmpint(primitive, ==, PURPLE_PRESENCE_PRIMITIVE_STREAMING);
+
+ g_assert_cmpstr(message, ==,
+ "I'm live on twitch at https://twitch.tv/rw_grim/");
+ g_clear_pointer(&message, g_free);
+
+ g_assert_cmpstr(emoji, ==, "💀");
+ g_clear_pointer(&emoji, g_free);
+
+ g_clear_pointer(&last_used, g_date_time_unref);
+ g_clear_object(&presence);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar *argv[]) {
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/saved-presence/new",
+ test_purple_saved_presence_new);
+ g_test_add_func("/saved-presence/properties",
+ test_purple_saved_presence_properties);
+
+
+ return g_test_run();
+}