[gtk/wip/otte/listview: 10/146] Add GtkSorter



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

    Add GtkSorter
    
    This is a helper object for sorting, similar to GtkFilter.

 docs/reference/gtk/gtk4-docs.xml     |   3 +
 docs/reference/gtk/gtk4-sections.txt |  22 ++++
 docs/reference/gtk/gtk4.types.in     |   1 +
 gtk/gtk.h                            |   1 +
 gtk/gtkenums.h                       |  20 ++++
 gtk/gtksorter.c                      | 194 +++++++++++++++++++++++++++++++++++
 gtk/gtksorter.h                      | 144 ++++++++++++++++++++++++++
 gtk/meson.build                      |   2 +
 8 files changed, 387 insertions(+)
---
diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml
index 9507f89b34..704f6a107d 100644
--- a/docs/reference/gtk/gtk4-docs.xml
+++ b/docs/reference/gtk/gtk4-docs.xml
@@ -54,6 +54,9 @@
       <xi:include href="xml/gtkmaplistmodel.xml" />
       <xi:include href="xml/gtkslicelistmodel.xml" />
       <xi:include href="xml/gtksortlistmodel.xml" />
+      <section>
+        <xi:include href="xml/gtksorter.xml" />
+      </section>
       <xi:include href="xml/gtktreelistmodel.xml" />
       <xi:include href="xml/gtkselectionmodel.xml" />
       <xi:include href="xml/gtknoselection.xml" />
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 9465474a81..e9dac2c9e9 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -2769,6 +2769,26 @@ GTK_SLICE_LIST_MODEL_GET_CLASS
 gtk_slice_list_model_get_type
 </SECTION>
 
+<SECTION>
+<FILE>gtksorter</FILE>
+<TITLE>GtkSorter</TITLE>
+GtkSorter
+GtkSorderOrder
+GtkSorterChange
+gtk_sorter_compare
+gtk_sorter_get_order
+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>
@@ -5379,6 +5399,8 @@ GtkDeleteType
 GtkDirectionType
 GtkJustification
 GtkMovementStep
+GtkOrdering
+gtk_ordering_from_cmpfunc
 GtkOrientation
 GtkPackType
 GtkPositionType
diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in
index a83456346d..3e8ddf531c 100644
--- a/docs/reference/gtk/gtk4.types.in
+++ b/docs/reference/gtk/gtk4.types.in
@@ -181,6 +181,7 @@ gtk_size_group_get_type
 gtk_slice_list_model_get_type
 gtk_snapshot_get_type
 gtk_sort_list_model_get_type
+gtk_sorter_get_type
 gtk_spin_button_get_type
 gtk_spinner_get_type
 gtk_stack_get_type
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 6edd812874..df2c7569b5 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -219,6 +219,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>
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 9bd6f1632e..7b0cfa6815 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -607,6 +607,26 @@ typedef enum
   GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT  /*< nick=btrl >*/
 } GtkNumberUpLayout;
 
