pidgin/pidgin

Add PurpleRequest{Group,Page}:valid properties

14 months ago, Elliott Sales de Andrade
2bcdf415a79d
Parents 41b9c5be8965
Children f012db35bbc5
Add PurpleRequest{Group,Page}:valid properties

This collects the validity of the fields/groups within the group/page into a single value.

Testing Done:
Compiled and ran `ninja test`.

Reviewed at https://reviews.imfreedom.org/r/2348/
--- a/finch/gntrequest.c Thu Mar 16 22:36:19 2023 -0500
+++ b/finch/gntrequest.c Thu Mar 16 22:50:08 2023 -0500
@@ -379,7 +379,7 @@
if (!g_object_get_data(G_OBJECT(button), "cancellation-function") &&
(!purple_request_page_all_required_filled(page) ||
- !purple_request_page_all_valid(page))) {
+ !purple_request_page_is_valid(page))) {
purple_notify_error(button, _("Error"),
_("You must properly fill all the required fields."),
_("The required fields are underlined."), NULL);
--- a/libpurple/purplerequestgroup.c Thu Mar 16 22:36:19 2023 -0500
+++ b/libpurple/purplerequestgroup.c Thu Mar 16 22:50:08 2023 -0500
@@ -32,11 +32,13 @@
char *title;
GList *fields;
+ GHashTable *invalid_fields;
};
enum {
PROP_0,
PROP_TITLE,
+ PROP_VALID,
N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
@@ -53,6 +55,31 @@
}
/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_request_group_notify_field_cb(GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ gpointer data)
+{
+ PurpleRequestGroup *group = PURPLE_REQUEST_GROUP(data);
+ PurpleRequestField *field = PURPLE_REQUEST_FIELD(obj);
+ gboolean before, after;
+
+ before = purple_request_group_is_valid(group);
+ if(purple_request_field_is_valid(field, NULL)) {
+ g_hash_table_remove(group->invalid_fields, field);
+ } else {
+ g_hash_table_add(group->invalid_fields, field);
+ }
+ after = purple_request_group_is_valid(group);
+
+ if(before != after) {
+ g_object_notify_by_pspec(G_OBJECT(group), properties[PROP_VALID]);
+ }
+}
+
+/******************************************************************************
* GListModel Implementation
*****************************************************************************/
static GType
@@ -104,6 +131,9 @@
case PROP_TITLE:
g_value_set_string(value, purple_request_group_get_title(group));
break;
+ case PROP_VALID:
+ g_value_set_boolean(value, purple_request_group_is_valid(group));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
@@ -133,12 +163,14 @@
g_free(group->title);
g_list_free_full(group->fields, g_object_unref);
+ g_clear_pointer(&group->invalid_fields, g_hash_table_destroy);
G_OBJECT_CLASS(purple_request_group_parent_class)->finalize(obj);
}
static void
-purple_request_group_init(G_GNUC_UNUSED PurpleRequestGroup *group) {
+purple_request_group_init(PurpleRequestGroup *group) {
+ group->invalid_fields = g_hash_table_new(g_direct_hash, g_direct_equal);
}
static void
@@ -162,6 +194,19 @@
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ /**
+ * PurpleRequestGroup:valid:
+ *
+ * Whether all fields in a group are valid.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_VALID] = g_param_spec_boolean(
+ "valid", "valid",
+ "Whether all fields in a group are valid.",
+ TRUE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}
@@ -216,6 +261,10 @@
position = g_list_length(group->fields);
group->fields = g_list_append(group->fields, field);
+ purple_request_group_notify_field_cb(G_OBJECT(field), NULL, group);
+ g_signal_connect(field, "notify::valid",
+ G_CALLBACK(purple_request_group_notify_field_cb), group);
+
if(PURPLE_IS_REQUEST_PAGE(group->page)) {
_purple_request_page_add_field(group->page, field);
}
@@ -248,3 +297,10 @@
return group->page;
}
+
+gboolean
+purple_request_group_is_valid(PurpleRequestGroup *group) {
+ g_return_val_if_fail(PURPLE_IS_REQUEST_GROUP(group), FALSE);
+
+ return g_hash_table_size(group->invalid_fields) == 0;
+}
--- a/libpurple/purplerequestgroup.h Thu Mar 16 22:36:19 2023 -0500
+++ b/libpurple/purplerequestgroup.h Thu Mar 16 22:50:08 2023 -0500
@@ -107,6 +107,18 @@
*/
PurpleRequestPage *purple_request_group_get_page(PurpleRequestGroup *group);
+/**
+ * purple_request_group_is_valid:
+ * @group: The field.
+ *
+ * Returns whether or not all fields are valid.
+ *
+ * Returns: %TRUE if all fields in the group are valid, %FALSE otherwise.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_request_group_is_valid(PurpleRequestGroup *group);
+
G_END_DECLS
#endif /* PURPLE_REQUEST_GROUP_H */
--- a/libpurple/purplerequestpage.c Thu Mar 16 22:36:19 2023 -0500
+++ b/libpurple/purplerequestpage.c Thu Mar 16 22:50:08 2023 -0500
@@ -34,6 +34,7 @@
GObject parent;
GList *groups;
+ GHashTable *invalid_groups;
GHashTable *fields;
@@ -42,6 +43,38 @@
GList *validated_fields;
};
+enum {
+ PROP_0,
+ PROP_VALID,
+ N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {NULL, };
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_request_page_notify_group_cb(GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ gpointer data)
+{
+ PurpleRequestPage *page = PURPLE_REQUEST_PAGE(data);
+ PurpleRequestGroup *group = PURPLE_REQUEST_GROUP(obj);
+ gboolean before, after;
+
+ before = purple_request_page_is_valid(page);
+ if(purple_request_group_is_valid(group)) {
+ g_hash_table_remove(page->invalid_groups, group);
+ } else {
+ g_hash_table_add(page->invalid_groups, group);
+ }
+ after = purple_request_page_is_valid(page);
+
+ if(before != after) {
+ g_object_notify_by_pspec(G_OBJECT(page), properties[PROP_VALID]);
+ }
+}
+
/******************************************************************************
* GListModel Implementation
*****************************************************************************/
@@ -85,10 +118,27 @@
purple_request_page_list_model_init))
static void
+purple_request_page_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurpleRequestPage *page = PURPLE_REQUEST_PAGE(obj);
+
+ switch(param_id) {
+ case PROP_VALID:
+ g_value_set_boolean(value, purple_request_page_is_valid(page));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
purple_request_page_finalize(GObject *obj) {
PurpleRequestPage *page = PURPLE_REQUEST_PAGE(obj);
g_list_free_full(page->groups, g_object_unref);
+ g_clear_pointer(&page->invalid_groups, g_hash_table_destroy);
g_list_free(page->required_fields);
g_list_free(page->validated_fields);
g_hash_table_destroy(page->fields);
@@ -99,6 +149,7 @@
static void
purple_request_page_init(PurpleRequestPage *page) {
page->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ page->invalid_groups = g_hash_table_new(g_direct_hash, g_direct_equal);
}
static void
@@ -106,6 +157,22 @@
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
obj_class->finalize = purple_request_page_finalize;
+ obj_class->get_property = purple_request_page_get_property;
+
+ /**
+ * PurpleRequestPage:valid:
+ *
+ * Whether all fields in a page are valid.
+ *
+ * Since: 3.0.0
+ */
+ properties[PROP_VALID] = g_param_spec_boolean(
+ "valid", "valid",
+ "Whether all fields in a page are valid.",
+ TRUE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}
/******************************************************************************
@@ -177,6 +244,10 @@
_purple_request_group_set_page(group, page);
+ purple_request_page_notify_group_cb(G_OBJECT(group), NULL, page);
+ g_signal_connect(group, "notify::valid",
+ G_CALLBACK(purple_request_page_notify_group_cb), page);
+
for (l = purple_request_group_get_fields(group);
l != NULL;
l = l->next) {
@@ -248,19 +319,10 @@
}
gboolean
-purple_request_page_all_valid(PurpleRequestPage *page) {
- GList *l;
-
+purple_request_page_is_valid(PurpleRequestPage *page) {
g_return_val_if_fail(PURPLE_IS_REQUEST_PAGE(page), FALSE);
- for(l = page->validated_fields; l != NULL; l = l->next) {
- PurpleRequestField *field = PURPLE_REQUEST_FIELD(l->data);
-
- if (!purple_request_field_is_valid(field, NULL))
- return FALSE;
- }
-
- return TRUE;
+ return g_hash_table_size(page->invalid_groups) == 0;
}
PurpleRequestField *
--- a/libpurple/purplerequestpage.h Thu Mar 16 22:36:19 2023 -0500
+++ b/libpurple/purplerequestpage.h Thu Mar 16 22:50:08 2023 -0500
@@ -122,16 +122,16 @@
gboolean purple_request_page_all_required_filled(PurpleRequestPage *page);
/**
- * purple_request_page_all_valid:
+ * purple_request_page_is_valid:
* @page: The fields page.
*
* Returns whether or not all fields are valid.
*
- * Returns: TRUE if all fields are valid, or FALSE.
+ * Returns: %TRUE if all fields in the page are valid, %FALSE otherwise.
*
* Since: 3.0.0
*/
-gboolean purple_request_page_all_valid(PurpleRequestPage *page);
+gboolean purple_request_page_is_valid(PurpleRequestPage *page);
/**
* purple_request_page_get_field:
--- a/libpurple/tests/meson.build Thu Mar 16 22:36:19 2023 -0500
+++ b/libpurple/tests/meson.build Thu Mar 16 22:50:08 2023 -0500
@@ -27,6 +27,8 @@
'purplepath',
'queued_output_stream',
'request_field',
+ 'request_group',
+ 'request_page',
'str',
'tags',
'util',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_request_group.c Thu Mar 16 22:50:08 2023 -0500
@@ -0,0 +1,103 @@
+/*
+ * 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_request_group_valid_changed_cb(G_GNUC_UNUSED GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ gpointer data)
+{
+ gint *called = data;
+
+ *called += 1;
+}
+
+static void
+test_request_group_valid(void) {
+ PurpleRequestGroup *group = NULL;
+ PurpleRequestField *field1 = NULL, *field2 = NULL, *field3 = NULL;
+ gint called = 0;
+
+ group = purple_request_group_new("test-group");
+ g_signal_connect(group, "notify::valid",
+ G_CALLBACK(test_request_group_valid_changed_cb), &called);
+
+ /* Empty groups are always valid. */
+ g_assert_true(purple_request_group_is_valid(group));
+
+ /* An added valid field keeps the group valid. */
+ called = 0;
+ field1 = purple_request_field_int_new("test-int", "Test int", 50, 0, 100);
+ purple_request_group_add_field(group, field1);
+ g_assert_true(purple_request_group_is_valid(group));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Making the field invalid makes the group invalid. */
+ called = 0;
+ purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field1), -42);
+ g_assert_false(purple_request_group_is_valid(group));
+ g_assert_cmpint(called, ==, 1);
+
+ /* Adding an invalid field keeps the group invalid. */
+ called = 0;
+ field2 = purple_request_field_int_new("invalid", "Invalid", -42, 0, 100);
+ purple_request_group_add_field(group, field2);
+ g_assert_false(purple_request_group_is_valid(group));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Adding a valid field to an already invalid group does not change it to
+ * valid accidentally. */
+ called = 0;
+ field3 = purple_request_field_int_new("valid", "Valid", 42, 0, 100);
+ purple_request_group_add_field(group, field3);
+ g_assert_false(purple_request_group_is_valid(group));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Making one field valid while others are still invalid keeps the group
+ * invalid. */
+ called = 0;
+ purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field1), 42);
+ g_assert_false(purple_request_group_is_valid(group));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Making last invalid field valid makes the group valid again. */
+ called = 0;
+ purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field2), 42);
+ g_assert_true(purple_request_group_is_valid(group));
+ g_assert_cmpint(called, ==, 1);
+
+ g_object_unref(group);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar *argv[]) {
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/request-group/valid", test_request_group_valid);
+
+ return g_test_run();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_request_page.c Thu Mar 16 22:50:08 2023 -0500
@@ -0,0 +1,194 @@
+/*
+ * 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>
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static gboolean
+test_request_page_string_validator(PurpleRequestField *field, char **errmsg,
+ G_GNUC_UNUSED gpointer data)
+{
+ /* Validator for a string field that is valid if the value is "valid". */
+ PurpleRequestFieldString *strfield = PURPLE_REQUEST_FIELD_STRING(field);
+ const char *value = NULL;
+ gboolean result = TRUE;
+
+ value = purple_request_field_string_get_value(strfield);
+ if(!purple_strequal(value, "valid")) {
+ if(errmsg != NULL) {
+ *errmsg = g_strdup_printf("String value is not valid: %s", value);
+ }
+ result = FALSE;
+ }
+
+ return result;
+}
+
+static PurpleRequestGroup *
+test_request_page_new_valid_group(const char *name) {
+ PurpleRequestGroup *group = NULL;
+ PurpleRequestField *field = NULL;
+ char *field_name = NULL;
+
+ group = purple_request_group_new(name);
+
+ /* Field is valid, making the group valid. */
+ field_name = g_strdup_printf("%s-string", name);
+ field = purple_request_field_string_new(field_name, field_name, "valid",
+ FALSE);
+ purple_request_field_set_validator(field,
+ test_request_page_string_validator,
+ NULL, NULL);
+ g_free(field_name);
+ purple_request_group_add_field(group, field);
+
+ return group;
+}
+
+static PurpleRequestGroup *
+test_request_page_new_invalid_group(const char *name) {
+ PurpleRequestGroup *group = NULL;
+ PurpleRequestField *field = NULL;
+ char *field_name = NULL;
+
+ group = purple_request_group_new(name);
+
+ /* Field is invalid, making the group invalid. */
+ field_name = g_strdup_printf("%s-string", name);
+ field = purple_request_field_string_new(field_name, field_name, "invalid",
+ FALSE);
+ purple_request_field_set_validator(field,
+ test_request_page_string_validator,
+ NULL, NULL);
+ g_free(field_name);
+ purple_request_group_add_field(group, field);
+
+ return group;
+}
+
+static void
+test_request_page_make_group_valid(PurpleRequestGroup *group) {
+ GListModel *model = G_LIST_MODEL(group);
+ guint n_items;
+
+ n_items = g_list_model_get_n_items(model);
+ for(guint index = 0; index < n_items; index++) {
+ PurpleRequestFieldString *field = g_list_model_get_item(model, index);
+ purple_request_field_string_set_value(field, "valid");
+ g_object_unref(field);
+ }
+}
+
+static void
+test_request_page_make_group_invalid(PurpleRequestGroup *group) {
+ GListModel *model = G_LIST_MODEL(group);
+ guint n_items;
+
+ n_items = g_list_model_get_n_items(model);
+ for(guint index = 0; index < n_items; index++) {
+ PurpleRequestFieldString *field = g_list_model_get_item(model, index);
+ purple_request_field_string_set_value(field, "invalid");
+ g_object_unref(field);
+ }
+}
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_request_page_valid_changed_cb(G_GNUC_UNUSED GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ gpointer data)
+{
+ gint *called = data;
+
+ *called += 1;
+}
+
+static void
+test_request_page_valid(void) {
+ PurpleRequestPage *page = NULL;
+ PurpleRequestGroup *group1 = NULL, *group2 = NULL, *group3 = NULL;
+ gint called = FALSE;
+
+ page = purple_request_page_new();
+ g_signal_connect(page, "notify::valid",
+ G_CALLBACK(test_request_page_valid_changed_cb), &called);
+
+ /* Empty pages are always valid. */
+ g_assert_true(purple_request_page_is_valid(page));
+
+ /* An added valid group keeps the page valid. */
+ called = 0;
+ group1 = test_request_page_new_valid_group("group1");
+ purple_request_page_add_group(page, group1);
+ g_assert_true(purple_request_page_is_valid(page));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Making the group invalid makes the page invalid. */
+ called = 0;
+ test_request_page_make_group_invalid(group1);
+ g_assert_false(purple_request_page_is_valid(page));
+ g_assert_cmpint(called, ==, 1);
+
+ /* Adding an invalid group keeps the page invalid. */
+ called = 0;
+ group2 = test_request_page_new_invalid_group("group2");
+ purple_request_page_add_group(page, group2);
+ g_assert_false(purple_request_page_is_valid(page));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Adding a valid group to an already invalid page does not change it to
+ * valid accidentally. */
+ called = 0;
+ group3 = test_request_page_new_valid_group("group3");
+ purple_request_page_add_group(page, group3);
+ g_assert_false(purple_request_page_is_valid(page));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Making one group valid while others are still invalid keeps the group
+ * invalid. */
+ called = 0;
+ test_request_page_make_group_valid(group1);
+ g_assert_false(purple_request_page_is_valid(page));
+ g_assert_cmpint(called, ==, 0);
+
+ /* Making last invalid group valid makes the page valid again. */
+ called = 0;
+ test_request_page_make_group_valid(group2);
+ g_assert_true(purple_request_page_is_valid(page));
+ g_assert_cmpint(called, ==, 1);
+
+ g_object_unref(page);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar *argv[]) {
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/request-page/valid", test_request_page_valid);
+
+ return g_test_run();
+}
--- a/pidgin/gtkrequest.c Thu Mar 16 22:36:19 2023 -0500
+++ b/pidgin/gtkrequest.c Thu Mar 16 22:50:08 2023 -0500
@@ -235,7 +235,7 @@
gtk_widget_set_sensitive(req_data->ok_button,
purple_request_page_all_required_filled(page) &&
- purple_request_page_all_valid(page));
+ purple_request_page_is_valid(page));
}
static void
@@ -2208,7 +2208,7 @@
gtk_widget_set_sensitive(data->ok_button, FALSE);
}
- if(!purple_request_page_all_valid(page)) {
+ if(!purple_request_page_is_valid(page)) {
gtk_widget_set_sensitive(data->ok_button, FALSE);
}