gtk2/gtk/gtkmultifilter.c
Benjamin Otte 96e1b85c2c gdkarray: Add a "stolen" boolean to splice()
If set to TRUE, does not call the free func for the removed items.

This can be used to move items between arrays without having to do the
refcounting dance.
2020-12-24 06:38:45 +01:00

411 lines
10 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkmultifilter.h"
#include "gtkbuildable.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
#define GDK_ARRAY_TYPE_NAME GtkFilters
#define GDK_ARRAY_NAME gtk_filters
#define GDK_ARRAY_ELEMENT_TYPE GtkFilter *
#define GDK_ARRAY_FREE_FUNC g_object_unref
#include "gdk/gdkarrayimpl.c"
/*** MULTI FILTER ***/
/**
* SECTION:gtkmultifilter
* @Title: GtkMultiFilter
* @Short_description: Combining multiple filters
*
* GtkMultiFilter is the base type that implements support for handling
* multiple filters.
*
* GtkAnyFilter is a subclass of GtkMultiFilter that matches an item
* when at least one of its filters matches.
*
* GtkEveryFilter is a subclass of GtkMultiFilter that matches an item
* when each of its filters matches.
*/
struct _GtkMultiFilter
{
GtkFilter parent_instance;
GtkFilters 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 gtk_filters_get_size (&self->filters);
}
static gpointer
gtk_multi_filter_get_item (GListModel *list,
guint position)
{
GtkMultiFilter *self = GTK_MULTI_FILTER (list);
if (position < gtk_filters_get_size (&self->filters))
return g_object_ref (gtk_filters_get (&self->filters, position));
else
return NULL;
}
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 char *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_dispose (GObject *object)
{
GtkMultiFilter *self = GTK_MULTI_FILTER (object);
guint i;
for (i = 0; i < gtk_filters_get_size (&self->filters); i++)
{
GtkFilter *filter = gtk_filters_get (&self->filters, i);
g_signal_handlers_disconnect_by_func (filter, gtk_multi_filter_changed_cb, self);
}
gtk_filters_clear (&self->filters);
G_OBJECT_CLASS (gtk_multi_filter_parent_class)->dispose (object);
}
static void
gtk_multi_filter_class_init (GtkMultiFilterClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = gtk_multi_filter_dispose;
}
static void
gtk_multi_filter_init (GtkMultiFilter *self)
{
gtk_filters_init (&self->filters);
}
/**
* gtk_multi_filter_append:
* @self: a #GtkMultiFilter
* @filter: (transfer full): 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);
gtk_filters_append (&self->filters, 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)
{
guint length;
GtkFilter *filter;
length = gtk_filters_get_size (&self->filters);
if (position >= length)
return;
filter = gtk_filters_get (&self->filters, position);
g_signal_handlers_disconnect_by_func (filter, gtk_multi_filter_changed_cb, self);
gtk_filters_splice (&self->filters, position, 1, FALSE, NULL, 0);
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);
guint i;
for (i = 0; i < gtk_filters_get_size (&self->filters); i++)
{
GtkFilter *child = gtk_filters_get (&self->filters, i);
if (gtk_filter_match (child, item))
return TRUE;
}
return FALSE;
}
static GtkFilterMatch
gtk_any_filter_get_strictness (GtkFilter *filter)
{
GtkMultiFilter *self = GTK_MULTI_FILTER (filter);
guint i;
GtkFilterMatch result = GTK_FILTER_MATCH_NONE;
for (i = 0; i < gtk_filters_get_size (&self->filters); i++)
{
GtkFilter *child = gtk_filters_get (&self->filters, i);
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 #GtkAnyFilter
**/
GtkAnyFilter *
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);
guint i;
for (i = 0; i < gtk_filters_get_size (&self->filters); i++)
{
GtkFilter *child = gtk_filters_get (&self->filters, i);
if (!gtk_filter_match (child, item))
return FALSE;
}
return TRUE;
}
static GtkFilterMatch
gtk_every_filter_get_strictness (GtkFilter *filter)
{
GtkMultiFilter *self = GTK_MULTI_FILTER (filter);
guint i;
GtkFilterMatch result = GTK_FILTER_MATCH_ALL;
for (i = 0; i < gtk_filters_get_size (&self->filters); i++)
{
GtkFilter *child = gtk_filters_get (&self->filters, i);
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 each 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 #GtkEveryFilter
**/
GtkEveryFilter *
gtk_every_filter_new (void)
{
return g_object_new (GTK_TYPE_EVERY_FILTER, NULL);
}