The most common use case for CcNumberRow is to bind it to a GSettings key value. So, let's add an easy function to perform such a binding. It binds the "selected" property of the underlying AdwComboRow GSettings value using mapping functions. If the value does not exist in the CcNumberRow yet, it will be added to the list of the row and selected. It makes sure both unsigned and signed values are properly handled, as CcNumberRow only holds signed values.
713 lines
22 KiB
C
713 lines
22 KiB
C
/* cc-number-row.c
|
|
*
|
|
* Copyright 2024 Matthijs Velsink <mvelsink@gnome.org>
|
|
*
|
|
* 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 3 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
#undef G_LOG_DOMAIN
|
|
#define G_LOG_DOMAIN "cc-number-row"
|
|
|
|
#include "cc-number-row.h"
|
|
#include "cc-number-row-enums.h"
|
|
#include "cc-util.h"
|
|
|
|
/**
|
|
* CcNumberObject:
|
|
*
|
|
* Simple object that wraps an integer number, with the option of
|
|
* mapping it to a custom string and have it be sorted in a
|
|
* specific manner.
|
|
*/
|
|
|
|
struct _CcNumberObject {
|
|
GObject parent_instance;
|
|
|
|
int value;
|
|
char *string;
|
|
CcNumberOrder order;
|
|
};
|
|
|
|
G_DEFINE_TYPE (CcNumberObject, cc_number_object, G_TYPE_OBJECT);
|
|
|
|
enum {
|
|
OBJ_PROP_0,
|
|
OBJ_PROP_VALUE,
|
|
OBJ_PROP_STRING,
|
|
OBJ_PROP_ORDER,
|
|
OBJ_N_PROPS
|
|
};
|
|
|
|
static GParamSpec *obj_props[OBJ_N_PROPS];
|
|
|
|
static void
|
|
cc_number_object_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
CcNumberObject *self = CC_NUMBER_OBJECT (object);
|
|
|
|
switch (prop_id) {
|
|
case OBJ_PROP_VALUE:
|
|
g_value_set_int (value, self->value);
|
|
break;
|
|
case OBJ_PROP_STRING:
|
|
g_value_set_string (value, self->string);
|
|
break;
|
|
case OBJ_PROP_ORDER:
|
|
g_value_set_enum (value, self->order);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_number_object_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
CcNumberObject *self = CC_NUMBER_OBJECT (object);
|
|
|
|
switch (prop_id) {
|
|
case OBJ_PROP_VALUE:
|
|
self->value = g_value_get_int (value);
|
|
break;
|
|
case OBJ_PROP_STRING:
|
|
self->string = g_value_dup_string (value); /* Construct only, no need to free old str */
|
|
break;
|
|
case OBJ_PROP_ORDER:
|
|
self->order = g_value_get_enum (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_number_object_dispose (GObject *object)
|
|
{
|
|
CcNumberObject *self = CC_NUMBER_OBJECT (object);
|
|
|
|
g_clear_pointer (&self->string, g_free);
|
|
|
|
G_OBJECT_CLASS (cc_number_object_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
cc_number_object_class_init (CcNumberObjectClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = cc_number_object_dispose;
|
|
object_class->get_property = cc_number_object_get_property;
|
|
object_class->set_property = cc_number_object_set_property;
|
|
|
|
/**
|
|
* CcNumberObject:value: (attributes org.gtk.Property.get=cc_number_object_get_value)
|
|
*
|
|
* The numeric value.
|
|
*/
|
|
obj_props[OBJ_PROP_VALUE] =
|
|
g_param_spec_int ("value", NULL, NULL,
|
|
INT_MIN, INT_MAX, 0,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* CcNumberObject:string: (attributes org.gtk.Property.get=cc_number_object_get_string)
|
|
*
|
|
* The (optional) fixed string representation of the stored value.
|
|
*/
|
|
obj_props[OBJ_PROP_STRING] =
|
|
g_param_spec_string ("string", NULL, NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* CcNumberObject:order: (attributes org.gtk.Property.get=cc_number_object_get_order)
|
|
*
|
|
* The (optional) fixed ordering of the `CcNumberObject` inside a `CcNumberRow` list.
|
|
*/
|
|
obj_props[OBJ_PROP_ORDER] =
|
|
g_param_spec_enum ("order", NULL, NULL,
|
|
CC_TYPE_NUMBER_ORDER, CC_NUMBER_ORDER_DEFAULT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (object_class, OBJ_N_PROPS, obj_props);
|
|
}
|
|
|
|
static void
|
|
cc_number_object_init (CcNumberObject *self)
|
|
{
|
|
}
|
|
|
|
|
|
/**
|
|
* cc_number_object_new:
|
|
* @value: the value to store in the `CcNumberObject`
|
|
*
|
|
* Creates a new `CcNumberObject` holding @value.
|
|
*
|
|
* Returns: the newly created `CcNumberObject`
|
|
*/
|
|
CcNumberObject *
|
|
cc_number_object_new (int value)
|
|
{
|
|
return g_object_new (CC_TYPE_NUMBER_OBJECT, "value", value, NULL);
|
|
}
|
|
|
|
/**
|
|
* cc_number_object_new_full:
|
|
* @value: the value to store in the `CcNumberObject`
|
|
* @string: (nullable): the fixed string representation of @value
|
|
* @order: the fixed ordering of @value
|
|
*
|
|
* Creates a new `CcNumberObject` holding @value, with special @string
|
|
* representation and @order ordering.
|
|
*
|
|
* Returns: the newly created `CcNumberObject`
|
|
*/
|
|
CcNumberObject *
|
|
cc_number_object_new_full (int value,
|
|
const char *string,
|
|
CcNumberOrder order)
|
|
{
|
|
return g_object_new (CC_TYPE_NUMBER_OBJECT, "value", value, "string", string,
|
|
"order", order, NULL);
|
|
}
|
|
|
|
/**
|
|
* cc_number_object_get_value:
|
|
* @self: a `CcNumberObject`
|
|
*
|
|
* Gets the stored value.
|
|
*
|
|
* Returns: the stored value
|
|
*/
|
|
int
|
|
cc_number_object_get_value (CcNumberObject *self)
|
|
{
|
|
g_return_val_if_fail (CC_IS_NUMBER_OBJECT (self), 0);
|
|
|
|
return self->value;
|
|
}
|
|
|
|
/**
|
|
* cc_number_object_get_string:
|
|
* @self: a `CcNumberObject`
|
|
*
|
|
* Gets the fixed string representation of the stored value.
|
|
*
|
|
* Returns: (transfer full) (nullable): the fixed string representation
|
|
*/
|
|
char*
|
|
cc_number_object_get_string (CcNumberObject *self)
|
|
{
|
|
g_return_val_if_fail (CC_IS_NUMBER_OBJECT (self), NULL);
|
|
|
|
return g_strdup (self->string);
|
|
}
|
|
|
|
/**
|
|
* cc_number_object_get_order:
|
|
* @self: a `CcNumberObject`
|
|
*
|
|
* Gets the fixed orderering of @self inside a `CcNumberRow` list.
|
|
*
|
|
* Returns: (nullable): the fixed orderering
|
|
*/
|
|
CcNumberOrder
|
|
cc_number_object_get_order (CcNumberObject *self)
|
|
{
|
|
g_return_val_if_fail (CC_IS_NUMBER_OBJECT (self), CC_NUMBER_ORDER_DEFAULT);
|
|
|
|
return self->order;
|
|
}
|
|
|
|
#define MILLIS_PER_SEC (1000)
|
|
#define MILLIS_PER_MIN (60 * MILLIS_PER_SEC)
|
|
#define MILLIS_PER_HOUR (60 * MILLIS_PER_MIN)
|
|
|
|
static char *
|
|
cc_number_object_to_string_for_seconds (CcNumberObject *self)
|
|
{
|
|
if (self->string)
|
|
return g_strdup (self->string);
|
|
|
|
return cc_util_time_to_string_text ((gint64) MILLIS_PER_SEC * self->value);
|
|
}
|
|
|
|
static char *
|
|
cc_number_object_to_string_for_minutes (CcNumberObject *self)
|
|
{
|
|
if (self->string)
|
|
return g_strdup (self->string);
|
|
|
|
return cc_util_time_to_string_text ((gint64) MILLIS_PER_MIN * self->value);
|
|
}
|
|
|
|
static char *
|
|
cc_number_object_to_string_for_hours (CcNumberObject *self)
|
|
{
|
|
if (self->string)
|
|
return g_strdup (self->string);
|
|
|
|
return cc_util_time_to_string_text ((gint64) MILLIS_PER_HOUR * self->value);
|
|
}
|
|
|
|
/**
|
|
* CcNumberRow:
|
|
*
|
|
* `CcNumberRow` is an `AdwComboRow` with a model that wraps a GListStore of
|
|
* `CcStringObject`. It has convenient methods to add values directly.
|
|
*/
|
|
|
|
struct _CcNumberRow {
|
|
AdwComboRow parent_instance;
|
|
|
|
GListStore *store;
|
|
CcNumberValueType value_type;
|
|
CcNumberSortType sort_type;
|
|
};
|
|
|
|
G_DEFINE_TYPE(CcNumberRow, cc_number_row, ADW_TYPE_COMBO_ROW)
|
|
|
|
enum {
|
|
ROW_PROP_0,
|
|
ROW_PROP_VALUE_TYPE,
|
|
ROW_PROP_SORT_TYPE,
|
|
ROW_PROP_VALUES,
|
|
ROW_PROP_SPECIAL_VALUE,
|
|
ROW_N_PROPS
|
|
};
|
|
|
|
static GParamSpec *row_props[ROW_N_PROPS];
|
|
|
|
static void
|
|
cc_number_row_add_values_from_variant (CcNumberRow *self,
|
|
GVariant *variant)
|
|
{
|
|
const int *values;
|
|
gsize n_elements, i;
|
|
g_autoptr(GPtrArray) numbers = NULL;
|
|
|
|
if (!variant)
|
|
return;
|
|
|
|
/* Splice onto the store to not have a notify for every addition */
|
|
values = g_variant_get_fixed_array (variant, &n_elements, sizeof (*values));
|
|
numbers = g_ptr_array_new_full (n_elements, g_object_unref);
|
|
|
|
for (i = 0; i < n_elements; i++)
|
|
g_ptr_array_add (numbers, cc_number_object_new (values[i]));
|
|
|
|
/* Just splice at the start, it gets sorted in constructed() anyways */
|
|
g_list_store_splice (self->store, 0, 0, numbers->pdata, n_elements);
|
|
}
|
|
|
|
static void
|
|
cc_number_row_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
CcNumberRow *self = CC_NUMBER_ROW (object);
|
|
CcNumberObject *number;
|
|
|
|
switch (prop_id) {
|
|
case ROW_PROP_VALUE_TYPE:
|
|
self->value_type = g_value_get_enum (value);
|
|
break;
|
|
case ROW_PROP_SORT_TYPE:
|
|
self->sort_type = g_value_get_enum (value);
|
|
break;
|
|
case ROW_PROP_VALUES:
|
|
cc_number_row_add_values_from_variant (self, g_value_get_variant (value));
|
|
break;
|
|
case ROW_PROP_SPECIAL_VALUE:
|
|
/* Construct-only property, so check for NULL, as NULL is passed if not provided */
|
|
number = g_value_get_object (value);
|
|
if (number)
|
|
g_list_store_append (self->store, number);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static int
|
|
compare_numbers (CcNumberObject *number_a,
|
|
CcNumberObject *number_b,
|
|
CcNumberRow *self)
|
|
{
|
|
/* Handle special order first (works because of the ordering of CcNumberOrder) */
|
|
if (number_a->order != CC_NUMBER_ORDER_DEFAULT ||
|
|
number_b->order != CC_NUMBER_ORDER_DEFAULT)
|
|
return number_a->order - number_b->order;
|
|
|
|
/* Otherwise normal value comparison */
|
|
if (self->sort_type == CC_NUMBER_SORT_ASCENDING)
|
|
return number_a->value - number_b->value;
|
|
else if (self->sort_type == CC_NUMBER_SORT_DESCENDING)
|
|
return number_b->value - number_a->value;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
cc_number_row_constructed (GObject *obj)
|
|
{
|
|
CcNumberRow *self = CC_NUMBER_ROW (obj);
|
|
|
|
/* Sort now, as construct-only values could be added before sort-type was changed */
|
|
g_list_store_sort (self->store, (GCompareDataFunc) compare_numbers, self);
|
|
|
|
/* Only now add it as a model */
|
|
adw_combo_row_set_model (ADW_COMBO_ROW (self), G_LIST_MODEL (self->store));
|
|
|
|
/* And set the expression based on the value-type */
|
|
if (self->value_type != CC_NUMBER_VALUE_CUSTOM) {
|
|
char * (*number_to_string_func)(CcNumberObject *);
|
|
g_autoptr(GtkExpression) expression = NULL;
|
|
|
|
switch (self->value_type) {
|
|
case CC_NUMBER_VALUE_STRING:
|
|
number_to_string_func = cc_number_object_get_string;
|
|
break;
|
|
case CC_NUMBER_VALUE_SECONDS:
|
|
number_to_string_func = cc_number_object_to_string_for_seconds;
|
|
break;
|
|
case CC_NUMBER_VALUE_MINUTES:
|
|
number_to_string_func = cc_number_object_to_string_for_minutes;
|
|
break;
|
|
case CC_NUMBER_VALUE_HOURS:
|
|
number_to_string_func = cc_number_object_to_string_for_hours;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
|
|
0, NULL,
|
|
G_CALLBACK (number_to_string_func),
|
|
NULL, NULL);
|
|
adw_combo_row_set_expression (ADW_COMBO_ROW (self), expression);
|
|
}
|
|
|
|
G_OBJECT_CLASS (cc_number_row_parent_class)->constructed (obj);
|
|
}
|
|
|
|
static void
|
|
cc_number_row_dispose (GObject *object)
|
|
{
|
|
CcNumberRow *self = CC_NUMBER_ROW (object);
|
|
|
|
g_clear_object (&self->store);
|
|
|
|
G_OBJECT_CLASS (cc_number_row_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
cc_number_row_class_init (CcNumberRowClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
g_autoptr(GVariantType) values_type = NULL;
|
|
|
|
object_class->constructed = cc_number_row_constructed;
|
|
object_class->dispose = cc_number_row_dispose;
|
|
object_class->set_property = cc_number_row_set_property;
|
|
|
|
/**
|
|
* CcNumberRow:value-type:
|
|
*
|
|
* The interpretation of the values in the list of the row. Determines what
|
|
* strings will be generated to represent the value in the list. The
|
|
* `string` property of a number will always take priority if it is set.
|
|
* Sets the `expression` property of the underlying AdwComboRow, unless the
|
|
* value type is `CC_NUMBER_VALUE_CUSTOM`.
|
|
*/
|
|
row_props[ROW_PROP_VALUE_TYPE] =
|
|
g_param_spec_enum ("value-type", NULL, NULL,
|
|
CC_TYPE_NUMBER_VALUE_TYPE, CC_NUMBER_VALUE_CUSTOM,
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* CcNumberRow:sort-type:
|
|
*
|
|
* The sorting of the numbers in the list of the row.
|
|
*/
|
|
row_props[ROW_PROP_SORT_TYPE] =
|
|
g_param_spec_enum ("sort-type", NULL, NULL,
|
|
CC_TYPE_NUMBER_SORT_TYPE, CC_NUMBER_SORT_ASCENDING,
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* CcNumberRow:values:
|
|
*
|
|
* A variant array of integer values. Mainly useful in .ui files, where it
|
|
* allows for convenient array notation like [num1, num2, num3, ...].
|
|
*/
|
|
values_type = g_variant_type_new_array (G_VARIANT_TYPE_INT32);
|
|
row_props[ROW_PROP_VALUES] =
|
|
g_param_spec_variant ("values", NULL, NULL,
|
|
values_type, NULL,
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* CcNumberRow:special-value:
|
|
*
|
|
* One special value to add to the list of the row. Mainly useful in .ui files.
|
|
* If more special values are needed, use `cc_number_row_add_value_full()`.
|
|
*/
|
|
row_props[ROW_PROP_SPECIAL_VALUE] =
|
|
g_param_spec_object ("special-value", NULL, NULL,
|
|
CC_TYPE_NUMBER_OBJECT,
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (object_class, ROW_N_PROPS, row_props);
|
|
}
|
|
|
|
static void
|
|
cc_number_row_init (CcNumberRow *self)
|
|
{
|
|
self->store = g_list_store_new (CC_TYPE_NUMBER_OBJECT);
|
|
}
|
|
|
|
/**
|
|
* cc_number_row_new:
|
|
* @sort_type: the sorting of the numbers in the list of the row
|
|
*
|
|
* Creates a new `CcNumberRow`, with sorting based on @sort_type.
|
|
*
|
|
* Returns: the newly created `CcNumberRow`
|
|
*/
|
|
CcNumberRow *
|
|
cc_number_row_new (CcNumberValueType value_type,
|
|
CcNumberSortType sort_type)
|
|
{
|
|
return g_object_new (CC_TYPE_NUMBER_ROW,
|
|
"value-type", value_type,
|
|
"sort-type", sort_type,
|
|
NULL);
|
|
}
|
|
|
|
static guint
|
|
cc_number_row_add_number (CcNumberRow *self,
|
|
CcNumberObject *number)
|
|
{
|
|
g_return_val_if_fail (CC_IS_NUMBER_ROW (self), 0);
|
|
g_return_val_if_fail (CC_IS_NUMBER_OBJECT (number), 0);
|
|
|
|
return g_list_store_insert_sorted (self->store, number,
|
|
(GCompareDataFunc) compare_numbers, self);
|
|
}
|
|
|
|
/**
|
|
* cc_number_row_add_value:
|
|
* @self: a `CcNumberRow`
|
|
* @value: the value to store in the list of @self
|
|
*
|
|
* Adds a new `CcNumberObject` based on @value to the list of @self.
|
|
* The value will be inserted with the correct sorting.
|
|
*
|
|
* Also see `cc_number_object_new()`.
|
|
*
|
|
* Returns: the position in the list where the value got stored
|
|
*/
|
|
guint
|
|
cc_number_row_add_value (CcNumberRow *self,
|
|
int value)
|
|
{
|
|
g_autoptr(CcNumberObject) number = cc_number_object_new (value);
|
|
|
|
return cc_number_row_add_number (self, number);
|
|
}
|
|
|
|
/**
|
|
* cc_number_row_add_value_full:
|
|
* @self: a `CcNumberRow`
|
|
* @value: the value to store in the list of @self
|
|
* @string: (nullable): the fixed string representation of @value
|
|
* @order: the fixed ordering of @value
|
|
*
|
|
* Adds a new `CcNumberObject` based on @value, @string, and @order to the
|
|
* list of @self.
|
|
* The value will be inserted with the correct sorting, which takes @order
|
|
* into account. If two `CcNumberObject`s have the same special @order, their
|
|
* ordering is based on the order in which they were added to the list.
|
|
*
|
|
* Also see `cc_number_object_new_full()`.
|
|
*
|
|
* Returns: the position in the list where the value got stored
|
|
*/
|
|
guint
|
|
cc_number_row_add_value_full (CcNumberRow *self,
|
|
int value,
|
|
const char *string,
|
|
CcNumberOrder order)
|
|
{
|
|
g_autoptr(CcNumberObject) number = cc_number_object_new_full (value, string, order);
|
|
|
|
return cc_number_row_add_number (self, number);
|
|
}
|
|
|
|
/**
|
|
* cc_number_row_get_value:
|
|
* @self: a `CcNumberRow`
|
|
* @position: the position of the value to fetch
|
|
*
|
|
* Get the value at @position in the list of @self.
|
|
*
|
|
* Returns: the value at @position
|
|
*/
|
|
int
|
|
cc_number_row_get_value (CcNumberRow *self,
|
|
guint position)
|
|
{
|
|
g_autoptr(CcNumberObject) number = NULL;
|
|
|
|
g_return_val_if_fail (CC_IS_NUMBER_ROW (self), -1);
|
|
|
|
number = g_list_model_get_item (G_LIST_MODEL (self->store), position);
|
|
|
|
g_return_val_if_fail (number != NULL, -1);
|
|
|
|
return number->value;
|
|
}
|
|
|
|
static gboolean
|
|
equal_numbers (CcNumberObject *number,
|
|
gconstpointer not_a_number,
|
|
int *value)
|
|
{
|
|
if (!number)
|
|
return FALSE;
|
|
|
|
return number->value == *value;
|
|
}
|
|
|
|
/**
|
|
* cc_number_row_has_value:
|
|
* @self: a `CcNumberRow`
|
|
* @value: a value
|
|
* @position: (out) (optional): the first position of @value, if it was found
|
|
*
|
|
* Looks up the given @value in the list of @self. If the value is not found,
|
|
* @position will not be set and the return value will be false.
|
|
*
|
|
* Returns: true if the list contains @value, false otherwise
|
|
*/
|
|
gboolean
|
|
cc_number_row_has_value (CcNumberRow *self,
|
|
int value,
|
|
guint *position)
|
|
{
|
|
g_return_val_if_fail (CC_IS_NUMBER_ROW (self), FALSE);
|
|
|
|
return g_list_store_find_with_equal_func_full (self->store, NULL,
|
|
(GEqualFuncFull) equal_numbers, &value,
|
|
position);
|
|
}
|
|
|
|
static gboolean
|
|
number_row_settings_get_mapping (GValue *position_value,
|
|
GVariant *key_variant,
|
|
gpointer user_data)
|
|
{
|
|
CcNumberRow *self = CC_NUMBER_ROW (user_data);
|
|
int value;
|
|
guint position = 0;
|
|
|
|
if (g_variant_is_of_type (key_variant, G_VARIANT_TYPE_UINT32))
|
|
if (g_variant_get_uint32 (key_variant) <= INT_MAX) {
|
|
value = g_variant_get_uint32 (key_variant);
|
|
} else {
|
|
g_warning ("Unsigned GSettings value out of range for CcNumberRow");
|
|
position = GTK_INVALID_LIST_POSITION;
|
|
}
|
|
else
|
|
value = g_variant_get_int32 (key_variant);
|
|
|
|
if (position != GTK_INVALID_LIST_POSITION)
|
|
if (!cc_number_row_has_value (self, value, &position))
|
|
position = cc_number_row_add_value (self, value);
|
|
|
|
/* Always set position succesfully, even if invalid, otherwise GSettings will try to map
|
|
default values */
|
|
g_value_set_uint (position_value, position);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GVariant *
|
|
number_row_settings_set_mapping (const GValue *position_value,
|
|
const GVariantType *key_variant_type,
|
|
gpointer user_data)
|
|
{
|
|
CcNumberRow *self = CC_NUMBER_ROW (user_data);
|
|
guint position;
|
|
int value;
|
|
GVariant *key_variant = NULL;
|
|
|
|
position = g_value_get_uint (position_value);
|
|
g_return_val_if_fail (position != GTK_INVALID_LIST_POSITION, NULL);
|
|
|
|
value = cc_number_row_get_value (self, position);
|
|
if (g_variant_type_equal (key_variant_type, G_VARIANT_TYPE_UINT32))
|
|
if (value >= 0)
|
|
key_variant = g_variant_new_uint32 (value);
|
|
else
|
|
g_warning ("Negative CcNumberRow value out of range for unsigned GSettings value");
|
|
else
|
|
key_variant = g_variant_new_int32 (value);
|
|
|
|
return key_variant;
|
|
}
|
|
|
|
/**
|
|
* cc_number_row_bind_settings:
|
|
* @self: a `CcNumberRow`
|
|
* @settings: a `GSettings` object
|
|
* @key: the key to bind
|
|
*
|
|
* Creates a binding between the @key in the @settings object, and the
|
|
* selected value of @self. The value type of @key must be an (unsigned)
|
|
* integer.
|
|
*
|
|
* If the value of @key does not exist yet in the the list of @self, it
|
|
* will be added.
|
|
*/
|
|
void
|
|
cc_number_row_bind_settings (CcNumberRow *self,
|
|
GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
g_autoptr(GVariant) key_variant = NULL;
|
|
|
|
g_return_if_fail (CC_IS_NUMBER_ROW (self));
|
|
g_return_if_fail (G_IS_SETTINGS (settings));
|
|
|
|
/* Make sure the key has uint or int value type, otherwise it can't map to a CcNumberRow */
|
|
key_variant = g_settings_get_value (settings, key);
|
|
g_return_if_fail (g_variant_is_of_type (key_variant, G_VARIANT_TYPE_UINT32) ||
|
|
g_variant_is_of_type (key_variant, G_VARIANT_TYPE_INT32));
|
|
|
|
g_settings_bind_with_mapping (settings, key, self, "selected", G_SETTINGS_BIND_DEFAULT,
|
|
number_row_settings_get_mapping, number_row_settings_set_mapping,
|
|
self, NULL);
|
|
}
|