[gtk/prop-list: 4/12] Add GtkSorter



commit 14e24b973e2ab07108593d154ebfee5f4356c8c6
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Dec 2 23:43:14 2019 -0500

    Add GtkSorter
    
    This is a helper object for sorting. Also add
    two implementations, GtkStringSorter for collating
    strings, and GtkCustomSorter, which uses a GCompareDataFunc.

 docs/reference/gtk/gtk4-docs.xml     |   1 +
 docs/reference/gtk/gtk4-sections.txt |  18 +++
 gtk/gtk.h                            |   4 +
 gtk/gtkcustomsorter.c                |  89 +++++++++++++
 gtk/gtkcustomsorter.h                |  43 ++++++
 gtk/gtkinvertiblesorter.c            | 238 +++++++++++++++++++++++++++++++++
 gtk/gtkinvertiblesorter.h            |  56 ++++++++
 gtk/gtksorter.c                      | 155 ++++++++++++++++++++++
 gtk/gtksorter.h                      |  88 ++++++++++++
 gtk/gtkstringsorter.c                | 250 +++++++++++++++++++++++++++++++++++
 gtk/gtkstringsorter.h                |  52 ++++++++
 gtk/meson.build                      |   8 ++
 12 files changed, 1002 insertions(+)
---
diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml
index 34124bd0aa..7951991d82 100644
--- a/docs/reference/gtk/gtk4-docs.xml
+++ b/docs/reference/gtk/gtk4-docs.xml
@@ -49,6 +49,7 @@
       <xi:include href="xml/gtkflattenlistmodel.xml" />
       <xi:include href="xml/gtkmaplistmodel.xml" />
       <xi:include href="xml/gtkslicelistmodel.xml" />
+      <xi:include href="xml/gtksorter.xml" />
       <xi:include href="xml/gtksortlistmodel.xml" />
       <xi:include href="xml/gtktreelistmodel.xml" />
       <xi:include href="xml/gtkselectionmodel.xml" />
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 850aafff11..198dd155ea 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -2876,6 +2876,24 @@ GTK_SLICE_LIST_MODEL_GET_CLASS
 gtk_slice_list_model_get_type
 </SECTION>
 
+<SECTION>
+<FILE>gtksorter</FILE>
+<TITLE>GtkSorter</TITLE>
+GtkSorter
+GtkSorterChange
+gtk_sorter_compare
+gtk_sorter_changed
+<SUBSECTION Standard>
+GTK_SORTER
+GTK_IS_SORTER
+GTK_TYPE_SORTER
+GTK_SORTER_CLASS
+GTK_IS_SORTER_CLASS
+GTK_SORTER_GET_CLASS
+<SUBSECTION Private>
+gtk_sorter_get_type
+</SECTION>
+
 <SECTION>
 <FILE>gtksortlistmodel</FILE>
 <TITLE>GtkSortListModel</TITLE>
diff --git a/gtk/gtk.h b/gtk/gtk.h
index d2e9b8b079..0d97331c50 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -93,6 +93,7 @@
 #include <gtk/gtkcoverflow.h>
 #include <gtk/gtkcssprovider.h>
 #include <gtk/gtkcustomlayout.h>
+#include <gtk/gtkcustomsorter.h>
 #include <gtk/gtkdebug.h>
 #include <gtk/gtkdialog.h>
 #include <gtk/gtkdirectorylist.h>
@@ -154,6 +155,7 @@
 #include <gtk/gtkimcontextsimple.h>
 #include <gtk/gtkimmulticontext.h>
 #include <gtk/gtkinfobar.h>
+#include <gtk/gtkinvertiblesorter.h>
 #include <gtk/gtklabel.h>
 #include <gtk/gtklayoutmanager.h>
 #include <gtk/gtklayoutchild.h>
@@ -230,6 +232,7 @@
 #include <gtk/gtksingleselection.h>
 #include <gtk/gtkslicelistmodel.h>
 #include <gtk/gtksnapshot.h>
+#include <gtk/gtksorter.h>
 #include <gtk/gtksortlistmodel.h>
 #include <gtk/gtkstacksidebar.h>
 #include <gtk/gtksizegroup.h>
@@ -240,6 +243,7 @@
 #include <gtk/gtkstackswitcher.h>
 #include <gtk/gtkstatusbar.h>
 #include <gtk/gtkstringfilter.h>
+#include <gtk/gtkstringsorter.h>
 #include <gtk/gtkstylecontext.h>
 #include <gtk/gtkstyleprovider.h>
 #include <gtk/gtkswitch.h>
diff --git a/gtk/gtkcustomsorter.c b/gtk/gtkcustomsorter.c
new file mode 100644
index 0000000000..ca68b2f727
--- /dev/null
+++ b/gtk/gtkcustomsorter.c
@@ -0,0 +1,89 @@
+/*
+ * 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 "gtkcustomsorter.h"
+
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+
+struct _GtkCustomSorter
+{
+  GtkSorter parent_instance;
+
+  GCompareDataFunc sort_func;
+  gpointer user_data;
+  GDestroyNotify user_destroy;
+};
+
+G_DEFINE_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK_TYPE_SORTER)
+
+static int
+gtk_custom_sorter_compare (GtkSorter *sorter,
+                           gpointer   item1,
+                           gpointer   item2)
+{
+  GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
+
+  return self->sort_func (item1, item2, self->user_data);
+}
+
+static void
+gtk_custom_sorter_dispose (GObject *object)
+{
+  GtkCustomSorter *self = GTK_CUSTOM_SORTER (object);
+
+  if (self->user_destroy)
+    self->user_destroy (self->user_data);
+
+  G_OBJECT_CLASS (gtk_custom_sorter_parent_class)->dispose (object);
+}
+
+static void
+gtk_custom_sorter_class_init (GtkCustomSorterClass *class)
+{
+  GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  sorter_class->compare = gtk_custom_sorter_compare;
+
+  object_class->dispose = gtk_custom_sorter_dispose;
+}
+
+static void
+gtk_custom_sorter_init (GtkCustomSorter *self)
+{
+}
+
+GtkSorter *
+gtk_custom_sorter_new (GCompareDataFunc sort_func,
+                       gpointer         user_data,
+                       GDestroyNotify   user_destroy)
+{
+  GtkCustomSorter *sorter;
+
+  sorter = g_object_new (GTK_TYPE_CUSTOM_SORTER, NULL);
+
+  sorter->sort_func = sort_func;
+  sorter->user_data = user_data;
+  sorter->user_destroy = user_destroy;
+
+  return GTK_SORTER (sorter);
+}
diff --git a/gtk/gtkcustomsorter.h b/gtk/gtkcustomsorter.h
new file mode 100644
index 0000000000..3cfadef152
--- /dev/null
+++ b/gtk/gtkcustomsorter.h
@@ -0,0 +1,43 @@
+/*
+ * 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_CUSTOM_SORTER_H__
+#define __GTK_CUSTOM_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_CUSTOM_SORTER             (gtk_custom_sorter_get_type ())
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK, CUSTOM_SORTER, GtkSorter)
+
+GDK_AVAILABLE_IN_ALL
+GtkSorter *             gtk_custom_sorter_new (GCompareDataFunc sort_func,
+                                               gpointer         user_data,
+                                               GDestroyNotify   user_destroy);
+
+G_END_DECLS
+
+#endif /* __GTK_CUSTOM_SORTER_H__ */
diff --git a/gtk/gtkinvertiblesorter.c b/gtk/gtkinvertiblesorter.c
new file mode 100644
index 0000000000..9231a83bf8
--- /dev/null
+++ b/gtk/gtkinvertiblesorter.c
@@ -0,0 +1,238 @@
+/*
+ * 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 "gtkinvertiblesorter.h"
+
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+
+struct _GtkInvertibleSorter
+{
+  GtkSorter parent_instance;
+
+  GtkSortType direction;
+
+  GtkSorter *sorter;
+};
+
+enum {
+  PROP_0,
+  PROP_SORTER,
+  PROP_DIRECTION,
+  NUM_PROPERTIES
+};
+
+G_DEFINE_TYPE (GtkInvertibleSorter, gtk_invertible_sorter, GTK_TYPE_SORTER)
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+static int
+gtk_invertible_sorter_compare (GtkSorter *sorter,
+                               gpointer   item1,
+                               gpointer   item2)
+{
+  GtkInvertibleSorter *self = GTK_INVERTIBLE_SORTER (sorter);
+  int result;
+
+  if (self->sorter == NULL)
+    return 0;
+
+  result = gtk_sorter_compare (self->sorter, item1, item2);
+
+  if (self->direction == GTK_SORT_ASCENDING)
+    return result;
+  else
+    return - result;
+}
+
+static void
+gtk_invertible_sorter_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GtkInvertibleSorter *self = GTK_INVERTIBLE_SORTER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SORTER:
+      gtk_invertible_sorter_set_sorter (self, g_value_get_object (value));
+      break;
+
+    case PROP_DIRECTION:
+      gtk_invertible_sorter_set_direction (self, g_value_get_enum (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void 
+gtk_invertible_sorter_get_property (GObject     *object,
+                                    guint        prop_id,
+                                    GValue      *value,
+                                    GParamSpec  *pspec)
+{
+  GtkInvertibleSorter *self = GTK_INVERTIBLE_SORTER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SORTER:
+      g_value_set_object (value, self->sorter);
+      break;
+
+    case PROP_DIRECTION:
+      g_value_set_enum (value, self->direction);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void changed_cb (GtkSorter *sorter, int change, gpointer data);
+
+static void
+gtk_invertible_sorter_dispose (GObject *object)
+{
+  GtkInvertibleSorter *self = GTK_INVERTIBLE_SORTER (object);
+
+  if (self->sorter)
+    g_signal_handlers_disconnect_by_func (self->sorter, changed_cb, self);
+  g_clear_object (&self->sorter);
+
+  G_OBJECT_CLASS (gtk_invertible_sorter_parent_class)->dispose (object);
+}
+
+static void
+gtk_invertible_sorter_class_init (GtkInvertibleSorterClass *class)
+{
+  GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  sorter_class->compare = gtk_invertible_sorter_compare;
+
+  object_class->get_property = gtk_invertible_sorter_get_property;
+  object_class->set_property = gtk_invertible_sorter_set_property;
+  object_class->dispose = gtk_invertible_sorter_dispose;
+
+  /**
+   * GtkInvertibleSorter:sorter:
+   *
+   * The underlying sorter
+   */
+  properties[PROP_SORTER] =
+      g_param_spec_object ("sorter",
+                          P_("Sorter"),
+                          P_("The underlying sorter"),
+                          GTK_TYPE_SORTER,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GtkInvertibleSorter:direction:
+   *
+   * The direction to sort in
+   */
+  properties[PROP_DIRECTION] =
+      g_param_spec_enum ("direction",
+                         P_("Direction"),
+                         P_("The direction to sort in"),
+                         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_invertible_sorter_init (GtkInvertibleSorter *self)
+{
+  self->direction = GTK_SORT_ASCENDING;
+}
+
+GtkSorter *
+gtk_invertible_sorter_new (GtkSorter *sorter)
+{
+  return g_object_new (GTK_TYPE_INVERTIBLE_SORTER,
+                       "sorter", sorter,
+                       NULL);
+}
+
+GtkSorter *
+gtk_invertible_sorter_get_sorter (GtkInvertibleSorter *self)
+{
+  g_return_val_if_fail (GTK_IS_INVERTIBLE_SORTER (self), NULL);
+
+  return self->sorter;
+}
+
+static void
+changed_cb (GtkSorter *sorter, int change, gpointer data)
+{
+  gtk_sorter_changed (GTK_SORTER (data), change);
+}
+
+void
+gtk_invertible_sorter_set_sorter (GtkInvertibleSorter *self,
+                                  GtkSorter           *sorter)
+{
+  g_return_if_fail (GTK_IS_INVERTIBLE_SORTER (self));
+  g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
+
+  if (self->sorter == sorter)
+    return;
+
+  g_signal_handlers_disconnect_by_func (self->sorter, changed_cb, self);
+  g_set_object (&self->sorter, sorter);
+  if (self->sorter)
+    g_signal_connect (self->sorter, "changed", G_CALLBACK (changed_cb), self);
+
+  gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
+}
+
+GtkSortType
+gtk_invertible_sorter_get_direction (GtkInvertibleSorter *self)
+{
+  g_return_val_if_fail (GTK_IS_INVERTIBLE_SORTER (self), GTK_SORT_ASCENDING);
+
+  return self->direction;
+}
+
+void
+gtk_invertible_sorter_set_direction (GtkInvertibleSorter *self,
+                                     GtkSortType          direction)
+{
+  g_return_if_fail (GTK_IS_INVERTIBLE_SORTER (self));
+
+  if (self->direction == direction)
+    return;
+
+  self->direction = direction;
+
+  gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_INVERTED);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DIRECTION]);
+}
diff --git a/gtk/gtkinvertiblesorter.h b/gtk/gtkinvertiblesorter.h
new file mode 100644
index 0000000000..691b32ab78
--- /dev/null
+++ b/gtk/gtkinvertiblesorter.h
@@ -0,0 +1,56 @@
+/*
+ * 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_INVERTIBLE_SORTER_H__
+#define __GTK_INVERTIBLE_SORTER_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+#include <gtk/gtksorter.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_INVERTIBLE_SORTER             (gtk_invertible_sorter_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkInvertibleSorter, gtk_invertible_sorter, GTK, INVERTIBLE_SORTER, GtkSorter)
+
+GDK_AVAILABLE_IN_ALL
+GtkSorter * gtk_invertible_sorter_new                (GtkSorter *sorter);
+
+GDK_AVAILABLE_IN_ALL
+void        gtk_invertible_sorter_set_sorter (GtkInvertibleSorter *self,
+                                              GtkSorter           *sorter);
+GDK_AVAILABLE_IN_ALL
+GtkSorter * gtk_invertible_sorter_get_sorter (GtkInvertibleSorter *self);
+
+GDK_AVAILABLE_IN_ALL
+void        gtk_invertible_sorter_set_direction (GtkInvertibleSorter *self,
+                                                 GtkSortType          direction);
+
+GDK_AVAILABLE_IN_ALL
+GtkSortType gtk_invertible_sorter_get_direction (GtkInvertibleSorter *self);
+
+G_END_DECLS
+
+#endif /* __GTK_SORTER_H__ */
+
diff --git a/gtk/gtksorter.c b/gtk/gtksorter.c
new file mode 100644
index 0000000000..8be6872e7e
--- /dev/null
+++ b/gtk/gtksorter.c
@@ -0,0 +1,155 @@
+/*
+ * 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 "gtksorter.h"
+
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+
+/**
+ * SECTION:gtksorter
+ * @title: GtkSorter
+ * @Short_description: Sorting items in GTK
+ * @See_also: #GtkSortListModel
+ *
+ * #GtkSorter is the way to describe sorting criteria to be used in #GtkSortListModel.
+ *
+ * The model will use a sorter to determine the order in which its items should appear
+ * by calling gtk_sorter_compare() for pairs of items.
+ *
+ * Sorters may change through their lifetime. In that case, they call gtk_sorter_changed(),
+ * which will emit the #GtkSorter::changed signal to notify that the sort order is no
+ * longer valid and should be updated by calling gtk_sortre_compare() again.
+ *
+ * GTK provides various pre-made sorter implementations for common sorting operations.
+ * #GtkColumnView and #GtkColumnViewColumn have built-in support for supporting sorted
+ * lists where the user can change the sorting by clicking on list headers.
+ *
+ * However, in particular for large lists, it is also possible to subclass #GtkSorter
+ * and provide one's own sorter.
+ */
+
+enum {
+  CHANGED,
+  LAST_SIGNAL
+};
+
+G_DEFINE_TYPE (GtkSorter, gtk_sorter, G_TYPE_OBJECT)
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static int
+gtk_sorter_default_compare (GtkSorter *self,
+                            gpointer   item1,
+                            gpointer   item2)
+{
+  g_critical ("Sorter of type '%s' does not implement GtkSorter::compare", G_OBJECT_TYPE_NAME (self));
+
+  return 0;
+}
+
+static void
+gtk_sorter_class_init (GtkSorterClass *class)
+{
+  class->compare = gtk_sorter_default_compare;
+
+  /**
+   * GtkSearch:changed:
+   * @self: The #GtkSorter
+   * @change: how the sorter changed
+   *
+   * This signal is emitted whenever the sorter changed. Users of the sorter
+   * should then update the sort order again via gtk_sorter_compare().
+   *
+   * #GtkSortListModel handles this signal automatically.
+   *
+   * Depending on the @change parameter, it may be possible to update
+   * the sort order without a full resorting. Refer to the #GtkSorterChange
+   * documentation for details.
+   */
+  signals[CHANGED] =
+    g_signal_new (I_("changed"),
+                  G_TYPE_FROM_CLASS (class),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__ENUM,
+                  G_TYPE_NONE, 1,
+                  GTK_TYPE_SORTER_CHANGE);
+  g_signal_set_va_marshaller (signals[CHANGED],
+                              G_TYPE_FROM_CLASS (class),
+                              g_cclosure_marshal_VOID__ENUMv);
+}
+
+static void
+gtk_sorter_init (GtkSorter *self)
+{
+}
+
+/**
+ * gtk_sorter_compare:
+ * @self: a #GtkSorter
+ * @item1: (type GObject) (transfer none): first item to compare
+ * @item2: (type GObject) (transfer none): second item to compare
+ *
+ * Compares two given items according to the sort order implemented
+ * by the sorter.
+ *
+ * Returns: zero if @item1 == @item2,
+ *     a negative value if @item1 < @item2,
+ *     a positive value if @item1 > @item2
+ */
+int
+gtk_sorter_compare (GtkSorter *self,
+                    gpointer   item1,
+                    gpointer   item2)
+{
+  g_return_val_if_fail (GTK_IS_SORTER (self), 0);
+  g_return_val_if_fail (item1 && item2, 0);
+
+  if (item1 == item2)
+    return 0;
+
+  return GTK_SORTER_GET_CLASS (self)->compare (self, item1, item2);
+}
+
+/**
+ * gtk_sorter_changed:
+ * @self: a #GtkSorter
+ * @change: How the sorter changed
+ *
+ * Emits the #GtkSorter::changed signal to notify all users of the sorter
+ * that it has changed. Users of the sorter should then update the sort
+ * order via gtk_sorter_compare().
+ *
+ * Depending on the @change parameter, it may be possible to update
+ * the sort order without a full resorting. Refer to the #GtkSorterChange
+ * documentation for details.
+ *
+ * This function is intended for implementors of #GtkSorter subclasses and
+ * should not be called from other functions.
+ */
+void
+gtk_sorter_changed (GtkSorter       *self,
+                    GtkSorterChange  change)
+{
+  g_signal_emit (self, signals[CHANGED], 0, change);
+}
diff --git a/gtk/gtksorter.h b/gtk/gtksorter.h
new file mode 100644
index 0000000000..62225e2430
--- /dev/null
+++ b/gtk/gtksorter.h
@@ -0,0 +1,88 @@
+/*
+ * 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_SORTER_H__
+#define __GTK_SORTER_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+#include <gtk/gtkenums.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GtkSorterChange:
+ * @GTK_SORTER_CHANGE_DIFFERENT: The sorter change cannot be described
+ *     by any of the other enumeration values
+ * @GTK_SORTER_CHANGE_INVERTED: The sort order was inverted
+ * @GTK_SORTER_CHANGE_LESS_STRICT: The sorter is less strict than
+ *     before: More items will compare as equal now.
+ * @GTK_SORTER_CHANGE_MORE_STRICT: The sorter is more strict than
+ *     before: Less itesm will compare as equal now.
+ *
+ * Describes changes in a sorter in more detail and allows users
+ * to optimize resorting.
+ */
+typedef enum {
+  GTK_SORTER_CHANGE_DIFFERENT,
+  GTK_SORTER_CHANGE_INVERTED,
+  GTK_SORTER_CHANGE_LESS_STRICT,
+  GTK_SORTER_CHANGE_MORE_STRICT
+} GtkSorterChange;
+
+#define GTK_TYPE_SORTER             (gtk_sorter_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (GtkSorter, gtk_sorter, GTK, SORTER, GObject)
+
+struct _GtkSorterClass
+{
+  GObjectClass parent_class;
+
+  int (* compare)                              (GtkSorter      *self,
+                                                gpointer        item1,
+                                                gpointer        item2);
+
+  /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
+  void (*_gtk_reserved2) (void);
+  void (*_gtk_reserved3) (void);
+  void (*_gtk_reserved4) (void);
+  void (*_gtk_reserved5) (void);
+  void (*_gtk_reserved6) (void);
+  void (*_gtk_reserved7) (void);
+  void (*_gtk_reserved8) (void);
+};
+
+GDK_AVAILABLE_IN_ALL
+int  gtk_sorter_compare (GtkSorter *self,
+                         gpointer   item1,
+                         gpointer   item2);
+
+GDK_AVAILABLE_IN_ALL
+void gtk_sorter_changed (GtkSorter       *self,
+                         GtkSorterChange  change);
+
+G_END_DECLS
+
+#endif /* __GTK_SORTER_H__ */
+
diff --git a/gtk/gtkstringsorter.c b/gtk/gtkstringsorter.c
new file mode 100644
index 0000000000..ae109cd164
--- /dev/null
+++ b/gtk/gtkstringsorter.c
@@ -0,0 +1,250 @@
+/*
+ * 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 "gtkstringsorter.h"
+
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+
+struct _GtkStringSorter
+{
+  GtkSorter parent_instance;
+
+  gboolean ignore_case;
+
+  GtkExpression *expression;
+};
+
+enum {
+  PROP_0,
+  PROP_EXPRESSION,
+  PROP_IGNORE_CASE,
+  NUM_PROPERTIES
+};
+
+G_DEFINE_TYPE (GtkStringSorter, gtk_string_sorter, GTK_TYPE_SORTER)
+
+static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
+
+static int
+gtk_string_sorter_compare (GtkSorter *sorter,
+                           gpointer   item1,
+                           gpointer   item2)
+{
+  GtkStringSorter *self = GTK_STRING_SORTER (sorter);
+  GValue value1 = G_VALUE_INIT;
+  GValue value2 = G_VALUE_INIT;
+  const char *s1, *s2;
+  int result = 0;
+
+  if (self->expression == NULL ||
+      !gtk_expression_evaluate (self->expression, item1, &value1) ||
+      !gtk_expression_evaluate (self->expression, item2, &value2))
+    goto out;
+
+  s1 = g_value_get_string (&value1);
+  s2 = g_value_get_string (&value2);
+
+  if (s1 == NULL || s2 == NULL)
+    goto out;
+
+  if (self->ignore_case)
+    {
+      char *t1, *t2;
+
+      t1 = g_utf8_casefold (s1, -1);
+      t2 = g_utf8_casefold (s2, -1);
+
+      result = g_utf8_collate (t1, t2);
+
+      g_free (t1);
+      g_free (t2);
+    }
+  else
+    result = g_utf8_collate (s1, s2);
+
+out:
+  g_value_unset (&value1);
+  g_value_unset (&value2);
+
+  return result;
+}
+
+static void
+gtk_string_sorter_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GtkStringSorter *self = GTK_STRING_SORTER (object);
+
+  switch (prop_id)
+    {
+    case PROP_EXPRESSION:
+      gtk_string_sorter_set_expression (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_IGNORE_CASE:
+      gtk_string_sorter_set_ignore_case (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void 
+gtk_string_sorter_get_property (GObject     *object,
+                                guint        prop_id,
+                                GValue      *value,
+                                GParamSpec  *pspec)
+{
+  GtkStringSorter *self = GTK_STRING_SORTER (object);
+
+  switch (prop_id)
+    {
+    case PROP_EXPRESSION:
+      g_value_set_boxed (value, self->expression);
+      break;
+
+    case PROP_IGNORE_CASE:
+      g_value_set_boolean (value, self->ignore_case);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_string_sorter_dispose (GObject *object)
+{
+  GtkStringSorter *self = GTK_STRING_SORTER (object);
+
+  g_clear_pointer (&self->expression, gtk_expression_unref);
+
+  G_OBJECT_CLASS (gtk_string_sorter_parent_class)->dispose (object);
+}
+
+static void
+gtk_string_sorter_class_init (GtkStringSorterClass *class)
+{
+  GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  sorter_class->compare = gtk_string_sorter_compare;
+
+  object_class->get_property = gtk_string_sorter_get_property;
+  object_class->set_property = gtk_string_sorter_set_property;
+  object_class->dispose = gtk_string_sorter_dispose;
+
+  /**
+   * GtkStringSorter:expression:
+   *
+   * The expression to evalute on item to get a string 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);
+
+  /**
+   * GtkStringSorter:ignore-case:
+   *
+   * If matching is case sensitive
+   */
+  properties[PROP_IGNORE_CASE] =
+      g_param_spec_boolean ("ignore-case",
+                            P_("Ignore case"),
+                            P_("If matching is case sensitive"),
+                            TRUE,
+                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+}
+
+static void
+gtk_string_sorter_init (GtkStringSorter *self)
+{
+  self->ignore_case = TRUE;
+}
+
+GtkSorter *
+gtk_string_sorter_new (void)
+{
+  return g_object_new (GTK_TYPE_STRING_SORTER, NULL);
+}
+
+GtkExpression *
+gtk_string_sorter_get_expression (GtkStringSorter *self)
+{
+  g_return_val_if_fail (GTK_IS_STRING_SORTER (self), NULL);
+
+  return self->expression;
+}
+
+void
+gtk_string_sorter_set_expression (GtkStringSorter *self,
+                                  GtkExpression   *expression)
+{
+  g_return_if_fail (GTK_IS_STRING_SORTER (self));
+  if (expression)
+    g_return_if_fail (gtk_expression_get_value_type (expression) == G_TYPE_STRING);
+
+  if (self->expression == expression)
+    return;
+
+  g_clear_pointer (&self->expression, gtk_expression_unref);
+  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]);
+}
+
+gboolean
+gtk_string_sorter_get_ignore_case (GtkStringSorter *self)
+{
+  g_return_val_if_fail (GTK_IS_STRING_SORTER (self), TRUE);
+
+  return self->ignore_case;
+}
+
+void
+gtk_string_sorter_set_ignore_case (GtkStringSorter *self,
+                                   gboolean         ignore_case)
+{
+  g_return_if_fail (GTK_IS_STRING_SORTER (self));
+
+  if (self->ignore_case == ignore_case)
+    return;
+
+  self->ignore_case = ignore_case;
+
+  gtk_sorter_changed (GTK_SORTER (self), ignore_case ? GTK_SORTER_CHANGE_LESS_STRICT : 
GTK_SORTER_CHANGE_MORE_STRICT);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IGNORE_CASE]);
+}
diff --git a/gtk/gtkstringsorter.h b/gtk/gtkstringsorter.h
new file mode 100644
index 0000000000..d0c749c020
--- /dev/null
+++ b/gtk/gtkstringsorter.h
@@ -0,0 +1,52 @@
+/*
+ * 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_STRING_SORTER_H__
+#define __GTK_STRING_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_STRING_SORTER             (gtk_string_sorter_get_type ())
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkStringSorter, gtk_string_sorter, GTK, STRING_SORTER, GtkSorter)
+
+GDK_AVAILABLE_IN_ALL
+GtkSorter *             gtk_string_sorter_new                   (void);
+
+GDK_AVAILABLE_IN_ALL
+GtkExpression *         gtk_string_sorter_get_expression        (GtkStringSorter        *self);
+GDK_AVAILABLE_IN_ALL
+void                    gtk_string_sorter_set_expression        (GtkStringSorter        *self,
+                                                                 GtkExpression          *expression);
+GDK_AVAILABLE_IN_ALL
+gboolean                gtk_string_sorter_get_ignore_case       (GtkStringSorter        *self);
+GDK_AVAILABLE_IN_ALL
+void                    gtk_string_sorter_set_ignore_case       (GtkStringSorter        *self,
+                                                                 gboolean                ignore_case);
+
+G_END_DECLS
+
+#endif /* __GTK_STRING_SORTER_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index e887d3e464..98b304bc0c 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -121,6 +121,7 @@ gtk_private_sources = files([
   'gtkiconcache.c',
   'tools/gtkiconcachevalidator.c',
   'gtkiconhelper.c',
+  'gtkinvertiblesorter.c',
   'gtkkineticscrolling.c',
   'gtkkeyhash.c',
   'gtkmagnifier.c',
@@ -220,6 +221,7 @@ gtk_public_sources = files([
   'gtkconstraint.c',
   'gtkcontainer.c',
   'gtkcoverflow.c',
+  'gtkcustomsorter.c',
   'gtkcssprovider.c',
   'gtkdialog.c',
   'gtkdirectorylist.c',
@@ -379,6 +381,7 @@ gtk_public_sources = files([
   'gtksizerequest.c',
   'gtkslicelistmodel.c',
   'gtksnapshot.c',
+  'gtksorter.c',
   'gtksortlistmodel.c',
   'gtkspinbutton.c',
   'gtkspinner.c',
@@ -387,6 +390,7 @@ gtk_public_sources = files([
   'gtkstackswitcher.c',
   'gtkstatusbar.c',
   'gtkstringfilter.c',
+  'gtkstringsorter.c',
   'gtkstylecontext.c',
   'gtkstyleprovider.c',
   'gtkswitch.c',
@@ -569,6 +573,7 @@ gtk_public_headers = files([
   'gtkimmodule.h',
   'gtkimmulticontext.h',
   'gtkinfobar.h',
+  'gtkinvertiblesorter.h',
   'gtklabel.h',
   'gtklayoutchild.h',
   'gtklayoutmanager.h',
@@ -647,6 +652,8 @@ gtk_public_headers = files([
   'gtksizerequest.h',
   'gtkslicelistmodel.h',
   'gtksnapshot.h',
+  'gtksorter.h',
+  'gtkcustomsorter.h',
   'gtksortlistmodel.h',
   'gtkspinbutton.h',
   'gtkspinner.h',
@@ -655,6 +662,7 @@ gtk_public_headers = files([
   'gtkstackswitcher.h',
   'gtkstatusbar.h',
   'gtkstringfilter.h',
+  'gtkstringsorter.h',
   'gtkstylecontext.h',
   'gtkstyleprovider.h',
   'gtkswitch.h',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]