mirror of
https://gitlab.gnome.org/GNOME/calls.git
synced 2024-12-13 16:07:36 +00:00
Add various gtk list models
This is an exact copy from GTK4. as grabbed by chattys commit 1ed5084fb965908e3ee0304781b0de06479c869b Slightly adapted for Calls. Based on GTKs 01bd4cc4e18a1ea697fe61791ba710d0d55e8290
This commit is contained in:
parent
4188af73af
commit
01aa8c04c2
23 changed files with 4941 additions and 0 deletions
8
src/gtklistmodels/gtk.h
Normal file
8
src/gtklistmodels/gtk.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "gtksorter.h"
|
||||
#include "gtkcustomsorter.h"
|
||||
#include "gtkfilter.h"
|
||||
#include "gtkcustomfilter.h"
|
||||
#include "gtksortlistmodel.h"
|
||||
#include "gtkfilterlistmodel.h"
|
||||
#include "gtkflattenlistmodel.h"
|
||||
#include "gtkslicelistmodel.h"
|
157
src/gtklistmodels/gtkcustomfilter.c
Normal file
157
src/gtklistmodels/gtkcustomfilter.c
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* 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 "gtkcustomfilter.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcustomfilter
|
||||
* @Title: GtkCustomFilter
|
||||
* @Short_description: Filtering with callbacks
|
||||
*
|
||||
* #GtkCustomFilter is a #GtkFilter that uses a callback to determine whether
|
||||
* to include an item or not.
|
||||
*/
|
||||
struct _GtkCustomFilter
|
||||
{
|
||||
GtkFilter parent_instance;
|
||||
|
||||
GtkCustomFilterFunc match_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCustomFilter, gtk_custom_filter, GTK_TYPE_FILTER)
|
||||
|
||||
static gboolean
|
||||
gtk_custom_filter_match (GtkFilter *filter,
|
||||
gpointer item)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
|
||||
|
||||
if (!self->match_func)
|
||||
return TRUE;
|
||||
|
||||
return self->match_func (item, self->user_data);
|
||||
}
|
||||
|
||||
static GtkFilterMatch
|
||||
gtk_custom_filter_get_strictness (GtkFilter *filter)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
|
||||
|
||||
if (!self->match_func)
|
||||
return GTK_FILTER_MATCH_ALL;
|
||||
|
||||
return GTK_FILTER_MATCH_SOME;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_dispose (GObject *object)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
G_OBJECT_CLASS (gtk_custom_filter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_class_init (GtkCustomFilterClass *class)
|
||||
{
|
||||
GtkFilterClass *filter_class = GTK_FILTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
filter_class->match = gtk_custom_filter_match;
|
||||
filter_class->get_strictness = gtk_custom_filter_get_strictness;
|
||||
|
||||
object_class->dispose = gtk_custom_filter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_init (GtkCustomFilter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_filter_new:
|
||||
* @match_func: (nullable): function to filter items
|
||||
* @user_data: (nullable): user data to pass to @match_func
|
||||
* @user_destroy: destory notify
|
||||
*
|
||||
* Creates a new filter using the given @match_func to filter
|
||||
* items.
|
||||
*
|
||||
* If the filter func changes its filtering behavior,
|
||||
* gtk_filter_changed() needs to be called.
|
||||
*
|
||||
* Returns: a new #GtkFilter
|
||||
**/
|
||||
GtkFilter *
|
||||
gtk_custom_filter_new (GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkCustomFilter *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_CUSTOM_FILTER, NULL);
|
||||
|
||||
gtk_custom_filter_set_filter_func (result, match_func, user_data, user_destroy);
|
||||
|
||||
return GTK_FILTER (result);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_filter_set_filter_func:
|
||||
* @self: a #GtkCustomFilter
|
||||
* @match_func: (nullable): function to filter items
|
||||
* @user_data: (nullable): user data to pass to @match_func
|
||||
* @user_destroy: destory notify
|
||||
*
|
||||
* Sets (or unsets) the function used for filtering items.
|
||||
*
|
||||
* If the filter func changes its filtering behavior,
|
||||
* gtk_filter_changed() needs to be called.
|
||||
*
|
||||
* If a previous function was set, its @user_destroy will be
|
||||
* called now.
|
||||
**/
|
||||
void
|
||||
gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
|
||||
GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CUSTOM_FILTER (self));
|
||||
g_return_if_fail (match_func || (user_data == NULL && !user_destroy));
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
self->match_func = match_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_DIFFERENT);
|
||||
}
|
61
src/gtklistmodels/gtkcustomfilter.h
Normal file
61
src/gtklistmodels/gtkcustomfilter.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CUSTOM_FILTER_H__
|
||||
#define __GTK_CUSTOM_FILTER_H__
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include "gtkfilter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GtkCustomFilterFunc:
|
||||
* @item: (type GObject): The item to be matched
|
||||
* @user_data: user data
|
||||
*
|
||||
* User function that is called to determine if the @item should be matched.
|
||||
* If the filter matches the item, this function must return %TRUE. If the
|
||||
* item should be filtered out, %FALSE must be returned.
|
||||
*
|
||||
* Returns: %TRUE to keep the item around
|
||||
*/
|
||||
typedef gboolean (* GtkCustomFilterFunc) (gpointer item, gpointer user_data);
|
||||
|
||||
#define GTK_TYPE_CUSTOM_FILTER (gtk_custom_filter_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCustomFilter, gtk_custom_filter, GTK, CUSTOM_FILTER, GtkFilter)
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_custom_filter_new (GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_custom_filter_set_filter_func (GtkCustomFilter *self,
|
||||
GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CUSTOM_FILTER_H__ */
|
165
src/gtklistmodels/gtkcustomsorter.c
Normal file
165
src/gtklistmodels/gtkcustomsorter.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcustomsorter
|
||||
* @Title: GtkCustomSorter
|
||||
* @Short_description: Sorting with a callback
|
||||
*
|
||||
* GtkCustomSorter is a #GtkSorter implementation that sorts
|
||||
* via a traditional #GCompareDataFunc callback.
|
||||
*/
|
||||
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 inline GtkOrdering
|
||||
gtk_ordering_from_cmpfunc (int cmpfunc_result)
|
||||
{
|
||||
return (GtkOrdering) ((cmpfunc_result > 0) - (cmpfunc_result < 0));
|
||||
}
|
||||
|
||||
static GtkOrdering
|
||||
gtk_custom_sorter_compare (GtkSorter *sorter,
|
||||
gpointer item1,
|
||||
gpointer item2)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
|
||||
|
||||
if (!self->sort_func)
|
||||
return GTK_ORDERING_EQUAL;
|
||||
|
||||
return gtk_ordering_from_cmpfunc (self->sort_func (item1, item2, self->user_data));
|
||||
}
|
||||
|
||||
static GtkSorterOrder
|
||||
gtk_custom_sorter_get_order (GtkSorter *sorter)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
|
||||
|
||||
if (!self->sort_func)
|
||||
return GTK_SORTER_ORDER_NONE;
|
||||
|
||||
return GTK_SORTER_ORDER_PARTIAL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_dispose (GObject *object)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
self->sort_func = NULL;
|
||||
self->user_destroy = NULL;
|
||||
self->user_data = NULL;
|
||||
|
||||
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;
|
||||
sorter_class->get_order = gtk_custom_sorter_get_order;
|
||||
|
||||
object_class->dispose = gtk_custom_sorter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_init (GtkCustomSorter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_sorter_new:
|
||||
* @sort_func: the #GCompareDataFunc to use for sorting
|
||||
* @user_data: (nullable): user data to pass to @sort_func
|
||||
* @user_destroy: (nullable): destroy notify for @user_data
|
||||
*
|
||||
* Creates a new #GtkSorter that works by calling
|
||||
* @sort_func to compare items.
|
||||
*
|
||||
* Returns: a new #GTkSorter
|
||||
*/
|
||||
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);
|
||||
|
||||
gtk_custom_sorter_set_sort_func (sorter, sort_func, user_data, user_destroy);
|
||||
|
||||
return GTK_SORTER (sorter);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_sorter_set_sort_func:
|
||||
* @self: a #GtkCustomSorter
|
||||
* @sort_func: (nullable): function to sort items
|
||||
* @user_data: (nullable): user data to pass to @match_func
|
||||
* @user_destroy: destory notify
|
||||
*
|
||||
* Sets (or unsets) the function used for sorting items.
|
||||
*
|
||||
* If the sort func changes its sorting behavior,
|
||||
* gtk_sorter_changed() needs to be called.
|
||||
*
|
||||
* If a previous function was set, its @user_destroy will be
|
||||
* called now.
|
||||
**/
|
||||
void
|
||||
gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
|
||||
GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CUSTOM_SORTER (self));
|
||||
g_return_if_fail (sort_func || (user_data == NULL && !user_destroy));
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
self->sort_func = sort_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
}
|
50
src/gtklistmodels/gtkcustomsorter.h
Normal file
50
src/gtklistmodels/gtkcustomsorter.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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__
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
/* #include <gtk/gtkexpression.h> */
|
||||
#include "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);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_custom_sorter_set_sort_func (GtkCustomSorter *self,
|
||||
GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CUSTOM_SORTER_H__ */
|
181
src/gtklistmodels/gtkfilter.c
Normal file
181
src/gtklistmodels/gtkfilter.c
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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 "gtkfilter.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkfilter
|
||||
* @Title: GtkFilter
|
||||
* @Short_description: Filtering items
|
||||
* @See_also: #GtkFilerListModel
|
||||
*
|
||||
* #GtkFilter is the way to describe filters to be used in #GtkFilterListModel.
|
||||
*
|
||||
* The model will use a filter to determine if it should filter items or not
|
||||
* by calling gtk_filter_match() for each item and only keeping the ones
|
||||
* visible that the function returns %TRUE for.
|
||||
*
|
||||
* Filters may change what items they match through their lifetime. In that
|
||||
* case, they can call gtk_filter_changed() which will emit the #GtkFilter:changed
|
||||
* signal to notify that previous filter results are no longer valid and that
|
||||
* items should be checked via gtk_filter_match() again.
|
||||
*
|
||||
* GTK provides various premade filter implementations for common filtering
|
||||
* operations. These filters often include properties that can be linked to
|
||||
* various widgets to easily allow searches.
|
||||
*
|
||||
* However, in particular for large lists or complex search methods, it is
|
||||
* also possible to subclass #GtkFilter and provide one's own filter.
|
||||
*/
|
||||
|
||||
enum {
|
||||
CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkFilter, gtk_filter, G_TYPE_OBJECT)
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gboolean
|
||||
gtk_filter_default_match (GtkFilter *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_critical ("Filter of type '%s' does not implement GtkFilter::match", G_OBJECT_TYPE_NAME (self));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GtkFilterMatch
|
||||
gtk_filter_default_get_strictness (GtkFilter *self)
|
||||
{
|
||||
return GTK_FILTER_MATCH_SOME;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_class_init (GtkFilterClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
class->match = gtk_filter_default_match;
|
||||
class->get_strictness = gtk_filter_default_get_strictness;
|
||||
|
||||
/**
|
||||
* GtkFilter:changed:
|
||||
* @self: The #GtkFilter
|
||||
* @change: how the filter changed
|
||||
*
|
||||
* This signal is emitted whenever the filter changed. Users of the filter
|
||||
* should then check items again via gtk_filter_match().
|
||||
*
|
||||
* #GtkFilterListModel handles this signal automatically.
|
||||
*
|
||||
* Depending on the @change parameter, not all items need to be changed, but
|
||||
* only some. Refer to the #GtkFilterChange documentation for details.
|
||||
*/
|
||||
signals[CHANGED] =
|
||||
g_signal_new (I_("changed"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__ENUM,
|
||||
G_TYPE_NONE, 1,
|
||||
GTK_TYPE_FILTER_CHANGE);
|
||||
g_signal_set_va_marshaller (signals[CHANGED],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__ENUMv);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_init (GtkFilter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_match:
|
||||
* @self: a #GtkFilter
|
||||
* @item: (type GObject) (transfer none): The item to check
|
||||
*
|
||||
* Checks if the given @item is matched by the filter or not.
|
||||
*
|
||||
* Returns: %TRUE if the filter matches the item and a filter model should
|
||||
* keep it, %FALSE if not.
|
||||
*/
|
||||
gboolean
|
||||
gtk_filter_match (GtkFilter *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER (self), FALSE);
|
||||
g_return_val_if_fail (item != NULL, FALSE);
|
||||
|
||||
return GTK_FILTER_GET_CLASS (self)->match (self, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_get_strictness:
|
||||
* @self: a #GtkFilter
|
||||
*
|
||||
* Gets the known strictness of @filters. If the strictness is not known,
|
||||
* %GTK_FILTER_MATCH_SOME is returned.
|
||||
*
|
||||
* This value may change after emission of the GtkFilter:changed signal.
|
||||
*
|
||||
* This function is meant purely for optimization purposes, filters can
|
||||
* choose to omit implementing it, but #GtkFilterListModel uses it.
|
||||
*
|
||||
* Returns: the strictness of @self
|
||||
**/
|
||||
GtkFilterMatch
|
||||
gtk_filter_get_strictness (GtkFilter *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER (self), GTK_FILTER_MATCH_SOME);
|
||||
|
||||
return GTK_FILTER_GET_CLASS (self)->get_strictness (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_changed:
|
||||
* @self: a #GtkFilter
|
||||
* @change: How the filter changed
|
||||
*
|
||||
* Emits the #GtkFilter:changed signal to notify all users of the filter that
|
||||
* the filter changed. Users of the filter should then check items again via
|
||||
* gtk_filter_match().
|
||||
*
|
||||
* Depending on the @change parameter, not all items need to be changed, but
|
||||
* only some. Refer to the #GtkFilterChange documentation for details.
|
||||
*
|
||||
* This function is intended for implementors of #GtkFilter subclasses and
|
||||
* should not be called from other functions.
|
||||
*/
|
||||
void
|
||||
gtk_filter_changed (GtkFilter *self,
|
||||
GtkFilterChange change)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_FILTER (self));
|
||||
|
||||
g_signal_emit (self, signals[CHANGED], 0, change);
|
||||
}
|
||||
|
122
src/gtklistmodels/gtkfilter.h
Normal file
122
src/gtklistmodels/gtkfilter.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FILTER_H__
|
||||
#define __GTK_FILTER_H__
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GtkFilterMatch:
|
||||
* @GTK_FILTER_MATCH_SOME: The filter matches some items,
|
||||
* gtk_filter_match() may return %TRUE or %FALSE
|
||||
* @GTK_FILTER_MATCH_NONE: The filter does not match any item,
|
||||
* gtk_filter_match() will always return %FALSE.
|
||||
* @GTK_FILTER_MATCH_ALL: The filter matches all items,
|
||||
* gtk_filter_match() will alays return %TRUE.
|
||||
*
|
||||
* Describes the known strictness of a filter.
|
||||
*
|
||||
* Note that for filters where the strictness is not known,
|
||||
* %@GTK_FILTER_MATCH_SOME is always an acceptable value,
|
||||
* even if a filter does match all or no items.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_FILTER_MATCH_SOME = 0,
|
||||
GTK_FILTER_MATCH_NONE,
|
||||
GTK_FILTER_MATCH_ALL
|
||||
} GtkFilterMatch;
|
||||
|
||||
/**
|
||||
* GtkFilterChange:
|
||||
* @GTK_FILTER_CHANGE_DIFFERENT: The filter change cannot be
|
||||
* described with any of the other enumeration values.
|
||||
* @GTK_FILTER_CHANGE_LESS_STRICT: The filter is less strict than
|
||||
* it was before: All items that it used to return %TRUE for
|
||||
* still return %TRUE, others now may, too.
|
||||
* @GTK_FILTER_CHANGE_MORE_STRICT: The filter is more strict than
|
||||
* it was before: All items that it used to return %FALSE for
|
||||
* still return %FALSE, others now may, too.
|
||||
*
|
||||
* Describes changes in a filter in more detail and allows objects
|
||||
* using the filter to optimize refiltering items.
|
||||
*
|
||||
* If you are writing an implementation and are not sure which
|
||||
* value to pass, @GTK_FILTER_CHANGE_DIFFERENT is always a correct
|
||||
* choice.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_FILTER_CHANGE_DIFFERENT = 0,
|
||||
GTK_FILTER_CHANGE_LESS_STRICT,
|
||||
GTK_FILTER_CHANGE_MORE_STRICT,
|
||||
} GtkFilterChange;
|
||||
|
||||
#define GTK_TYPE_FILTER (gtk_filter_get_type ())
|
||||
|
||||
/**
|
||||
* GtkFilter:
|
||||
*
|
||||
* The object describing a filter.
|
||||
*/
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_DERIVABLE_TYPE (GtkFilter, gtk_filter, GTK, FILTER, GObject)
|
||||
|
||||
struct _GtkFilterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gboolean (* match) (GtkFilter *self,
|
||||
gpointer item);
|
||||
|
||||
/* optional */
|
||||
GtkFilterMatch (* get_strictness) (GtkFilter *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
|
||||
gboolean gtk_filter_match (GtkFilter *self,
|
||||
gpointer item);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterMatch gtk_filter_get_strictness (GtkFilter *self);
|
||||
|
||||
/* for filter implementations */
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_changed (GtkFilter *self,
|
||||
GtkFilterChange change);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FILTER_H__ */
|
916
src/gtklistmodels/gtkfilterlistmodel.c
Normal file
916
src/gtklistmodels/gtkfilterlistmodel.c
Normal file
|
@ -0,0 +1,916 @@
|
|||
/*
|
||||
* Copyright © 2018 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 "gtkfilterlistmodel.h"
|
||||
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkfilterlistmodel
|
||||
* @title: GtkFilterListModel
|
||||
* @short_description: A list model that filters its items
|
||||
* @see_also: #GListModel, #GtkFilter
|
||||
*
|
||||
* #GtkFilterListModel is a list model that filters a given other
|
||||
* listmodel.
|
||||
* It hides some elements from the other model according to
|
||||
* criteria given by a #GtkFilter.
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FILTER,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_MODEL,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
typedef struct _FilterNode FilterNode;
|
||||
typedef struct _FilterAugment FilterAugment;
|
||||
|
||||
struct _FilterNode
|
||||
{
|
||||
guint visible : 1;
|
||||
};
|
||||
|
||||
struct _FilterAugment
|
||||
{
|
||||
guint n_items;
|
||||
guint n_visible;
|
||||
};
|
||||
|
||||
struct _GtkFilterListModel
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GType item_type;
|
||||
GListModel *model;
|
||||
GtkFilter *filter;
|
||||
GtkFilterMatch strictness;
|
||||
|
||||
GtkRbTree *items; /* NULL if strictness != GTK_FILTER_MATCH_SOME */
|
||||
};
|
||||
|
||||
struct _GtkFilterListModelClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_augment (GtkRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FilterNode *node = _node;
|
||||
FilterAugment *aug = _aug;
|
||||
|
||||
aug->n_items = 1;
|
||||
aug->n_visible = node->visible ? 1 : 0;
|
||||
|
||||
if (left)
|
||||
{
|
||||
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_visible += left_aug->n_visible;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_visible += right_aug->n_visible;
|
||||
}
|
||||
}
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth_filtered (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *out_unfiltered)
|
||||
{
|
||||
FilterNode *node, *tmp;
|
||||
guint unfiltered;
|
||||
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
unfiltered = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_visible)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
position -= aug->n_visible;
|
||||
unfiltered += aug->n_items;
|
||||
}
|
||||
|
||||
if (node->visible)
|
||||
{
|
||||
if (position == 0)
|
||||
break;
|
||||
position--;
|
||||
}
|
||||
|
||||
unfiltered++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
if (out_unfiltered)
|
||||
*out_unfiltered = unfiltered;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *out_filtered)
|
||||
{
|
||||
FilterNode *node, *tmp;
|
||||
guint filtered;
|
||||
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
filtered = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
position -= aug->n_items;
|
||||
filtered += aug->n_visible;
|
||||
}
|
||||
|
||||
if (position == 0)
|
||||
break;
|
||||
|
||||
position--;
|
||||
if (node->visible)
|
||||
filtered++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
if (out_filtered)
|
||||
*out_filtered = filtered;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_filter_list_model_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
|
||||
|
||||
return self->item_type;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_filter_list_model_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
|
||||
FilterAugment *aug;
|
||||
FilterNode *node;
|
||||
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return 0;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
return g_list_model_get_n_items (self->model);
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
break;
|
||||
}
|
||||
|
||||
node = gtk_rb_tree_get_root (self->items);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
aug = gtk_rb_tree_get_augment (self->items, node);
|
||||
return aug->n_visible;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_filter_list_model_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
|
||||
guint unfiltered;
|
||||
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return NULL;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
unfiltered = position;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
|
||||
break;
|
||||
}
|
||||
|
||||
return g_list_model_get_item (self->model, unfiltered);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_filter_list_model_get_item_type;
|
||||
iface->get_n_items = gtk_filter_list_model_get_n_items;
|
||||
iface->get_item = gtk_filter_list_model_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkFilterListModel, gtk_filter_list_model, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_filter_list_model_model_init))
|
||||
|
||||
static gboolean
|
||||
gtk_filter_list_model_run_filter (GtkFilterListModel *self,
|
||||
guint position)
|
||||
{
|
||||
gpointer item;
|
||||
gboolean visible;
|
||||
|
||||
/* all other cases should have beeen optimized away */
|
||||
g_assert (self->strictness == GTK_FILTER_MATCH_SOME);
|
||||
|
||||
item = g_list_model_get_item (self->model, position);
|
||||
visible = gtk_filter_match (self->filter, item);
|
||||
g_object_unref (item);
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_filter_list_model_add_items (GtkFilterListModel *self,
|
||||
FilterNode *after,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
FilterNode *node;
|
||||
guint i, n_visible;
|
||||
|
||||
n_visible = 0;
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
node = gtk_rb_tree_insert_before (self->items, after);
|
||||
node->visible = gtk_filter_list_model_run_filter (self, position + i);
|
||||
if (node->visible)
|
||||
n_visible++;
|
||||
}
|
||||
|
||||
return n_visible;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkFilterListModel *self)
|
||||
{
|
||||
FilterNode *node;
|
||||
guint i, filter_position, filter_removed, filter_added;
|
||||
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
return;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
break;
|
||||
}
|
||||
|
||||
node = gtk_filter_list_model_get_nth (self->items, position, &filter_position);
|
||||
|
||||
filter_removed = 0;
|
||||
for (i = 0; i < removed; i++)
|
||||
{
|
||||
FilterNode *next = gtk_rb_tree_node_get_next (node);
|
||||
if (node->visible)
|
||||
filter_removed++;
|
||||
gtk_rb_tree_remove (self->items, node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
filter_added = gtk_filter_list_model_add_items (self, node, position, added);
|
||||
|
||||
if (filter_removed > 0 || filter_added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), filter_position, filter_removed, filter_added);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FILTER:
|
||||
gtk_filter_list_model_set_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_filter_list_model_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FILTER:
|
||||
g_value_set_object (value, self->filter);
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, self->item_type);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_clear_model (GtkFilterListModel *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model, gtk_filter_list_model_items_changed_cb, self);
|
||||
g_clear_object (&self->model);
|
||||
if (self->items)
|
||||
gtk_rb_tree_remove_all (self->items);
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* gtk_filter_list_model_find_filtered:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @start: (out) (caller-allocates): number of unfiltered items
|
||||
* at start of list
|
||||
* @end: (out) (caller-allocates): number of unfiltered items
|
||||
* at end of list
|
||||
* @n_items: (out) (caller-allocates): number of unfiltered items in
|
||||
* list
|
||||
*
|
||||
* Checks if elements in self->items are filtered out and returns
|
||||
* the range that they occupy.
|
||||
* This function is intended to be used for GListModel::items-changed
|
||||
* emissions, so it is called in an intermediate state for @self.
|
||||
*
|
||||
* Returns: %TRUE if elements are filtered out, %FALSE if none are
|
||||
**/
|
||||
static gboolean
|
||||
gtk_filter_list_model_find_filtered (GtkFilterListModel *self,
|
||||
guint *start,
|
||||
guint *end,
|
||||
guint *n_items)
|
||||
{
|
||||
FilterNode *root, *node, *tmp;
|
||||
FilterAugment *aug;
|
||||
|
||||
if (self->items == NULL || self->model == NULL)
|
||||
return FALSE;
|
||||
|
||||
root = gtk_rb_tree_get_root (self->items);
|
||||
if (root == NULL)
|
||||
return FALSE; /* empty parent model */
|
||||
|
||||
aug = gtk_rb_tree_get_augment (self->items, root);
|
||||
if (aug->n_items == aug->n_visible)
|
||||
return FALSE; /* all items visible */
|
||||
|
||||
/* find first filtered */
|
||||
*start = 0;
|
||||
*end = 0;
|
||||
*n_items = aug->n_visible;
|
||||
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*start += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*start)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
/* find last filtered by doing everything the opposite way */
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_right (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*end += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*end)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_left (node);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self);
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
|
||||
{
|
||||
GtkFilterMatch new_strictness;
|
||||
|
||||
if (self->model == NULL)
|
||||
new_strictness = GTK_FILTER_MATCH_NONE;
|
||||
else if (self->filter == NULL)
|
||||
new_strictness = GTK_FILTER_MATCH_ALL;
|
||||
else
|
||||
new_strictness = gtk_filter_get_strictness (self->filter);
|
||||
|
||||
/* don't set self->strictness yet so get_n_items() and friends return old values */
|
||||
|
||||
switch (new_strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
guint n_before = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
self->strictness = new_strictness;
|
||||
if (n_before > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_before, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
self->strictness = new_strictness;
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, g_list_model_get_n_items (self->model));
|
||||
break;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
self->strictness = new_strictness;
|
||||
break;
|
||||
default:
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
self->strictness = new_strictness;
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_before))
|
||||
{
|
||||
n_after = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
guint n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_after = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (self->model));
|
||||
if (n_after > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, n_after);
|
||||
}
|
||||
break;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_before = g_list_model_get_n_items (self->model);
|
||||
gtk_filter_list_model_add_items (self, NULL, 0, n_before);
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_after))
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_refilter (self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_filter_changed_cb (GtkFilter *filter,
|
||||
GtkFilterChange change,
|
||||
GtkFilterListModel *self)
|
||||
{
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_clear_filter (GtkFilterListModel *self)
|
||||
{
|
||||
if (self->filter == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->filter, gtk_filter_list_model_filter_changed_cb, self);
|
||||
g_clear_object (&self->filter);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_dispose (GObject *object)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
|
||||
|
||||
gtk_filter_list_model_clear_model (self);
|
||||
gtk_filter_list_model_clear_filter (self);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_filter_list_model_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_filter_list_model_set_property;
|
||||
gobject_class->get_property = gtk_filter_list_model_get_property;
|
||||
gobject_class->dispose = gtk_filter_list_model_dispose;
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:filter:
|
||||
*
|
||||
* The filter for this model
|
||||
*/
|
||||
properties[PROP_FILTER] =
|
||||
g_param_spec_object ("filter",
|
||||
P_("Filter"),
|
||||
P_("The filter set for this model"),
|
||||
GTK_TYPE_FILTER,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:item-type:
|
||||
*
|
||||
* The #GType for elements of this object
|
||||
*/
|
||||
properties[PROP_ITEM_TYPE] =
|
||||
g_param_spec_gtype ("item-type",
|
||||
P_("Item type"),
|
||||
P_("The type of elements of this object"),
|
||||
G_TYPE_OBJECT,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:model:
|
||||
*
|
||||
* The model being filtered
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("The model being filtered"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_init (GtkFilterListModel *self)
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_new:
|
||||
* @model: the model to sort
|
||||
* @filter: (allow-none): filter or %NULL to not filter items
|
||||
*
|
||||
* Creates a new #GtkFilterListModel that will filter @model using the given
|
||||
* @filter.
|
||||
*
|
||||
* Returns: a new #GtkFilterListModel
|
||||
**/
|
||||
GtkFilterListModel *
|
||||
gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
GtkFilterListModel *result;
|
||||
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
|
||||
"item-type", g_list_model_get_item_type (model),
|
||||
"model", model,
|
||||
"filter", filter,
|
||||
NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_new_for_type:
|
||||
* @item_type: the type of the items that will be returned
|
||||
*
|
||||
* Creates a new empty filter list model set up to return items of type @item_type.
|
||||
* It is up to the application to set a proper filter and model to ensure
|
||||
* the item type is matched.
|
||||
*
|
||||
* Returns: a new #GtkFilterListModel
|
||||
**/
|
||||
GtkFilterListModel *
|
||||
gtk_filter_list_model_new_for_type (GType item_type)
|
||||
{
|
||||
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
|
||||
"item-type", item_type,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_set_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @filter: (allow-none) (transfer none): filter to use or %NULL to not filter items
|
||||
*
|
||||
* Sets the filter used to filter items.
|
||||
**/
|
||||
void
|
||||
gtk_filter_list_model_set_filter (GtkFilterListModel *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
if (self->filter == filter)
|
||||
return;
|
||||
|
||||
gtk_filter_list_model_clear_filter (self);
|
||||
|
||||
if (filter)
|
||||
{
|
||||
self->filter = g_object_ref (filter);
|
||||
g_signal_connect (filter, "changed", G_CALLBACK (gtk_filter_list_model_filter_changed_cb), self);
|
||||
gtk_filter_list_model_filter_changed_cb (filter, GTK_FILTER_CHANGE_DIFFERENT, self);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_get_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Gets the #GtkFilter currently set on @self.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The filter currently in use
|
||||
* or %NULL if the list isn't filtered
|
||||
**/
|
||||
GtkFilter *
|
||||
gtk_filter_list_model_get_filter (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
|
||||
|
||||
return self->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_set_model:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @model: (allow-none): The model to be filtered
|
||||
*
|
||||
* Sets the model to be filtered.
|
||||
*
|
||||
* Note that GTK makes no effort to ensure that @model conforms to
|
||||
* the item type of @self. It assumes that the caller knows what they
|
||||
* are doing and have set up an appropriate filter to ensure that item
|
||||
* types match.
|
||||
**/
|
||||
void
|
||||
gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
GListModel *model)
|
||||
{
|
||||
guint removed, added;
|
||||
|
||||
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
/* Note: We don't check for matching item type here, we just assume the
|
||||
* filter func takes care of filtering wrong items. */
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
gtk_filter_list_model_clear_model (self);
|
||||
|
||||
if (model)
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_filter_list_model_items_changed_cb), self);
|
||||
if (removed == 0)
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
added = 0;
|
||||
}
|
||||
else if (self->items)
|
||||
added = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
|
||||
else
|
||||
added = g_list_model_get_n_items (model);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
added = 0;
|
||||
}
|
||||
|
||||
if (removed > 0 || added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_get_model:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Gets the model currently filtered or %NULL if none.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model that gets filtered
|
||||
**/
|
||||
GListModel *
|
||||
gtk_filter_list_model_get_model (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self)
|
||||
{
|
||||
FilterNode *node;
|
||||
guint i, first_change, last_change;
|
||||
guint n_is_visible, n_was_visible;
|
||||
gboolean visible;
|
||||
|
||||
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
|
||||
|
||||
if (self->items == NULL || self->model == NULL)
|
||||
return;
|
||||
|
||||
first_change = G_MAXUINT;
|
||||
last_change = 0;
|
||||
n_is_visible = 0;
|
||||
n_was_visible = 0;
|
||||
for (i = 0, node = gtk_rb_tree_get_first (self->items);
|
||||
node != NULL;
|
||||
i++, node = gtk_rb_tree_node_get_next (node))
|
||||
{
|
||||
visible = gtk_filter_list_model_run_filter (self, i);
|
||||
if (visible == node->visible)
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
n_is_visible++;
|
||||
n_was_visible++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
node->visible = visible;
|
||||
gtk_rb_tree_node_mark_dirty (node);
|
||||
first_change = MIN (n_is_visible, first_change);
|
||||
if (visible)
|
||||
n_is_visible++;
|
||||
else
|
||||
n_was_visible++;
|
||||
last_change = MAX (n_is_visible, last_change);
|
||||
}
|
||||
|
||||
if (first_change <= last_change)
|
||||
{
|
||||
g_list_model_items_changed (G_LIST_MODEL (self),
|
||||
first_change,
|
||||
last_change - first_change + n_was_visible - n_is_visible,
|
||||
last_change - first_change);
|
||||
}
|
||||
}
|
||||
|
59
src/gtklistmodels/gtkfilterlistmodel.h
Normal file
59
src/gtklistmodels/gtkfilterlistmodel.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright © 2018 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>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FILTER_LIST_MODEL_H__
|
||||
#define __GTK_FILTER_LIST_MODEL_H__
|
||||
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "gtkfilter.h"
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_FILTER_LIST_MODEL (gtk_filter_list_model_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkFilterListModel, gtk_filter_list_model, GTK, FILTER_LIST_MODEL, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterListModel * gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterListModel * gtk_filter_list_model_new_for_type (GType item_type);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_set_filter (GtkFilterListModel *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_filter_list_model_get_filter (GtkFilterListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_filter_list_model_get_model (GtkFilterListModel *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FILTER_LIST_MODEL_H__ */
|
541
src/gtklistmodels/gtkflattenlistmodel.c
Normal file
541
src/gtklistmodels/gtkflattenlistmodel.c
Normal file
|
@ -0,0 +1,541 @@
|
|||
/*
|
||||
* Copyright © 2018 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 "gtkflattenlistmodel.h"
|
||||
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkflattenlistmodel
|
||||
* @title: GtkFlattenListModel
|
||||
* @short_description: A list model that flattens a list of lists
|
||||
* @see_also: #GListModel
|
||||
*
|
||||
* #GtkFlattenListModel is a list model that takes a list model containing
|
||||
* list models and flattens it into a single model.
|
||||
*
|
||||
* Another term for this is concatenation: #GtkFlattenListModel takes a
|
||||
* list of lists and concatenates them into a single list.
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_MODEL,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
typedef struct _FlattenNode FlattenNode;
|
||||
typedef struct _FlattenAugment FlattenAugment;
|
||||
|
||||
struct _FlattenNode
|
||||
{
|
||||
GListModel *model;
|
||||
GtkFlattenListModel *list;
|
||||
};
|
||||
|
||||
struct _FlattenAugment
|
||||
{
|
||||
guint n_items;
|
||||
guint n_models;
|
||||
};
|
||||
|
||||
struct _GtkFlattenListModel
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GType item_type;
|
||||
GListModel *model;
|
||||
GtkRbTree *items; /* NULL if model == NULL */
|
||||
};
|
||||
|
||||
struct _GtkFlattenListModelClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static FlattenNode *
|
||||
gtk_flatten_list_model_get_nth (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *model_position)
|
||||
{
|
||||
FlattenNode *node, *tmp;
|
||||
guint model_n_items;
|
||||
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
position -= aug->n_items;
|
||||
}
|
||||
|
||||
model_n_items = g_list_model_get_n_items (node->model);
|
||||
if (position < model_n_items)
|
||||
break;
|
||||
position -= model_n_items;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
if (model_position)
|
||||
*model_position = node ? position : 0;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static FlattenNode *
|
||||
gtk_flatten_list_model_get_nth_model (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *items_before)
|
||||
{
|
||||
FlattenNode *node, *tmp;
|
||||
guint before;
|
||||
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
before = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_models)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
position -= aug->n_models;
|
||||
before += aug->n_items;
|
||||
}
|
||||
|
||||
if (position == 0)
|
||||
break;
|
||||
position--;
|
||||
before += g_list_model_get_n_items (node->model);
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
if (items_before)
|
||||
*items_before = before;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_flatten_list_model_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
|
||||
|
||||
return self->item_type;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_flatten_list_model_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
|
||||
FlattenAugment *aug;
|
||||
FlattenNode *node;
|
||||
|
||||
if (!self->items)
|
||||
return 0;
|
||||
|
||||
node = gtk_rb_tree_get_root (self->items);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
aug = gtk_rb_tree_get_augment (self->items, node);
|
||||
return aug->n_items;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_flatten_list_model_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (list);
|
||||
FlattenNode *node;
|
||||
guint model_pos;
|
||||
|
||||
if (!self->items)
|
||||
return NULL;
|
||||
|
||||
node = gtk_flatten_list_model_get_nth (self->items, position, &model_pos);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_list_model_get_item (node->model, model_pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_flatten_list_model_get_item_type;
|
||||
iface->get_n_items = gtk_flatten_list_model_get_n_items;
|
||||
iface->get_item = gtk_flatten_list_model_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkFlattenListModel, gtk_flatten_list_model, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_flatten_list_model_model_init))
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
gpointer _node)
|
||||
{
|
||||
FlattenNode *node = _node, *parent, *left;
|
||||
GtkFlattenListModel *self = node->list;
|
||||
guint real_position;
|
||||
|
||||
gtk_rb_tree_node_mark_dirty (node);
|
||||
real_position = position;
|
||||
|
||||
left = gtk_rb_tree_node_get_left (node);
|
||||
if (left)
|
||||
{
|
||||
FlattenAugment *aug = gtk_rb_tree_get_augment (self->items, left);
|
||||
real_position += aug->n_items;
|
||||
}
|
||||
|
||||
for (;
|
||||
(parent = gtk_rb_tree_node_get_parent (node)) != NULL;
|
||||
node = parent)
|
||||
{
|
||||
left = gtk_rb_tree_node_get_left (parent);
|
||||
if (left != node)
|
||||
{
|
||||
if (left)
|
||||
{
|
||||
FlattenAugment *aug = gtk_rb_tree_get_augment (self->items, left);
|
||||
real_position += aug->n_items;
|
||||
}
|
||||
real_position += g_list_model_get_n_items (parent->model);
|
||||
}
|
||||
}
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), real_position, removed, added);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_clear_node (gpointer _node)
|
||||
{
|
||||
FlattenNode *node= _node;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (node->model, gtk_flatten_list_model_items_changed_cb, node);
|
||||
g_object_unref (node->model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_augment (GtkRbTree *flatten,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FlattenNode *node = _node;
|
||||
FlattenAugment *aug = _aug;
|
||||
|
||||
aug->n_items = g_list_model_get_n_items (node->model);
|
||||
aug->n_models = 1;
|
||||
|
||||
if (left)
|
||||
{
|
||||
FlattenAugment *left_aug = gtk_rb_tree_get_augment (flatten, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_models += left_aug->n_models;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FlattenAugment *right_aug = gtk_rb_tree_get_augment (flatten, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_models += right_aug->n_models;
|
||||
}
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_flatten_list_model_add_items (GtkFlattenListModel *self,
|
||||
FlattenNode *after,
|
||||
guint position,
|
||||
guint n)
|
||||
{
|
||||
FlattenNode *node;
|
||||
guint added, i;
|
||||
|
||||
added = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
node = gtk_rb_tree_insert_before (self->items, after);
|
||||
node->model = g_list_model_get_item (self->model, position + i);
|
||||
g_warn_if_fail (g_type_is_a (g_list_model_get_item_type (node->model), self->item_type));
|
||||
g_signal_connect (node->model,
|
||||
"items-changed",
|
||||
G_CALLBACK (gtk_flatten_list_model_items_changed_cb),
|
||||
node);
|
||||
node->list = self;
|
||||
added +=g_list_model_get_n_items (node->model);
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE:
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_flatten_list_model_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, self->item_type);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_model_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkFlattenListModel *self)
|
||||
{
|
||||
FlattenNode *node;
|
||||
guint i, real_position, real_removed, real_added;
|
||||
|
||||
node = gtk_flatten_list_model_get_nth_model (self->items, position, &real_position);
|
||||
|
||||
real_removed = 0;
|
||||
for (i = 0; i < removed; i++)
|
||||
{
|
||||
FlattenNode *next = gtk_rb_tree_node_get_next (node);
|
||||
real_removed += g_list_model_get_n_items (node->model);
|
||||
gtk_rb_tree_remove (self->items, node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
real_added = gtk_flatten_list_model_add_items (self, node, position, added);
|
||||
|
||||
if (real_removed > 0 || real_added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), real_position, real_removed, real_added);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_clear_model (GtkFlattenListModel *self)
|
||||
{
|
||||
if (self->model)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (self->model, gtk_flatten_list_model_model_items_changed_cb, self);
|
||||
g_clear_object (&self->model);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_dispose (GObject *object)
|
||||
{
|
||||
GtkFlattenListModel *self = GTK_FLATTEN_LIST_MODEL (object);
|
||||
|
||||
gtk_flatten_list_clear_model (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_flatten_list_model_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_class_init (GtkFlattenListModelClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_flatten_list_model_set_property;
|
||||
gobject_class->get_property = gtk_flatten_list_model_get_property;
|
||||
gobject_class->dispose = gtk_flatten_list_model_dispose;
|
||||
|
||||
/**
|
||||
* GtkFlattenListModel:item-type:
|
||||
*
|
||||
* The #GTpe for elements of this object
|
||||
*/
|
||||
properties[PROP_ITEM_TYPE] =
|
||||
g_param_spec_gtype ("item-type",
|
||||
P_("Item type"),
|
||||
P_("The type of elements of this object"),
|
||||
G_TYPE_OBJECT,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFlattenListModel:model:
|
||||
*
|
||||
* The model being flattened
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("The model being flattened"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_init (GtkFlattenListModel *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_flatten_list_model_new:
|
||||
* @item_type: The type of items in the to-be-flattened models
|
||||
* @model: (nullable) (transfer none): the item to be flattened
|
||||
*
|
||||
* Creates a new #GtkFlattenListModel that flattens @list. The
|
||||
* models returned by @model must conform to the given @item_type,
|
||||
* either by having an identical type or a subtype.
|
||||
*
|
||||
* Returns: a new #GtkFlattenListModel
|
||||
**/
|
||||
GtkFlattenListModel *
|
||||
gtk_flatten_list_model_new (GType item_type,
|
||||
GListModel *model)
|
||||
{
|
||||
GtkFlattenListModel *result;
|
||||
|
||||
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
|
||||
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_FLATTEN_LIST_MODEL,
|
||||
"item-type", item_type,
|
||||
"model", model,
|
||||
NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_flatten_list_model_set_model:
|
||||
* @self: a #GtkFlattenListModel
|
||||
* @model: (nullable) (transfer none): the new model or %NULL
|
||||
*
|
||||
* Sets a new model to be flattened. The model must contain items of
|
||||
* #GtkListModel that conform to the item type of @self.
|
||||
**/
|
||||
void
|
||||
gtk_flatten_list_model_set_model (GtkFlattenListModel *self,
|
||||
GListModel *model)
|
||||
{
|
||||
guint removed, added = 0;
|
||||
|
||||
g_return_if_fail (GTK_IS_FLATTEN_LIST_MODEL (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
if (model)
|
||||
{
|
||||
g_return_if_fail (g_type_is_a (g_list_model_get_item_type (model), G_TYPE_LIST_MODEL));
|
||||
}
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
gtk_flatten_list_clear_model (self);
|
||||
|
||||
self->model = model;
|
||||
|
||||
if (model)
|
||||
{
|
||||
g_object_ref (model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_flatten_list_model_model_items_changed_cb), self);
|
||||
self->items = gtk_rb_tree_new (FlattenNode,
|
||||
FlattenAugment,
|
||||
gtk_flatten_list_model_augment,
|
||||
gtk_flatten_list_model_clear_node,
|
||||
NULL);
|
||||
|
||||
added = gtk_flatten_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
|
||||
}
|
||||
|
||||
if (removed > 0 || added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_flatten_list_model_get_model:
|
||||
* @self: a #GtkFlattenListModel
|
||||
*
|
||||
* Gets the model set via gtk_flatten_list_model_set_model().
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model flattened by @self
|
||||
**/
|
||||
GListModel *
|
||||
gtk_flatten_list_model_get_model (GtkFlattenListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FLATTEN_LIST_MODEL (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
51
src/gtklistmodels/gtkflattenlistmodel.h
Normal file
51
src/gtklistmodels/gtkflattenlistmodel.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright © 2018 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>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FLATTEN_LIST_MODEL_H__
|
||||
#define __GTK_FLATTEN_LIST_MODEL_H__
|
||||
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_FLATTEN_LIST_MODEL (gtk_flatten_list_model_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkFlattenListModel, gtk_flatten_list_model, GTK, FLATTEN_LIST_MODEL, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFlattenListModel * gtk_flatten_list_model_new (GType item_type,
|
||||
GListModel *model);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_flatten_list_model_set_model (GtkFlattenListModel *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_flatten_list_model_get_model (GtkFlattenListModel *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FLATTEN_LIST_MODEL_H__ */
|
22
src/gtklistmodels/gtkintl.h
Normal file
22
src/gtklistmodels/gtkintl.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef __GTKINTL_H__
|
||||
#define __GTKINTL_H__
|
||||
|
||||
#ifndef GETTEXT_PACKAGE
|
||||
# define GETTEXT_PACKAGE
|
||||
#endif
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
#ifndef G_GNUC_FALLTHROUGH
|
||||
#define G_GNUC_FALLTHROUGH __attribute__((fallthrough))
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
#define P_(String) g_dgettext(GETTEXT_PACKAGE "-properties",String)
|
||||
#else
|
||||
#define P_(String) (String)
|
||||
#endif
|
||||
|
||||
/* not really I18N-related, but also a string marker macro */
|
||||
#define I_(string) g_intern_static_string (string)
|
||||
|
||||
#endif
|
38
src/gtklistmodels/gtkprivate.h
Normal file
38
src/gtklistmodels/gtkprivate.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
*
|
||||
* 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 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#ifndef __GTK_PRIVATE_H__
|
||||
#define __GTK_PRIVATE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
|
||||
#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
|
||||
#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_PRIVATE_H__ */
|
800
src/gtklistmodels/gtkrbtree.c
Normal file
800
src/gtklistmodels/gtkrbtree.c
Normal file
|
@ -0,0 +1,800 @@
|
|||
/* gtkrbtree.c
|
||||
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* #include "config.h" */
|
||||
|
||||
#include "gtkrbtreeprivate.h"
|
||||
|
||||
/* #include "gtkdebug.h" */
|
||||
|
||||
/* Define the following to print adds and removals to stdout.
|
||||
* The format of the printout will be suitable for addition as a new test to
|
||||
* testsuite/gtk/rbtree-crash.c
|
||||
* by just grepping the printouts from the relevant rbtree.
|
||||
*
|
||||
* This is meant to be a trivial way to add rbtree tests to the testsuite.
|
||||
*/
|
||||
#undef DUMP_MODIFICATION
|
||||
|
||||
typedef struct _GtkRbNode GtkRbNode;
|
||||
|
||||
struct _GtkRbTree
|
||||
{
|
||||
guint ref_count;
|
||||
|
||||
gsize element_size;
|
||||
gsize augment_size;
|
||||
GtkRbTreeAugmentFunc augment_func;
|
||||
GDestroyNotify clear_func;
|
||||
GDestroyNotify clear_augment_func;
|
||||
|
||||
GtkRbNode *root;
|
||||
};
|
||||
|
||||
struct _GtkRbNode
|
||||
{
|
||||
guint red :1;
|
||||
guint dirty :1;
|
||||
|
||||
GtkRbNode *left;
|
||||
GtkRbNode *right;
|
||||
/* The difference between tree and parent here is that we OR the tree with 1 and because
|
||||
* pointers are always multiples of 4, we can know if we've stored a parent or the tree here */
|
||||
union {
|
||||
gpointer parent_or_tree;
|
||||
GtkRbNode *parent;
|
||||
GtkRbTree *tree;
|
||||
};
|
||||
};
|
||||
|
||||
#define NODE_FROM_POINTER(ptr) ((GtkRbNode *) ((ptr) ? (((guchar *) (ptr)) - sizeof (GtkRbNode)) : NULL))
|
||||
#define NODE_TO_POINTER(node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkRbNode)) : NULL))
|
||||
#define NODE_TO_AUG_POINTER(tree, node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkRbNode) + (tree)->element_size) : NULL))
|
||||
|
||||
static inline gboolean
|
||||
is_root (GtkRbNode *node)
|
||||
{
|
||||
return GPOINTER_TO_SIZE (node->parent_or_tree) & 1 ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
static inline GtkRbNode *
|
||||
parent (GtkRbNode *node)
|
||||
{
|
||||
if (is_root (node))
|
||||
return NULL;
|
||||
else
|
||||
return node->parent;
|
||||
}
|
||||
|
||||
static GtkRbTree *
|
||||
tree (GtkRbNode *node)
|
||||
{
|
||||
while (!is_root (node))
|
||||
node = parent (node);
|
||||
|
||||
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (node->tree) & ~1);
|
||||
}
|
||||
|
||||
static void
|
||||
set_parent (GtkRbTree *tree,
|
||||
GtkRbNode *node,
|
||||
GtkRbNode *new_parent)
|
||||
{
|
||||
|
||||
if (new_parent != NULL)
|
||||
{
|
||||
node->parent = new_parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->tree = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (tree) | 1);
|
||||
tree->root = node;
|
||||
}
|
||||
}
|
||||
|
||||
static inline gsize
|
||||
gtk_rb_node_get_size (GtkRbTree *tree)
|
||||
{
|
||||
return sizeof (GtkRbNode) + tree->element_size + tree->augment_size;
|
||||
}
|
||||
|
||||
static GtkRbNode *
|
||||
gtk_rb_node_new (GtkRbTree *tree)
|
||||
{
|
||||
GtkRbNode *result;
|
||||
|
||||
result = g_slice_alloc0 (gtk_rb_node_get_size (tree));
|
||||
|
||||
result->red = TRUE;
|
||||
result->dirty = TRUE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_rb_node_free (GtkRbTree *tree,
|
||||
GtkRbNode *node)
|
||||
{
|
||||
if (tree->clear_func)
|
||||
tree->clear_func (NODE_TO_POINTER (node));
|
||||
if (tree->clear_augment_func)
|
||||
tree->clear_augment_func (NODE_TO_AUG_POINTER (tree, node));
|
||||
|
||||
g_slice_free1 (gtk_rb_node_get_size (tree), node);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_rb_node_free_deep (GtkRbTree *tree,
|
||||
GtkRbNode *node)
|
||||
{
|
||||
GtkRbNode *right = node->right;
|
||||
|
||||
if (node->left)
|
||||
gtk_rb_node_free_deep (tree, node->left);
|
||||
|
||||
gtk_rb_node_free (tree, node);
|
||||
|
||||
if (right)
|
||||
gtk_rb_node_free_deep (tree, right);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_rb_node_mark_dirty (GtkRbNode *node,
|
||||
gboolean mark_parent)
|
||||
{
|
||||
if (node->dirty)
|
||||
return;
|
||||
|
||||
node->dirty = TRUE;
|
||||
|
||||
if (mark_parent && parent (node))
|
||||
gtk_rb_node_mark_dirty (parent (node), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_rb_node_clean (GtkRbTree *tree,
|
||||
GtkRbNode *node)
|
||||
{
|
||||
if (!node->dirty)
|
||||
return;
|
||||
|
||||
node->dirty = FALSE;
|
||||
if (tree->augment_func)
|
||||
tree->augment_func (tree,
|
||||
NODE_TO_AUG_POINTER (tree, node),
|
||||
NODE_TO_POINTER (node),
|
||||
NODE_TO_POINTER (node->left),
|
||||
NODE_TO_POINTER (node->right));
|
||||
}
|
||||
|
||||
static GtkRbNode *
|
||||
gtk_rb_node_get_first (GtkRbNode *node)
|
||||
{
|
||||
while (node->left)
|
||||
node = node->left;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GtkRbNode *
|
||||
gtk_rb_node_get_last (GtkRbNode *node)
|
||||
{
|
||||
while (node->right)
|
||||
node = node->right;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GtkRbNode *
|
||||
gtk_rb_node_get_previous (GtkRbNode *node)
|
||||
{
|
||||
GtkRbNode *p;
|
||||
|
||||
if (node->left)
|
||||
return gtk_rb_node_get_last (node->left);
|
||||
|
||||
for (p = parent (node); p != NULL; p = parent (node))
|
||||
{
|
||||
if (p->right == node)
|
||||
return p;
|
||||
|
||||
node = p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GtkRbNode *
|
||||
gtk_rb_node_get_next (GtkRbNode *node)
|
||||
{
|
||||
GtkRbNode *p;
|
||||
|
||||
if (node->right)
|
||||
return gtk_rb_node_get_first (node->right);
|
||||
|
||||
for (p = parent (node); p != NULL; p = parent (node))
|
||||
{
|
||||
if (p->left == node)
|
||||
return p;
|
||||
|
||||
node = p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DUMP_MODIFICATION
|
||||
static guint
|
||||
position (GtkRbTree *tree,
|
||||
GtkRbNode *node)
|
||||
{
|
||||
GtkRbNode *n;
|
||||
guint i;
|
||||
|
||||
i = 0;
|
||||
for (n = gtk_rb_node_get_first (tree->root);
|
||||
n != node;
|
||||
n = gtk_rb_node_get_next (n))
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
gtk_rb_node_rotate_left (GtkRbTree *tree,
|
||||
GtkRbNode *node)
|
||||
{
|
||||
GtkRbNode *right, *p;
|
||||
|
||||
right = node->right;
|
||||
p = parent (node);
|
||||
|
||||
node->right = right->left;
|
||||
if (right->left)
|
||||
set_parent (tree, right->left, node);
|
||||
|
||||
set_parent (tree, right, p);
|
||||
if (p)
|
||||
{
|
||||
if (node == p->left)
|
||||
p->left = right;
|
||||
else
|
||||
p->right = right;
|
||||
}
|
||||
|
||||
right->left = node;
|
||||
set_parent (tree, node, right);
|
||||
|
||||
gtk_rb_node_mark_dirty (node, FALSE);
|
||||
gtk_rb_node_mark_dirty (right, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_rb_node_rotate_right (GtkRbTree *tree,
|
||||
GtkRbNode *node)
|
||||
{
|
||||
GtkRbNode *left, *p;
|
||||
|
||||
left = node->left;
|
||||
p = parent (node);
|
||||
|
||||
node->left = left->right;
|
||||
if (left->right)
|
||||
set_parent (tree, left->right, node);
|
||||
|
||||
set_parent (tree, left, p);
|
||||
if (p)
|
||||
{
|
||||
if (node == p->right)
|
||||
p->right = left;
|
||||
else
|
||||
p->left = left;
|
||||
}
|
||||
|
||||
/* link node and left */
|
||||
left->right = node;
|
||||
set_parent (tree, node, left);
|
||||
|
||||
gtk_rb_node_mark_dirty (node, FALSE);
|
||||
gtk_rb_node_mark_dirty (left, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_red (GtkRbNode *node_or_null)
|
||||
{
|
||||
if (node_or_null == NULL)
|
||||
return FALSE;
|
||||
else
|
||||
return node_or_null->red;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
is_black (GtkRbNode *node_or_null)
|
||||
{
|
||||
return !is_red (node_or_null);
|
||||
}
|
||||
|
||||
static void
|
||||
set_black (GtkRbNode *node_or_null)
|
||||
{
|
||||
if (node_or_null == NULL)
|
||||
return;
|
||||
|
||||
node_or_null->red = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
set_red (GtkRbNode *node_or_null)
|
||||
{
|
||||
if (node_or_null == NULL)
|
||||
return;
|
||||
|
||||
node_or_null->red = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_rb_tree_insert_fixup (GtkRbTree *tree,
|
||||
GtkRbNode *node)
|
||||
{
|
||||
GtkRbNode *p;
|
||||
|
||||
/* check Red-Black properties */
|
||||
for (p = parent (node);
|
||||
p && is_red (p);
|
||||
p = parent (node))
|
||||
{
|
||||
GtkRbNode *pp = parent (p);
|
||||
|
||||
/* we have a violation */
|
||||
g_assert (pp);
|
||||
|
||||
if (p == pp->left)
|
||||
{
|
||||
GtkRbNode *uncle = pp->right;
|
||||
|
||||
if (is_red (uncle))
|
||||
{
|
||||
/* uncle is red */
|
||||
set_black (p);
|
||||
set_black (uncle);
|
||||
set_red (pp);
|
||||
node = pp;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* uncle is black */
|
||||
if (node == p->right)
|
||||
{
|
||||
/* make node a left child */
|
||||
node = p;
|
||||
gtk_rb_node_rotate_left (tree, node);
|
||||
p = parent (node);
|
||||
pp = parent (p);
|
||||
}
|
||||
/* recolor and rotate */
|
||||
set_black (p);
|
||||
set_red (pp);
|
||||
gtk_rb_node_rotate_right (tree, pp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* mirror image of above code */
|
||||
GtkRbNode *uncle = pp->left;
|
||||
|
||||
if (is_red (uncle))
|
||||
{
|
||||
/* uncle is red */
|
||||
set_black (p);
|
||||
set_black (uncle);
|
||||
set_red (pp);
|
||||
node = pp;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* uncle is black */
|
||||
if (node == p->left)
|
||||
{
|
||||
node = p;
|
||||
gtk_rb_node_rotate_right (tree, node);
|
||||
p = parent (node);
|
||||
pp = parent (p);
|
||||
}
|
||||
set_black (p);
|
||||
set_red (pp);
|
||||
gtk_rb_node_rotate_left (tree, pp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_black (tree->root);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_rb_tree_remove_node_fixup (GtkRbTree *tree,
|
||||
GtkRbNode *node,
|
||||
GtkRbNode *p)
|
||||
{
|
||||
while (node != tree->root && is_black (node))
|
||||
{
|
||||
if (node == p->left)
|
||||
{
|
||||
GtkRbNode *w = p->right;
|
||||
|
||||
if (is_red (w))
|
||||
{
|
||||
set_black (w);
|
||||
set_red (p);
|
||||
gtk_rb_node_rotate_left (tree, p);
|
||||
w = p->right;
|
||||
}
|
||||
if (is_black (w->left) && is_black (w->right))
|
||||
{
|
||||
set_red (w);
|
||||
node = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_black (w->right))
|
||||
{
|
||||
set_black (w->left);
|
||||
set_red (w);
|
||||
gtk_rb_node_rotate_right (tree, w);
|
||||
w = p->right;
|
||||
}
|
||||
w->red = p->red;
|
||||
set_black (p);
|
||||
set_black (w->right);
|
||||
gtk_rb_node_rotate_left (tree, p);
|
||||
node = tree->root;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkRbNode *w = p->left;
|
||||
if (is_red (w))
|
||||
{
|
||||
set_black (w);
|
||||
set_red (p);
|
||||
gtk_rb_node_rotate_right (tree, p);
|
||||
w = p->left;
|
||||
}
|
||||
if (is_black (w->right) && is_black (w->left))
|
||||
{
|
||||
set_red (w);
|
||||
node = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_black (w->left))
|
||||
{
|
||||
set_black (w->right);
|
||||
set_red (w);
|
||||
gtk_rb_node_rotate_left (tree, w);
|
||||
w = p->left;
|
||||
}
|
||||
w->red = p->red;
|
||||
set_black (p);
|
||||
set_black (w->left);
|
||||
gtk_rb_node_rotate_right (tree, p);
|
||||
node = tree->root;
|
||||
}
|
||||
}
|
||||
|
||||
p = parent (node);
|
||||
}
|
||||
|
||||
set_black (node);
|
||||
}
|
||||
|
||||
GtkRbTree *
|
||||
gtk_rb_tree_new_for_size (gsize element_size,
|
||||
gsize augment_size,
|
||||
GtkRbTreeAugmentFunc augment_func,
|
||||
GDestroyNotify clear_func,
|
||||
GDestroyNotify clear_augment_func)
|
||||
{
|
||||
GtkRbTree *tree;
|
||||
|
||||
tree = g_slice_new0 (GtkRbTree);
|
||||
tree->ref_count = 1;
|
||||
|
||||
tree->element_size = element_size;
|
||||
tree->augment_size = augment_size;
|
||||
tree->augment_func = augment_func;
|
||||
tree->clear_func = clear_func;
|
||||
tree->clear_augment_func = clear_augment_func;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
GtkRbTree *
|
||||
gtk_rb_tree_ref (GtkRbTree *tree)
|
||||
{
|
||||
tree->ref_count++;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_rb_tree_unref (GtkRbTree *tree)
|
||||
{
|
||||
tree->ref_count--;
|
||||
if (tree->ref_count > 0)
|
||||
return;
|
||||
|
||||
if (tree->root)
|
||||
gtk_rb_node_free_deep (tree, tree->root);
|
||||
|
||||
g_slice_free (GtkRbTree, tree);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_get_first (GtkRbTree *tree)
|
||||
{
|
||||
if (tree->root == NULL)
|
||||
return NULL;
|
||||
|
||||
return NODE_TO_POINTER (gtk_rb_node_get_first (tree->root));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_get_last (GtkRbTree *tree)
|
||||
{
|
||||
if (tree->root == NULL)
|
||||
return NULL;
|
||||
|
||||
return NODE_TO_POINTER (gtk_rb_node_get_last (tree->root));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_node_get_previous (gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (gtk_rb_node_get_previous (NODE_FROM_POINTER (node)));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_node_get_next (gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (gtk_rb_node_get_next (NODE_FROM_POINTER (node)));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_get_root (GtkRbTree *tree)
|
||||
{
|
||||
return NODE_TO_POINTER (tree->root);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_node_get_parent (gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (parent (NODE_FROM_POINTER (node)));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_node_get_left (gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (NODE_FROM_POINTER (node)->left);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_node_get_right (gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (NODE_FROM_POINTER (node)->right);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_get_augment (GtkRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkRbNode *rbnode = NODE_FROM_POINTER (node);
|
||||
|
||||
gtk_rb_node_clean (tree, rbnode);
|
||||
|
||||
return NODE_TO_AUG_POINTER (tree, rbnode);
|
||||
}
|
||||
|
||||
GtkRbTree *
|
||||
gtk_rb_tree_node_get_tree (gpointer node)
|
||||
{
|
||||
return tree (NODE_FROM_POINTER (node));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_rb_tree_node_mark_dirty (gpointer node)
|
||||
{
|
||||
gtk_rb_node_mark_dirty (NODE_FROM_POINTER (node), TRUE);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_insert_before (GtkRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkRbNode *result;
|
||||
|
||||
|
||||
if (tree->root == NULL)
|
||||
{
|
||||
#ifdef DUMP_MODIFICATION
|
||||
g_print ("add (tree, 0); /* 0x%p */\n", tree);
|
||||
#endif /* DUMP_MODIFICATION */
|
||||
|
||||
g_assert (node == NULL);
|
||||
|
||||
result = gtk_rb_node_new (tree);
|
||||
tree->root = result;
|
||||
}
|
||||
else if (node == NULL)
|
||||
{
|
||||
return gtk_rb_tree_insert_after (tree, gtk_rb_tree_get_last (tree));
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkRbNode *current = NODE_FROM_POINTER (node);
|
||||
|
||||
#ifdef DUMP_MODIFICATION
|
||||
g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current), tree);
|
||||
#endif /* DUMP_MODIFICATION */
|
||||
|
||||
/* setup new node */
|
||||
result = gtk_rb_node_new (tree);
|
||||
|
||||
if (current->left)
|
||||
{
|
||||
current = gtk_rb_node_get_last (current->left);
|
||||
current->right = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->left = result;
|
||||
}
|
||||
set_parent (tree, result, current);
|
||||
gtk_rb_node_mark_dirty (current, TRUE);
|
||||
}
|
||||
|
||||
gtk_rb_tree_insert_fixup (tree, result);
|
||||
|
||||
return NODE_TO_POINTER (result);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_rb_tree_insert_after (GtkRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkRbNode *current, *result;
|
||||
|
||||
if (node == NULL)
|
||||
return gtk_rb_tree_insert_before (tree, gtk_rb_tree_get_first (tree));
|
||||
|
||||
current = NODE_FROM_POINTER (node);
|
||||
|
||||
#ifdef DUMP_MODIFICATION
|
||||
g_print ("add (tree, %u); /* 0x%p */\n", position (tree, current) + 1, tree);
|
||||
#endif /* DUMP_MODIFICATION */
|
||||
|
||||
/* setup new node */
|
||||
result = gtk_rb_node_new (tree);
|
||||
|
||||
if (current->right)
|
||||
{
|
||||
current = gtk_rb_node_get_first (current->right);
|
||||
current->left = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->right = result;
|
||||
}
|
||||
set_parent (tree, result, current);
|
||||
gtk_rb_node_mark_dirty (current, TRUE);
|
||||
|
||||
gtk_rb_tree_insert_fixup (tree, result);
|
||||
|
||||
return NODE_TO_POINTER (result);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_rb_tree_remove (GtkRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkRbNode *x, *y, *p, *real_node;
|
||||
|
||||
real_node = NODE_FROM_POINTER (node);
|
||||
|
||||
#ifdef DUMP_MODIFICATION
|
||||
g_print ("delete (tree, %u); /* 0x%p */\n", position (tree, real_node), tree);
|
||||
#endif /* DUMP_MODIFICATION */
|
||||
|
||||
y = real_node;
|
||||
if (y->left && y->right)
|
||||
{
|
||||
y = y->right;
|
||||
|
||||
while (y->left)
|
||||
y = y->left;
|
||||
}
|
||||
|
||||
/* x is y's only child, or nil */
|
||||
if (y->left)
|
||||
x = y->left;
|
||||
else
|
||||
x = y->right;
|
||||
|
||||
/* remove y from the parent chain */
|
||||
p = parent (y);
|
||||
if (x != NULL)
|
||||
set_parent (tree, x, p);
|
||||
if (p)
|
||||
{
|
||||
if (y == p->left)
|
||||
p->left = x;
|
||||
else
|
||||
p->right = x;
|
||||
gtk_rb_node_mark_dirty (p, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == NULL)
|
||||
tree->root = NULL;
|
||||
}
|
||||
|
||||
/* We need to clean up the validity of the tree.
|
||||
*/
|
||||
if (is_black (y))
|
||||
gtk_rb_tree_remove_node_fixup (tree, x, p);
|
||||
|
||||
if (y != real_node)
|
||||
{
|
||||
/* Move the node over */
|
||||
if (is_red (real_node) != is_red (y))
|
||||
y->red = !y->red;
|
||||
|
||||
y->left = real_node->left;
|
||||
if (y->left)
|
||||
set_parent (tree, y->left, y);
|
||||
y->right = real_node->right;
|
||||
if (y->right)
|
||||
set_parent (tree, y->right, y);
|
||||
p = parent (real_node);
|
||||
set_parent (tree, y, p);
|
||||
if (p)
|
||||
{
|
||||
if (p->left == real_node)
|
||||
p->left = y;
|
||||
else
|
||||
p->right = y;
|
||||
gtk_rb_node_mark_dirty (p, TRUE);
|
||||
}
|
||||
gtk_rb_node_mark_dirty (y, TRUE);
|
||||
}
|
||||
|
||||
gtk_rb_node_free (tree, real_node);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_rb_tree_remove_all (GtkRbTree *tree)
|
||||
{
|
||||
#ifdef DUMP_MODIFICATION
|
||||
g_print ("delete_all (tree); /* 0x%p */\n", tree);
|
||||
#endif /* DUMP_MODIFICATION */
|
||||
|
||||
if (tree->root)
|
||||
gtk_rb_node_free_deep (tree, tree->root);
|
||||
|
||||
tree->root = NULL;
|
||||
}
|
||||
|
75
src/gtklistmodels/gtkrbtreeprivate.h
Normal file
75
src/gtklistmodels/gtkrbtreeprivate.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/* gtkrbtree.h
|
||||
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* A Red-Black Tree implementation used specifically by GtkTreeView.
|
||||
*/
|
||||
#ifndef __GTK_RB_TREE_H__
|
||||
#define __GTK_RB_TREE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
typedef struct _GtkRbTree GtkRbTree;
|
||||
|
||||
typedef void (* GtkRbTreeAugmentFunc) (GtkRbTree *tree,
|
||||
gpointer node_augment,
|
||||
gpointer node,
|
||||
gpointer left,
|
||||
gpointer right);
|
||||
|
||||
GtkRbTree * gtk_rb_tree_new_for_size (gsize element_size,
|
||||
gsize augment_size,
|
||||
GtkRbTreeAugmentFunc augment_func,
|
||||
GDestroyNotify clear_func,
|
||||
GDestroyNotify clear_augment_func);
|
||||
#define gtk_rb_tree_new(type, augment_type, augment_func, clear_func, clear_augment_func) \
|
||||
gtk_rb_tree_new_for_size (sizeof (type), sizeof (augment_type), (augment_func), (clear_func), (clear_augment_func))
|
||||
|
||||
GtkRbTree * gtk_rb_tree_ref (GtkRbTree *tree);
|
||||
void gtk_rb_tree_unref (GtkRbTree *tree);
|
||||
|
||||
gpointer gtk_rb_tree_get_root (GtkRbTree *tree);
|
||||
gpointer gtk_rb_tree_get_first (GtkRbTree *tree);
|
||||
gpointer gtk_rb_tree_get_last (GtkRbTree *tree);
|
||||
|
||||
gpointer gtk_rb_tree_node_get_previous (gpointer node);
|
||||
gpointer gtk_rb_tree_node_get_next (gpointer node);
|
||||
gpointer gtk_rb_tree_node_get_parent (gpointer node);
|
||||
gpointer gtk_rb_tree_node_get_left (gpointer node);
|
||||
gpointer gtk_rb_tree_node_get_right (gpointer node);
|
||||
GtkRbTree * gtk_rb_tree_node_get_tree (gpointer node);
|
||||
void gtk_rb_tree_node_mark_dirty (gpointer node);
|
||||
|
||||
gpointer gtk_rb_tree_get_augment (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
|
||||
gpointer gtk_rb_tree_insert_before (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_rb_tree_insert_after (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
void gtk_rb_tree_remove (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
void gtk_rb_tree_remove_all (GtkRbTree *tree);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GTK_RB_TREE_H__ */
|
529
src/gtklistmodels/gtkslicelistmodel.c
Normal file
529
src/gtklistmodels/gtkslicelistmodel.c
Normal file
|
@ -0,0 +1,529 @@
|
|||
/*
|
||||
* Copyright © 2018 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 "gtkslicelistmodel.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkslicelistmodel
|
||||
* @title: GtkSliceListModel
|
||||
* @short_description: A list model that presents a slice out of a larger list
|
||||
* @see_also: #GListModel
|
||||
*
|
||||
* #GtkSliceListModel is a list model that takes a list model and presents a slice of
|
||||
* that model.
|
||||
*
|
||||
* This is useful when implementing paging by setting the size to the number of elements
|
||||
* per page and updating the offset whenever a different page is opened.
|
||||
*/
|
||||
|
||||
#define DEFAULT_SIZE 10
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_MODEL,
|
||||
PROP_OFFSET,
|
||||
PROP_SIZE,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
struct _GtkSliceListModel
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GType item_type;
|
||||
GListModel *model;
|
||||
guint offset;
|
||||
guint size;
|
||||
|
||||
guint n_items;
|
||||
};
|
||||
|
||||
struct _GtkSliceListModelClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static GType
|
||||
gtk_slice_list_model_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
|
||||
|
||||
return self->item_type;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_slice_list_model_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
|
||||
guint n_items;
|
||||
|
||||
if (self->model == NULL)
|
||||
return 0;
|
||||
|
||||
/* XXX: This can be done without calling g_list_model_get_n_items() on the parent model
|
||||
* by checking if model.get_item(offset + size) != NULL */
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
if (n_items <= self->offset)
|
||||
return 0;
|
||||
|
||||
n_items -= self->offset;
|
||||
return MIN (n_items, self->size);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_slice_list_model_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return NULL;
|
||||
|
||||
if (position >= self->size)
|
||||
return NULL;
|
||||
|
||||
return g_list_model_get_item (self->model, position + self->offset);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_slice_list_model_get_item_type;
|
||||
iface->get_n_items = gtk_slice_list_model_get_n_items;
|
||||
iface->get_item = gtk_slice_list_model_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkSliceListModel, gtk_slice_list_model, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_slice_list_model_model_init))
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkSliceListModel *self)
|
||||
{
|
||||
if (position >= self->offset + self->size)
|
||||
return;
|
||||
|
||||
if (position < self->offset)
|
||||
{
|
||||
guint skip = MIN (removed, added);
|
||||
skip = MIN (skip, self->offset - position);
|
||||
|
||||
position += skip;
|
||||
removed -= skip;
|
||||
added -= skip;
|
||||
}
|
||||
|
||||
if (removed == added)
|
||||
{
|
||||
guint changed = removed;
|
||||
|
||||
if (changed == 0)
|
||||
return;
|
||||
|
||||
g_assert (position >= self->offset);
|
||||
position -= self->offset;
|
||||
changed = MIN (changed, self->size - position);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, changed, changed);
|
||||
}
|
||||
else
|
||||
{
|
||||
guint n_after, n_before;
|
||||
guint skip;
|
||||
|
||||
if (position > self->offset)
|
||||
skip = position - self->offset;
|
||||
else
|
||||
skip = 0;
|
||||
|
||||
n_after = g_list_model_get_n_items (self->model);
|
||||
n_before = n_after - added + removed;
|
||||
n_after = CLAMP (n_after, self->offset, self->offset + self->size) - self->offset;
|
||||
n_before = CLAMP (n_before, self->offset, self->offset + self->size) - self->offset;
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), skip, n_before - skip, n_after - skip);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE:
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_slice_list_model_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_OFFSET:
|
||||
gtk_slice_list_model_set_offset (self, g_value_get_uint (value));
|
||||
break;
|
||||
|
||||
case PROP_SIZE:
|
||||
gtk_slice_list_model_set_size (self, g_value_get_uint (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, self->item_type);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
case PROP_OFFSET:
|
||||
g_value_set_uint (value, self->offset);
|
||||
break;
|
||||
|
||||
case PROP_SIZE:
|
||||
g_value_set_uint (value, self->size);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_clear_model (GtkSliceListModel *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model, gtk_slice_list_model_items_changed_cb, self);
|
||||
g_clear_object (&self->model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_dispose (GObject *object)
|
||||
{
|
||||
GtkSliceListModel *self = GTK_SLICE_LIST_MODEL (object);
|
||||
|
||||
gtk_slice_list_model_clear_model (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_slice_list_model_parent_class)->dispose (object);
|
||||
};
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_class_init (GtkSliceListModelClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_slice_list_model_set_property;
|
||||
gobject_class->get_property = gtk_slice_list_model_get_property;
|
||||
gobject_class->dispose = gtk_slice_list_model_dispose;
|
||||
|
||||
/**
|
||||
* GtkSliceListModel:item-type:
|
||||
*
|
||||
* The #GType for elements of this object
|
||||
*/
|
||||
properties[PROP_ITEM_TYPE] =
|
||||
g_param_spec_gtype ("item-type",
|
||||
P_("Item type"),
|
||||
P_("The type of elements of this object"),
|
||||
G_TYPE_OBJECT,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkSliceListModel:model:
|
||||
*
|
||||
* Child model to take slice from
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Child model to take slice from"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkSliceListModel:offset:
|
||||
*
|
||||
* Offset of slice
|
||||
*/
|
||||
properties[PROP_OFFSET] =
|
||||
g_param_spec_uint ("offset",
|
||||
P_("Offset"),
|
||||
P_("Offset of slice"),
|
||||
0, G_MAXUINT, 0,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkSliceListModel:size:
|
||||
*
|
||||
* Maximum size of slice
|
||||
*/
|
||||
properties[PROP_SIZE] =
|
||||
g_param_spec_uint ("size",
|
||||
P_("Size"),
|
||||
P_("Maximum size of slice"),
|
||||
0, G_MAXUINT, DEFAULT_SIZE,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_slice_list_model_init (GtkSliceListModel *self)
|
||||
{
|
||||
self->size = DEFAULT_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_new:
|
||||
* @model: (transfer none): The model to use
|
||||
* @offset: the offset of the slice
|
||||
* @size: maximum size of the slice
|
||||
*
|
||||
* Creates a new slice model that presents the slice from @offset to
|
||||
* @offset + @size our of the given @model.
|
||||
*
|
||||
* Returns: A new #GtkSliceListModel
|
||||
**/
|
||||
GtkSliceListModel *
|
||||
gtk_slice_list_model_new (GListModel *model,
|
||||
guint offset,
|
||||
guint size)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_SLICE_LIST_MODEL,
|
||||
"item-type", g_list_model_get_item_type (model),
|
||||
"model", model,
|
||||
"offset", offset,
|
||||
"size", size,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_new_for_type:
|
||||
* @item_type: the type of items
|
||||
*
|
||||
* Creates a new empty #GtkSliceListModel for the given @item_type that
|
||||
* can be set up later.
|
||||
*
|
||||
* Returns: a new empty #GtkSliceListModel
|
||||
**/
|
||||
GtkSliceListModel *
|
||||
gtk_slice_list_model_new_for_type (GType item_type)
|
||||
{
|
||||
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_SLICE_LIST_MODEL,
|
||||
"item-type", item_type,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_set_model:
|
||||
* @self: a #GtkSliceListModel
|
||||
* @model: (allow-none): The model to be sliced
|
||||
*
|
||||
* Sets the model to show a slice of. The model's item type must conform
|
||||
* to @self's item type.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gtk_slice_list_model_set_model (GtkSliceListModel *self,
|
||||
GListModel *model)
|
||||
{
|
||||
guint removed, added;
|
||||
|
||||
g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
gtk_slice_list_model_clear_model (self);
|
||||
|
||||
if (model)
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_slice_list_model_items_changed_cb), self);
|
||||
added = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
}
|
||||
else
|
||||
{
|
||||
added = 0;
|
||||
}
|
||||
|
||||
if (removed > 0 || added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_get_model:
|
||||
* @self: a #GtkSliceListModel
|
||||
*
|
||||
* Gets the model that is curently being used or %NULL if none.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_slice_list_model_get_model (GtkSliceListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_set_offset:
|
||||
* @self: a #GtkSliceListModel
|
||||
* @offset: the new offset to use
|
||||
*
|
||||
* Sets the offset into the original model for this slice.
|
||||
*
|
||||
* If the offset is too large for the sliced model,
|
||||
* @self will end up empty.
|
||||
**/
|
||||
void
|
||||
gtk_slice_list_model_set_offset (GtkSliceListModel *self,
|
||||
guint offset)
|
||||
{
|
||||
guint before, after;
|
||||
|
||||
g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
|
||||
|
||||
if (self->offset == offset)
|
||||
return;
|
||||
|
||||
before = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
|
||||
self->offset = offset;
|
||||
|
||||
after = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
|
||||
if (before > 0 || after > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, before, after);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_OFFSET]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_get_offset:
|
||||
* @self: a #GtkSliceListModel
|
||||
*
|
||||
* Gets the offset set via gtk_slice_list_model_set_offset()
|
||||
*
|
||||
* Returns: The offset
|
||||
**/
|
||||
guint
|
||||
gtk_slice_list_model_get_offset (GtkSliceListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), 0);
|
||||
|
||||
return self->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_set_size:
|
||||
* @self: a #GtkSliceListModel
|
||||
* @size: the maximum size
|
||||
*
|
||||
* Sets the maximum size. @self will never have more items
|
||||
* than @size.
|
||||
*
|
||||
* It can however have fewer items if the offset is too large or
|
||||
* the model sliced from doesn't have enough items.
|
||||
*/
|
||||
void
|
||||
gtk_slice_list_model_set_size (GtkSliceListModel *self,
|
||||
guint size)
|
||||
{
|
||||
guint before, after;
|
||||
|
||||
g_return_if_fail (GTK_IS_SLICE_LIST_MODEL (self));
|
||||
|
||||
if (self->size == size)
|
||||
return;
|
||||
|
||||
before = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
|
||||
self->size = size;
|
||||
|
||||
after = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
|
||||
if (before > after)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), after, before - after, 0);
|
||||
else if (before < after)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), before, 0, after - before);
|
||||
/* else nothing */
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SIZE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_slice_list_model_get_size:
|
||||
* @self: a #GtkSliceListModel
|
||||
*
|
||||
* Gets the size set via gtk_slice_list_model_set_size().
|
||||
*
|
||||
* Returns: The size
|
||||
**/
|
||||
guint
|
||||
gtk_slice_list_model_get_size (GtkSliceListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SLICE_LIST_MODEL (self), DEFAULT_SIZE);
|
||||
|
||||
return self->size;
|
||||
}
|
||||
|
||||
|
65
src/gtklistmodels/gtkslicelistmodel.h
Normal file
65
src/gtklistmodels/gtkslicelistmodel.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright © 2018 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>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_SLICE_LIST_MODEL_H__
|
||||
#define __GTK_SLICE_LIST_MODEL_H__
|
||||
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SLICE_LIST_MODEL (gtk_slice_list_model_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkSliceListModel, gtk_slice_list_model, GTK, SLICE_LIST_MODEL, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSliceListModel * gtk_slice_list_model_new (GListModel *model,
|
||||
guint offset,
|
||||
guint size);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSliceListModel * gtk_slice_list_model_new_for_type (GType item_type);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_slice_list_model_set_model (GtkSliceListModel *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_slice_list_model_get_model (GtkSliceListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_slice_list_model_set_offset (GtkSliceListModel *self,
|
||||
guint offset);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_slice_list_model_get_offset (GtkSliceListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_slice_list_model_set_size (GtkSliceListModel *self,
|
||||
guint size);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_slice_list_model_get_size (GtkSliceListModel *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SLICE_LIST_MODEL_H__ */
|
207
src/gtklistmodels/gtksorter.c
Normal file
207
src/gtklistmodels/gtksorter.c
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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.
|
||||
* Its 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 the #GtkColumnViewColumn:sorter
|
||||
* property, where the user can change the sorting by clicking on list headers.
|
||||
*
|
||||
* Of course, 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_EQUAL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* GtkSorter::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 and b < a, then a = b
|
||||
* * It is transitive, ie given any 3 items with a ≤ b and 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)
|
||||
{
|
||||
GtkOrdering result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SORTER (self), GTK_ORDERING_EQUAL);
|
||||
g_return_val_if_fail (item1 && item2, GTK_ORDERING_EQUAL);
|
||||
|
||||
if (item1 == item2)
|
||||
return GTK_ORDERING_EQUAL;
|
||||
|
||||
result = GTK_SORTER_GET_CLASS (self)->compare (self, item1, item2);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if (result < -1 || result > 1)
|
||||
{
|
||||
g_critical ("A sorter of type \"%s\" returned %d, which is not a valid GtkOrdering result.\n"
|
||||
"Did you forget to call gtk_ordering_from_cmpfunc()?",
|
||||
G_OBJECT_TYPE_NAME (self), (int) result);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
131
src/gtklistmodels/gtksorter.h
Normal file
131
src/gtklistmodels/gtksorter.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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__
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#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_NONE,
|
||||
GTK_SORTER_ORDER_TOTAL
|
||||
} GtkSorterOrder;
|
||||
|
||||
typedef enum {
|
||||
GTK_ORDERING_SMALLER = -1,
|
||||
GTK_ORDERING_EQUAL = 0,
|
||||
GTK_ORDERING_LARGER = 1
|
||||
} GtkOrdering;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SORTER_H__ */
|
||||
|
595
src/gtklistmodels/gtksortlistmodel.c
Normal file
595
src/gtklistmodels/gtksortlistmodel.c
Normal file
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
* Copyright © 2018 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 "gtksortlistmodel.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtksortlistmodel
|
||||
* @title: GtkSortListModel
|
||||
* @short_description: A list model that sorts its items
|
||||
* @see_also: #GListModel, #GtkSorter
|
||||
*
|
||||
* #GtkSortListModel is a list model that takes a list model and
|
||||
* sorts its elements according to a #GtkSorter.
|
||||
*
|
||||
* #GtkSortListModel is a generic model and because of that it
|
||||
* cannot take advantage of any external knowledge when sorting.
|
||||
* If you run into performance issues with #GtkSortListModel, it
|
||||
* is strongly recommended that you write your own sorting list
|
||||
* model.
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_MODEL,
|
||||
PROP_SORTER,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
typedef struct _GtkSortListEntry GtkSortListEntry;
|
||||
|
||||
struct _GtkSortListModel
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GType item_type;
|
||||
GListModel *model;
|
||||
GtkSorter *sorter;
|
||||
|
||||
GSequence *sorted; /* NULL if known unsorted */
|
||||
GSequence *unsorted; /* NULL if known unsorted */
|
||||
};
|
||||
|
||||
struct _GtkSortListModelClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct _GtkSortListEntry
|
||||
{
|
||||
GSequenceIter *sorted_iter;
|
||||
GSequenceIter *unsorted_iter;
|
||||
gpointer item; /* holds ref */
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_sort_list_entry_free (gpointer data)
|
||||
{
|
||||
GtkSortListEntry *entry = data;
|
||||
|
||||
g_object_unref (entry->item);
|
||||
|
||||
g_slice_free (GtkSortListEntry, entry);
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_sort_list_model_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
|
||||
|
||||
return self->item_type;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_sort_list_model_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
|
||||
|
||||
if (self->model == NULL)
|
||||
return 0;
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_sort_list_model_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkSortListModel *self = GTK_SORT_LIST_MODEL (list);
|
||||
GSequenceIter *iter;
|
||||
GtkSortListEntry *entry;
|
||||
|
||||
if (self->model == NULL)
|
||||
return NULL;
|
||||
|
||||
if (self->unsorted == NULL)
|
||||
return g_list_model_get_item (self->model, position);
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->sorted, position);
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
return NULL;
|
||||
|
||||
entry = g_sequence_get (iter);
|
||||
|
||||
return g_object_ref (entry->item);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_sort_list_model_get_item_type;
|
||||
iface->get_n_items = gtk_sort_list_model_get_n_items;
|
||||
iface->get_item = gtk_sort_list_model_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkSortListModel, gtk_sort_list_model, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sort_list_model_model_init))
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_remove_items (GtkSortListModel *self,
|
||||
guint position,
|
||||
guint n_items,
|
||||
guint *unmodified_start,
|
||||
guint *unmodified_end)
|
||||
{
|
||||
GSequenceIter *unsorted_iter;
|
||||
guint i, pos, start, end, length_before;
|
||||
|
||||
start = end = length_before = g_sequence_get_length (self->sorted);
|
||||
unsorted_iter = g_sequence_get_iter_at_pos (self->unsorted, position);
|
||||
|
||||
for (i = 0; i < n_items ; i++)
|
||||
{
|
||||
GtkSortListEntry *entry;
|
||||
GSequenceIter *next;
|
||||
|
||||
next = g_sequence_iter_next (unsorted_iter);
|
||||
|
||||
entry = g_sequence_get (unsorted_iter);
|
||||
pos = g_sequence_iter_get_position (entry->sorted_iter);
|
||||
start = MIN (start, pos);
|
||||
end = MIN (end, length_before - i - 1 - pos);
|
||||
|
||||
g_sequence_remove (entry->unsorted_iter);
|
||||
g_sequence_remove (entry->sorted_iter);
|
||||
|
||||
unsorted_iter = next;
|
||||
}
|
||||
|
||||
*unmodified_start = start;
|
||||
*unmodified_end = end;
|
||||
}
|
||||
|
||||
static int
|
||||
_sort_func (gconstpointer item1,
|
||||
gconstpointer item2,
|
||||
gpointer data)
|
||||
{
|
||||
GtkSortListEntry *entry1 = (GtkSortListEntry *) item1;
|
||||
GtkSortListEntry *entry2 = (GtkSortListEntry *) item2;
|
||||
GtkOrdering result;
|
||||
|
||||
result = gtk_sorter_compare (GTK_SORTER (data), entry1->item, entry2->item);
|
||||
|
||||
if (result == GTK_ORDERING_EQUAL)
|
||||
result = g_sequence_iter_compare (entry1->unsorted_iter, entry2->unsorted_iter);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_add_items (GtkSortListModel *self,
|
||||
guint position,
|
||||
guint n_items,
|
||||
guint *unmodified_start,
|
||||
guint *unmodified_end)
|
||||
{
|
||||
GSequenceIter *unsorted_end;
|
||||
guint i, pos, start, end, length_before;
|
||||
|
||||
unsorted_end = g_sequence_get_iter_at_pos (self->unsorted, position);
|
||||
start = end = length_before = g_sequence_get_length (self->sorted);
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
GtkSortListEntry *entry = g_slice_new0 (GtkSortListEntry);
|
||||
|
||||
entry->item = g_list_model_get_item (self->model, position + i);
|
||||
entry->unsorted_iter = g_sequence_insert_before (unsorted_end, entry);
|
||||
entry->sorted_iter = g_sequence_insert_sorted (self->sorted, entry, _sort_func, self->sorter);
|
||||
if (unmodified_start != NULL || unmodified_end != NULL)
|
||||
{
|
||||
pos = g_sequence_iter_get_position (entry->sorted_iter);
|
||||
start = MIN (start, pos);
|
||||
end = MIN (end, length_before + i - pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (unmodified_start)
|
||||
*unmodified_start = start;
|
||||
if (unmodified_end)
|
||||
*unmodified_end = end;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkSortListModel *self)
|
||||
{
|
||||
guint n_items, start, end, start2, end2;
|
||||
|
||||
if (removed == 0 && added == 0)
|
||||
return;
|
||||
|
||||
if (self->sorted == NULL)
|
||||
{
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_sort_list_model_remove_items (self, position, removed, &start, &end);
|
||||
gtk_sort_list_model_add_items (self, position, added, &start2, &end2);
|
||||
start = MIN (start, start2);
|
||||
end = MIN (end, end2);
|
||||
|
||||
n_items = g_sequence_get_length (self->sorted) - start - end;
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_items - added + removed, n_items);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE:
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_sort_list_model_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
gtk_sort_list_model_set_sorter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, self->item_type);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
g_value_set_object (value, self->sorter);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_sort_list_model_resort (GtkSortListModel *self);
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_sorter_changed_cb (GtkSorter *sorter,
|
||||
int change,
|
||||
GtkSortListModel *self)
|
||||
{
|
||||
gtk_sort_list_model_resort (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_clear_model (GtkSortListModel *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model, gtk_sort_list_model_items_changed_cb, self);
|
||||
g_clear_object (&self->model);
|
||||
g_clear_pointer (&self->sorted, g_sequence_free);
|
||||
g_clear_pointer (&self->unsorted, g_sequence_free);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_clear_sorter (GtkSortListModel *self)
|
||||
{
|
||||
if (self->sorter == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sort_list_model_sorter_changed_cb, self);
|
||||
g_clear_object (&self->sorter);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_dispose (GObject *object)
|
||||
{
|
||||
GtkSortListModel *self = GTK_SORT_LIST_MODEL (object);
|
||||
|
||||
gtk_sort_list_model_clear_model (self);
|
||||
gtk_sort_list_model_clear_sorter (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_sort_list_model_parent_class)->dispose (object);
|
||||
};
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_class_init (GtkSortListModelClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_sort_list_model_set_property;
|
||||
gobject_class->get_property = gtk_sort_list_model_get_property;
|
||||
gobject_class->dispose = gtk_sort_list_model_dispose;
|
||||
|
||||
/**
|
||||
* GtkSortListModel:sorter:
|
||||
*
|
||||
* The sorter for this model
|
||||
*/
|
||||
properties[PROP_SORTER] =
|
||||
g_param_spec_object ("sorter",
|
||||
P_("Sorter"),
|
||||
P_("The sorter for this model"),
|
||||
GTK_TYPE_SORTER,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkSortListModel:item-type:
|
||||
*
|
||||
* The #GType for items of this model
|
||||
*/
|
||||
properties[PROP_ITEM_TYPE] =
|
||||
g_param_spec_gtype ("item-type",
|
||||
P_("Item type"),
|
||||
P_("The type of items of this list"),
|
||||
G_TYPE_OBJECT,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkSortListModel:model:
|
||||
*
|
||||
* The model being sorted
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("The model being sorted"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_init (GtkSortListModel *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_sort_list_model_new:
|
||||
* @model: the model to sort
|
||||
* @sorter: (allow-none): the #GtkSorter to sort @model with
|
||||
*
|
||||
* Creates a new sort list model that uses the @sorter to sort @model.
|
||||
*
|
||||
* Returns: a new #GtkSortListModel
|
||||
**/
|
||||
GtkSortListModel *
|
||||
gtk_sort_list_model_new (GListModel *model,
|
||||
GtkSorter *sorter)
|
||||
{
|
||||
GtkSortListModel *result;
|
||||
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
|
||||
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_SORT_LIST_MODEL,
|
||||
"item-type", g_list_model_get_item_type (model),
|
||||
"model", model,
|
||||
"sorter", sorter,
|
||||
NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_sort_list_model_new_for_type:
|
||||
* @item_type: the type of the items that will be returned
|
||||
*
|
||||
* Creates a new empty sort list model set up to return items of type @item_type.
|
||||
* It is up to the application to set a proper sort function and model to ensure
|
||||
* the item type is matched.
|
||||
*
|
||||
* Returns: a new #GtkSortListModel
|
||||
**/
|
||||
GtkSortListModel *
|
||||
gtk_sort_list_model_new_for_type (GType item_type)
|
||||
{
|
||||
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_SORT_LIST_MODEL,
|
||||
"item-type", item_type,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_create_sequences (GtkSortListModel *self)
|
||||
{
|
||||
if (self->sorter == NULL || self->model == NULL)
|
||||
return;
|
||||
|
||||
self->sorted = g_sequence_new (gtk_sort_list_entry_free);
|
||||
self->unsorted = g_sequence_new (NULL);
|
||||
|
||||
gtk_sort_list_model_add_items (self, 0, g_list_model_get_n_items (self->model), NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_sort_list_model_set_model:
|
||||
* @self: a #GtkSortListModel
|
||||
* @model: (allow-none): The model to be sorted
|
||||
*
|
||||
* Sets the model to be sorted. The @model's item type must conform to
|
||||
* the item type of @self.
|
||||
**/
|
||||
void
|
||||
gtk_sort_list_model_set_model (GtkSortListModel *self,
|
||||
GListModel *model)
|
||||
{
|
||||
guint removed, added;
|
||||
|
||||
g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
if (model)
|
||||
{
|
||||
g_return_if_fail (g_type_is_a (g_list_model_get_item_type (model), self->item_type));
|
||||
}
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
gtk_sort_list_model_clear_model (self);
|
||||
|
||||
if (model)
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sort_list_model_items_changed_cb), self);
|
||||
added = g_list_model_get_n_items (model);
|
||||
|
||||
gtk_sort_list_model_create_sequences (self);
|
||||
}
|
||||
else
|
||||
added = 0;
|
||||
|
||||
if (removed > 0 || added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_sort_list_model_get_model:
|
||||
* @self: a #GtkSortListModel
|
||||
*
|
||||
* Gets the model currently sorted or %NULL if none.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model that gets sorted
|
||||
**/
|
||||
GListModel *
|
||||
gtk_sort_list_model_get_model (GtkSortListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SORT_LIST_MODEL (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_sort_list_model_resort (GtkSortListModel *self)
|
||||
{
|
||||
guint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
|
||||
|
||||
if (self->sorted == NULL)
|
||||
return;
|
||||
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
if (n_items <= 1)
|
||||
return;
|
||||
|
||||
g_sequence_sort (self->sorted, _sort_func, self->sorter);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_sort_list_model_set_sorter:
|
||||
* @self: a #GtkSortListModel
|
||||
* @sorter: (allow-none): the #GtkSorter to sort @model with
|
||||
*
|
||||
* Sets a new sorter on @self.
|
||||
*/
|
||||
void
|
||||
gtk_sort_list_model_set_sorter (GtkSortListModel *self,
|
||||
GtkSorter *sorter)
|
||||
{
|
||||
guint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_SORT_LIST_MODEL (self));
|
||||
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
|
||||
|
||||
gtk_sort_list_model_clear_sorter (self);
|
||||
|
||||
if (sorter)
|
||||
{
|
||||
self->sorter = g_object_ref (sorter);
|
||||
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sort_list_model_sorter_changed_cb), self);
|
||||
}
|
||||
|
||||
g_clear_pointer (&self->unsorted, g_sequence_free);
|
||||
g_clear_pointer (&self->sorted, g_sequence_free);
|
||||
|
||||
gtk_sort_list_model_create_sequences (self);
|
||||
|
||||
n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
if (n_items > 1)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_sort_list_model_get_sorter:
|
||||
* @self: a #GtkSortLisTModel
|
||||
*
|
||||
* Gets the sorter that is used to sort @self.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): the sorter of #self
|
||||
*/
|
||||
GtkSorter *
|
||||
gtk_sort_list_model_get_sorter (GtkSortListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SORT_LIST_MODEL (self), NULL);
|
||||
|
||||
return self->sorter;
|
||||
}
|
61
src/gtklistmodels/gtksortlistmodel.h
Normal file
61
src/gtklistmodels/gtksortlistmodel.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright © 2018 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>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_SORT_LIST_MODEL_H__
|
||||
#define __GTK_SORT_LIST_MODEL_H__
|
||||
|
||||
|
||||
#define GTK_COMPILATION
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include "gtksorter.h"
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SORT_LIST_MODEL (gtk_sort_list_model_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkSortListModel, gtk_sort_list_model, GTK, SORT_LIST_MODEL, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSortListModel * gtk_sort_list_model_new (GListModel *model,
|
||||
GtkSorter *sorter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSortListModel * gtk_sort_list_model_new_for_type (GType item_type);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_sort_list_model_set_sorter (GtkSortListModel *self,
|
||||
GtkSorter *sorter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_sort_list_model_get_sorter (GtkSortListModel *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_sort_list_model_set_model (GtkSortListModel *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_sort_list_model_get_model (GtkSortListModel *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SORT_LIST_MODEL_H__ */
|
89
src/gtklistmodels/gtktypebuiltins.c
Normal file
89
src/gtklistmodels/gtktypebuiltins.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include <gtk/gtk.h>
|
||||
#include "gtkprivate.h"
|
||||
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/* enumerations from "gtksorter.h" */
|
||||
GType
|
||||
gtk_sorter_order_get_type (void)
|
||||
{
|
||||
static volatile gsize g_define_type_id__volatile = 0;
|
||||
|
||||
if (g_once_init_enter (&g_define_type_id__volatile))
|
||||
{
|
||||
static const GEnumValue values[] = {
|
||||
{ GTK_SORTER_ORDER_PARTIAL, "GTK_SORTER_ORDER_PARTIAL", "partial" },
|
||||
{ GTK_SORTER_ORDER_NONE, "GTK_SORTER_ORDER_NONE", "none" },
|
||||
{ GTK_SORTER_ORDER_TOTAL, "GTK_SORTER_ORDER_TOTAL", "total" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
GType g_define_type_id =
|
||||
g_enum_register_static (g_intern_static_string ("GtkSorterOrder"), values);
|
||||
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
|
||||
}
|
||||
|
||||
return g_define_type_id__volatile;
|
||||
}
|
||||
GType
|
||||
gtk_sorter_change_get_type (void)
|
||||
{
|
||||
static volatile gsize g_define_type_id__volatile = 0;
|
||||
|
||||
if (g_once_init_enter (&g_define_type_id__volatile))
|
||||
{
|
||||
static const GEnumValue values[] = {
|
||||
{ GTK_SORTER_CHANGE_DIFFERENT, "GTK_SORTER_CHANGE_DIFFERENT", "different" },
|
||||
{ GTK_SORTER_CHANGE_INVERTED, "GTK_SORTER_CHANGE_INVERTED", "inverted" },
|
||||
{ GTK_SORTER_CHANGE_LESS_STRICT, "GTK_SORTER_CHANGE_LESS_STRICT", "less-strict" },
|
||||
{ GTK_SORTER_CHANGE_MORE_STRICT, "GTK_SORTER_CHANGE_MORE_STRICT", "more-strict" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
GType g_define_type_id =
|
||||
g_enum_register_static (g_intern_static_string ("GtkSorterChange"), values);
|
||||
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
|
||||
}
|
||||
|
||||
return g_define_type_id__volatile;
|
||||
}
|
||||
|
||||
/* enumerations from "gtkfilter.h" */
|
||||
GType
|
||||
gtk_filter_match_get_type (void)
|
||||
{
|
||||
static volatile gsize g_define_type_id__volatile = 0;
|
||||
|
||||
if (g_once_init_enter (&g_define_type_id__volatile))
|
||||
{
|
||||
static const GEnumValue values[] = {
|
||||
{ GTK_FILTER_MATCH_SOME, "GTK_FILTER_MATCH_SOME", "some" },
|
||||
{ GTK_FILTER_MATCH_NONE, "GTK_FILTER_MATCH_NONE", "none" },
|
||||
{ GTK_FILTER_MATCH_ALL, "GTK_FILTER_MATCH_ALL", "all" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
GType g_define_type_id =
|
||||
g_enum_register_static (g_intern_static_string ("GtkFilterMatch"), values);
|
||||
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
|
||||
}
|
||||
|
||||
return g_define_type_id__volatile;
|
||||
}
|
||||
GType
|
||||
gtk_filter_change_get_type (void)
|
||||
{
|
||||
static volatile gsize g_define_type_id__volatile = 0;
|
||||
|
||||
if (g_once_init_enter (&g_define_type_id__volatile))
|
||||
{
|
||||
static const GEnumValue values[] = {
|
||||
{ GTK_FILTER_CHANGE_DIFFERENT, "GTK_FILTER_CHANGE_DIFFERENT", "different" },
|
||||
{ GTK_FILTER_CHANGE_LESS_STRICT, "GTK_FILTER_CHANGE_LESS_STRICT", "less-strict" },
|
||||
{ GTK_FILTER_CHANGE_MORE_STRICT, "GTK_FILTER_CHANGE_MORE_STRICT", "more-strict" },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
GType g_define_type_id =
|
||||
g_enum_register_static (g_intern_static_string ("GtkFilterChange"), values);
|
||||
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
|
||||
}
|
||||
|
||||
return g_define_type_id__volatile;
|
||||
}
|
18
src/gtklistmodels/gtktypebuiltins.h
Normal file
18
src/gtklistmodels/gtktypebuiltins.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <glib-object.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#include "gtksorter.h"
|
||||
#include "gtkfilter.h"
|
||||
|
||||
/* enumerations from "gtksorter.h" */
|
||||
GDK_AVAILABLE_IN_ALL GType gtk_sorter_order_get_type (void) G_GNUC_CONST;
|
||||
#define GTK_TYPE_SORTER_ORDER (gtk_sorter_order_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL GType gtk_sorter_change_get_type (void) G_GNUC_CONST;
|
||||
#define GTK_TYPE_SORTER_CHANGE (gtk_sorter_change_get_type ())
|
||||
|
||||
/* enumerations from "gtkfilter.h" */
|
||||
GDK_AVAILABLE_IN_ALL GType gtk_filter_match_get_type (void) G_GNUC_CONST;
|
||||
#define GTK_TYPE_FILTER_MATCH (gtk_filter_match_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL GType gtk_filter_change_get_type (void) G_GNUC_CONST;
|
||||
#define GTK_TYPE_FILTER_CHANGE (gtk_filter_change_get_type ())
|
||||
|
Loading…
Reference in a new issue