stringsorter: Add a collation property

The new property lets us choose between
Unicode collation, filename collation, and
plain strcmp.

This will be used in the filechooser.
This commit is contained in:
Matthias Clasen 2022-10-11 12:57:09 -04:00
parent 8ba7840528
commit 141aac1a60
2 changed files with 127 additions and 15 deletions

View File

@ -42,6 +42,7 @@ struct _GtkStringSorter
GtkSorter parent_instance; GtkSorter parent_instance;
gboolean ignore_case; gboolean ignore_case;
GtkCollation collation;
GtkExpression *expression; GtkExpression *expression;
}; };
@ -50,6 +51,7 @@ enum {
PROP_0, PROP_0,
PROP_EXPRESSION, PROP_EXPRESSION,
PROP_IGNORE_CASE, PROP_IGNORE_CASE,
PROP_COLLATION,
NUM_PROPERTIES NUM_PROPERTIES
}; };
@ -60,10 +62,13 @@ static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static char * static char *
gtk_string_sorter_get_key (GtkExpression *expression, gtk_string_sorter_get_key (GtkExpression *expression,
gboolean ignore_case, gboolean ignore_case,
GtkCollation collation,
gpointer item1) gpointer item1)
{ {
GValue value = G_VALUE_INIT; GValue value = G_VALUE_INIT;
const char *string;
char *s; char *s;
char *key;
if (expression == NULL) if (expression == NULL)
return NULL; return NULL;
@ -71,23 +76,35 @@ gtk_string_sorter_get_key (GtkExpression *expression,
if (!gtk_expression_evaluate (expression, item1, &value)) if (!gtk_expression_evaluate (expression, item1, &value))
return NULL; return NULL;
/* If strings are NULL, order them before "". */ string = g_value_get_string (&value);
if (ignore_case)
{
char *t;
t = g_utf8_casefold (g_value_get_string (&value), -1); if (ignore_case)
s = g_utf8_collate_key (t, -1); s = g_utf8_casefold (string, -1);
g_free (t);
}
else else
s = (char *) string;
switch (collation)
{ {
s = g_utf8_collate_key (g_value_get_string (&value), -1); case GTK_COLLATION_NONE:
key = s;
break;
case GTK_COLLATION_UNICODE:
key = g_utf8_collate_key (s, -1);
break;
case GTK_COLLATION_FILENAME:
key = g_utf8_collate_key_for_filename (s, -1);
break;
default:
g_assert_not_reached ();
break;
} }
if (s != string)
g_free (s);
g_value_unset (&value); g_value_unset (&value);
return s; return key;
} }
static GtkOrdering static GtkOrdering
@ -102,8 +119,8 @@ gtk_string_sorter_compare (GtkSorter *sorter,
if (self->expression == NULL) if (self->expression == NULL)
return GTK_ORDERING_EQUAL; return GTK_ORDERING_EQUAL;
s1 = gtk_string_sorter_get_key (self->expression, self->ignore_case, item1); s1 = gtk_string_sorter_get_key (self->expression, self->ignore_case, self->collation, item1);
s2 = gtk_string_sorter_get_key (self->expression, self->ignore_case, item2); s2 = gtk_string_sorter_get_key (self->expression, self->ignore_case, self->collation, item2);
result = gtk_ordering_from_cmpfunc (g_strcmp0 (s1, s2)); result = gtk_ordering_from_cmpfunc (g_strcmp0 (s1, s2));
@ -131,6 +148,7 @@ struct _GtkStringSortKeys
GtkExpression *expression; GtkExpression *expression;
gboolean ignore_case; gboolean ignore_case;
GtkCollation collation;
}; };
static void static void
@ -173,7 +191,7 @@ gtk_string_sort_keys_init_key (GtkSortKeys *keys,
GtkStringSortKeys *self = (GtkStringSortKeys *) keys; GtkStringSortKeys *self = (GtkStringSortKeys *) keys;
char **key = (char **) key_memory; char **key = (char **) key_memory;
*key = gtk_string_sorter_get_key (self->expression, self->ignore_case, item); *key = gtk_string_sorter_get_key (self->expression, self->ignore_case, self->collation, item);
} }
static void static void
@ -209,6 +227,7 @@ gtk_string_sort_keys_new (GtkStringSorter *self)
result->expression = gtk_expression_ref (self->expression); result->expression = gtk_expression_ref (self->expression);
result->ignore_case = self->ignore_case; result->ignore_case = self->ignore_case;
result->collation = self->collation;
return (GtkSortKeys *) result; return (GtkSortKeys *) result;
} }
@ -231,13 +250,17 @@ gtk_string_sorter_set_property (GObject *object,
gtk_string_sorter_set_ignore_case (self, g_value_get_boolean (value)); gtk_string_sorter_set_ignore_case (self, g_value_get_boolean (value));
break; break;
case PROP_COLLATION:
gtk_string_sorter_set_collation (self, g_value_get_enum (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
} }
static void static void
gtk_string_sorter_get_property (GObject *object, gtk_string_sorter_get_property (GObject *object,
guint prop_id, guint prop_id,
GValue *value, GValue *value,
@ -255,6 +278,10 @@ gtk_string_sorter_get_property (GObject *object,
g_value_set_boolean (value, self->ignore_case); g_value_set_boolean (value, self->ignore_case);
break; break;
case PROP_COLLATION:
g_value_set_enum (value, self->collation);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -296,13 +323,26 @@ gtk_string_sorter_class_init (GtkStringSorterClass *class)
/** /**
* GtkStringSorter:ignore-case: (attributes org.gtk.Property.get=gtk_string_sorter_get_ignore_case org.gtk.Property.set=gtk_string_sorter_set_ignore_case) * GtkStringSorter:ignore-case: (attributes org.gtk.Property.get=gtk_string_sorter_get_ignore_case org.gtk.Property.set=gtk_string_sorter_set_ignore_case)
* *
* If matching is case sensitive. * If sorting is case sensitive.
*/ */
properties[PROP_IGNORE_CASE] = properties[PROP_IGNORE_CASE] =
g_param_spec_boolean ("ignore-case", NULL, NULL, g_param_spec_boolean ("ignore-case", NULL, NULL,
TRUE, TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkStringSorter:collation: (attributes org.gtk.Property.get=gtk_string_sorter_get_collation org.gtk.Property.set=gtk_string_sorter_set_collation)
*
* The collation method to use for sorting.
*
* Since: 4.10
*/
properties[PROP_COLLATION] =
g_param_spec_enum ("collationmode", NULL, NULL,
GTK_TYPE_COLLATION,
GTK_COLLATION_UNICODE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
} }
@ -311,6 +351,7 @@ static void
gtk_string_sorter_init (GtkStringSorter *self) gtk_string_sorter_init (GtkStringSorter *self)
{ {
self->ignore_case = TRUE; self->ignore_case = TRUE;
self->collation = GTK_COLLATION_UNICODE;
gtk_sorter_changed_with_keys (GTK_SORTER (self), gtk_sorter_changed_with_keys (GTK_SORTER (self),
GTK_SORTER_CHANGE_DIFFERENT, GTK_SORTER_CHANGE_DIFFERENT,
@ -429,3 +470,48 @@ gtk_string_sorter_set_ignore_case (GtkStringSorter *self,
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IGNORE_CASE]); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IGNORE_CASE]);
} }
/**
* gtk_string_sorter_get_collation: (attributes org.gtk.Method.get_property=collation)
* @self: a `GtkStringSorter`
*
* Gets which collation method the sorter uses.
*
* Returns: The collation method
*
* Since: 4.10
*/
GtkCollation
gtk_string_sorter_get_collation (GtkStringSorter *self)
{
g_return_val_if_fail (GTK_IS_STRING_SORTER (self), GTK_COLLATION_UNICODE);
return self->collation;
}
/**
* gtk_string_sorter_set_collation: (attributes org.gtk.Method.set_property=collation)
* @self: a `GtkStringSorter`
* @collation: the collation method
*
* Sets the collation method to use for sorting.
*
* Since: 4.10
*/
void
gtk_string_sorter_set_collation (GtkStringSorter *self,
GtkCollation collation)
{
g_return_if_fail (GTK_IS_STRING_SORTER (self));
if (self->collation == collation)
return;
self->collation = collation;
gtk_sorter_changed_with_keys (GTK_SORTER (self),
GTK_SORTER_CHANGE_DIFFERENT,
gtk_string_sort_keys_new (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLLATION]);
}

View File

@ -47,6 +47,32 @@ GDK_AVAILABLE_IN_ALL
void gtk_string_sorter_set_ignore_case (GtkStringSorter *self, void gtk_string_sorter_set_ignore_case (GtkStringSorter *self,
gboolean ignore_case); gboolean ignore_case);
/**
* GtkCollation:
* @GTK_COLLATION_NONE: Don't do any collation
* @GTK_COLLATION_UNICODE: Use [func@GLib.g_utf8_collate_key]
* @GTK_COLLATION_FILENAME: Use [func@GLib.g_utf8_collate_key_for_filename]
*
* Describes how a [class@Gtk.StringSorter] turns strings into sort keys to
* compare them.
*
* Note that the result of sorting will in general depend on the current locale
* unless the mode is @GTK_COLLATION_NONE.
*/
typedef enum
{
GTK_COLLATION_NONE,
GTK_COLLATION_UNICODE,
GTK_COLLATION_FILENAME
} GtkCollation;
GDK_AVAILABLE_IN_4_10
void gtk_string_sorter_set_collation (GtkStringSorter *self,
GtkCollation collation);
GDK_AVAILABLE_IN_4_10
GtkCollation gtk_string_sorter_get_collation (GtkStringSorter *self);
G_END_DECLS G_END_DECLS
#endif /* __GTK_STRING_SORTER_H__ */ #endif /* __GTK_STRING_SORTER_H__ */