diff options
-rw-r--r-- | docs/reference/gtk/gtk4-docs.xml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 17 | ||||
-rw-r--r-- | docs/reference/gtk/gtk4.types.in | 1 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtkmultisorter.c | 310 | ||||
-rw-r--r-- | gtk/gtkmultisorter.h | 49 | ||||
-rw-r--r-- | gtk/meson.build | 2 |
7 files changed, 381 insertions, 0 deletions
diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 67040526c6..b83897cdc8 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/gtkmultisorter.xml" /> </section> <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 eed0ef504a..e04a0b94f6 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -2476,6 +2476,23 @@ gtk_custom_sorter_get_type </SECTION> <SECTION> +<FILE>gtkmultisorter</FILE> +<TITLE>GtkMultiSorter</TITLE> +GtkMultiSorter +gtk_multi_sorter_new +gtk_multi_sorter_append +gtk_multi_sorter_remove +<SUBSECTION Standard> +GTK_MULTI_SORTER +GTK_IS_MULTI_SORTER +GTK_TYPE_MULTI_SORTER +GTK_IS_MULTI_SORTER_CLASS +GTK_MULTI_SORTER_GET_CLASS +<SUBSECTION Private> +gtk_multi_sorter_get_type +</SECTION> + +<SECTION> <FILE>gtksortlistmodel</FILE> <TITLE>GtkSortListModel</TITLE> GtkSortListModel diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in index 982b398517..473c4d13cc 100644 --- a/docs/reference/gtk/gtk4.types.in +++ b/docs/reference/gtk/gtk4.types.in @@ -131,6 +131,7 @@ gtk_menu_button_get_type gtk_message_dialog_get_type gtk_mount_operation_get_type gtk_multi_filter_get_type +gtk_multi_sorter_get_type gtk_native_get_type gtk_native_dialog_get_type gtk_no_selection_get_type @@ -164,6 +164,7 @@ #include <gtk/gtkmessagedialog.h> #include <gtk/gtkmountoperation.h> #include <gtk/gtkmultifilter.h> +#include <gtk/gtkmultisorter.h> #include <gtk/gtknative.h> #include <gtk/gtknativedialog.h> #include <gtk/gtknoselection.h> diff --git a/gtk/gtkmultisorter.c b/gtk/gtkmultisorter.c new file mode 100644 index 0000000000..66400c50a2 --- /dev/null +++ b/gtk/gtkmultisorter.c @@ -0,0 +1,310 @@ +/* + * 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 "gtkmultisorter.h" + +#include "gtkbuildable.h" +#include "gtkintl.h" +#include "gtktypebuiltins.h" + +/** + * SECTION:gtkmultisorter + * @Title: GtkMultiSorter + * @Short_description: Combining multiple sorters + * + * GtkMultiSorter combines multiple sorters by trying them + * in turn. If the first sorter compares two items as equal, + * the second is tried next, and so on. + */ +struct _GtkMultiSorter +{ + GtkSorter parent_instance; + + GSequence *sorters; +}; + +static GType +gtk_multi_sorter_get_item_type (GListModel *list) +{ + return GTK_TYPE_SORTER; +} + +static guint +gtk_multi_sorter_get_n_items (GListModel *list) +{ + GtkMultiSorter *self = GTK_MULTI_SORTER (list); + + return g_sequence_get_length (self->sorters); +} + +static gpointer +gtk_multi_sorter_get_item (GListModel *list, + guint position) +{ + GtkMultiSorter *self = GTK_MULTI_SORTER (list); + GSequenceIter *iter; + + iter = g_sequence_get_iter_at_pos (self->sorters, position); + + if (g_sequence_iter_is_end (iter)) + return NULL; + else + return g_object_ref (g_sequence_get (iter)); +} + +static void +gtk_multi_sorter_list_model_init (GListModelInterface *iface) +{ + iface->get_item_type = gtk_multi_sorter_get_item_type; + iface->get_n_items = gtk_multi_sorter_get_n_items; + iface->get_item = gtk_multi_sorter_get_item; +} + +static GtkBuildableIface *parent_buildable_iface; + +static void +gtk_multi_sorter_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (GTK_IS_SORTER (child)) + gtk_multi_sorter_append (GTK_MULTI_SORTER (buildable), g_object_ref (GTK_SORTER (child))); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + +static void +gtk_multi_sorter_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + + iface->add_child = gtk_multi_sorter_buildable_add_child; +} + +G_DEFINE_TYPE_WITH_CODE (GtkMultiSorter, gtk_multi_sorter, GTK_TYPE_SORTER, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_multi_sorter_list_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_multi_sorter_buildable_init)) + +static GtkOrdering +gtk_multi_sorter_compare (GtkSorter *sorter, + gpointer item1, + gpointer item2) +{ + GtkMultiSorter *self = GTK_MULTI_SORTER (sorter); + GtkOrdering result = GTK_ORDERING_EQUAL; + GSequenceIter *iter; + + for (iter = g_sequence_get_begin_iter (self->sorters); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + GtkSorter *child = g_sequence_get (iter); + + result = gtk_sorter_compare (child, item1, item2); + if (result != GTK_ORDERING_EQUAL) + break; + } + + return result; +} + +static GtkSorterOrder +gtk_multi_sorter_get_order (GtkSorter *sorter) +{ + GtkMultiSorter *self = GTK_MULTI_SORTER (sorter); + GtkSorterOrder result = GTK_SORTER_ORDER_NONE; + GSequenceIter *iter; + + for (iter = g_sequence_get_begin_iter (self->sorters); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + GtkSorter *child = g_sequence_get (iter); + GtkSorterOrder child_order; + + child_order = gtk_sorter_get_order (child); + switch (child_order) + { + case GTK_SORTER_ORDER_PARTIAL: + result = GTK_SORTER_ORDER_PARTIAL; + break; + case GTK_SORTER_ORDER_NONE: + break; + case GTK_SORTER_ORDER_TOTAL: + return GTK_SORTER_ORDER_TOTAL; + default: + g_assert_not_reached (); + break; + } + } + + return result; +} + +static void +gtk_multi_sorter_changed_cb (GtkSorter *sorter, + GtkSorterChange change, + GtkMultiSorter *self) +{ + /* Using an enum on purpose, so gcc complains about this case if + * new values are added to the enum */ + switch (change) + { + case GTK_SORTER_CHANGE_INVERTED: + /* This could do a lot better with change handling, in particular in cases where self->n_sorters == 1 + * or if sorter == self->sorters[0] */ + change = GTK_SORTER_CHANGE_DIFFERENT; + break; + + case GTK_SORTER_CHANGE_DIFFERENT: + case GTK_SORTER_CHANGE_LESS_STRICT: + case GTK_SORTER_CHANGE_MORE_STRICT: + break; + + default: + g_assert_not_reached (); + change = GTK_SORTER_CHANGE_DIFFERENT; + } + gtk_sorter_changed (GTK_SORTER (self), change); +} + +static void +gtk_multi_sorter_remove_iter (GtkMultiSorter *self, + GSequenceIter *iter) +{ + GtkSorter *sorter; + + sorter = g_sequence_get (iter); + g_signal_handlers_disconnect_by_func (sorter, gtk_multi_sorter_changed_cb, self); + g_object_unref (sorter); + g_sequence_remove (iter); +} + +static void +gtk_multi_sorter_dispose (GObject *object) +{ + GtkMultiSorter *self = GTK_MULTI_SORTER (object); + + while (!g_sequence_is_empty (self->sorters)) + gtk_multi_sorter_remove_iter (self, g_sequence_get_begin_iter (self->sorters)); + + G_OBJECT_CLASS (gtk_multi_sorter_parent_class)->dispose (object); +} + +static void +gtk_multi_sorter_finalize (GObject *object) +{ + GtkMultiSorter *self = GTK_MULTI_SORTER (object); + + g_sequence_free (self->sorters); + + G_OBJECT_CLASS (gtk_multi_sorter_parent_class)->finalize (object); +} + +static void +gtk_multi_sorter_class_init (GtkMultiSorterClass *class) +{ + GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + sorter_class->compare = gtk_multi_sorter_compare; + sorter_class->get_order = gtk_multi_sorter_get_order; + + object_class->dispose = gtk_multi_sorter_dispose; + object_class->finalize = gtk_multi_sorter_finalize; +} + +static void +gtk_multi_sorter_init (GtkMultiSorter *self) +{ + self->sorters = g_sequence_new (NULL); +} + +/** + * gtk_multi_sorter_new: + * + * Creates a new multi sorter. + * + * This sorter compares items by trying each of the sorters + * in turn, until one returns non-zero. In particular, if + * no sorter has been added to it, it will always compare + * items as equal. + * + * Returns: a new #GtkSorter + */ +GtkSorter * +gtk_multi_sorter_new (void) +{ + return g_object_new (GTK_TYPE_MULTI_SORTER, NULL); +} + +/** + * gtk_multi_sorter_append: + * @self: a #GtkMultiSorter + * @sorter: (transfer full): a sorter to add + * + * Add @sorter to @self to use for sorting at the end. @self + * will consult all existing sorters before it will sort with + * the given @sorter. + */ +void +gtk_multi_sorter_append (GtkMultiSorter *self, + GtkSorter *sorter) +{ + g_return_if_fail (GTK_IS_MULTI_SORTER (self)); + g_return_if_fail (GTK_IS_SORTER (sorter)); + + g_signal_connect (sorter, "changed", G_CALLBACK (gtk_multi_sorter_changed_cb), self); + g_sequence_append (self->sorters, sorter); + + gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_MORE_STRICT); +} + +/** + * gtk_multi_sorter_remove: + * @self: a #GtkMultiSorter + * @position: position of sorter to remove + * + * Removes the sorter at the given @position from the list of sorter + * used by @self. + * + * If @position is larger than the number of sorters, nothing happens. + */ +void +gtk_multi_sorter_remove (GtkMultiSorter *self, + guint position) +{ + GSequenceIter *iter; + guint length; + + g_return_if_fail (GTK_IS_MULTI_SORTER (self)); + + length = g_sequence_get_length (self->sorters); + if (position >= length) + return; + + iter = g_sequence_get_iter_at_pos (self->sorters, position); + gtk_multi_sorter_remove_iter (self, iter); + + gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_LESS_STRICT); +} + diff --git a/gtk/gtkmultisorter.h b/gtk/gtkmultisorter.h new file mode 100644 index 0000000000..4f265bf6a2 --- /dev/null +++ b/gtk/gtkmultisorter.h @@ -0,0 +1,49 @@ +/* + * 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_MULTI_SORTER_H__ +#define __GTK_MULTI_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_MULTI_SORTER (gtk_multi_sorter_get_type ()) +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkMultiSorter, gtk_multi_sorter, GTK, MULTI_SORTER, GtkSorter) + +GDK_AVAILABLE_IN_ALL +GtkSorter * gtk_multi_sorter_new (void); + +GDK_AVAILABLE_IN_ALL +void gtk_multi_sorter_append (GtkMultiSorter *self, + GtkSorter *sorter); + +GDK_AVAILABLE_IN_ALL +void gtk_multi_sorter_remove (GtkMultiSorter *self, + guint position); + +G_END_DECLS + +#endif /* __GTK_MULTI_SORTER_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index b0fe6c89fd..4663211d73 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -285,6 +285,7 @@ gtk_public_sources = files([ 'gtkmodules.c', 'gtkmountoperation.c', 'gtkmultifilter.c', + 'gtkmultisorter.c', 'gtknativedialog.c', 'gtknomediafile.c', 'gtknoselection.c', @@ -543,6 +544,7 @@ gtk_public_headers = files([ 'gtkmessagedialog.h', 'gtkmountoperation.h', 'gtkmultifilter.h', + 'gtkmultisorter.h', 'gtknative.h', 'gtknativedialog.h', 'gtknoselection.h', |