1/*
2 * Copyright © 2019 Matthias Clasen
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Matthias Clasen <mclasen@redhat.com>
18 */
19
20#include "config.h"
21
22#include "gtknumericsorter.h"
23
24#include "gtkintl.h"
25#include "gtksorterprivate.h"
26#include "gtktypebuiltins.h"
27
28#include <math.h>
29
30/**
31 * GtkNumericSorter:
32 *
33 * `GtkNumericSorter` is a `GtkSorter` that compares numbers.
34 *
35 * To obtain the numbers to compare, this sorter evaluates a
36 * [class@Gtk.Expression].
37 */
38
39struct _GtkNumericSorter
40{
41 GtkSorter parent_instance;
42
43 GtkSortType sort_order;
44
45 GtkExpression *expression;
46};
47
48enum {
49 PROP_0,
50 PROP_EXPRESSION,
51 PROP_SORT_ORDER,
52 NUM_PROPERTIES
53};
54
55G_DEFINE_TYPE (GtkNumericSorter, gtk_numeric_sorter, GTK_TYPE_SORTER)
56
57static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
58
59#define DO_COMPARISON(_result, _type, _getter, _order) G_STMT_START{ \
60 _type num1 = _getter (&value1); \
61 _type num2 = _getter (&value2); \
62\
63 if (num1 < num2) \
64 _result = _order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER; \
65 else if (num1 > num2) \
66 _result = _order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER; \
67 else \
68 _result = GTK_ORDERING_EQUAL; \
69}G_STMT_END
70
71typedef struct _GtkNumericSortKeys GtkNumericSortKeys;
72struct _GtkNumericSortKeys
73{
74 GtkSortKeys keys;
75
76 GtkExpression *expression;
77};
78
79static void
80gtk_numeric_sort_keys_free (GtkSortKeys *keys)
81{
82 GtkNumericSortKeys *self = (GtkNumericSortKeys *) keys;
83
84 gtk_expression_unref (self: self->expression);
85 g_slice_free (GtkNumericSortKeys, self);
86}
87
88#define COMPARE_FUNC(type, name, _a, _b) \
89static int \
90gtk_ ## type ## _sort_keys_compare_ ## name (gconstpointer a, \
91 gconstpointer b, \
92 gpointer unused) \
93{ \
94 type num1 = *(type *) _a; \
95 type num2 = *(type *) _b; \
96\
97 if (num1 < num2) \
98 return GTK_ORDERING_SMALLER; \
99 else if (num1 > num2) \
100 return GTK_ORDERING_LARGER; \
101 else \
102 return GTK_ORDERING_EQUAL; \
103}
104#define COMPARE_FUNCS(type) \
105 COMPARE_FUNC(type, ascending, a, b) \
106 COMPARE_FUNC(type, descending, b, a)
107
108#define FLOAT_COMPARE_FUNC(type, name, _a, _b) \
109static int \
110gtk_ ## type ## _sort_keys_compare_ ## name (gconstpointer a, \
111 gconstpointer b, \
112 gpointer unused) \
113{ \
114 type num1 = *(type *) _a; \
115 type num2 = *(type *) _b; \
116\
117 if (isnan (num1) && isnan (num2)) \
118 return GTK_ORDERING_EQUAL; \
119 else if (isnan (num1)) \
120 return GTK_ORDERING_LARGER; \
121 else if (isnan (num2)) \
122 return GTK_ORDERING_SMALLER; \
123 else if (num1 < num2) \
124 return GTK_ORDERING_SMALLER; \
125 else if (num1 > num2) \
126 return GTK_ORDERING_LARGER; \
127 else \
128 return GTK_ORDERING_EQUAL; \
129}
130#define FLOAT_COMPARE_FUNCS(type) \
131 FLOAT_COMPARE_FUNC(type, ascending, a, b) \
132 FLOAT_COMPARE_FUNC(type, descending, b, a)
133
134COMPARE_FUNCS(char)
135COMPARE_FUNCS(guchar)
136COMPARE_FUNCS(int)
137COMPARE_FUNCS(guint)
138FLOAT_COMPARE_FUNCS(float)
139FLOAT_COMPARE_FUNCS(double)
140COMPARE_FUNCS(long)
141COMPARE_FUNCS(gulong)
142COMPARE_FUNCS(gint64)
143COMPARE_FUNCS(guint64)
144
145G_GNUC_BEGIN_IGNORE_DEPRECATIONS
146
147#define NUMERIC_SORT_KEYS(TYPE, key_type, type, default_value) \
148static void \
149gtk_ ## type ## _sort_keys_init_key (GtkSortKeys *keys, \
150 gpointer item, \
151 gpointer key_memory) \
152{ \
153 GtkNumericSortKeys *self = (GtkNumericSortKeys *) keys; \
154 key_type *key = (key_type *) key_memory; \
155 GValue value = G_VALUE_INIT; \
156\
157 if (gtk_expression_evaluate (self->expression, item, &value)) \
158 *key = g_value_get_ ## type (&value); \
159 else \
160 *key = default_value; \
161\
162 g_value_unset (&value); \
163} \
164\
165static gboolean \
166gtk_ ## type ## _sort_keys_is_compatible (GtkSortKeys *keys, \
167 GtkSortKeys *other); \
168\
169static const GtkSortKeysClass GTK_ASCENDING_ ## TYPE ## _SORT_KEYS_CLASS = \
170{ \
171 gtk_numeric_sort_keys_free, \
172 gtk_ ## key_type ## _sort_keys_compare_ascending, \
173 gtk_ ## type ## _sort_keys_is_compatible, \
174 gtk_ ## type ## _sort_keys_init_key, \
175 NULL \
176}; \
177\
178static const GtkSortKeysClass GTK_DESCENDING_ ## TYPE ## _SORT_KEYS_CLASS = \
179{ \
180 gtk_numeric_sort_keys_free, \
181 gtk_ ## key_type ## _sort_keys_compare_descending, \
182 gtk_ ## type ## _sort_keys_is_compatible, \
183 gtk_ ## type ## _sort_keys_init_key, \
184 NULL \
185}; \
186\
187static gboolean \
188gtk_ ## type ## _sort_keys_is_compatible (GtkSortKeys *keys, \
189 GtkSortKeys *other) \
190{ \
191 GtkNumericSorter *self = (GtkNumericSorter *) keys; \
192 GtkNumericSorter *compare = (GtkNumericSorter *) other; \
193\
194 if (other->klass != &GTK_ASCENDING_ ## TYPE ## _SORT_KEYS_CLASS && \
195 other->klass != &GTK_DESCENDING_ ## TYPE ## _SORT_KEYS_CLASS) \
196 return FALSE; \
197\
198 return self->expression == compare->expression; \
199}
200
201NUMERIC_SORT_KEYS(BOOLEAN, char, boolean, FALSE)
202NUMERIC_SORT_KEYS(CHAR, char, char, G_MININT8)
203NUMERIC_SORT_KEYS(UCHAR, guchar, uchar, G_MAXUINT8)
204NUMERIC_SORT_KEYS(INT, int, int, G_MININT)
205NUMERIC_SORT_KEYS(UINT, guint, uint, G_MAXUINT)
206NUMERIC_SORT_KEYS(FLOAT, float, float, NAN)
207NUMERIC_SORT_KEYS(DOUBLE, double, double, NAN)
208NUMERIC_SORT_KEYS(LONG, long, long, G_MINLONG)
209NUMERIC_SORT_KEYS(ULONG, gulong, ulong, G_MAXLONG)
210NUMERIC_SORT_KEYS(INT64, gint64, int64, G_MININT64)
211NUMERIC_SORT_KEYS(UINT64, guint64, uint64, G_MAXUINT64)
212
213G_GNUC_END_IGNORE_DEPRECATIONS
214
215static GtkSortKeys *
216gtk_numeric_sort_keys_new (GtkNumericSorter *self)
217{
218 GtkNumericSortKeys *result;
219
220 if (self->expression == NULL)
221 return gtk_sort_keys_new_equal ();
222
223 switch (gtk_expression_get_value_type (self: self->expression))
224 {
225 case G_TYPE_BOOLEAN:
226 result = gtk_sort_keys_new (GtkNumericSortKeys,
227 self->sort_order == GTK_SORT_ASCENDING
228 ? &GTK_ASCENDING_BOOLEAN_SORT_KEYS_CLASS
229 : &GTK_DESCENDING_BOOLEAN_SORT_KEYS_CLASS,
230 sizeof (char),
231 sizeof (char));
232 break;
233
234 case G_TYPE_CHAR:
235 result = gtk_sort_keys_new (GtkNumericSortKeys,
236 self->sort_order == GTK_SORT_ASCENDING
237 ? &GTK_ASCENDING_CHAR_SORT_KEYS_CLASS
238 : &GTK_DESCENDING_CHAR_SORT_KEYS_CLASS,
239 sizeof (char),
240 sizeof (char));
241 break;
242
243 case G_TYPE_UCHAR:
244 result = gtk_sort_keys_new (GtkNumericSortKeys,
245 self->sort_order == GTK_SORT_ASCENDING
246 ? &GTK_ASCENDING_UCHAR_SORT_KEYS_CLASS
247 : &GTK_DESCENDING_UCHAR_SORT_KEYS_CLASS,
248 sizeof (guchar),
249 sizeof (guchar));
250 break;
251
252 case G_TYPE_INT:
253 result = gtk_sort_keys_new (GtkNumericSortKeys,
254 self->sort_order == GTK_SORT_ASCENDING
255 ? &GTK_ASCENDING_INT_SORT_KEYS_CLASS
256 : &GTK_DESCENDING_INT_SORT_KEYS_CLASS,
257 sizeof (int),
258 sizeof (int));
259 break;
260
261 case G_TYPE_UINT:
262 result = gtk_sort_keys_new (GtkNumericSortKeys,
263 self->sort_order == GTK_SORT_ASCENDING
264 ? &GTK_ASCENDING_UINT_SORT_KEYS_CLASS
265 : &GTK_DESCENDING_UINT_SORT_KEYS_CLASS,
266 sizeof (guint),
267 sizeof (guint));
268 break;
269
270 case G_TYPE_FLOAT:
271 result = gtk_sort_keys_new (GtkNumericSortKeys,
272 self->sort_order == GTK_SORT_ASCENDING
273 ? &GTK_ASCENDING_FLOAT_SORT_KEYS_CLASS
274 : &GTK_DESCENDING_FLOAT_SORT_KEYS_CLASS,
275 sizeof (float),
276 sizeof (float));
277 break;
278
279 case G_TYPE_DOUBLE:
280 result = gtk_sort_keys_new (GtkNumericSortKeys,
281 self->sort_order == GTK_SORT_ASCENDING
282 ? &GTK_ASCENDING_DOUBLE_SORT_KEYS_CLASS
283 : &GTK_DESCENDING_DOUBLE_SORT_KEYS_CLASS,
284 sizeof (double),
285 sizeof (double));
286 break;
287
288 case G_TYPE_LONG:
289 result = gtk_sort_keys_new (GtkNumericSortKeys,
290 self->sort_order == GTK_SORT_ASCENDING
291 ? &GTK_ASCENDING_LONG_SORT_KEYS_CLASS
292 : &GTK_DESCENDING_LONG_SORT_KEYS_CLASS,
293 sizeof (long),
294 sizeof (long));
295 break;
296
297 case G_TYPE_ULONG:
298 result = gtk_sort_keys_new (GtkNumericSortKeys,
299 self->sort_order == GTK_SORT_ASCENDING
300 ? &GTK_ASCENDING_ULONG_SORT_KEYS_CLASS
301 : &GTK_DESCENDING_ULONG_SORT_KEYS_CLASS,
302 sizeof (gulong),
303 sizeof (gulong));
304 break;
305
306 case G_TYPE_INT64:
307 result = gtk_sort_keys_new (GtkNumericSortKeys,
308 self->sort_order == GTK_SORT_ASCENDING
309 ? &GTK_ASCENDING_INT64_SORT_KEYS_CLASS
310 : &GTK_DESCENDING_INT64_SORT_KEYS_CLASS,
311 sizeof (gint64),
312 sizeof (gint64));
313 break;
314
315 case G_TYPE_UINT64:
316 result = gtk_sort_keys_new (GtkNumericSortKeys,
317 self->sort_order == GTK_SORT_ASCENDING
318 ? &GTK_ASCENDING_UINT64_SORT_KEYS_CLASS
319 : &GTK_DESCENDING_UINT64_SORT_KEYS_CLASS,
320 sizeof (guint64),
321 sizeof (guint64));
322 break;
323
324 default:
325 g_critical ("Invalid value type %s for expression\n", g_type_name (gtk_expression_get_value_type (self->expression)));
326 return gtk_sort_keys_new_equal ();
327 }
328
329 result->expression = gtk_expression_ref (self: self->expression);
330
331 return (GtkSortKeys *) result;
332}
333
334G_GNUC_BEGIN_IGNORE_DEPRECATIONS
335
336static GtkOrdering
337gtk_numeric_sorter_compare (GtkSorter *sorter,
338 gpointer item1,
339 gpointer item2)
340{
341 GtkNumericSorter *self = GTK_NUMERIC_SORTER (ptr: sorter);
342 GValue value1 = G_VALUE_INIT;
343 GValue value2 = G_VALUE_INIT;
344 gboolean res1, res2;
345 GtkOrdering result;
346
347 if (self->expression == NULL)
348 return GTK_ORDERING_EQUAL;
349
350 res1 = gtk_expression_evaluate (self: self->expression, this_: item1, value: &value1);
351 res2 = gtk_expression_evaluate (self: self->expression, this_: item2, value: &value2);
352
353 /* If items don't evaluate, order them at the end, so they aren't
354 * in the way. */
355 if (!res1)
356 {
357 result = res2 ? GTK_ORDERING_LARGER : GTK_ORDERING_EQUAL;
358 goto out;
359 }
360 else if (!res2)
361 {
362 result = GTK_ORDERING_SMALLER;
363 goto out;
364 }
365
366 switch (g_type_fundamental (G_VALUE_TYPE (&value1)))
367 {
368 case G_TYPE_BOOLEAN:
369 DO_COMPARISON (result, gboolean, g_value_get_boolean, self->sort_order);
370 break;
371
372 case G_TYPE_CHAR:
373 DO_COMPARISON (result, char, g_value_get_char, self->sort_order);
374 break;
375
376 case G_TYPE_UCHAR:
377 DO_COMPARISON (result, guchar, g_value_get_uchar, self->sort_order);
378 break;
379
380 case G_TYPE_INT:
381 DO_COMPARISON (result, int, g_value_get_int, self->sort_order);
382 break;
383
384 case G_TYPE_UINT:
385 DO_COMPARISON (result, guint, g_value_get_uint, self->sort_order);
386 break;
387
388 case G_TYPE_FLOAT:
389 {
390 float num1 = g_value_get_float (value: &value1);
391 float num2 = g_value_get_float (value: &value2);
392
393 if (isnan (num1) && isnan (num2))
394 result = GTK_ORDERING_EQUAL;
395 else if (isnan (num1))
396 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER;
397 else if (isnan (num2))
398 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER;
399 else if (num1 < num2)
400 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER;
401 else if (num1 > num2)
402 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER;
403 else
404 result = GTK_ORDERING_EQUAL;
405 break;
406 }
407
408 case G_TYPE_DOUBLE:
409 {
410 double num1 = g_value_get_double (value: &value1);
411 double num2 = g_value_get_double (value: &value2);
412
413 if (isnan (num1) && isnan (num2))
414 result = GTK_ORDERING_EQUAL;
415 else if (isnan (num1))
416 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER;
417 else if (isnan (num2))
418 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER;
419 else if (num1 < num2)
420 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER;
421 else if (num1 > num2)
422 result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER;
423 else
424 result = GTK_ORDERING_EQUAL;
425 break;
426 }
427
428 case G_TYPE_LONG:
429 DO_COMPARISON (result, long, g_value_get_long, self->sort_order);
430 break;
431
432 case G_TYPE_ULONG:
433 DO_COMPARISON (result, gulong, g_value_get_ulong, self->sort_order);
434 break;
435
436 case G_TYPE_INT64:
437 DO_COMPARISON (result, gint64, g_value_get_int64, self->sort_order);
438 break;
439
440 case G_TYPE_UINT64:
441 DO_COMPARISON (result, guint64, g_value_get_uint64, self->sort_order);
442 break;
443
444 default:
445 g_critical ("Invalid value type %s for expression\n", g_type_name (gtk_expression_get_value_type (self->expression)));
446 result = GTK_ORDERING_EQUAL;
447 break;
448 }
449
450out:
451 g_value_unset (value: &value1);
452 g_value_unset (value: &value2);
453
454 return result;
455}
456
457G_GNUC_END_IGNORE_DEPRECATIONS
458
459static GtkSorterOrder
460gtk_numeric_sorter_get_order (GtkSorter *sorter)
461{
462 GtkNumericSorter *self = GTK_NUMERIC_SORTER (ptr: sorter);
463
464 if (self->expression == NULL)
465 return GTK_SORTER_ORDER_NONE;
466
467 return GTK_SORTER_ORDER_PARTIAL;
468}
469
470static void
471gtk_numeric_sorter_set_property (GObject *object,
472 guint prop_id,
473 const GValue *value,
474 GParamSpec *pspec)
475{
476 GtkNumericSorter *self = GTK_NUMERIC_SORTER (ptr: object);
477
478 switch (prop_id)
479 {
480 case PROP_EXPRESSION:
481 gtk_numeric_sorter_set_expression (self, expression: gtk_value_get_expression (value));
482 break;
483
484 case PROP_SORT_ORDER:
485 gtk_numeric_sorter_set_sort_order (self, sort_order: g_value_get_enum (value));
486 break;
487
488 default:
489 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
490 break;
491 }
492}
493
494static void
495gtk_numeric_sorter_get_property (GObject *object,
496 guint prop_id,
497 GValue *value,
498 GParamSpec *pspec)
499{
500 GtkNumericSorter *self = GTK_NUMERIC_SORTER (ptr: object);
501
502 switch (prop_id)
503 {
504 case PROP_EXPRESSION:
505 gtk_value_set_expression (value, expression: self->expression);
506 break;
507
508 case PROP_SORT_ORDER:
509 g_value_set_enum (value, v_enum: self->sort_order);
510 break;
511
512 default:
513 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
514 break;
515 }
516}
517
518static void
519gtk_numeric_sorter_dispose (GObject *object)
520{
521 GtkNumericSorter *self = GTK_NUMERIC_SORTER (ptr: object);
522
523 g_clear_pointer (&self->expression, gtk_expression_unref);
524
525 G_OBJECT_CLASS (gtk_numeric_sorter_parent_class)->dispose (object);
526}
527
528static void
529gtk_numeric_sorter_class_init (GtkNumericSorterClass *class)
530{
531 GtkSorterClass *sorter_class = GTK_SORTER_CLASS (ptr: class);
532 GObjectClass *object_class = G_OBJECT_CLASS (class);
533
534 sorter_class->compare = gtk_numeric_sorter_compare;
535 sorter_class->get_order = gtk_numeric_sorter_get_order;
536
537 object_class->get_property = gtk_numeric_sorter_get_property;
538 object_class->set_property = gtk_numeric_sorter_set_property;
539 object_class->dispose = gtk_numeric_sorter_dispose;
540
541 /**
542 * GtkNumericSorter:expression: (type GtkExpression) (attributes org.gtk.Property.get=gtk_numeric_sorter_get_expression org.gtk.Property.set=gtk_numeric_sorter_set_expression)
543 *
544 * The expression to evaluate on items to get a number to compare with.
545 */
546 properties[PROP_EXPRESSION] =
547 gtk_param_spec_expression (name: "expression",
548 P_("Expression"),
549 P_("Expression to compare with"),
550 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
551
552 /**
553 * GtkNumericSorter:sort-order: (attributes org.gtk.Property.get=gtk_numeric_sorter_get_sort_order org.gtk.Property.set=gtk_numeric_sorter_set_sort_order)
554 *
555 * Whether the sorter will sort smaller numbers first.
556 */
557 properties[PROP_SORT_ORDER] =
558 g_param_spec_enum (name: "sort-order",
559 P_("Sort order"),
560 P_("Whether to sort smaller numbers first"),
561 enum_type: GTK_TYPE_SORT_TYPE,
562 default_value: GTK_SORT_ASCENDING,
563 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
564
565 g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: properties);
566
567}
568
569static void
570gtk_numeric_sorter_init (GtkNumericSorter *self)
571{
572 self->sort_order = GTK_SORT_ASCENDING;
573
574 gtk_sorter_changed_with_keys (self: GTK_SORTER (ptr: self),
575 change: GTK_SORTER_CHANGE_DIFFERENT,
576 keys: gtk_numeric_sort_keys_new (self));
577}
578
579/**
580 * gtk_numeric_sorter_new:
581 * @expression: (transfer full) (nullable): The expression to evaluate
582 *
583 * Creates a new numeric sorter using the given @expression.
584 *
585 * Smaller numbers will be sorted first. You can call
586 * [method@Gtk.NumericSorter.set_sort_order] to change this.
587 *
588 * Returns: a new `GtkNumericSorter`
589 */
590GtkNumericSorter *
591gtk_numeric_sorter_new (GtkExpression *expression)
592{
593 GtkNumericSorter *result;
594
595 result = g_object_new (GTK_TYPE_NUMERIC_SORTER,
596 first_property_name: "expression", expression,
597 NULL);
598
599 g_clear_pointer (&expression, gtk_expression_unref);
600
601 return result;
602}
603
604/**
605 * gtk_numeric_sorter_get_expression: (attributes org.gtk.Method.get_property=expression)
606 * @self: a `GtkNumericSorter`
607 *
608 * Gets the expression that is evaluated to obtain numbers from items.
609 *
610 * Returns: (transfer none) (nullable): a `GtkExpression`
611 */
612GtkExpression *
613gtk_numeric_sorter_get_expression (GtkNumericSorter *self)
614{
615 g_return_val_if_fail (GTK_IS_NUMERIC_SORTER (self), NULL);
616
617 return self->expression;
618}
619
620/**
621 * gtk_numeric_sorter_set_expression: (attributes org.gtk.Method.set_property=expression)
622 * @self: a `GtkNumericSorter`
623 * @expression: (nullable) (transfer none): a `GtkExpression`
624 *
625 * Sets the expression that is evaluated to obtain numbers from items.
626 *
627 * Unless an expression is set on @self, the sorter will always
628 * compare items as invalid.
629 *
630 * The expression must have a return type that can be compared
631 * numerically, such as %G_TYPE_INT or %G_TYPE_DOUBLE.
632 */
633void
634gtk_numeric_sorter_set_expression (GtkNumericSorter *self,
635 GtkExpression *expression)
636{
637 g_return_if_fail (GTK_IS_NUMERIC_SORTER (self));
638
639 if (self->expression == expression)
640 return;
641
642 g_clear_pointer (&self->expression, gtk_expression_unref);
643 if (expression)
644 self->expression = gtk_expression_ref (self: expression);
645
646 gtk_sorter_changed_with_keys (self: GTK_SORTER (ptr: self),
647 change: GTK_SORTER_CHANGE_DIFFERENT,
648 keys: gtk_numeric_sort_keys_new (self));
649
650 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_EXPRESSION]);
651}
652
653/**
654 * gtk_numeric_sorter_set_sort_order: (attributes org.gtk.Method.set_property=sort-order)
655 * @self: a `GtkNumericSorter`
656 * @sort_order: whether to sort smaller numbers first
657 *
658 * Sets whether to sort smaller numbers before larger ones.
659 */
660void
661gtk_numeric_sorter_set_sort_order (GtkNumericSorter *self,
662 GtkSortType sort_order)
663{
664 g_return_if_fail (GTK_IS_NUMERIC_SORTER (self));
665
666 if (self->sort_order == sort_order)
667 return;
668
669 self->sort_order = sort_order;
670
671 gtk_sorter_changed_with_keys (self: GTK_SORTER (ptr: self),
672 change: GTK_SORTER_CHANGE_INVERTED,
673 keys: gtk_numeric_sort_keys_new (self));
674
675 g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SORT_ORDER]);
676}
677
678/**
679 * gtk_numeric_sorter_get_sort_order: (attributes org.gtk.Method.get_property=sort-order)
680 * @self: a `GtkNumericSorter`
681 *
682 * Gets whether this sorter will sort smaller numbers first.
683 *
684 * Returns: the order of the numbers
685 */
686GtkSortType
687gtk_numeric_sorter_get_sort_order (GtkNumericSorter *self)
688{
689 g_return_val_if_fail (GTK_IS_NUMERIC_SORTER (self), GTK_SORT_ASCENDING);
690
691 return self->sort_order;
692}
693

source code of gtk/gtk/gtknumericsorter.c