/* SPDX-FileCopyrightText: 2024 - Sébastien Wilmet
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#include "tepl-settings-binding-group.h"
#include <gio/gio.h>

/**
 * SECTION:settings-binding-group
 * @Title: TeplSettingsBindingGroup
 * @Short_description: A group of #GSettings bindings
 *
 * #TeplSettingsBindingGroup is a small utility to call g_settings_unbind() on a
 * group of properties (on the same #GObject).
 *
 * The documentation of g_settings_unbind() says that bindings are automatically
 * removed when the object is finalized. The purpose of
 * #TeplSettingsBindingGroup is to unbind the settings in `dispose()` instead.
 * Setting a property while an object has already been disposed but not
 * finalized is usually not well tested, and can result to crashes, so it is
 * more robust to unbind the settings in `dispose()`.
 *
 * To achieve the same, you can also write a list of g_settings_unbind() calls
 * at the appropriate place. But where g_settings_bind() (or similar) and
 * g_settings_unbind() need to be called are at different places so it is more
 * error-prone to maintain. With this utility, tepl_settings_binding_group_add()
 * is called at the same place as g_settings_bind().
 */

struct _TeplSettingsBindingGroup
{
	GSList *property_names;
};

/**
 * tepl_settings_binding_group_new: (skip)
 *
 * Returns: (transfer full): a new #TeplSettingsBindingGroup instance.
 * Since: 6.11
 */
TeplSettingsBindingGroup *
tepl_settings_binding_group_new (void)
{
	return g_new0 (TeplSettingsBindingGroup, 1);
}

/**
 * tepl_settings_binding_group_free: (skip)
 * @group: (nullable): a #TeplSettingsBindingGroup.
 *
 * Frees a #TeplSettingsBindingGroup instance.
 *
 * Since: 6.11
 */
void
tepl_settings_binding_group_free (TeplSettingsBindingGroup *group)
{
	if (group != NULL)
	{
		g_slist_free_full (group->property_names, g_free);
		g_free (group);
	}
}

/**
 * tepl_settings_binding_group_add:
 * @group: a #TeplSettingsBindingGroup.
 * @property_name: the name of a #GObject property.
 *
 * Adds @property_name to @group.
 *
 * Each time that you call g_settings_bind() (or similar), you should call this
 * function too.
 *
 * Since: 6.11
 */
void
tepl_settings_binding_group_add (TeplSettingsBindingGroup *group,
				 const gchar              *property_name)
{
	g_return_if_fail (group != NULL);
	g_return_if_fail (property_name != NULL);

	group->property_names = g_slist_prepend (group->property_names,
						 g_strdup (property_name));
}

/**
 * tepl_settings_binding_group_unbind:
 * @group: a #TeplSettingsBindingGroup.
 * @object: a #GObject.
 *
 * Calls g_settings_unbind() on @object for all properties that have been added
 * to @group.
 *
 * This function is intended to be called in `dispose()`.
 *
 * Since: 6.11
 */
void
tepl_settings_binding_group_unbind (TeplSettingsBindingGroup *group,
				    GObject                  *object)
{
	GSList *l;

	g_return_if_fail (group != NULL);
	g_return_if_fail (G_IS_OBJECT (object));

	for (l = group->property_names; l != NULL; l = l->next)
	{
		const gchar *property_name = l->data;

		g_settings_unbind (object, property_name);
	}
}
