--- a/libpurple/protocols/demo/purpledemoprotocolactions.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/protocols/demo/purpledemoprotocolactions.c Thu Mar 16 20:43:41 2023 -0500
@@ -502,13 +502,13 @@
purple_request_field_set_validator(field,
purple_request_field_alphanumeric_validator,
purple_request_group_add_field(group, field);
field = purple_request_field_string_new("email", _("An email"),
_("me@example.com"), FALSE);
purple_request_field_set_validator(field,
purple_request_field_email_validator,
purple_request_group_add_field(group, field);
field = purple_request_field_int_new("int", _("An integer"), 123, -42, 1337);
purple_request_group_add_field(group, field);
--- a/libpurple/purplerequestfield.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/purplerequestfield.c Thu Mar 16 20:43:41 2023 -0500
@@ -36,8 +36,7 @@
- PurpleRequestFieldValidator validator;
} PurpleRequestFieldPrivate;
@@ -50,6 +49,7 @@
@@ -111,6 +111,10 @@
g_value_set_boolean(value,
purple_request_field_is_filled(field));
+ g_value_set_boolean(value, + purple_request_field_is_valid(field, NULL)); case PROP_IS_VALIDATABLE:
g_value_set_boolean(value,
purple_request_field_is_validatable(field));
@@ -170,6 +174,7 @@
+ g_clear_pointer(&priv->validator, g_closure_unref); G_OBJECT_CLASS(purple_request_field_parent_class)->finalize(obj);
@@ -297,6 +302,19 @@
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ * PurpleRequestField:valid: + * Whether the field has a valid value. + properties[PROP_VALID] = g_param_spec_boolean( + "Whether the field has a valid value.", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); * PurpleRequestField:is-validatable:
* Whether the field can be validated by the requestor.
@@ -505,7 +523,9 @@
purple_request_field_set_validator(PurpleRequestField *field,
- PurpleRequestFieldValidator validator, void *user_data)
+ PurpleRequestFieldValidator validator, + GDestroyNotify destroy_data) PurpleRequestFieldPrivate *priv = NULL;
@@ -513,8 +533,14 @@
priv = purple_request_field_get_instance_private(field);
- priv->validator = validator;
- priv->validator_data = validator ? user_data : NULL;
+ g_clear_pointer(&priv->validator, g_closure_unref); + if(validator != NULL) { + priv->validator = g_cclosure_new(G_CALLBACK(validator), user_data, + (GClosureNotify)G_CALLBACK(destroy_data)); + g_closure_ref(priv->validator); + g_closure_sink(priv->validator); + g_closure_set_marshal(priv->validator, g_cclosure_marshal_generic); if(PURPLE_IS_REQUEST_GROUP(priv->group)) {
_purple_request_group_set_field_validator(priv->group, field,
@@ -522,6 +548,7 @@
g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_IS_VALIDATABLE]);
+ g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALID]); @@ -539,25 +566,49 @@
purple_request_field_is_valid(PurpleRequestField *field, gchar **errmsg)
+ PurpleRequestFieldClass *klass = NULL; PurpleRequestFieldPrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_REQUEST_FIELD(field), FALSE);
- priv = purple_request_field_get_instance_private(field);
+ if(!purple_request_field_is_required(field) && + !purple_request_field_is_filled(field)) - if (!purple_request_field_is_required(field) &&
- !purple_request_field_is_filled(field))
+ klass = PURPLE_REQUEST_FIELD_GET_CLASS(field); + if(klass != NULL && klass->is_valid != NULL) { + valid = klass->is_valid(field, errmsg); - valid = priv->validator(field, errmsg, priv->validator_data);
+ priv = purple_request_field_get_instance_private(field); + if(valid && priv->validator != NULL) { + GValue result = G_VALUE_INIT; + GValue params[] = {G_VALUE_INIT, G_VALUE_INIT}; + g_value_init(&result, G_TYPE_BOOLEAN); + g_value_set_instance(g_value_init(¶ms[0], + PURPLE_TYPE_REQUEST_FIELD), + g_value_set_pointer(g_value_init(¶ms[1], G_TYPE_POINTER), errmsg); + g_closure_invoke(priv->validator, &result, + G_N_ELEMENTS(params), params, NULL); + valid = g_value_get_boolean(&result); + g_value_unset(&result); + for(gsize i = 0; i < G_N_ELEMENTS(params); i++) { + g_value_unset(¶ms[i]);
+ if(!valid && errmsg != NULL && *errmsg == NULL) { + *errmsg = g_strdup(_("Validation failed without setting an error " --- a/libpurple/purplerequestfield.h Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/purplerequestfield.h Thu Mar 16 20:43:41 2023 -0500
@@ -54,13 +54,23 @@
gboolean (*is_filled)(PurpleRequestField *field);
+ gboolean (*is_valid)(PurpleRequestField *field, char **errmsg); -typedef gboolean (*PurpleRequestFieldValidator)(PurpleRequestField *field,
- gchar **errmsg, gpointer user_data);
+ * PurpleRequestFieldValidator: + * @errmsg: (nullable) (optional) (out): A location to store an error message + * if the field is invalid. + * @user_data: (closure): The data passed to + * [method@Purple.RequestField.set_validator]. + * A callback to check whether a field is valid. +typedef gboolean (*PurpleRequestFieldValidator)(PurpleRequestField *field, char **errmsg, gpointer user_data); @@ -207,13 +217,14 @@
* purple_request_field_set_validator:
- * @validator: (scope notified): The validator callback, NULL to disable validation.
- * @user_data: The data to pass to the callback.
+ * @validator: (scope notified) (closure user_data): The validator callback, or + * %NULL to disable additional validation. + * @user_data: The data to pass to the validator callback. + * @destroy_data: A cleanup function for @user_data. - * Sets validator for a single field.
+ * Set an additional validator for a field. -void purple_request_field_set_validator(PurpleRequestField *field,
- PurpleRequestFieldValidator validator, void *user_data);
+void purple_request_field_set_validator(PurpleRequestField *field, PurpleRequestFieldValidator validator, gpointer user_data, GDestroyNotify destroy_data); * purple_request_field_is_validatable:
@@ -228,8 +239,8 @@
* purple_request_field_is_valid:
- * @errmsg: If non-NULL, the memory area, where the pointer to validation
- * failure message will be set.
+ * @errmsg: (nullable) (optional) (out): If non-%NULL, the memory area, where + * the validation failure message will be returned. * Checks, if specified field is valid.
--- a/libpurple/request/purplerequestfieldaccount.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/request/purplerequestfieldaccount.c Thu Mar 16 20:43:41 2023 -0500
@@ -217,7 +217,10 @@
g_return_if_fail(PURPLE_IS_REQUEST_FIELD_ACCOUNT(field));
if(g_set_object(&field->account, value)) {
+ g_object_freeze_notify(G_OBJECT(field)); g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALUE]);
+ g_object_notify(G_OBJECT(field), "valid"); + g_object_thaw_notify(G_OBJECT(field)); --- a/libpurple/request/purplerequestfieldbool.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/request/purplerequestfieldbool.c Thu Mar 16 20:43:41 2023 -0500
@@ -173,7 +173,10 @@
- g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_DEFAULT_VALUE]);
+ g_object_freeze_notify(G_OBJECT(field)); + g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALUE]); + g_object_notify(G_OBJECT(field), "valid"); + g_object_thaw_notify(G_OBJECT(field)); --- a/libpurple/request/purplerequestfieldchoice.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/request/purplerequestfieldchoice.c Thu Mar 16 20:43:41 2023 -0500
@@ -207,7 +207,10 @@
+ g_object_freeze_notify(G_OBJECT(field)); g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALUE]);
+ g_object_notify(G_OBJECT(field), "valid"); + g_object_thaw_notify(G_OBJECT(field)); --- a/libpurple/request/purplerequestfieldint.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/request/purplerequestfieldint.c Thu Mar 16 20:43:41 2023 -0500
@@ -45,6 +45,32 @@
static GParamSpec *properties[N_PROPERTIES] = {NULL, };
/******************************************************************************
+ * PurpleRequestField Implementation + *****************************************************************************/ +purple_request_field_int_is_valid(PurpleRequestField *field, char **errmsg) { + PurpleRequestFieldInt *intfield = PURPLE_REQUEST_FIELD_INT(field); + if(intfield->value < intfield->lower_bound) { + *errmsg = g_strdup_printf(_("Int value %d exceeds lower bound %d"), + intfield->value, intfield->lower_bound); + if(intfield->value > intfield->upper_bound) { + *errmsg = g_strdup_printf(_("Int value %d exceeds upper bound %d"), + intfield->value, intfield->upper_bound); +/****************************************************************************** *****************************************************************************/
G_DEFINE_TYPE(PurpleRequestFieldInt, purple_request_field_int,
@@ -112,8 +138,11 @@
purple_request_field_int_class_init(PurpleRequestFieldIntClass *klass) {
+ PurpleRequestFieldClass *request_class = PURPLE_REQUEST_FIELD_CLASS(klass); GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ request_class->is_valid = purple_request_field_int_is_valid; obj_class->get_property = purple_request_field_int_get_property;
obj_class->set_property = purple_request_field_int_set_property;
@@ -220,7 +249,10 @@
field->lower_bound = lower_bound;
+ g_object_freeze_notify(G_OBJECT(field)); g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_LOWER_BOUND]);
+ g_object_notify(G_OBJECT(field), "valid"); + g_object_thaw_notify(G_OBJECT(field)); @@ -235,26 +267,26 @@
field->upper_bound = upper_bound;
+ g_object_freeze_notify(G_OBJECT(field)); g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_UPPER_BOUND]);
+ g_object_notify(G_OBJECT(field), "valid"); + g_object_thaw_notify(G_OBJECT(field)); purple_request_field_int_set_value(PurpleRequestFieldInt *field, int value) {
g_return_if_fail(PURPLE_IS_REQUEST_FIELD_INT(field));
- if(value < field->lower_bound || value > field->upper_bound) {
- g_warning("Int value %d out of bounds (%d, %d)", value,
- field->lower_bound, field->upper_bound);
if(field->value == value) {
+ g_object_freeze_notify(G_OBJECT(field)); g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALUE]);
+ g_object_notify(G_OBJECT(field), "valid"); + g_object_thaw_notify(G_OBJECT(field)); --- a/libpurple/request/purplerequestfieldlist.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/request/purplerequestfieldlist.c Thu Mar 16 20:43:41 2023 -0500
@@ -190,6 +190,7 @@
kvp = purple_key_value_pair_new_full(item, g_strdup(icon_path), g_free);
field->items = g_list_append(field->items, kvp);
g_hash_table_insert(field->item_data, g_strdup(item), data);
+ g_object_notify(G_OBJECT(field), "valid"); @@ -208,6 +209,7 @@
field->selected = g_list_append(field->selected, g_strdup(item));
g_hash_table_add(field->selected_table, g_strdup(item));
+ g_object_notify(G_OBJECT(field), "valid"); @@ -218,6 +220,7 @@
g_hash_table_remove_all(field->selected_table);
+ g_object_notify(G_OBJECT(field), "valid"); @@ -242,6 +245,8 @@
field->selected = g_list_append(field->selected, g_strdup(selected));
g_hash_table_add(field->selected_table, g_strdup(selected));
+ g_object_notify(G_OBJECT(field), "valid"); --- a/libpurple/request/purplerequestfieldstring.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/request/purplerequestfieldstring.c Thu Mar 16 20:43:41 2023 -0500
@@ -265,6 +265,7 @@
g_object_freeze_notify(G_OBJECT(field));
g_object_notify_by_pspec(G_OBJECT(field), properties[PROP_VALUE]);
+ g_object_notify(G_OBJECT(field), "valid"); g_object_notify(G_OBJECT(field), "filled");
--- a/libpurple/request/purplerequestfieldstring.h Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/request/purplerequestfieldstring.h Thu Mar 16 20:43:41 2023 -0500
@@ -134,7 +134,7 @@
* Validates a field which should contain an email address.
- * See purple_request_field_set_validator().
+ * See [method@Purple.RequestField.set_validator]. * Returns: TRUE, if field contains valid email address.
@@ -149,7 +149,7 @@
* Validates a field which should contain alphanumeric content.
- * See purple_request_field_set_validator().
+ * See [method@Purple.RequestField.set_validator]. * Returns: TRUE, if field contains only alphanumeric characters.
--- a/libpurple/tests/test_request_field.c Tue Mar 14 00:42:47 2023 -0500
+++ b/libpurple/tests/test_request_field.c Thu Mar 16 20:43:41 2023 -0500
@@ -113,6 +113,96 @@
+test_request_field_valid_int(void) { + PurpleRequestField *field = NULL; + field = purple_request_field_int_new("test-int", "Test int", 50, 0, 100); + result = purple_request_field_is_valid(field, &errmsg); + purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field), -42); + result = purple_request_field_is_valid(field, &errmsg); + g_assert_cmpstr(errmsg, ==, "Int value -42 exceeds lower bound 0"); + g_assert_false(result); + /* Don't crash if no error message is requested. */ + result = purple_request_field_is_valid(field, NULL); + g_assert_false(result); + purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field), 1337); + result = purple_request_field_is_valid(field, &errmsg); + g_assert_cmpstr(errmsg, ==, "Int value 1337 exceeds upper bound 100"); + g_assert_false(result); + /* Don't crash if no error message is requested. */ + result = purple_request_field_is_valid(field, NULL); + g_assert_false(result); +test_request_field_validator_is_even(PurpleRequestField *field, char **errmsg, + G_GNUC_UNUSED gpointer data) + gboolean *called = data; + g_return_val_if_fail(PURPLE_IS_REQUEST_FIELD_INT(field), FALSE); + value = purple_request_field_int_get_value(PURPLE_REQUEST_FIELD_INT(field)); + *errmsg = g_strdup_printf("Value %d is not even.", value); +test_request_field_valid_custom(void) { + PurpleRequestField *field = NULL; + gboolean called = FALSE; + field = purple_request_field_int_new("test-int", "Test int", 50, 0, 100); + purple_request_field_set_validator(field, + test_request_field_validator_is_even, + result = purple_request_field_is_valid(field, &errmsg); + g_assert_cmpstr(errmsg, ==, NULL); + /* Default validator (i.e., the bounds) is checked first. */ + purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field), -42); + result = purple_request_field_is_valid(field, &errmsg); + g_assert_cmpstr(errmsg, ==, "Int value -42 exceeds lower bound 0"); + g_assert_false(result); + g_assert_false(called); + /* But if default validator passes, then the custom one is checked. */ + purple_request_field_int_set_value(PURPLE_REQUEST_FIELD_INT(field), 23); + result = purple_request_field_is_valid(field, &errmsg); + g_assert_cmpstr(errmsg, ==, "Value 23 is not even."); + g_assert_false(result); /******************************************************************************
*****************************************************************************/
@@ -125,5 +215,9 @@
g_test_add_func("/request-field/filled-nonstring",
test_request_field_filled_nonstring);
+ g_test_add_func("/request-field/valid-int", test_request_field_valid_int); + g_test_add_func("/request-field/valid-custom", + test_request_field_valid_custom); --- a/pidgin/gtkdialogs.c Tue Mar 14 00:42:47 2023 -0500
+++ b/pidgin/gtkdialogs.c Thu Mar 16 20:43:41 2023 -0500
@@ -96,7 +96,8 @@
field = purple_request_field_string_new("screenname", _("_Name"), NULL, FALSE);
purple_request_field_set_type_hint(field, "screenname");
purple_request_field_set_required(field, TRUE);
- purple_request_field_set_validator(field, pidgin_dialogs_im_name_validator, page);
+ purple_request_field_set_validator(field, pidgin_dialogs_im_name_validator, purple_request_group_add_field(group, field);
field = purple_request_field_account_new("account", _("_Account"), NULL);