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 | |
39 | struct _GtkNumericSorter |
40 | { |
41 | GtkSorter parent_instance; |
42 | |
43 | GtkSortType sort_order; |
44 | |
45 | GtkExpression *expression; |
46 | }; |
47 | |
48 | enum { |
49 | PROP_0, |
50 | PROP_EXPRESSION, |
51 | PROP_SORT_ORDER, |
52 | NUM_PROPERTIES |
53 | }; |
54 | |
55 | G_DEFINE_TYPE (GtkNumericSorter, gtk_numeric_sorter, GTK_TYPE_SORTER) |
56 | |
57 | static 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 | |
71 | typedef struct _GtkNumericSortKeys GtkNumericSortKeys; |
72 | struct _GtkNumericSortKeys |
73 | { |
74 | GtkSortKeys keys; |
75 | |
76 | GtkExpression *expression; |
77 | }; |
78 | |
79 | static void |
80 | gtk_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) \ |
89 | static int \ |
90 | gtk_ ## 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) \ |
109 | static int \ |
110 | gtk_ ## 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 | |
134 | COMPARE_FUNCS(char) |
135 | COMPARE_FUNCS(guchar) |
136 | COMPARE_FUNCS(int) |
137 | COMPARE_FUNCS(guint) |
138 | FLOAT_COMPARE_FUNCS(float) |
139 | FLOAT_COMPARE_FUNCS(double) |
140 | COMPARE_FUNCS(long) |
141 | COMPARE_FUNCS(gulong) |
142 | COMPARE_FUNCS(gint64) |
143 | COMPARE_FUNCS(guint64) |
144 | |
145 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
146 | |
147 | #define NUMERIC_SORT_KEYS(TYPE, key_type, type, default_value) \ |
148 | static void \ |
149 | gtk_ ## 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 | \ |
165 | static gboolean \ |
166 | gtk_ ## type ## _sort_keys_is_compatible (GtkSortKeys *keys, \ |
167 | GtkSortKeys *other); \ |
168 | \ |
169 | static 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 | \ |
178 | static 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 | \ |
187 | static gboolean \ |
188 | gtk_ ## 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 != >K_ASCENDING_ ## TYPE ## _SORT_KEYS_CLASS && \ |
195 | other->klass != >K_DESCENDING_ ## TYPE ## _SORT_KEYS_CLASS) \ |
196 | return FALSE; \ |
197 | \ |
198 | return self->expression == compare->expression; \ |
199 | } |
200 | |
201 | NUMERIC_SORT_KEYS(BOOLEAN, char, boolean, FALSE) |
202 | NUMERIC_SORT_KEYS(CHAR, char, char, G_MININT8) |
203 | NUMERIC_SORT_KEYS(UCHAR, guchar, uchar, G_MAXUINT8) |
204 | NUMERIC_SORT_KEYS(INT, int, int, G_MININT) |
205 | NUMERIC_SORT_KEYS(UINT, guint, uint, G_MAXUINT) |
206 | NUMERIC_SORT_KEYS(FLOAT, float, float, NAN) |
207 | NUMERIC_SORT_KEYS(DOUBLE, double, double, NAN) |
208 | NUMERIC_SORT_KEYS(LONG, long, long, G_MINLONG) |
209 | NUMERIC_SORT_KEYS(ULONG, gulong, ulong, G_MAXLONG) |
210 | NUMERIC_SORT_KEYS(INT64, gint64, int64, G_MININT64) |
211 | NUMERIC_SORT_KEYS(UINT64, guint64, uint64, G_MAXUINT64) |
212 | |
213 | G_GNUC_END_IGNORE_DEPRECATIONS |
214 | |
215 | static GtkSortKeys * |
216 | gtk_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 | ? >K_ASCENDING_BOOLEAN_SORT_KEYS_CLASS |
229 | : >K_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 | ? >K_ASCENDING_CHAR_SORT_KEYS_CLASS |
238 | : >K_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 | ? >K_ASCENDING_UCHAR_SORT_KEYS_CLASS |
247 | : >K_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 | ? >K_ASCENDING_INT_SORT_KEYS_CLASS |
256 | : >K_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 | ? >K_ASCENDING_UINT_SORT_KEYS_CLASS |
265 | : >K_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 | ? >K_ASCENDING_FLOAT_SORT_KEYS_CLASS |
274 | : >K_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 | ? >K_ASCENDING_DOUBLE_SORT_KEYS_CLASS |
283 | : >K_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 | ? >K_ASCENDING_LONG_SORT_KEYS_CLASS |
292 | : >K_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 | ? >K_ASCENDING_ULONG_SORT_KEYS_CLASS |
301 | : >K_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 | ? >K_ASCENDING_INT64_SORT_KEYS_CLASS |
310 | : >K_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 | ? >K_ASCENDING_UINT64_SORT_KEYS_CLASS |
319 | : >K_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 | |
334 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
335 | |
336 | static GtkOrdering |
337 | gtk_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 | |
450 | out: |
451 | g_value_unset (value: &value1); |
452 | g_value_unset (value: &value2); |
453 | |
454 | return result; |
455 | } |
456 | |
457 | G_GNUC_END_IGNORE_DEPRECATIONS |
458 | |
459 | static GtkSorterOrder |
460 | gtk_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 | |
470 | static void |
471 | gtk_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 | |
494 | static void |
495 | gtk_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 | |
518 | static void |
519 | gtk_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 | |
528 | static void |
529 | gtk_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 | |
569 | static void |
570 | gtk_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 | */ |
590 | GtkNumericSorter * |
591 | gtk_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 | */ |
612 | GtkExpression * |
613 | gtk_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 | */ |
633 | void |
634 | gtk_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 | */ |
660 | void |
661 | gtk_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 | */ |
686 | GtkSortType |
687 | gtk_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 | |