1
0
Fork 0
mirror of https://gitlab.gnome.org/GNOME/calls.git synced 2024-12-04 20:07:36 +00:00

gtklistmodels: remove gtklistmodels polyfills

Part-of: <https://gitlab.gnome.org/GNOME/calls/-/merge_requests/714>
This commit is contained in:
Anton Lazarev 2023-12-13 11:20:35 -08:00
parent d7504da0d2
commit bc90d6e64f
29 changed files with 7 additions and 5013 deletions

13
debian/copyright vendored
View file

@ -31,19 +31,6 @@ Comment:
From gnome-control-center, From gnome-control-center,
see https://bugzilla.gnome.org/show_bug.cgi?id=792243 see https://bugzilla.gnome.org/show_bug.cgi?id=792243
Files:
src/gtklistmodels/*
Copyright:
2018-2019 Benjamin Otte
2019 Matthias Clasen
License: LGPL-2.1+
Files:
src/gtklistmodels/gtkprivate.h
Copyright:
1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
License: LGPL-2+
Files: Files:
data/org.gnome.Calls.metainfo.xml data/org.gnome.Calls.metainfo.xml
Copyright: Copyright:

View file

@ -35,9 +35,6 @@
#include "calls-plugin-manager.h" #include "calls-plugin-manager.h"
#include "calls-util.h" #include "calls-util.h"
#include "gtkcustomfilter.h"
#include "gtkfilterlistmodel.h"
#include <glib/gi18n-lib.h> #include <glib/gi18n-lib.h>
@ -397,7 +394,7 @@ calls_account_overview_init (CallsAccountOverview *self)
gtk_widget_init_template (GTK_WIDGET (self)); gtk_widget_init_template (GTK_WIDGET (self));
self->account_provider_filter = gtk_custom_filter_new (match_account_provider, NULL, NULL); self->account_provider_filter = GTK_FILTER (gtk_custom_filter_new (match_account_provider, NULL, NULL));
self->providers = self->providers =
G_LIST_MODEL (gtk_filter_list_model_new (all_providers, G_LIST_MODEL (gtk_filter_list_model_new (all_providers,
self->account_provider_filter)); self->account_provider_filter));
@ -410,7 +407,7 @@ calls_account_overview_init (CallsAccountOverview *self)
0, 0, g_list_model_get_n_items (self->providers), 0, 0, g_list_model_get_n_items (self->providers),
self); self);
self->account_filter = gtk_custom_filter_new (match_account, NULL, NULL); self->account_filter = GTK_FILTER (gtk_custom_filter_new (match_account, NULL, NULL));
self->accounts = self->accounts =
G_LIST_MODEL (gtk_filter_list_model_new (all_origins, G_LIST_MODEL (gtk_filter_list_model_new (all_origins,
self->account_filter)); self->account_filter));

View file

@ -27,7 +27,6 @@
#include "calls-history-box.h" #include "calls-history-box.h"
#include "calls-call-record.h" #include "calls-call-record.h"
#include "calls-call-record-row.h" #include "calls-call-record-row.h"
#include "gtklistmodels/gtkmodels.h"
#include "calls-util.h" #include "calls-util.h"
#include <glib/gi18n.h> #include <glib/gi18n.h>

View file

@ -40,10 +40,6 @@
#include "enum-types.h" #include "enum-types.h"
#include "gtkcustomfilter.h"
#include "gtkfilterlistmodel.h"
#include "gtkflattenlistmodel.h"
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <libpeas/peas.h> #include <libpeas/peas.h>
@ -454,9 +450,6 @@ calls_manager_finalize (GObject *object)
g_clear_object (&self->origins); g_clear_object (&self->origins);
g_clear_object (&self->contacts_provider); g_clear_object (&self->contacts_provider);
g_clear_pointer (&self->origins_by_protocol, g_hash_table_unref);
g_clear_pointer (&self->dial_actions_by_protocol, g_hash_table_unref);
G_OBJECT_CLASS (calls_manager_parent_class)->finalize (object); G_OBJECT_CLASS (calls_manager_parent_class)->finalize (object);
} }
@ -599,7 +592,7 @@ calls_manager_init (CallsManager *self)
self->state_flags = CALLS_MANAGER_FLAGS_UNKNOWN; self->state_flags = CALLS_MANAGER_FLAGS_UNKNOWN;
self->origins = g_list_store_new (G_TYPE_LIST_MODEL); /* list of lists */ self->origins = g_list_store_new (G_TYPE_LIST_MODEL); /* list of lists */
self->origins_flat = gtk_flatten_list_model_new (CALLS_TYPE_ORIGIN, G_LIST_MODEL (self->origins)); self->origins_flat = gtk_flatten_list_model_new (G_LIST_MODEL (self->origins));
providers = calls_plugin_manager_get_providers (plugin_manager); providers = calls_plugin_manager_get_providers (plugin_manager);
g_signal_connect_object (providers, g_signal_connect_object (providers,
@ -616,8 +609,8 @@ calls_manager_init (CallsManager *self)
g_object_unref); g_object_unref);
for (guint i = 0; i < G_N_ELEMENTS (protocols); i++) { for (guint i = 0; i < G_N_ELEMENTS (protocols); i++) {
g_autoptr (GtkFilter) filter = GtkFilter* filter =
gtk_custom_filter_new (match_origin_supports_protocol, (gpointer) protocols[i], NULL); GTK_FILTER (gtk_custom_filter_new (match_origin_supports_protocol, (gpointer) protocols[i], NULL));
GtkFilterListModel *f_list = GtkFilterListModel *f_list =
gtk_filter_list_model_new (G_LIST_MODEL (self->origins_flat), filter); gtk_filter_list_model_new (G_LIST_MODEL (self->origins_flat), filter);

View file

@ -1,157 +0,0 @@
/*
* 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 "calls-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);
}

View file

@ -1,61 +0,0 @@
/*
* 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__ */

View file

@ -1,165 +0,0 @@
/*
* 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 "calls-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);
}

View file

@ -1,50 +0,0 @@
/*
* 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__ */

View file

@ -1,181 +0,0 @@
/*
* 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 "calls-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);
}

View file

@ -1,122 +0,0 @@
/*
* 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__ */

View file

@ -1,916 +0,0 @@
/*
* 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 "calls-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);
}
}

View file

@ -1,59 +0,0 @@
/*
* 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__ */

View file

@ -1,541 +0,0 @@
/*
* 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 "calls-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;
}

View file

@ -1,51 +0,0 @@
/*
* 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__ */

View file

@ -1,22 +0,0 @@
#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

View file

@ -1,8 +0,0 @@
#include "gtksorter.h"
#include "gtkcustomsorter.h"
#include "gtkfilter.h"
#include "gtkcustomfilter.h"
#include "gtksortlistmodel.h"
#include "gtkfilterlistmodel.h"
#include "gtkflattenlistmodel.h"
#include "gtkslicelistmodel.h"

View file

@ -1,38 +0,0 @@
/* 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__ */

View file

@ -1,800 +0,0 @@
/* 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 "calls-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;
}

View file

@ -1,75 +0,0 @@
/* 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__ */

View file

@ -1,529 +0,0 @@
/*
* 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 "calls-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;
}

View file

@ -1,65 +0,0 @@
/*
* 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__ */

View file

@ -1,207 +0,0 @@
/*
* 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 "calls-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);
}

View file

@ -1,131 +0,0 @@
/*
* 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__ */

View file

@ -1,595 +0,0 @@
/*
* 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 "calls-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;
}

View file

@ -1,61 +0,0 @@
/*
* 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__ */

View file

@ -1,89 +0,0 @@
#include <gtk/gtk.h>
#include "gtkprivate.h"
#include "gtktypebuiltins.h"
/* enumerations from "gtksorter.h" */
GType
gtk_sorter_order_get_type (void)
{
static 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 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 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 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;
}

View file

@ -1,18 +0,0 @@
#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 ())

View file

@ -1,39 +0,0 @@
#
# Copyright (C) 2022 Purism SPC
#
# This file is part of Calls.
#
# Calls is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Calls 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Calls. If not, see <http://www.gnu.org/licenses/>.
#
# Author: Evangelos Ribeiro Tzaras <devrtz@fortysixandtwo.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
gtklistmodel_sources = files([
'gtkmodels.h',
'gtkcustomfilter.c', 'gtkcustomfilter.h',
'gtkcustomsorter.c', 'gtkcustomsorter.h',
'gtkfilter.c', 'gtkfilter.h',
'gtkfilterlistmodel.c', 'gtkfilterlistmodel.h',
'gtkflattenlistmodel.c', 'gtkflattenlistmodel.h',
'gtkintl.h',
'gtkprivate.h',
'gtkrbtree.c',
'gtkrbtreeprivate.h',
'gtkslicelistmodel.c', 'gtkslicelistmodel.h',
'gtksorter.c', 'gtksorter.h',
'gtksortlistmodel.c', 'gtksortlistmodel.h',
'gtktypebuiltins.c', 'gtktypebuiltins.h',
])

View file

@ -26,7 +26,7 @@ gnome = import('gnome')
subdir('dbus') subdir('dbus')
src_include = include_directories('.', 'gtklistmodels') src_include = include_directories('.')
calls_includes = [ top_include, src_include ] calls_includes = [ top_include, src_include ]
calls_deps = [ dependency('gobject-2.0', version: '>= 2.58'), calls_deps = [ dependency('gobject-2.0', version: '>= 2.58'),
@ -86,8 +86,6 @@ calls_generated_sources = [
generated_dbus_sources, generated_dbus_sources,
] ]
subdir('gtklistmodels')
calls_sources = files([ calls_sources = files([
'calls-account.c', 'calls-account.h', 'calls-account.c', 'calls-account.h',
'calls-account-overview.c', 'calls-account-overview.h', 'calls-account-overview.c', 'calls-account-overview.h',
@ -126,7 +124,7 @@ calls_sources = files([
'calls-ui-call-data.c', 'calls-ui-call-data.h', 'calls-ui-call-data.c', 'calls-ui-call-data.h',
'calls-ussd.c', 'calls-ussd.h', 'calls-ussd.c', 'calls-ussd.h',
'calls-util.c', 'calls-util.h', 'calls-util.c', 'calls-util.h',
]) + calls_generated_sources + gtklistmodel_sources ]) + calls_generated_sources
calls_config_data = config_data calls_config_data = config_data