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:
parent
d7504da0d2
commit
bc90d6e64f
29 changed files with 7 additions and 5013 deletions
13
debian/copyright
vendored
13
debian/copyright
vendored
|
@ -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:
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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__ */
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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
|
|
|
@ -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"
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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__ */
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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__ */
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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__ */
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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 ())
|
|
||||||
|
|
|
@ -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',
|
|
||||||
])
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue