diff options
-rw-r--r-- | config.h.meson | 6 | ||||
-rw-r--r-- | docs/reference/gtk/gtk4-docs.xml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 19 | ||||
-rw-r--r-- | docs/reference/gtk/gtk4.types.in | 1 | ||||
-rw-r--r-- | gtk/fallback-c89.c | 24 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtknumericsorter.c | 420 | ||||
-rw-r--r-- | gtk/gtknumericsorter.h | 53 | ||||
-rw-r--r-- | gtk/meson.build | 2 | ||||
-rw-r--r-- | meson.build | 1 |
10 files changed, 527 insertions, 1 deletions
diff --git a/config.h.meson b/config.h.meson index 06705aefc7..fac759a2a7 100644 --- a/config.h.meson +++ b/config.h.meson @@ -35,6 +35,10 @@ */ #mesondefine HAVE_DECL_ISNAN +/* Define to 1 if you have the declaration of `isnanf', and to 0 if you don't. + */ +#mesondefine HAVE_DECL_ISNANF + /* Define to 1 if you have the <dlfcn.h> header file. */ #mesondefine HAVE_DLFCN_H @@ -294,4 +298,4 @@ #mesondefine ISO_CODES_PREFIX /* Define if tracker3 is available */ -#mesondefine HAVE_TRACKER3
\ No newline at end of file +#mesondefine HAVE_TRACKER3 diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index b83897cdc8..979ec7192a 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -64,6 +64,7 @@ <xi:include href="xml/gtksorter.xml" /> <xi:include href="xml/gtkcustomsorter.xml" /> <xi:include href="xml/gtkstringsorter.xml" /> + <xi:include href="xml/gtknumericsorter.xml" /> <xi:include href="xml/gtkmultisorter.xml" /> </section> <xi:include href="xml/gtkselectionmodel.xml" /> diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index e04a0b94f6..c54826ffb2 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -2461,6 +2461,25 @@ gtk_string_sorter_get_type </SECTION> <SECTION> +<FILE>gtknumericsorter</FILE> +<TITLE>GtkNumericSorter</TITLE> +GtkNumericSorter +gtk_numeric_sorter_new +gtk_numeric_sorter_get_expression +gtk_numeric_sorter_set_expression +gtk_numeric_sorter_get_sort_order +gtk_numeric_sorter_set_sort_order +<SUBSECTION Standard> +GTK_NUMERIC_SORTER +GTK_IS_NUMERIC_SORTER +GTK_TYPE_NUMERIC_SORTER +GTK_IS_NUMERIC_SORTER_CLASS +GTK_NUMERIC_SORTER_GET_CLASS +<SUBSECTION Private> +gtk_numeric_sorter_get_type +</SECTION> + +<SECTION> <FILE>gtkcustomsorter</FILE> <TITLE>GtkCustomSorter</TITLE> GtkCustomSorter diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in index 473c4d13cc..0b74d93159 100644 --- a/docs/reference/gtk/gtk4.types.in +++ b/docs/reference/gtk/gtk4.types.in @@ -137,6 +137,7 @@ gtk_native_dialog_get_type gtk_no_selection_get_type gtk_notebook_get_type gtk_notebook_page_get_type +gtk_numeric_sorter_get_type gtk_orientable_get_type gtk_overlay_get_type gtk_overlay_layout_get_type diff --git a/gtk/fallback-c89.c b/gtk/fallback-c89.c index 61536771ee..a566e3423a 100644 --- a/gtk/fallback-c89.c +++ b/gtk/fallback-c89.c @@ -80,6 +80,30 @@ isinf (double x) } #endif +#ifndef HAVE_DECL_ISNAN +/* it seems of the supported compilers only + * MSVC does not have isnan(), but it does + * have _isnan() which does the same as isnan() + */ +static inline gboolean +isnan (double x) +{ + return _isnan (x); +} +#endif + +#ifndef HAVE_DECL_ISNANF +/* it seems of the supported compilers only + * MSVC does not have isnanf(), but it does + * have _isnanf() which does the same as isnanf() + */ +static inline gboolean +isnanf (float x) +{ + return _isnanf (x); +} +#endif + #ifndef INFINITY /* define INFINITY for compilers that lack support for it */ # ifdef HUGE_VALF @@ -169,6 +169,7 @@ #include <gtk/gtknativedialog.h> #include <gtk/gtknoselection.h> #include <gtk/gtknotebook.h> +#include <gtk/gtknumericsorter.h> #include <gtk/gtkorientable.h> #include <gtk/gtkoverlay.h> #include <gtk/gtkoverlaylayout.h> diff --git a/gtk/gtknumericsorter.c b/gtk/gtknumericsorter.c new file mode 100644 index 0000000000..253fd201d7 --- /dev/null +++ b/gtk/gtknumericsorter.c @@ -0,0 +1,420 @@ +/* + * Copyright © 2019 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Matthias Clasen <mclasen@redhat.com> + */ + +#include "config.h" + +#include "gtknumericsorter.h" + +#include "gtkintl.h" +#include "gtktypebuiltins.h" + +#include <math.h> +#include "fallback-c89.c" + +/** + * SECTION:gtknumericsorter + * @Title: GtkNumericSorter + * @Short_description: Sort by comparing numbers + * @See_also: #GtkExpression + * + * GtkNumericSorter is a #GtkSorter that compares numbers. + * + * To obtain the numbers to compare, this sorter evaluates a #GtkExpression. + */ + +struct _GtkNumericSorter +{ + GtkSorter parent_instance; + + GtkSortType sort_order; + + GtkExpression *expression; +}; + +enum { + PROP_0, + PROP_EXPRESSION, + PROP_SORT_ORDER, + NUM_PROPERTIES +}; + +G_DEFINE_TYPE (GtkNumericSorter, gtk_numeric_sorter, GTK_TYPE_SORTER) + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +#define DO_COMPARISON(_result, _type, _getter, _order) G_STMT_START{ \ + _type num1 = _getter (&value1); \ + _type num2 = _getter (&value2); \ +\ + if (num1 < num2) \ + _result = _order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER; \ + else if (num1 > num2) \ + _result = _order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER; \ + else \ + _result = GTK_ORDERING_EQUAL; \ +}G_STMT_END + +static GtkOrdering +gtk_numeric_sorter_compare (GtkSorter *sorter, + gpointer item1, + gpointer item2) +{ + GtkNumericSorter *self = GTK_NUMERIC_SORTER (sorter); + GValue value1 = G_VALUE_INIT; + GValue value2 = G_VALUE_INIT; + gboolean res1, res2; + GtkOrdering result; + + if (self->expression == NULL) + return GTK_ORDERING_EQUAL; + + res1 = gtk_expression_evaluate (self->expression, item1, &value1); + res2 = gtk_expression_evaluate (self->expression, item2, &value2); + + /* If items don't evaluate, order them at the end, so they aren't + * in the way. */ + if (!res1) + { + result = res2 ? GTK_ORDERING_LARGER : GTK_ORDERING_EQUAL; + goto out; + } + else if (!res2) + { + result = GTK_ORDERING_SMALLER; + goto out; + } + + switch (g_type_fundamental (G_VALUE_TYPE (&value1))) + { + case G_TYPE_BOOLEAN: + DO_COMPARISON (result, gboolean, g_value_get_boolean, self->sort_order); + break; + + case G_TYPE_CHAR: + DO_COMPARISON (result, char, g_value_get_char, self->sort_order); + break; + + case G_TYPE_UCHAR: + DO_COMPARISON (result, guchar, g_value_get_uchar, self->sort_order); + break; + + case G_TYPE_INT: + DO_COMPARISON (result, int, g_value_get_int, self->sort_order); + break; + + case G_TYPE_UINT: + DO_COMPARISON (result, guint, g_value_get_uint, self->sort_order); + break; + + case G_TYPE_FLOAT: + { + float num1 = g_value_get_float (&value1); + float num2 = g_value_get_float (&value2); + + if (isnanf (num1) && isnanf (num2)) + result = GTK_ORDERING_EQUAL; + else if (isnanf (num1)) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER; + else if (isnanf (num2)) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER; + else if (num1 < num2) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER; + else if (num1 > num2) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER; + else + result = GTK_ORDERING_EQUAL; + break; + } + + case G_TYPE_DOUBLE: + { + double num1 = g_value_get_double (&value1); + double num2 = g_value_get_double (&value2); + + if (isnan (num1) && isnan (num2)) + result = GTK_ORDERING_EQUAL; + else if (isnan (num1)) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER; + else if (isnan (num2)) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER; + else if (num1 < num2) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_SMALLER : GTK_ORDERING_LARGER; + else if (num1 > num2) + result = self->sort_order == GTK_SORT_ASCENDING ? GTK_ORDERING_LARGER : GTK_ORDERING_SMALLER; + else + result = GTK_ORDERING_EQUAL; + break; + } + + case G_TYPE_LONG: + DO_COMPARISON (result, long, g_value_get_long, self->sort_order); + break; + + case G_TYPE_ULONG: + DO_COMPARISON (result, gulong, g_value_get_ulong, self->sort_order); + break; + + case G_TYPE_INT64: + DO_COMPARISON (result, gint64, g_value_get_int64, self->sort_order); + break; + + case G_TYPE_UINT64: + DO_COMPARISON (result, guint64, g_value_get_uint64, self->sort_order); + break; + + default: + g_critical ("Invalid value type %s for expression\n", g_type_name (gtk_expression_get_value_type (self->expression))); + result = GTK_ORDERING_EQUAL; + break; + } + +out: + g_value_unset (&value1); + g_value_unset (&value2); + + return result; +} + +static GtkSorterOrder +gtk_numeric_sorter_get_order (GtkSorter *sorter) +{ + GtkNumericSorter *self = GTK_NUMERIC_SORTER (sorter); + + if (self->expression == NULL) + return GTK_SORTER_ORDER_NONE; + + return GTK_SORTER_ORDER_PARTIAL; +} + +static void +gtk_numeric_sorter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkNumericSorter *self = GTK_NUMERIC_SORTER (object); + + switch (prop_id) + { + case PROP_EXPRESSION: + gtk_numeric_sorter_set_expression (self, g_value_get_boxed (value)); + break; + + case PROP_SORT_ORDER: + gtk_numeric_sorter_set_sort_order (self, g_value_get_enum (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_numeric_sorter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkNumericSorter *self = GTK_NUMERIC_SORTER (object); + + switch (prop_id) + { + case PROP_EXPRESSION: + g_value_set_boxed (value, self->expression); + break; + + case PROP_SORT_ORDER: + g_value_set_enum (value, self->sort_order); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_numeric_sorter_dispose (GObject *object) +{ + GtkNumericSorter *self = GTK_NUMERIC_SORTER (object); + + g_clear_pointer (&self->expression, gtk_expression_unref); + + G_OBJECT_CLASS (gtk_numeric_sorter_parent_class)->dispose (object); +} + +static void +gtk_numeric_sorter_class_init (GtkNumericSorterClass *class) +{ + GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + sorter_class->compare = gtk_numeric_sorter_compare; + sorter_class->get_order = gtk_numeric_sorter_get_order; + + object_class->get_property = gtk_numeric_sorter_get_property; + object_class->set_property = gtk_numeric_sorter_set_property; + object_class->dispose = gtk_numeric_sorter_dispose; + + /** + * GtkNumericSorter:expression: + * + * The expression to evalute on items to get a number to compare with + */ + properties[PROP_EXPRESSION] = + g_param_spec_boxed ("expression", + P_("Expression"), + P_("Expression to compare with"), + GTK_TYPE_EXPRESSION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkNumericSorter:sort-order: + * + * Whether the sorter will sort smaller numbers first + */ + properties[PROP_SORT_ORDER] = + g_param_spec_enum ("sort-order", + P_("Sort order"), + P_("Whether to sort smaller numbers first"), + GTK_TYPE_SORT_TYPE, + GTK_SORT_ASCENDING, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); + +} + +static void +gtk_numeric_sorter_init (GtkNumericSorter *self) +{ + self->sort_order = GTK_SORT_ASCENDING; +} + +/** + * gtk_numeric_sorter_new: + * @expression: (transfer full) (nullable): The expression to evaluate + * + * Creates a new numeric sorter using the given @expression. + * + * Smaller numbers will be sorted first. You can call + * gtk_numeric_sorter_set_sort_order() to change this. + * + * Returns: a new #GtkSorter + */ +GtkSorter * +gtk_numeric_sorter_new (GtkExpression *expression) +{ + GtkSorter *result; + + result = g_object_new (GTK_TYPE_NUMERIC_SORTER, + "expression", expression, + NULL); + + g_clear_pointer (&expression, gtk_expression_unref); + + return result; +} + +/** + * gtk_numeric_sorter_get_expression: + * @self: a #GtkNumericSorter + * + * Gets the expression that is evaluated to obtain numbers from items. + * + * Returns: (nullable): a #GtkExpression, or %NULL + */ +GtkExpression * +gtk_numeric_sorter_get_expression (GtkNumericSorter *self) +{ + g_return_val_if_fail (GTK_IS_NUMERIC_SORTER (self), NULL); + + return self->expression; +} + +/** + * gtk_numeric_sorter_set_expression: + * @self: a #GtkNumericSorter + * @expression: (nullable) (transfer none): a #GtkExpression, or %NULL + * + * Sets the expression that is evaluated to obtain numbers from items. + * + * Unless an expression is set on @self, the sorter will always + * compare items as invalid. + * + * The expression must have a return type that can be compared + * numerically, such as #G_TYPE_INT or #G_TYPE_DOUBLE. + */ +void +gtk_numeric_sorter_set_expression (GtkNumericSorter *self, + GtkExpression *expression) +{ + g_return_if_fail (GTK_IS_NUMERIC_SORTER (self)); + + if (self->expression == expression) + return; + + g_clear_pointer (&self->expression, gtk_expression_unref); + if (expression) + self->expression = gtk_expression_ref (expression); + + gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXPRESSION]); +} + +/** + * gtk_numeric_sorter_set_sort_order: + * @self: a #GtkNumericSorter + * @sort_order: whether to sort smaller numbers first + * + * Sets whether to sort smaller numbers before larger ones. + */ +void +gtk_numeric_sorter_set_sort_order (GtkNumericSorter *self, + GtkSortType sort_order) +{ + g_return_if_fail (GTK_IS_NUMERIC_SORTER (self)); + + if (self->sort_order == sort_order) + return; + + self->sort_order = sort_order; + + gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_INVERTED); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORT_ORDER]); +} + +/** + * gtk_numeric_sorter_get_sort_order: + * @self: a #GtkNumericSorter + * + * Gets whether this sorter will sort smaller numbers first. + * + * Returns: the order of the numbers + */ +GtkSortType +gtk_numeric_sorter_get_sort_order (GtkNumericSorter *self) +{ + g_return_val_if_fail (GTK_IS_NUMERIC_SORTER (self), GTK_SORT_ASCENDING); + + return self->sort_order; +} diff --git a/gtk/gtknumericsorter.h b/gtk/gtknumericsorter.h new file mode 100644 index 0000000000..39bb48e212 --- /dev/null +++ b/gtk/gtknumericsorter.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2019 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Matthias Clasen <mclasen@redhat.com> + */ + +#ifndef __GTK_NUMERIC_SORTER_H__ +#define __GTK_NUMERIC_SORTER_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gtk/gtkexpression.h> +#include <gtk/gtksorter.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_NUMERIC_SORTER (gtk_numeric_sorter_get_type ()) +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkNumericSorter, gtk_numeric_sorter, GTK, NUMERIC_SORTER, GtkSorter) + +GDK_AVAILABLE_IN_ALL +GtkSorter * gtk_numeric_sorter_new (GtkExpression *expression); + +GDK_AVAILABLE_IN_ALL +GtkExpression * gtk_numeric_sorter_get_expression (GtkNumericSorter *self); +GDK_AVAILABLE_IN_ALL +void gtk_numeric_sorter_set_expression (GtkNumericSorter *self, + GtkExpression *expression); + +GDK_AVAILABLE_IN_ALL +GtkSortType gtk_numeric_sorter_get_sort_order (GtkNumericSorter *self); +GDK_AVAILABLE_IN_ALL +void gtk_numeric_sorter_set_sort_order (GtkNumericSorter *self, + GtkSortType sort_order); + +G_END_DECLS + +#endif /* __GTK_NUMERIC_SORTER_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index 4663211d73..f85d089a2f 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -290,6 +290,7 @@ gtk_public_sources = files([ 'gtknomediafile.c', 'gtknoselection.c', 'gtknotebook.c', + 'gtknumericsorter.c', 'gtkorientable.c', 'gtkoverlay.c', 'gtkoverlaylayout.c', @@ -549,6 +550,7 @@ gtk_public_headers = files([ 'gtknativedialog.h', 'gtknoselection.h', 'gtknotebook.h', + 'gtknumericsorter.h', 'gtkorientable.h', 'gtkoverlay.h', 'gtkoverlaylayout.h', diff --git a/meson.build b/meson.build index 912c0f8a95..874fee25a8 100644 --- a/meson.build +++ b/meson.build @@ -213,6 +213,7 @@ endforeach cdata.set('HAVE_DECL_ISINF', cc.has_header_symbol('math.h', 'isinf')) cdata.set('HAVE_DECL_ISNAN', cc.has_header_symbol('math.h', 'isnan')) +cdata.set('HAVE_DECL_ISNANF', cc.has_header_symbol('math.h', 'isnanf')) # Disable deprecation checks for all libraries we depend on on stable branches. # This is so newer versions of those libraries don't cause more warnings with |