From c13e615f70f8de077fa6d0c3d9c11ed5aee018ca Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 11 Dec 2019 21:28:38 +0100 Subject: [PATCH] Add GtkMultiFilter, GtkAnyFilter, GtkEveryFilter GtkMultiFilter is the abstract base class for managing multiple child filter. GtkAnyFilter and GtkEveryFilter are the actual implementations. --- docs/reference/gtk/gtk4-docs.xml | 1 + docs/reference/gtk/gtk4-sections.txt | 23 ++ docs/reference/gtk/gtk4.types.in | 3 + gtk/gtk.h | 1 + gtk/gtkmultifilter.c | 430 +++++++++++++++++++++++++++ gtk/gtkmultifilter.h | 58 ++++ gtk/meson.build | 2 + 7 files changed, 518 insertions(+) create mode 100644 gtk/gtkmultifilter.c create mode 100644 gtk/gtkmultifilter.h diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 1bb8301b87..21981b8b5f 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -50,6 +50,7 @@ + diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index eb9724f025..0f2495999f 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -1407,6 +1407,29 @@ GTK_CUSTOM_FILTER_GET_CLASS gtk_custom_filter_get_type +
+gtkmultifilter +GtkMultiFilter +GtkMultiFilter +gtk_multi_filter_append +gtk_multi_filter_remove + +GtkAnyFilter +gtk_any_filter_new + +GtkEveryFilter +gtk_every_filter_new + +GTK_CUSTOM_FILTER +GTK_IS_CUSTOM_FILTER +GTK_TYPE_CUSTOM_FILTER +GTK_CUSTOM_FILTER_CLASS +GTK_IS_CUSTOM_FILTER_CLASS +GTK_CUSTOM_FILTER_GET_CLASS + +gtk_custom_filter_get_type +
+
gtkfilterlistmodel GtkFilterListModel diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in index 88763d40a2..4554049f8a 100644 --- a/docs/reference/gtk/gtk4.types.in +++ b/docs/reference/gtk/gtk4.types.in @@ -9,6 +9,7 @@ gtk_accessible_get_type gtk_actionable_get_type gtk_action_bar_get_type gtk_adjustment_get_type +gtk_any_filter_get_type gtk_app_chooser_get_type gtk_app_chooser_button_get_type gtk_app_chooser_dialog_get_type @@ -68,6 +69,7 @@ gtk_event_controller_key_get_type gtk_event_controller_legacy_get_type gtk_event_controller_motion_get_type gtk_event_controller_scroll_get_type +gtk_every_filter_get_type gtk_expander_get_type gtk_file_chooser_button_get_type gtk_file_chooser_dialog_get_type @@ -128,6 +130,7 @@ gtk_menu_shell_get_type gtk_menu_tool_button_get_type gtk_message_dialog_get_type gtk_mount_operation_get_type +gtk_multi_filter_get_type gtk_native_get_type gtk_no_selection_get_type gtk_notebook_get_type diff --git a/gtk/gtk.h b/gtk/gtk.h index d7985b8e7a..6edd812874 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -168,6 +168,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkmultifilter.c b/gtk/gtkmultifilter.c new file mode 100644 index 0000000000..c340f8ba34 --- /dev/null +++ b/gtk/gtkmultifilter.c @@ -0,0 +1,430 @@ +/* + * Copyright © 2019 Benjamin Otte + * + * 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 . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkmultifilter.h" + +#include "gtkbuildable.h" +#include "gtkintl.h" +#include "gtktypebuiltins.h" + +/*** MULTI FILTER ***/ + +/** + * SECTION:gtkmultifilter + * @Title: GtkMultiFilter + * @Short_description: Combining multiple filter + * + * GtkMultiFilter is the base type that implements support for handling + * multiple filter. + * + * GtkAnyFilter is an implementation of GtkMultiFilter that matches an item + * when at least one of its filter matches. + * + * GtkEveryFilter is an implementation of GtkMultiFilter that matches an item + * when each of its child filter matches. + */ +struct _GtkMultiFilter +{ + GtkFilter parent_instance; + + GSequence *filters; +}; + +struct _GtkMultiFilterClass +{ + GtkFilterClass parent_class; + + GtkFilterChange addition_change; + GtkFilterChange removal_change; +}; + +static GType +gtk_multi_filter_get_item_type (GListModel *list) +{ + return GTK_TYPE_FILTER; +} + +static guint +gtk_multi_filter_get_n_items (GListModel *list) +{ + GtkMultiFilter *self = GTK_MULTI_FILTER (list); + + return g_sequence_get_length (self->filters); +} + +static gpointer +gtk_multi_filter_get_item (GListModel *list, + guint position) +{ + GtkMultiFilter *self = GTK_MULTI_FILTER (list); + GSequenceIter *iter; + + iter = g_sequence_get_iter_at_pos (self->filters, position); + + if (g_sequence_iter_is_end (iter)) + return NULL; + else + return g_object_ref (g_sequence_get (iter)); +} + +static void +gtk_multi_filter_list_model_init (GListModelInterface *iface) +{ + iface->get_item_type = gtk_multi_filter_get_item_type; + iface->get_n_items = gtk_multi_filter_get_n_items; + iface->get_item = gtk_multi_filter_get_item; +} + +static GtkBuildableIface *parent_buildable_iface; + +static void +gtk_multi_filter_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (GTK_IS_FILTER (child)) + gtk_multi_filter_append (GTK_MULTI_FILTER (buildable), g_object_ref (GTK_FILTER (child))); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + +static void +gtk_multi_filter_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + + iface->add_child = gtk_multi_filter_buildable_add_child; +} + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkMultiFilter, gtk_multi_filter, GTK_TYPE_FILTER, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_multi_filter_list_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_multi_filter_buildable_init)) + +static void +gtk_multi_filter_changed_cb (GtkFilter *filter, + GtkFilterChange change, + GtkMultiFilter *self) +{ + gtk_filter_changed (GTK_FILTER (self), change); +} + +static void +gtk_multi_filter_remove_iter (GtkMultiFilter *self, + GSequenceIter *iter) +{ + GtkFilter *filter; + + filter = g_sequence_get (iter); + g_signal_handlers_disconnect_by_func (filter, gtk_multi_filter_changed_cb, self); + g_object_unref (filter); + g_sequence_remove (iter); +} + +static void +gtk_multi_filter_dispose (GObject *object) +{ + GtkMultiFilter *self = GTK_MULTI_FILTER (object); + + while (!g_sequence_is_empty (self->filters)) + gtk_multi_filter_remove_iter (self, g_sequence_get_begin_iter (self->filters)); + + G_OBJECT_CLASS (gtk_multi_filter_parent_class)->dispose (object); +} + +static void +gtk_multi_filter_finalize (GObject *object) +{ + GtkMultiFilter *self = GTK_MULTI_FILTER (object); + + g_sequence_free (self->filters); + + G_OBJECT_CLASS (gtk_multi_filter_parent_class)->finalize (object); +} + +static void +gtk_multi_filter_class_init (GtkMultiFilterClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = gtk_multi_filter_dispose; + object_class->finalize = gtk_multi_filter_finalize; +} + +static void +gtk_multi_filter_init (GtkMultiFilter *self) +{ + self->filters = g_sequence_new (g_object_unref); +} + +/** + * gtk_multi_filter_append: + * @self: a #GtkMultiFilter + * @filter: (tranfer none): A new filter to use + * + * Adds a @filter to @self to use for matching. + **/ +void +gtk_multi_filter_append (GtkMultiFilter *self, + GtkFilter *filter) +{ + g_return_if_fail (GTK_IS_MULTI_FILTER (self)); + g_return_if_fail (GTK_IS_FILTER (filter)); + + g_signal_connect (filter, "changed", G_CALLBACK (gtk_multi_filter_changed_cb), self); + g_sequence_append (self->filters, g_object_ref (filter)); + + gtk_filter_changed (GTK_FILTER (self), + GTK_MULTI_FILTER_GET_CLASS (self)->addition_change); +} + +/** + * gtk_multi_filter_remove: + * @self: a #GtkMultiFilter + * @position: position of filter to remove + * + * Removes the filter at the given @position from the list of filters used + * by @self. + * If @position is larger than the number of filters, nothing happens and + * the function returns. + **/ +void +gtk_multi_filter_remove (GtkMultiFilter *self, + guint position) +{ + GSequenceIter *iter; + guint length; + + length = g_sequence_get_length (self->filters); + if (position >= length) + return; + + iter = g_sequence_get_iter_at_pos (self->filters, position); + gtk_multi_filter_remove_iter (self, iter); + + gtk_filter_changed (GTK_FILTER (self), + GTK_MULTI_FILTER_GET_CLASS (self)->removal_change); +} + +/*** ANY FILTER ***/ + +struct _GtkAnyFilter +{ + GtkMultiFilter parent_instance; +}; + +struct _GtkAnyFilterClass +{ + GtkMultiFilterClass parent_class; +}; + +G_DEFINE_TYPE (GtkAnyFilter, gtk_any_filter, GTK_TYPE_MULTI_FILTER) + +static gboolean +gtk_any_filter_match (GtkFilter *filter, + gpointer item) +{ + GtkMultiFilter *self = GTK_MULTI_FILTER (filter); + GSequenceIter *iter; + + for (iter = g_sequence_get_begin_iter (self->filters); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + GtkFilter *child = g_sequence_get (iter); + + if (gtk_filter_match (child, item)) + return TRUE; + } + + return FALSE; +} + +static GtkFilterMatch +gtk_any_filter_get_strictness (GtkFilter *filter) +{ + GtkMultiFilter *multi = GTK_MULTI_FILTER (filter); + GSequenceIter *iter; + GtkFilterMatch result = GTK_FILTER_MATCH_NONE; + + for (iter = g_sequence_get_begin_iter (multi->filters); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + GtkFilter *child = g_sequence_get (iter); + + switch (gtk_filter_get_strictness (child)) + { + case GTK_FILTER_MATCH_SOME: + result = GTK_FILTER_MATCH_SOME; + break; + case GTK_FILTER_MATCH_NONE: + break; + case GTK_FILTER_MATCH_ALL: + return GTK_FILTER_MATCH_ALL; + default: + g_return_val_if_reached (GTK_FILTER_MATCH_NONE); + break; + } + } + + return result; +} + +static void +gtk_any_filter_class_init (GtkAnyFilterClass *class) +{ + GtkMultiFilterClass *multi_filter_class = GTK_MULTI_FILTER_CLASS (class); + GtkFilterClass *filter_class = GTK_FILTER_CLASS (class); + + multi_filter_class->addition_change = GTK_FILTER_CHANGE_LESS_STRICT; + multi_filter_class->removal_change = GTK_FILTER_CHANGE_MORE_STRICT; + + filter_class->match = gtk_any_filter_match; + filter_class->get_strictness = gtk_any_filter_get_strictness; +} + +static void +gtk_any_filter_init (GtkAnyFilter *self) +{ +} + +/** + * gtk_any_filter_new: + * + * Creates a new empty "any" filter. + * Use gtk_multi_filter_append() to add filters to it. + * + * This filter matches an item if any of the filters added to it + * matches the item. + * In particular, this means that if no filter has been added to + * it, the filter matches no item. + * + * Returns: a new #GtkFilter + **/ +GtkFilter * +gtk_any_filter_new (void) +{ + return g_object_new (GTK_TYPE_ANY_FILTER, NULL); +} + +/*** EVERY FILTER ***/ + +struct _GtkEveryFilter +{ + GtkMultiFilter parent_instance; +}; + +struct _GtkEveryFilterClass +{ + GtkMultiFilterClass parent_class; +}; + +G_DEFINE_TYPE (GtkEveryFilter, gtk_every_filter, GTK_TYPE_MULTI_FILTER) + +static gboolean +gtk_every_filter_match (GtkFilter *filter, + gpointer item) +{ + GtkMultiFilter *self = GTK_MULTI_FILTER (filter); + GSequenceIter *iter; + + for (iter = g_sequence_get_begin_iter (self->filters); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + GtkFilter *child = g_sequence_get (iter); + + if (!gtk_filter_match (child, item)) + return FALSE; + } + + return TRUE; +} + +static GtkFilterMatch +gtk_every_filter_get_strictness (GtkFilter *filter) +{ + GtkMultiFilter *multi = GTK_MULTI_FILTER (filter); + GSequenceIter *iter; + GtkFilterMatch result = GTK_FILTER_MATCH_ALL; + + for (iter = g_sequence_get_begin_iter (multi->filters); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + GtkFilter *child = g_sequence_get (iter); + + switch (gtk_filter_get_strictness (child)) + { + case GTK_FILTER_MATCH_SOME: + result = GTK_FILTER_MATCH_SOME; + break; + case GTK_FILTER_MATCH_NONE: + return GTK_FILTER_MATCH_NONE; + case GTK_FILTER_MATCH_ALL: + break; + default: + g_return_val_if_reached (GTK_FILTER_MATCH_NONE); + break; + } + } + + return result; +} + +static void +gtk_every_filter_class_init (GtkEveryFilterClass *class) +{ + GtkMultiFilterClass *multi_filter_class = GTK_MULTI_FILTER_CLASS (class); + GtkFilterClass *filter_class = GTK_FILTER_CLASS (class); + + multi_filter_class->addition_change = GTK_FILTER_CHANGE_MORE_STRICT; + multi_filter_class->removal_change = GTK_FILTER_CHANGE_LESS_STRICT; + + filter_class->match = gtk_every_filter_match; + filter_class->get_strictness = gtk_every_filter_get_strictness; +} + +static void +gtk_every_filter_init (GtkEveryFilter *self) +{ +} + +/** + * gtk_every_filter_new: + * + * Creates a new empty "every" filter. + * Use gtk_multi_filter_append() to add filters to it. + * + * This filter matches an item if every of the filters added to it + * matches the item. + * In particular, this means that if no filter has been added to + * it, the filter matches every item. + * + * Returns: a new #GtkFilter + **/ +GtkFilter * +gtk_every_filter_new (void) +{ + return g_object_new (GTK_TYPE_EVERY_FILTER, NULL); +} + diff --git a/gtk/gtkmultifilter.h b/gtk/gtkmultifilter.h new file mode 100644 index 0000000000..eafa56e1d0 --- /dev/null +++ b/gtk/gtkmultifilter.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2019 Benjamin Otte + * + * 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 . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_MULTI_FILTER_H__ +#define __GTK_MULTI_FILTER_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_MULTI_FILTER (gtk_multi_filter_get_type ()) +GDK_AVAILABLE_IN_ALL +GDK_DECLARE_EXPORTED_TYPE (GtkMultiFilter, gtk_multi_filter, GTK, MULTI_FILTER) + +GDK_AVAILABLE_IN_ALL +void gtk_multi_filter_append (GtkMultiFilter *self, + GtkFilter *filter); +GDK_AVAILABLE_IN_ALL +void gtk_multi_filter_remove (GtkMultiFilter *self, + guint position); + +#define GTK_TYPE_ANY_FILTER (gtk_any_filter_get_type ()) +GDK_AVAILABLE_IN_ALL +GDK_DECLARE_EXPORTED_TYPE (GtkAnyFilter, gtk_any_filter, GTK, ANY_FILTER) +GDK_AVAILABLE_IN_ALL +GtkFilter * gtk_any_filter_new (void); + +#define GTK_TYPE_EVERY_FILTER (gtk_every_filter_get_type ()) +GDK_AVAILABLE_IN_ALL +GDK_DECLARE_EXPORTED_TYPE (GtkEveryFilter, gtk_every_filter, GTK, EVERY_FILTER) +GDK_AVAILABLE_IN_ALL +GtkFilter * gtk_every_filter_new (void); + + +G_END_DECLS + +#endif /* __GTK_MULTI_FILTER_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index e60c6be558..7624f7a7cd 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -299,6 +299,7 @@ gtk_public_sources = files([ 'gtkmodelmenuitem.c', 'gtkmodules.c', 'gtkmountoperation.c', + 'gtkmultifilter.c', 'gtknativedialog.c', 'gtknomediafile.c', 'gtknoselection.c', @@ -564,6 +565,7 @@ gtk_public_headers = files([ 'gtkmenutoolbutton.h', 'gtkmessagedialog.h', 'gtkmountoperation.h', + 'gtkmultifilter.h', 'gtknative.h', 'gtknativedialog.h', 'gtknoselection.h',