+/**
+ * GtkOrdering:
+ * @GTK_ORDERING_INVALID: the two values cannot be ordered
+ * @GTK_ORDERING_SMALLER: the first value is smaller than the second. This
+ *     is equivalent to a #GCompareFunc returning -1.
+ * @GTK_ORDERING_EQUAL: the two values are equal. This
+ *     is equivalent to a #GCompareFunc returning 0.
+ * @GTK_ORDERING_LARGER: the first value is larger than the second. This
+ *     is equivalent to a #GCompareFunc returning 1.
+ *
+ * Describes the way two values can be compared. See gtk_sorter_compare()
+ * for an example.
+ */
+typedef enum {
+  GTK_ORDERING_INVALID = 0,
+  GTK_ORDERING_SMALLER,
+  GTK_ORDERING_EQUAL,
+  GTK_ORDERING_LARGER
+} GtkOrdering;
+
 /**
  * GtkPageOrientation:
  * @GTK_PAGE_ORIENTATION_PORTRAIT: Portrait mode.
diff --git a/gtk/gtksorter.c b/gtk/gtksorter.c
new file mode 100644
index 0000000000..f907b049cc
--- /dev/null
+++ b/gtk/gtksorter.c
@@ -0,0 +1,194 @@
+/*
+ * 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
+ * @See_also: #GtkSortListModel
+ *
+ * #GtkSorter is the way to describe sorting criteria.  
+ * It primary user is #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 their sorting behavior 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_sorter_compare() again.
+ *
+ * GTK provides various pre-made sorter implementations for common sorting operations.
+ * #GtkColumnView has built-in support for sorting lists via GtkColumnViewColumn::sorter
+ * where the user can change the sorting by clicking on list headers.
+ *
+ * Of coure, 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 GtkOrdering
+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 GTK_ORDERING_INVALID;
+}
+
+static GtkSorterOrder
+gtk_sorter_default_get_order (GtkSorter *self)
+{
+  return GTK_SORTER_ORDER_PARTIAL;
+}
+
+static void
+gtk_sorter_class_init (GtkSorterClass *class)
+{
+  class->compare = gtk_sorter_default_compare;
+  class->get_order = gtk_sorter_default_get_order;
+
+  /**
+   * 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.
+ *
+ * Sorters implement a partial order:  
+ * * It is reflexive, ie `a == a`
+ * * It is antisymmetric, ie if `a < b` then `b > a`.
+ * * It is transitive, ie given any 3 items with `a <= b && b<= c`,
+ *   then `a <= c`.  
+ * 
+ * The sorter  may signal it conforms to additional constraints
+ * via the return value of gtk_sorter_get_order().
+ *
+ * Returns: %GTK_ORDERING_EQUAL if @item1 == @item2,
+ *     %GTK_ORDERING_SMALLER if @item1 < @item2,
+ *     %GTK_ORDERING_LARGER if @item1 > @item2
+ */
+GtkOrdering
+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 GTK_ORDERING_EQUAL;
+
+  return GTK_SORTER_GET_CLASS (self)->compare (self, item1, item2);
+}
+
+/**
+ * gtk_sorter_get_order:
+ * @self: a #GtkSorter
+ *
+ * Gets the order that @self conforms to. See #GtkSorterOrder for details
+ * of the possible return values.
+ *
+ * This function is intended to allow optimizations.
+ *
+ * Returns: The order
+ **/
+GtkSorterOrder
+gtk_sorter_get_order (GtkSorter *self)
+{
+  g_return_val_if_fail (GTK_IS_SORTER (self), GTK_SORTER_ORDER_PARTIAL);
+
+  return GTK_SORTER_GET_CLASS (self)->get_order (self);
+}
+
+/**
+ * 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_return_if_fail (GTK_IS_SORTER (self));
+
+  g_signal_emit (self, signals[CHANGED], 0, change);
+}
diff --git a/gtk/gtksorter.h b/gtk/gtksorter.h
new file mode 100644
index 0000000000..4b1981b92f
--- /dev/null
+++ b/gtk/gtksorter.h
@@ -0,0 +1,144 @@
+/*
+ * 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
+
+/**
+ * GtkSorterOrder:
+ * @GTK_SORTER_ORDER_PARTIAL: A partial order. And #GtkOrdering is possible.
+ * @GTK_SORTER_ORDER_INVALID: An invalid order. gtk_sorter_compare() will
+ *     always return %GTK_ORDERING_INVALID if both items are unequal.
+ * @GTK_SORTER_ORDER_NONE: No order, all elements are considered equal.
+ *     gtk_sorter_compare() will only return %GTK_ORDERING_EQUAL or
+ *     %GTK_ORDERING_INVALID.
+ * @GTK_SORTER_ORDER_TOTAL: A total order. gtk_sorter_compare() will only
+ *     return %GTK_ORDERING_EQUAL if an item is compared with itself. Two
+ *     different items will never cause this value to be returned.
+ *
+ * Describes the type of order that a #GtkSorter may describe.
+ */
+typedef enum {
+  GTK_SORTER_ORDER_PARTIAL,
+  GTK_SORTER_ORDER_INVALID,
+  GTK_SORTER_ORDER_NONE,
+  GTK_SORTER_ORDER_TOTAL
+} GtkSorterOrder;
+
+/**
+ * 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. Comparisons
+ *     that returned %GTK_ORDERING_SMALLER now return %GTK_ORDERING_LARGER
+ *     and vice versa. Other comparisons return the same values as before.
+ * @GTK_SORTER_CHANGE_LESS_STRICT: The sorter is less strict: Comparisons
+ *     may now return %GTK_ORDERING_EQUAL that did not do so before.
+ * @GTK_SORTER_CHANGE_MORE_STRICT: The sorter is more strict: Comparisons
+ *     that did return %GTK_ORDERING_EQUAL may not do so anymore.
+ *
+ * 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)
+
+/**
+ * GtkSorterClass
+ * @compare: Compare two items. See gtk_sorter_compare() for details.
+ * @get_order: Get the #GtkSorderOrder that applies to the current sorter.
+ *     If unimplemented, it returns %GTK_SORTER_ORDER_PARTIAL.
+ *
+ * The virtual table for #GtkSorter.
+ */
+struct _GtkSorterClass
+{
+  GObjectClass parent_class;
+
+  GtkOrdering           (* compare)                             (GtkSorter              *self,
+                                                                 gpointer                item1,
+                                                                 gpointer                item2);
+
+  /* optional */
+  GtkSorterOrder        (* get_order)                           (GtkSorter              *self);
+
+  /* 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
+GtkOrdering             gtk_sorter_compare                      (GtkSorter              *self,
+                                                                 gpointer                item1,
+                                                                 gpointer                item2);
+GDK_AVAILABLE_IN_ALL
+GtkSorterOrder          gtk_sorter_get_order                    (GtkSorter              *self);
+
+/* for sorter implementations */
+GDK_AVAILABLE_IN_ALL
+void                    gtk_sorter_changed                      (GtkSorter              *self,
+                                                                 GtkSorterChange         change);
+
+/**
+ * gtk_ordering_from_cmpfunc:
+ * @cmpfunc_result: Reslt of a comparison function
+ *
+ * Converts the result of a #GCompareFunc like strcmp() to a #GtkOrdering.
+ *
+ * Returns: the corresponding GtkOrdering
+ **/
+static inline GtkOrdering
+gtk_ordering_from_cmpfunc (int cmpfunc_result)
+{
+  if (cmpfunc_result < 0)
+    return GTK_ORDERING_SMALLER;
+  else if (cmpfunc_result > 0)
+    return GTK_ORDERING_LARGER;
+  else
+    return GTK_ORDERING_EQUAL;
+}
+
+G_END_DECLS
+
+#endif /* __GTK_SORTER_H__ */
+
diff --git a/gtk/meson.build b/gtk/meson.build
index 7624f7a7cd..61873947d6 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -362,6 +362,7 @@ gtk_public_sources = files([
   'gtksizerequest.c',
   'gtkslicelistmodel.c',
   'gtksnapshot.c',
+  'gtksorter.c',
   'gtksortlistmodel.c',
   'gtkspinbutton.c',
   'gtkspinner.c',
@@ -618,6 +619,7 @@ gtk_public_headers = files([
   'gtksizerequest.h',
   'gtkslicelistmodel.h',
   'gtksnapshot.h',
+  'gtksorter.h',
   'gtksortlistmodel.h',
   'gtkspinbutton.h',
   'gtkspinner.h',


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