From 6ace7e4ef5e70584ecebe53c200d6c5732e48ed6 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 14 Mar 2023 00:42:47 -0500 Subject: Add PurpleRequestField.is_filled vfunc, and a filled property And then move the string-specific check into the subclass. Note that this just does a `g_object_notify(G_OBJECT(field), "filled");` instead of adding a convenience function; not sure if I should do that. Testing Done: Compiled and ran `ninja test`. Reviewed at https://reviews.imfreedom.org/r/2345/ --- libpurple/purplerequestfield.c | 30 +++++-- libpurple/purplerequestfield.h | 7 +- libpurple/request/purplerequestfieldstring.c | 32 ++++++- libpurple/tests/meson.build | 1 + libpurple/tests/test_request_field.c | 129 +++++++++++++++++++++++++++ 5 files changed, 189 insertions(+), 10 deletions(-) create mode 100644 libpurple/tests/test_request_field.c (limited to 'libpurple') diff --git a/libpurple/purplerequestfield.c b/libpurple/purplerequestfield.c index 5c6f1611af..3b9bd233d8 100644 --- a/libpurple/purplerequestfield.c +++ b/libpurple/purplerequestfield.c @@ -21,7 +21,6 @@ #include #include "request.h" -#include "request/purplerequestfieldstring.h" #include "purpleprivate.h" typedef struct { @@ -50,6 +49,7 @@ enum { PROP_TYPE_HINT, PROP_TOOLTIP, PROP_REQUIRED, + PROP_FILLED, PROP_IS_VALIDATABLE, N_PROPERTIES, }; @@ -107,6 +107,10 @@ purple_request_field_get_property(GObject *obj, guint param_id, GValue *value, g_value_set_boolean(value, purple_request_field_is_required(field)); break; + case PROP_FILLED: + g_value_set_boolean(value, + purple_request_field_is_filled(field)); + break; case PROP_IS_VALIDATABLE: g_value_set_boolean(value, purple_request_field_is_validatable(field)); @@ -279,6 +283,19 @@ purple_request_field_class_init(PurpleRequestFieldClass *klass) { FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * PurpleRequestField:filled: + * + * Whether the field has been filled. + * + * Since: 3.0.0 + */ + properties[PROP_FILLED] = g_param_spec_boolean( + "filled", "filled", + "Whether the field has been filled.", + TRUE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /** * PurpleRequestField:is-validatable: * @@ -473,14 +490,17 @@ purple_request_field_is_required(PurpleRequestField *field) { gboolean purple_request_field_is_filled(PurpleRequestField *field) { + PurpleRequestFieldClass *klass = NULL; + gboolean filled = TRUE; + g_return_val_if_fail(PURPLE_IS_REQUEST_FIELD(field), FALSE); - if(PURPLE_IS_REQUEST_FIELD_STRING(field)) { - PurpleRequestFieldString *sfield = PURPLE_REQUEST_FIELD_STRING(field); - return !purple_strempty(purple_request_field_string_get_value(sfield)); + klass = PURPLE_REQUEST_FIELD_GET_CLASS(field); + if(klass != NULL && klass->is_filled != NULL) { + filled = klass->is_filled(field); } - return TRUE; + return filled; } void diff --git a/libpurple/purplerequestfield.h b/libpurple/purplerequestfield.h index b87f8889e4..b81aa6e642 100644 --- a/libpurple/purplerequestfield.h +++ b/libpurple/purplerequestfield.h @@ -53,6 +53,7 @@ struct _PurpleRequestFieldClass { GObjectClass parent_class; /*< public >*/ + gboolean (*is_filled)(PurpleRequestField *field); /*< private >*/ gpointer reserved[4]; @@ -193,7 +194,11 @@ gboolean purple_request_field_is_required(PurpleRequestField *field); * purple_request_field_is_filled: * @field: The field. * - * Checks, if specified field has value. + * Returns whether the field is currently filled. + * + * Note: For subclassers, if this is not overridden, then the field is assumed + * to always be filled. If the filled status changes, then subclasses should + * notify on [property@RequestField:filled]. * * Returns: TRUE if the field has value, or FALSE. */ diff --git a/libpurple/request/purplerequestfieldstring.c b/libpurple/request/purplerequestfieldstring.c index 7779ce3261..5f41ec3726 100644 --- a/libpurple/request/purplerequestfieldstring.c +++ b/libpurple/request/purplerequestfieldstring.c @@ -56,6 +56,16 @@ purple_request_field_string_set_multiline(PurpleRequestFieldString *field, g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_MULTILINE]); } +/****************************************************************************** + * PurpleRequestField Implementation + *****************************************************************************/ +static gboolean +purple_request_field_string_is_filled(PurpleRequestField *field) { + PurpleRequestFieldString *strfield = PURPLE_REQUEST_FIELD_STRING(field); + + return !purple_strempty(strfield->value); +} + /****************************************************************************** * GObject Implementation *****************************************************************************/ @@ -138,8 +148,11 @@ purple_request_field_string_init(G_GNUC_UNUSED PurpleRequestFieldString *field) static void purple_request_field_string_class_init(PurpleRequestFieldStringClass *klass) { + PurpleRequestFieldClass *field_class = PURPLE_REQUEST_FIELD_CLASS(klass); GObjectClass *obj_class = G_OBJECT_CLASS(klass); + field_class->is_filled = purple_request_field_string_is_filled; + obj_class->finalize = purple_request_field_string_finalize; obj_class->get_property = purple_request_field_string_get_property; obj_class->set_property = purple_request_field_string_set_property; @@ -237,14 +250,25 @@ void purple_request_field_string_set_value(PurpleRequestFieldString *field, const char *value) { + gboolean before, after; + g_return_if_fail(PURPLE_IS_REQUEST_FIELD_STRING(field)); - if(!purple_strequal(field->value, value)) { - g_free(field->value); - field->value = g_strdup(value); + if(purple_strequal(field->value, value)) { + return; + } + + before = purple_request_field_string_is_filled(PURPLE_REQUEST_FIELD(field)); + g_free(field->value); + field->value = g_strdup(value); + after = purple_request_field_string_is_filled(PURPLE_REQUEST_FIELD(field)); - g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALUE]); + g_object_freeze_notify(G_OBJECT(field)); + g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALUE]); + if(before != after) { + g_object_notify(G_OBJECT(field), "filled"); } + g_object_thaw_notify(G_OBJECT(field)); } void diff --git a/libpurple/tests/meson.build b/libpurple/tests/meson.build index 0f18916706..c922b80c3c 100644 --- a/libpurple/tests/meson.build +++ b/libpurple/tests/meson.build @@ -26,6 +26,7 @@ PROGS = [ 'protocol_xfer', 'purplepath', 'queued_output_stream', + 'request_field', 'str', 'tags', 'util', diff --git a/libpurple/tests/test_request_field.c b/libpurple/tests/test_request_field.c new file mode 100644 index 0000000000..a15d3e12b6 --- /dev/null +++ b/libpurple/tests/test_request_field.c @@ -0,0 +1,129 @@ +/* + * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers + * + * 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 . + */ + +#include + +#include + +/****************************************************************************** + * Tests + *****************************************************************************/ +static void +test_request_field_notify_filled_cb(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + gpointer data) +{ + gboolean *called = data; + + *called = TRUE; +} + +static void +test_request_field_filled_string(void) { + PurpleRequestField *field = NULL; + gboolean called = FALSE; + + field = purple_request_field_string_new("test-string", "Test string", NULL, + FALSE); + g_signal_connect(field, "notify::filled", + G_CALLBACK(test_request_field_notify_filled_cb), &called); + g_assert_false(purple_request_field_is_filled(field)); + + /* Passing same value should not trigger. */ + called = FALSE; + purple_request_field_string_set_value(PURPLE_REQUEST_FIELD_STRING(field), + NULL); + g_assert_false(called); + g_assert_false(purple_request_field_is_filled(field)); + + /* Passing an empty string should not trigger, as NULL and "" are + * considered the same. */ + called = FALSE; + purple_request_field_string_set_value(PURPLE_REQUEST_FIELD_STRING(field), + ""); + g_assert_false(called); + g_assert_false(purple_request_field_is_filled(field)); + + /* Now that there's a change from empty to filled, notify should occur. */ + called = FALSE; + purple_request_field_string_set_value(PURPLE_REQUEST_FIELD_STRING(field), + "text"); + g_assert_true(called); + g_assert_true(purple_request_field_is_filled(field)); + + /* Passing same value should not trigger. */ + called = FALSE; + purple_request_field_string_set_value(PURPLE_REQUEST_FIELD_STRING(field), + "text"); + g_assert_false(called); + g_assert_true(purple_request_field_is_filled(field)); + + /* And then going back to empty should notify. */ + called = FALSE; + purple_request_field_string_set_value(PURPLE_REQUEST_FIELD_STRING(field), + ""); + g_assert_true(called); + g_assert_false(purple_request_field_is_filled(field)); + + g_object_unref(field); +} + +static void +test_request_field_filled_nonstring(void) { + /* Anything that's not a string should always be considered filled and + * never notify. */ + PurpleRequestField *field = NULL; + gboolean called = FALSE; + + field = purple_request_field_int_new("test-int", "Test int", 50, 0, 100); + g_signal_connect(field, "notify::filled", + G_CALLBACK(test_request_field_notify_filled_cb), &called); + g_assert_true(purple_request_field_is_filled(field)); + + called = FALSE; + purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field), 50); + g_assert_false(called); + g_assert_true(purple_request_field_is_filled(field)); + + called = FALSE; + purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field), 0); + g_assert_false(called); + g_assert_true(purple_request_field_is_filled(field)); + + called = FALSE; + purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field), 100); + g_assert_false(called); + g_assert_true(purple_request_field_is_filled(field)); + + g_object_unref(field); +} + +/****************************************************************************** + * Main + *****************************************************************************/ +gint +main(gint argc, gchar *argv[]) { + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/request-field/filled-string", + test_request_field_filled_string); + g_test_add_func("/request-field/filled-nonstring", + test_request_field_filled_nonstring); + + return g_test_run(); +} -- cgit v1.2.1