Compare commits

...

21 Commits

Author SHA1 Message Date
Matthias Clasen
8815e1bb0c Add another incremental sort model
This one uses a very simple iterative mergesort.
2020-07-08 01:59:52 -04:00
Benjamin Otte
a57e0a4bf6 Add a 4th sortmodel implementation
This one tracks the original positions on top of just the items so that
it can remove items.

It now takes twice as much memory but removes half of a million items in
50ms.
2020-07-08 04:46:55 +02:00
Matthias Clasen
b74b0edf0d wip: incremental sort 2020-07-08 04:32:42 +02:00
Benjamin Otte
36fac37cda testsuite: Add some sorting performance tests 2020-07-08 04:12:33 +02:00
Benjamin Otte
91e2661306 stringlist: Convert to array
Stringlists are usually built and then never modified, and accessing
items through an array is faster.
2020-07-08 04:12:33 +02:00
Benjamin Otte
559ea908de gtk: Add a 2nd sortmodel implementation
This is the dumbest possible sortmodel using an array:
Just grab all the items, put them in the array, qsort() the array.
2020-07-08 04:12:33 +02:00
Benjamin Otte
f0b146d7c8 demo: Add faster sorters
This is just the existing sorters, but without the overhead of GObject
properties.
2020-07-08 00:44:40 +02:00
Benjamin Otte
a030197974 sorter: Remove a return_if_fail()
It's too expsensive.
2020-07-08 00:44:40 +02:00
Benjamin Otte
bf2bf948ab xxx: vector_set_size 2020-07-08 00:44:40 +02:00
Benjamin Otte
5cb49363fa sortlistmodel: Remove forgotten G_PARAM_CONSTRUCT_ONLY 2020-07-08 00:44:40 +02:00
Benjamin Otte
802c99d4b4 testsuite: Add some vector performance tests
They're not very conclusive though, because the testing isn't
fine-grained enough for these microbenchmarks.
2020-07-07 02:23:09 +02:00
Benjamin Otte
173534710a snapshot: Use GtkVector for the state stack 2020-07-07 02:23:09 +02:00
Benjamin Otte
347959a112 vector: Add a bunch of new features
* GTK_VECTOR_BY_VALUE
   #define this to get GArrray-like behavior
 * gtk_vector_splice (v, 0, 0, NULL, 25)
   Adding items but passing NULL as the items will zero() them.
 * gtk_vector_set_size()
   A nicer way to call gtk_vector_splice()
2020-07-07 02:23:09 +02:00
Benjamin Otte
1ae939cf5a icontheme: Use GtkVector 2020-07-07 02:23:09 +02:00
Benjamin Otte
edf523fe30 vector: Add null-termination 2020-07-07 02:23:09 +02:00
Benjamin Otte
bfe2970a98 snapshot: Port node list to vector 2020-07-07 02:23:09 +02:00
Benjamin Otte
c7749b9098 snapshot: Move structs into .c file
They aren't used anywhere else.
2020-07-07 02:23:09 +02:00
Benjamin Otte
2226fe0fca Remove preallocated array code
Now with GtkVector, we can use that one instead.
2020-07-07 02:23:09 +02:00
Benjamin Otte
cbf9375f97 main: Use a GtkVector 2020-07-07 02:23:09 +02:00
Benjamin Otte
5297eb36b9 cssselector: Use GtkVector 2020-07-07 02:23:09 +02:00
Benjamin Otte
b69e7777cd Add GtkVector
This is a scary idea where you #define a bunch of preprocessor values
and then #include "gtkvectorimpl.c" and end up with a dynamic array for
that data type.
2020-07-07 02:23:09 +02:00
28 changed files with 3943 additions and 386 deletions

View File

@@ -676,7 +676,7 @@ create_color_grid (void)
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
model = G_LIST_MODEL (gtk_sort_list_model_new (gtk_color_list_new (0), NULL));
model = G_LIST_MODEL (gtk_sor5_list_model_new (gtk_color_list_new (0), NULL));
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
@@ -835,6 +835,54 @@ update_selection_average (GListModel *model,
g_object_unref (color);
}
static int
compare_red (gconstpointer a,
gconstpointer b,
gpointer unused)
{
GtkColor *colora = (GtkColor *) a;
GtkColor *colorb = (GtkColor *) b;
if (colora->color.red < colorb->color.red)
return GTK_ORDERING_LARGER;
else if (colora->color.red > colorb->color.red)
return GTK_ORDERING_SMALLER;
else
return GTK_ORDERING_EQUAL;
}
static int
compare_green (gconstpointer a,
gconstpointer b,
gpointer unused)
{
GtkColor *colora = (GtkColor *) a;
GtkColor *colorb = (GtkColor *) b;
if (colora->color.green < colorb->color.green)
return GTK_ORDERING_LARGER;
else if (colora->color.green > colorb->color.green)
return GTK_ORDERING_SMALLER;
else
return GTK_ORDERING_EQUAL;
}
static int
compare_blue (gconstpointer a,
gconstpointer b,
gpointer unused)
{
GtkColor *colora = (GtkColor *) a;
GtkColor *colorb = (GtkColor *) b;
if (colora->color.blue < colorb->color.blue)
return GTK_ORDERING_LARGER;
else if (colora->color.blue > colorb->color.blue)
return GTK_ORDERING_SMALLER;
else
return GTK_ORDERING_EQUAL;
}
static GtkWidget *window = NULL;
GtkWidget *
@@ -964,7 +1012,7 @@ do_listview_colors (GtkWidget *do_widget)
button = gtk_button_new_with_mnemonic ("_Refill");
g_signal_connect (button, "clicked",
G_CALLBACK (refill),
gtk_sort_list_model_get_model (GTK_SORT_LIST_MODEL (model)));
gtk_sor5_list_model_get_model (GTK_SOR5_LIST_MODEL (model)));
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
@@ -987,7 +1035,7 @@ do_listview_colors (GtkWidget *do_widget)
gtk_drop_down_set_from_strings (GTK_DROP_DOWN (dropdown), (const char *[]) { "8", "64", "512", "4096", "32768", "262144", "2097152", "16777216", NULL });
g_signal_connect (dropdown, "notify::selected",
G_CALLBACK (limit_changed_cb),
gtk_sort_list_model_get_model (GTK_SORT_LIST_MODEL (model)));
gtk_sor5_list_model_get_model (GTK_SOR5_LIST_MODEL (model)));
g_signal_connect (dropdown, "notify::selected",
G_CALLBACK (limit_changed_cb2),
label);
@@ -1022,18 +1070,30 @@ do_listview_colors (GtkWidget *do_widget)
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
sorter = gtk_custom_sorter_new (compare_red, NULL, NULL);
set_title (sorter, "Red (fast)");
g_list_store_append (sorters, sorter);
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "green"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Green");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
sorter = gtk_custom_sorter_new (compare_green, NULL, NULL);
set_title (sorter, "Green (fast)");
g_list_store_append (sorters, sorter);
sorter = gtk_numeric_sorter_new (gtk_property_expression_new (GTK_TYPE_COLOR, NULL, "blue"));
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
set_title (sorter, "Blue");
g_list_store_append (sorters, sorter);
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), sorter);
sorter = gtk_custom_sorter_new (compare_blue, NULL, NULL);
set_title (sorter, "Blue (fast)");
g_list_store_append (sorters, sorter);
set_title (multi_sorter, "RGB");
g_list_store_append (sorters, multi_sorter);
g_object_unref (multi_sorter);

View File

@@ -233,6 +233,10 @@
#include <gtk/gtkslicelistmodel.h>
#include <gtk/gtksnapshot.h>
#include <gtk/gtksorter.h>
#include <gtk/gtksor2listmodel.h>
#include <gtk/gtksor3listmodel.h>
#include <gtk/gtksor4listmodel.h>
#include <gtk/gtksor5listmodel.h>
#include <gtk/gtksortlistmodel.h>
#include <gtk/gtkstacksidebar.h>
#include <gtk/gtksizegroup.h>

View File

@@ -1,137 +0,0 @@
#ifndef __GTK_ARRAY_IMPL_PRIVATE_H__
#define __GTK_ARRAY_IMPL_PRIVATE_H__
/* This is a dumbed-down GPtrArray, which takes some stack
* space to use. When using this, the general case should always
* be that the number of elements is lower than reserved_size.
* The GPtrArray should only be used in extreme cases.
*/
typedef struct
{
guint reserved_size;
guint len;
void **stack_space;
GPtrArray *ptr_array;
} GtkArray;
static inline void
gtk_array_init (GtkArray *self,
void **stack_space,
guint reserved_size)
{
self->reserved_size = reserved_size;
self->len = 0;
self->stack_space = stack_space;
self->ptr_array = NULL;
}
static inline void *
gtk_array_index (const GtkArray *self,
guint index)
{
g_assert (index < self->len);
if (G_LIKELY (!self->ptr_array))
return self->stack_space[index];
return g_ptr_array_index (self->ptr_array, index);
}
static inline void
gtk_array_add (GtkArray *self,
void *element)
{
if (G_LIKELY (self->len < self->reserved_size))
{
self->stack_space[self->len] = element;
self->len++;
return;
}
/* Need to fall back to the GPtrArray */
if (G_UNLIKELY (!self->ptr_array))
{
self->ptr_array = g_ptr_array_new_full (self->len + 1, NULL);
memcpy (self->ptr_array->pdata, self->stack_space, sizeof (void *) * self->len);
self->ptr_array->len = self->len;
}
g_ptr_array_add (self->ptr_array, element);
self->len++; /* We still count self->len */
}
static inline void
gtk_array_insert (GtkArray *self,
guint index,
void *element)
{
if (index >= self->len)
{
gtk_array_add (self, element);
return;
}
if (G_LIKELY (self->len < self->reserved_size))
{
memmove (self->stack_space + index + 1, self->stack_space + index,
sizeof (void *) * (self->len - index));
self->stack_space[index] = element;
self->len++;
return;
}
if (G_UNLIKELY (!self->ptr_array))
{
self->ptr_array = g_ptr_array_new_full (self->len + 1, NULL);
memcpy (self->ptr_array->pdata, self->stack_space, sizeof (void *) * self->len);
self->ptr_array->len = self->len;
}
g_assert (self->ptr_array);
g_ptr_array_insert (self->ptr_array, index, element);
self->len++;
}
static inline void
gtk_array_free (GtkArray *self,
GDestroyNotify element_free_func)
{
guint i;
if (G_LIKELY (!self->ptr_array))
{
if (element_free_func)
{
for (i = 0; i < self->len; i++)
element_free_func (self->stack_space[i]);
}
return;
}
g_assert (self->ptr_array);
if (element_free_func)
{
for (i = 0; i < self->ptr_array->len; i++)
element_free_func (g_ptr_array_index (self->ptr_array, i));
}
g_ptr_array_free (self->ptr_array, TRUE);
}
static inline void **
gtk_array_get_data (GtkArray *self)
{
if (G_LIKELY (!self->ptr_array))
return self->stack_space;
return self->ptr_array->pdata;
}
#endif

View File

@@ -391,9 +391,9 @@ gtk_css_provider_init (GtkCssProvider *css_provider)
}
static void
verify_tree_match_results (GtkCssProvider *provider,
GtkCssNode *node,
GtkArray *tree_rules)
verify_tree_match_results (GtkCssProvider *provider,
GtkCssNode *node,
GtkCssSelectorMatches *tree_rules)
{
#ifdef VERIFY_TREE
GtkCssProviderPrivate *priv = gtk_css_provider_get_instance_private (provider);
@@ -407,9 +407,9 @@ verify_tree_match_results (GtkCssProvider *provider,
ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
for (j = 0; j < tree_rules->len; j++)
for (j = 0; j < gtk_css_selector_matches_get_size (tree_rules); j++)
{
if (ruleset == gtk_array_index (tree_rules, j))
if (ruleset == gtk_css_selector_matches_get (tree_rules, j))
{
found = TRUE;
break;
@@ -459,22 +459,21 @@ gtk_css_style_provider_lookup (GtkStyleProvider *provider,
GtkCssRuleset *ruleset;
guint j;
int i;
GtkArray tree_rules_array;
GtkCssRuleset *rules_stack[32];
GtkCssSelectorMatches tree_rules;
if (_gtk_css_selector_tree_is_empty (priv->tree))
return;
gtk_array_init (&tree_rules_array, (void**)rules_stack, 32);
_gtk_css_selector_tree_match_all (priv->tree, filter, node, &tree_rules_array);
gtk_css_selector_matches_init (&tree_rules);
_gtk_css_selector_tree_match_all (priv->tree, filter, node, &tree_rules);
if (tree_rules_array.len > 0)
if (!gtk_css_selector_matches_is_empty (&tree_rules))
{
verify_tree_match_results (css_provider, node, &tree_rules_array);
verify_tree_match_results (css_provider, node, &tree_rules);
for (i = tree_rules_array.len - 1; i >= 0; i--)
for (i = gtk_css_selector_matches_get_size (&tree_rules) - 1; i >= 0; i--)
{
ruleset = gtk_array_index (&tree_rules_array, i);
ruleset = gtk_css_selector_matches_get (&tree_rules, i);
if (ruleset->styles == NULL)
continue;
@@ -493,9 +492,8 @@ gtk_css_style_provider_lookup (GtkStyleProvider *provider,
ruleset->styles[j].value);
}
}
gtk_array_free (&tree_rules_array, NULL);
}
gtk_css_selector_matches_clear (&tree_rules);
if (change)
*change = gtk_css_selector_tree_get_change_all (priv->tree, filter, node);

View File

@@ -24,7 +24,6 @@
#include "gtkcssprovider.h"
#include "gtkstylecontextprivate.h"
#include "gtkarrayimplprivate.h"
#include <errno.h>
#if defined(_MSC_VER) && _MSC_VER >= 1500
@@ -152,14 +151,14 @@ gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
}
static void
gtk_array_insert_sorted (GtkArray *array,
gpointer data)
gtk_css_selector_matches_insert_sorted (GtkCssSelectorMatches *matches,
gpointer data)
{
guint i;
for (i = 0; i < array->len; i++)
for (i = 0; i < gtk_css_selector_matches_get_size (matches); i++)
{
gpointer elem = gtk_array_index (array, i);
gpointer elem = gtk_css_selector_matches_get (matches, i);
if (data == elem)
return;
@@ -168,7 +167,7 @@ gtk_array_insert_sorted (GtkArray *array,
break;
}
gtk_array_insert (array, i, data);
gtk_css_selector_matches_splice (matches, i, 0, (gpointer[1]) { data }, 1);
}
static inline gboolean
@@ -1877,7 +1876,7 @@ gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
static void
gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
GtkArray *results)
GtkCssSelectorMatches *results)
{
int i;
gpointer *matches;
@@ -1887,7 +1886,7 @@ gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
return;
for (i = 0; matches[i] != NULL; i++)
gtk_array_insert_sorted (results, matches[i]);
gtk_css_selector_matches_insert_sorted (results, matches[i]);
}
static gboolean
@@ -1895,7 +1894,7 @@ gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
const GtkCountingBloomFilter *filter,
gboolean match_filter,
GtkCssNode *node,
GtkArray *results)
GtkCssSelectorMatches *results)
{
const GtkCssSelectorTree *prev;
GtkCssNode *child;
@@ -1932,7 +1931,7 @@ void
_gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
const GtkCountingBloomFilter *filter,
GtkCssNode *node,
GtkArray *out_tree_rules)
GtkCssSelectorMatches *out_tree_rules)
{
const GtkCssSelectorTree *iter;
@@ -2117,8 +2116,7 @@ subdivide_infos (GByteArray *array,
GHashTableIter iter;
guint max_count;
gpointer key, value;
void *exact_matches_stack[8];
GtkArray exact_matches_array;
GtkCssSelectorMatches exact_matches;
gint32 res;
guint i;
@@ -2160,7 +2158,7 @@ subdivide_infos (GByteArray *array,
matched_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
remaining_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
gtk_array_init (&exact_matches_array, (void**)exact_matches_stack, 8);
gtk_css_selector_matches_init (&exact_matches);
for (i = 0; i < n_infos; i++)
{
GtkCssSelectorRuleSetInfo *info = infos[i];
@@ -2171,7 +2169,7 @@ subdivide_infos (GByteArray *array,
if (info->current_selector == NULL)
{
/* Matches current node */
gtk_array_add (&exact_matches_array, info->match);
gtk_css_selector_matches_append (&exact_matches, info->match);
if (info->selector_match != NULL)
*info->selector_match = GUINT_TO_POINTER (tree_offset);
}
@@ -2188,17 +2186,16 @@ subdivide_infos (GByteArray *array,
}
}
if (exact_matches_array.len > 0)
if (!gtk_css_selector_matches_is_empty (&exact_matches))
{
gtk_array_add (&exact_matches_array, NULL); /* Null terminate */
gtk_css_selector_matches_append (&exact_matches, NULL); /* Null terminate */
res = array->len;
g_byte_array_append (array, (guint8 *)gtk_array_get_data (&exact_matches_array),
exact_matches_array.len * sizeof (gpointer));
gtk_array_free (&exact_matches_array, NULL);
g_byte_array_append (array, (guint8 *) gtk_css_selector_matches_get_data (&exact_matches),
gtk_css_selector_matches_get_size (&exact_matches) * sizeof (gpointer));
}
else
res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
gtk_css_selector_matches_clear (&exact_matches);
get_tree (array, tree_offset)->matches_offset = res;
res = subdivide_infos (array, matched_infos, n_matched, tree_offset);

View File

@@ -21,7 +21,12 @@
#include "gtk/gtkcountingbloomfilterprivate.h"
#include "gtk/gtkcsstypesprivate.h"
#include "gtk/gtkcssparserprivate.h"
#include "gtk/gtkarrayimplprivate.h"
#define GTK_VECTOR_ELEMENT_TYPE gpointer
#define GTK_VECTOR_TYPE_NAME GtkCssSelectorMatches
#define GTK_VECTOR_NAME gtk_css_selector_matches
#define GTK_VECTOR_PREALLOC 32
#include "gtk/gtkvectorimpl.c"
G_BEGIN_DECLS
@@ -45,8 +50,8 @@ int _gtk_css_selector_compare (const GtkCssSelector *a,
void _gtk_css_selector_tree_free (GtkCssSelectorTree *tree);
void _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
const GtkCountingBloomFilter *filter,
GtkCssNode *node,
GtkArray *out_tree_rules);
GtkCssNode *node,
GtkCssSelectorMatches *out_tree_rules);
GtkCssChange gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
const GtkCountingBloomFilter *filter,
GtkCssNode *node);

View File

@@ -55,6 +55,14 @@
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkprofilerprivate.h"
#define GTK_VECTOR_ELEMENT_TYPE char *
#define GTK_VECTOR_NULL_TERMINATED 1
#define GTK_VECTOR_FREE_FUNC g_free
#define GTK_VECTOR_TYPE_NAME GtkStrvBuilder
#define GTK_VECTOR_NAME gtk_strv_builder
#define GTK_VECTOR_PREALLOC 16
#include "gtkvectorimpl.c"
/**
* SECTION:gtkicontheme
* @Short_description: Looking up icons by name
@@ -2276,13 +2284,13 @@ real_choose_icon (GtkIconTheme *self,
}
static void
icon_name_list_add_icon (GPtrArray *icons,
const gchar *dir_suffix,
gchar *icon_name)
icon_name_list_add_icon (GtkStrvBuilder *icons,
const gchar *dir_suffix,
gchar *icon_name)
{
if (dir_suffix)
g_ptr_array_add (icons, g_strconcat (icon_name, dir_suffix, NULL));
g_ptr_array_add (icons, icon_name);
gtk_strv_builder_append (icons, g_strconcat (icon_name, dir_suffix, NULL));
gtk_strv_builder_append (icons, icon_name);
}
static GtkIconPaintable *
@@ -2296,7 +2304,7 @@ choose_icon (GtkIconTheme *self,
{
gboolean has_regular = FALSE, has_symbolic = FALSE;
GtkIconPaintable *icon;
GPtrArray *new_names;
GtkStrvBuilder new_names;
const gchar *dir_suffix;
guint i;
@@ -2327,73 +2335,70 @@ choose_icon (GtkIconTheme *self,
if ((flags & GTK_ICON_LOOKUP_FORCE_REGULAR) && has_symbolic)
{
new_names = g_ptr_array_new_with_free_func (g_free);
gtk_strv_builder_init (&new_names);
for (i = 0; icon_names[i]; i++)
{
if (icon_name_is_symbolic (icon_names[i], -1))
icon_name_list_add_icon (new_names, dir_suffix, g_strndup (icon_names[i], strlen (icon_names[i]) - strlen ("-symbolic")));
icon_name_list_add_icon (&new_names, dir_suffix, g_strndup (icon_names[i], strlen (icon_names[i]) - strlen ("-symbolic")));
else
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
}
for (i = 0; icon_names[i]; i++)
{
if (icon_name_is_symbolic (icon_names[i], -1))
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
}
g_ptr_array_add (new_names, NULL);
icon = real_choose_icon (self,
(const gchar **) new_names->pdata,
(const char **) gtk_strv_builder_get_data (&new_names),
size,
scale,
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
non_blocking);
g_ptr_array_free (new_names, TRUE);
gtk_strv_builder_clear (&new_names);
}
else if ((flags & GTK_ICON_LOOKUP_FORCE_SYMBOLIC) && has_regular)
{
new_names = g_ptr_array_new_with_free_func (g_free);
gtk_strv_builder_init (&new_names);
for (i = 0; icon_names[i]; i++)
{
if (!icon_name_is_symbolic (icon_names[i], -1))
icon_name_list_add_icon (new_names, dir_suffix, g_strconcat (icon_names[i], "-symbolic", NULL));
icon_name_list_add_icon (&new_names, dir_suffix, g_strconcat (icon_names[i], "-symbolic", NULL));
else
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
}
for (i = 0; icon_names[i]; i++)
{
if (!icon_name_is_symbolic (icon_names[i], -1))
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
}
g_ptr_array_add (new_names, NULL);
icon = real_choose_icon (self,
(const gchar **) new_names->pdata,
(const char **) gtk_strv_builder_get_data (&new_names),
size,
scale,
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
non_blocking);
g_ptr_array_free (new_names, TRUE);
gtk_strv_builder_clear (&new_names);
}
else if (dir_suffix)
{
new_names = g_ptr_array_new_with_free_func (g_free);
gtk_strv_builder_init (&new_names);
for (i = 0; icon_names[i]; i++)
{
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
}
g_ptr_array_add (new_names, NULL);
icon = real_choose_icon (self,
(const gchar **) new_names->pdata,
(const char **) gtk_strv_builder_get_data (&new_names),
size,
scale,
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
non_blocking);
g_ptr_array_free (new_names, TRUE);
gtk_strv_builder_clear (&new_names);
}
else
{

View File

@@ -95,7 +95,6 @@
#include "gdk/gdk-private.h"
#include "gsk/gskprivate.h"
#include "gsk/gskrendernodeprivate.h"
#include "gtkarrayimplprivate.h"
#include "gtknative.h"
#include <locale.h>
@@ -138,6 +137,13 @@
#include "a11y/gtkaccessibility.h"
#include "inspector/window.h"
#define GTK_VECTOR_ELEMENT_TYPE GtkWidget *
#define GTK_VECTOR_TYPE_NAME GtkWidgetStack
#define GTK_VECTOR_NAME gtk_widget_stack
#define GTK_VECTOR_FREE_FUNC g_object_unref
#define GTK_VECTOR_PREALLOC 16
#include "gtkvectorimpl.c"
static GtkWindowGroup *gtk_main_get_window_group (GtkWidget *widget);
static gint pre_initialized = FALSE;
@@ -1321,8 +1327,7 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
double x, y;
GtkWidget *prev;
gboolean seen_ancestor;
GtkArray target_array;
GtkWidget *stack_targets[16];
GtkWidgetStack target_array;
int i;
if (old_target == new_target)
@@ -1376,19 +1381,19 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
widget = _gtk_widget_get_parent (widget);
}
gtk_array_init (&target_array, (void**)stack_targets, 16);
gtk_widget_stack_init (&target_array);
for (widget = new_target; widget; widget = _gtk_widget_get_parent (widget))
gtk_array_add (&target_array, widget);
gtk_widget_stack_append (&target_array, g_object_ref (widget));
crossing.direction = GTK_CROSSING_IN;
seen_ancestor = FALSE;
for (i = (int)target_array.len - 1; i >= 0; i--)
for (i = gtk_widget_stack_get_size (&target_array) - 1; i >= 0; i--)
{
widget = gtk_array_index (&target_array, i);
widget = gtk_widget_stack_get (&target_array, i);
if (i < (int)target_array.len - 1)
crossing.new_descendent = gtk_array_index (&target_array, i + 1);
if (i < gtk_widget_stack_get_size (&target_array) - 1)
crossing.new_descendent = gtk_widget_stack_get (&target_array, i + 1);
else
crossing.new_descendent = NULL;
@@ -1417,7 +1422,7 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
}
gtk_array_free (&target_array, NULL);
gtk_widget_stack_clear (&target_array);
}
static GtkWidget *
@@ -1994,13 +1999,12 @@ gtk_propagate_event_internal (GtkWidget *widget,
{
gint handled_event = FALSE;
GtkWidget *target = widget;
GtkArray widget_array;
GtkWidget *stack_widgets[16];
GtkWidgetStack widget_array;
int i;
/* First, propagate event down */
gtk_array_init (&widget_array, (void**)stack_widgets, 16);
gtk_array_add (&widget_array, g_object_ref (widget));
gtk_widget_stack_init (&widget_array);
gtk_widget_stack_append (&widget_array, g_object_ref (widget));
for (;;)
{
@@ -2008,16 +2012,16 @@ gtk_propagate_event_internal (GtkWidget *widget,
if (!widget)
break;
gtk_array_add (&widget_array, g_object_ref (widget));
gtk_widget_stack_append (&widget_array, g_object_ref (widget));
if (widget == topmost)
break;
}
i = widget_array.len - 1;
i = gtk_widget_stack_get_size (&widget_array) - 1;
for (;;)
{
widget = gtk_array_index (&widget_array, i);
widget = gtk_widget_stack_get (&widget_array, i);
if (!_gtk_widget_is_sensitive (widget))
{
@@ -2050,9 +2054,9 @@ gtk_propagate_event_internal (GtkWidget *widget,
* parents can see the button and motion
* events of the children.
*/
for (i = 0; i < widget_array.len; i++)
for (i = 0; i < gtk_widget_stack_get_size (&widget_array); i++)
{
widget = gtk_array_index (&widget_array, i);
widget = gtk_widget_stack_get (&widget_array, i);
/* Scroll events are special cased here because it
* feels wrong when scrolling a GtkViewport, say,
@@ -2071,7 +2075,7 @@ gtk_propagate_event_internal (GtkWidget *widget,
}
}
gtk_array_free (&widget_array, g_object_unref);
gtk_widget_stack_clear (&widget_array);
return handled_event;
}

View File

@@ -34,6 +34,11 @@
#include "gtk/gskpango.h"
#define GTK_VECTOR_NAME gtk_snapshot_nodes
#define GTK_VECTOR_TYPE_NAME GtkSnapshotNodes
#define GTK_VECTOR_ELEMENT_TYPE GskRenderNode *
#define GTK_VECTOR_FREE_FUNC gsk_render_node_unref
#include "gtkvectorimpl.c"
/**
* SECTION:gtksnapshot
@@ -54,6 +59,85 @@
* use gtk_snapshot_new().
*/
typedef struct _GtkSnapshotState GtkSnapshotState;
typedef GskRenderNode * (* GtkSnapshotCollectFunc) (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes);
struct _GtkSnapshotState {
guint start_node_index;
guint n_nodes;
GskTransform * transform;
GtkSnapshotCollectFunc collect_func;
union {
struct {
double opacity;
} opacity;
struct {
double radius;
} blur;
struct {
graphene_matrix_t matrix;
graphene_vec4_t offset;
} color_matrix;
struct {
graphene_rect_t bounds;
graphene_rect_t child_bounds;
} repeat;
struct {
graphene_rect_t bounds;
} clip;
struct {
GskRoundedRect bounds;
} rounded_clip;
struct {
gsize n_shadows;
GskShadow *shadows;
GskShadow a_shadow; /* Used if n_shadows == 1 */
} shadow;
struct {
GskBlendMode blend_mode;
GskRenderNode *bottom_node;
} blend;
struct {
double progress;
GskRenderNode *start_node;
} cross_fade;
struct {
char *message;
} debug;
} data;
};
static void gtk_snapshot_state_clear (GtkSnapshotState *state);
#define GTK_VECTOR_NAME gtk_snapshot_states
#define GTK_VECTOR_TYPE_NAME GtkSnapshotStates
#define GTK_VECTOR_ELEMENT_TYPE GtkSnapshotState
#define GTK_VECTOR_FREE_FUNC gtk_snapshot_state_clear
#define GTK_VECTOR_BY_VALUE 1
#include "gtkvectorimpl.c"
/* This is a nasty little hack. We typedef GtkSnapshot to the fake object GdkSnapshot
* so that we don't need to typecast between them.
* After all, the GdkSnapshot only exist so poor language bindings don't trip. Hardcore
* C code can just blatantly ignore such layering violations with a typedef.
*/
struct _GdkSnapshot {
GObject parent_instance; /* it's really GdkSnapshot, but don't tell anyone! */
GtkSnapshotStates state_stack;
GtkSnapshotNodes nodes;
};
struct _GtkSnapshotClass {
GObjectClass parent_class; /* it's really GdkSnapshotClass, but don't tell anyone! */
};
G_DEFINE_TYPE (GtkSnapshot, gtk_snapshot, GDK_TYPE_SNAPSHOT)
static void
@@ -61,11 +145,11 @@ gtk_snapshot_dispose (GObject *object)
{
GtkSnapshot *snapshot = GTK_SNAPSHOT (object);
if (snapshot->state_stack)
if (!gtk_snapshot_states_is_empty (&snapshot->state_stack))
gsk_render_node_unref (gtk_snapshot_to_node (snapshot));
g_assert (snapshot->state_stack == NULL);
g_assert (snapshot->nodes == NULL);
g_assert (gtk_snapshot_states_is_empty (&snapshot->state_stack));
g_assert (gtk_snapshot_nodes_is_empty (&snapshot->nodes));
G_OBJECT_CLASS (gtk_snapshot_parent_class)->dispose (object);
}
@@ -112,15 +196,15 @@ gtk_snapshot_push_state (GtkSnapshot *snapshot,
GskTransform *transform,
GtkSnapshotCollectFunc collect_func)
{
const gsize n_states = snapshot->state_stack->len;
const gsize n_states = gtk_snapshot_states_get_size (&snapshot->state_stack);
GtkSnapshotState *state;
g_array_set_size (snapshot->state_stack, n_states + 1);
state = &g_array_index (snapshot->state_stack, GtkSnapshotState, n_states);
gtk_snapshot_states_set_size (&snapshot->state_stack, n_states + 1);
state = gtk_snapshot_states_get (&snapshot->state_stack, n_states);
state->transform = gsk_transform_ref (transform);
state->collect_func = collect_func;
state->start_node_index = snapshot->nodes->len;
state->start_node_index = gtk_snapshot_nodes_get_size (&snapshot->nodes);
state->n_nodes = 0;
return state;
@@ -129,17 +213,21 @@ gtk_snapshot_push_state (GtkSnapshot *snapshot,
static GtkSnapshotState *
gtk_snapshot_get_current_state (const GtkSnapshot *snapshot)
{
g_assert (snapshot->state_stack->len > 0);
gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
return &g_array_index (snapshot->state_stack, GtkSnapshotState, snapshot->state_stack->len - 1);
g_assert (size > 0);
return gtk_snapshot_states_get (&snapshot->state_stack, size - 1);
}
static GtkSnapshotState *
gtk_snapshot_get_previous_state (const GtkSnapshot *snapshot)
{
g_assert (snapshot->state_stack->len > 1);
gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
return &g_array_index (snapshot->state_stack, GtkSnapshotState, snapshot->state_stack->len - 2);
g_assert (size > 1);
return gtk_snapshot_states_get (&snapshot->state_stack, size - 2);
}
static void
@@ -162,9 +250,8 @@ gtk_snapshot_new (void)
snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL);
snapshot->state_stack = g_array_new (FALSE, TRUE, sizeof (GtkSnapshotState));
g_array_set_clear_func (snapshot->state_stack, (GDestroyNotify)gtk_snapshot_state_clear);
snapshot->nodes = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_render_node_unref);
gtk_snapshot_states_init (&snapshot->state_stack);
gtk_snapshot_nodes_init (&snapshot->nodes);
gtk_snapshot_push_state (snapshot,
NULL,
@@ -1029,30 +1116,28 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
guint state_index;
GskRenderNode *node;
if (snapshot->state_stack->len == 0)
if (gtk_snapshot_states_is_empty (&snapshot->state_stack))
{
g_warning ("Too many gtk_snapshot_pop() calls.");
return NULL;
}
state = gtk_snapshot_get_current_state (snapshot);
state_index = snapshot->state_stack->len - 1;
state_index = gtk_snapshot_states_get_size (&snapshot->state_stack) - 1;
if (state->collect_func)
{
node = state->collect_func (snapshot,
state,
(GskRenderNode **) snapshot->nodes->pdata + state->start_node_index,
(GskRenderNode **) gtk_snapshot_nodes_index (&snapshot->nodes, state->start_node_index),
state->n_nodes);
/* The collect func may not modify the state stack... */
g_assert (state_index == snapshot->state_stack->len - 1);
g_assert (state_index == gtk_snapshot_states_get_size (&snapshot->state_stack) - 1);
/* Remove all the state's nodes from the list of nodes */
g_assert (state->start_node_index + state->n_nodes == snapshot->nodes->len);
g_ptr_array_remove_range (snapshot->nodes,
snapshot->nodes->len - state->n_nodes,
state->n_nodes);
g_assert (state->start_node_index + state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
gtk_snapshot_nodes_splice (&snapshot->nodes, state->start_node_index, state->n_nodes, NULL, 0);
}
else
{
@@ -1063,10 +1148,10 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
/* move the nodes to the parent */
previous_state = gtk_snapshot_get_previous_state (snapshot);
previous_state->n_nodes += state->n_nodes;
g_assert (previous_state->start_node_index + previous_state->n_nodes == snapshot->nodes->len);
g_assert (previous_state->start_node_index + previous_state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
}
g_array_remove_index (snapshot->state_stack, state_index);
gtk_snapshot_states_splice (&snapshot->state_stack, state_index, 1, NULL, 0);
return node;
}
@@ -1081,7 +1166,7 @@ gtk_snapshot_append_node_internal (GtkSnapshot *snapshot,
if (current_state)
{
g_ptr_array_add (snapshot->nodes, node);
gtk_snapshot_nodes_append (&snapshot->nodes, node);
current_state->n_nodes ++;
}
else
@@ -1162,16 +1247,14 @@ gtk_snapshot_to_node (GtkSnapshot *snapshot)
result = gtk_snapshot_pop_internal (snapshot);
/* We should have exactly our initial state */
if (snapshot->state_stack->len > 0)
if (!gtk_snapshot_states_is_empty (&snapshot->state_stack))
{
g_warning ("Too many gtk_snapshot_push() calls. %u states remaining.", snapshot->state_stack->len);
g_warning ("Too many gtk_snapshot_push() calls. %zu states remaining.",
gtk_snapshot_states_get_size (&snapshot->state_stack));
}
g_array_free (snapshot->state_stack, TRUE);
g_ptr_array_free (snapshot->nodes, TRUE);
snapshot->state_stack = NULL;
snapshot->nodes = NULL;
gtk_snapshot_states_clear (&snapshot->state_stack);
gtk_snapshot_nodes_clear (&snapshot->nodes);
return result;
}

View File

@@ -24,76 +24,6 @@
G_BEGIN_DECLS
typedef struct _GtkSnapshotState GtkSnapshotState;
typedef GskRenderNode * (* GtkSnapshotCollectFunc) (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes);
struct _GtkSnapshotState {
guint start_node_index;
guint n_nodes;
GskTransform * transform;
GtkSnapshotCollectFunc collect_func;
union {
struct {
double opacity;
} opacity;
struct {
double radius;
} blur;
struct {
graphene_matrix_t matrix;
graphene_vec4_t offset;
} color_matrix;
struct {
graphene_rect_t bounds;
graphene_rect_t child_bounds;
} repeat;
struct {
graphene_rect_t bounds;
} clip;
struct {
GskRoundedRect bounds;
} rounded_clip;
struct {
gsize n_shadows;
GskShadow *shadows;
GskShadow a_shadow; /* Used if n_shadows == 1 */
} shadow;
struct {
GskBlendMode blend_mode;
GskRenderNode *bottom_node;
} blend;
struct {
double progress;
GskRenderNode *start_node;
} cross_fade;
struct {
char *message;
} debug;
} data;
};
/* This is a nasty little hack. We typedef GtkSnapshot to the fake object GdkSnapshot
* so that we don't need to typecast between them.
* After all, the GdkSnapshot only exist so poor language bindings don't trip. Hardcore
* C code can just blatantly ignore such layering violations with a typedef.
*/
struct _GdkSnapshot {
GObject parent_instance; /* it's really GdkSnapshot, but don't tell anyone! */
GArray *state_stack;
GPtrArray *nodes;
};
struct _GtkSnapshotClass {
GObjectClass parent_class; /* it's really GdkSnapshotClass, but don't tell anyone! */
};
void gtk_snapshot_append_text (GtkSnapshot *snapshot,
PangoFont *font,
PangoGlyphString *glyphs,

450
gtk/gtksor2listmodel.c Normal file
View File

@@ -0,0 +1,450 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtksor2listmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#define GTK_VECTOR_ELEMENT_TYPE GObject *
#define GTK_VECTOR_TYPE_NAME SortArray
#define GTK_VECTOR_NAME sort_array
#define GTK_VECTOR_FREE_FUNC g_object_unref
#include "gtkvectorimpl.c"
/**
* SECTION:gtksor2listmodel
* @title: GtkSor2ListModel
* @short_description: A list model that sorts its items
* @see_also: #GListModel, #GtkSorter
*
* #GtkSor2ListModel is a list model that takes a list model and
* sorts its elements according to a #GtkSorter.
*
* #GtkSor2ListModel 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 #GtkSor2ListModel, it
* is strongly recommended that you write your own sorting list
* model.
*/
enum {
PROP_0,
PROP_MODEL,
PROP_SORTER,
NUM_PROPERTIES
};
struct _GtkSor2ListModel
{
GObject parent_instance;
GListModel *model;
GtkSorter *sorter;
SortArray items; /* empty if known unsorted */
};
struct _GtkSor2ListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_sor2_list_model_get_item_type (GListModel *list)
{
return G_TYPE_OBJECT;
}
static guint
gtk_sor2_list_model_get_n_items (GListModel *list)
{
GtkSor2ListModel *self = GTK_SOR2_LIST_MODEL (list);
if (self->model == NULL)
return 0;
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_sor2_list_model_get_item (GListModel *list,
guint position)
{
GtkSor2ListModel *self = GTK_SOR2_LIST_MODEL (list);
if (self->model == NULL)
return NULL;
if (sort_array_is_empty (&self->items))
return g_list_model_get_item (self->model, position);
if (position >= sort_array_get_size (&self->items))
return NULL;
return g_object_ref (sort_array_get (&self->items, position));
}
static void
gtk_sor2_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_sor2_list_model_get_item_type;
iface->get_n_items = gtk_sor2_list_model_get_n_items;
iface->get_item = gtk_sor2_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSor2ListModel, gtk_sor2_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sor2_list_model_model_init))
static void
gtk_sor2_list_model_clear_items (GtkSor2ListModel *self)
{
sort_array_clear (&self->items);
}
static void
gtk_sor2_list_model_create_items (GtkSor2ListModel *self)
{
guint i, n_items;
if (self->sorter == NULL ||
self->model == NULL ||
gtk_sorter_get_order (self->sorter) == GTK_SORTER_ORDER_NONE)
return;
n_items = g_list_model_get_n_items (self->model);
sort_array_reserve (&self->items, n_items);
for (i = 0; i < n_items; i++)
{
sort_array_append (&self->items, g_list_model_get_item (self->model, i));
}
}
static int
sort_func (gconstpointer a,
gconstpointer b,
gpointer data)
{
return gtk_sorter_compare (data, *(GObject **) a, *(GObject **) b);
}
static void
gtk_sor2_list_model_resort (GtkSor2ListModel *self)
{
g_qsort_with_data (sort_array_get_data (&self->items),
sort_array_get_size (&self->items),
sizeof (GObject *),
sort_func,
self->sorter);
}
static void
gtk_sor2_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSor2ListModel *self)
{
guint n_items;
/* doesn't free() the array */
sort_array_set_size (&self->items, 0);
gtk_sor2_list_model_create_items (self);
gtk_sor2_list_model_resort (self);
n_items = g_list_model_get_n_items (model);
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items - added + removed, n_items);
}
static void
gtk_sor2_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSor2ListModel *self = GTK_SOR2_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_sor2_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_SORTER:
gtk_sor2_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_sor2_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSor2ListModel *self = GTK_SOR2_LIST_MODEL (object);
switch (prop_id)
{
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_sor2_list_model_sorter_changed_cb (GtkSorter *sorter,
int change,
GtkSor2ListModel *self)
{
guint n_items;
if (gtk_sorter_get_order (sorter) == GTK_SORTER_ORDER_NONE)
gtk_sor2_list_model_clear_items (self);
else if (sort_array_is_empty (&self->items))
gtk_sor2_list_model_create_items (self);
gtk_sor2_list_model_resort (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);
}
static void
gtk_sor2_list_model_clear_model (GtkSor2ListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_sor2_list_model_items_changed_cb, self);
g_clear_object (&self->model);
gtk_sor2_list_model_clear_items (self);
}
static void
gtk_sor2_list_model_clear_sorter (GtkSor2ListModel *self)
{
if (self->sorter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sor2_list_model_sorter_changed_cb, self);
g_clear_object (&self->sorter);
gtk_sor2_list_model_clear_items (self);
}
static void
gtk_sor2_list_model_dispose (GObject *object)
{
GtkSor2ListModel *self = GTK_SOR2_LIST_MODEL (object);
gtk_sor2_list_model_clear_model (self);
gtk_sor2_list_model_clear_sorter (self);
G_OBJECT_CLASS (gtk_sor2_list_model_parent_class)->dispose (object);
};
static void
gtk_sor2_list_model_class_init (GtkSor2ListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_sor2_list_model_set_property;
gobject_class->get_property = gtk_sor2_list_model_get_property;
gobject_class->dispose = gtk_sor2_list_model_dispose;
/**
* GtkSor2ListModel: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);
/**
* GtkSor2ListModel: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_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_sor2_list_model_init (GtkSor2ListModel *self)
{
}
/**
* gtk_sor2_list_model_new:
* @model: (allow-none): 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 #GtkSor2ListModel
**/
GtkSor2ListModel *
gtk_sor2_list_model_new (GListModel *model,
GtkSorter *sorter)
{
GtkSor2ListModel *result;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
result = g_object_new (GTK_TYPE_SOR2_LIST_MODEL,
"model", model,
"sorter", sorter,
NULL);
return result;
}
/**
* gtk_sor2_list_model_set_model:
* @self: a #GtkSor2ListModel
* @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_sor2_list_model_set_model (GtkSor2ListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SOR2_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_sor2_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sor2_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (model);
gtk_sor2_list_model_create_items (self);
gtk_sor2_list_model_resort (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_sor2_list_model_get_model:
* @self: a #GtkSor2ListModel
*
* Gets the model currently sorted or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets sorted
**/
GListModel *
gtk_sor2_list_model_get_model (GtkSor2ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR2_LIST_MODEL (self), NULL);
return self->model;
}
/**
* gtk_sor2_list_model_set_sorter:
* @self: a #GtkSor2ListModel
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Sets a new sorter on @self.
*/
void
gtk_sor2_list_model_set_sorter (GtkSor2ListModel *self,
GtkSorter *sorter)
{
g_return_if_fail (GTK_IS_SOR2_LIST_MODEL (self));
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
gtk_sor2_list_model_clear_sorter (self);
if (sorter)
{
self->sorter = g_object_ref (sorter);
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sor2_list_model_sorter_changed_cb), self);
gtk_sor2_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self);
}
else
{
guint 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_sor2_list_model_get_sorter:
* @self: a #GtkSor2LisTModel
*
* Gets the sorter that is used to sort @self.
*
* Returns: (nullable) (transfer none): the sorter of #self
*/
GtkSorter *
gtk_sor2_list_model_get_sorter (GtkSor2ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR2_LIST_MODEL (self), NULL);
return self->sorter;
}

57
gtk/gtksor2listmodel.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* 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_SOR2_LIST_MODEL_H__
#define __GTK_SOR2_LIST_MODEL_H__
#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 <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_SOR2_LIST_MODEL (gtk_sor2_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSor2ListModel, gtk_sor2_list_model, GTK, SOR2_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSor2ListModel * gtk_sor2_list_model_new (GListModel *model,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
void gtk_sor2_list_model_set_sorter (GtkSor2ListModel *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_sor2_list_model_get_sorter (GtkSor2ListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_sor2_list_model_set_model (GtkSor2ListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_sor2_list_model_get_model (GtkSor2ListModel *self);
G_END_DECLS
#endif /* __GTK_SOR2_LIST_MODEL_H__ */

604
gtk/gtksor3listmodel.c Normal file
View File

@@ -0,0 +1,604 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtksor3listmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#define GTK_VECTOR_ELEMENT_TYPE GObject *
#define GTK_VECTOR_TYPE_NAME SortArray
#define GTK_VECTOR_NAME sort_array
#define GTK_VECTOR_FREE_FUNC g_object_unref
#define GTK_VECTOR_PREALLOC 16
#include "gtkvectorimpl.c"
#undef GTK_VECTOR_ELEMENT_TYPE
#undef GTK_VECTOR_TYPE_NAME
#undef GTK_VECTOR_NAME
#undef GTK_VECTOR_FREE_FUNC
#undef GTK_VECTOR_PREALLOC
#define GTK_VECTOR_ELEMENT_TYPE guint
#define GTK_VECTOR_TYPE_NAME PivotStack
#define GTK_VECTOR_NAME pivot_stack
#define GTK_VECTOR_BY_VALUE
#define GTK_VECTOR_PREALLOC 16
#include "gtkvectorimpl.c"
#undef GTK_VECTOR_ELEMENT_TYPE
#undef GTK_VECTOR_TYPE_NAME
#undef GTK_VECTOR_NAME
#undef GTK_VECTOR_BY_VALUE
#undef GTK_VECTOR_FREE_FUNC
#undef GTK_VECTOR_PREALLOC
static inline void
pivot_stack_push (PivotStack *stack,
guint pos)
{
gsize size = pivot_stack_get_size (stack);
pivot_stack_set_size (stack, size + 1);
*pivot_stack_get (stack, size) = pos;
}
static inline guint
pivot_stack_top (PivotStack *stack)
{
return *pivot_stack_get (stack, pivot_stack_get_size (stack) - 1);
}
static inline guint
pivot_stack_pop (PivotStack *stack)
{
gsize size = pivot_stack_get_size (stack);
guint top = *pivot_stack_get (stack, size - 1);
pivot_stack_set_size (stack, size - 1);
return top;
}
/**
* SECTION:gtksor3listmodel
* @title: GtkSor3ListModel
* @short_description: A list model that sorts its items
* @see_also: #GListModel, #GtkSorter
*
* #GtkSor3ListModel is a list model that takes a list model and
* sorts its elements according to a #GtkSorter.
*
* #GtkSor3ListModel 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 #GtkSor3ListModel, it
* is strongly recommended that you write your own sorting list
* model.
*/
enum {
PROP_0,
PROP_MODEL,
PROP_SORTER,
NUM_PROPERTIES
};
struct _GtkSor3ListModel
{
GObject parent_instance;
GListModel *model;
GtkSorter *sorter;
SortArray items; /* empty if known unsorted */
guint sorting_cb;
guint sorted_to;
PivotStack stack;
};
struct _GtkSor3ListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_sor3_list_model_get_item_type (GListModel *list)
{
return G_TYPE_OBJECT;
}
static guint
gtk_sor3_list_model_get_n_items (GListModel *list)
{
GtkSor3ListModel *self = GTK_SOR3_LIST_MODEL (list);
if (self->model == NULL)
return 0;
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_sor3_list_model_get_item (GListModel *list,
guint position)
{
GtkSor3ListModel *self = GTK_SOR3_LIST_MODEL (list);
if (self->model == NULL)
return NULL;
if (sort_array_is_empty (&self->items))
return g_list_model_get_item (self->model, position);
if (position >= sort_array_get_size (&self->items))
return NULL;
return g_object_ref (sort_array_get (&self->items, position));
}
static void
gtk_sor3_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_sor3_list_model_get_item_type;
iface->get_n_items = gtk_sor3_list_model_get_n_items;
iface->get_item = gtk_sor3_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSor3ListModel, gtk_sor3_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sor3_list_model_model_init))
static void
gtk_sor3_list_model_clear_items (GtkSor3ListModel *self)
{
sort_array_clear (&self->items);
}
static void
gtk_sor3_list_model_create_items (GtkSor3ListModel *self)
{
guint i, n_items;
if (self->sorter == NULL ||
self->model == NULL ||
gtk_sorter_get_order (self->sorter) == GTK_SORTER_ORDER_NONE)
return;
n_items = g_list_model_get_n_items (self->model);
sort_array_reserve (&self->items, n_items);
for (i = 0; i < n_items; i++)
sort_array_append (&self->items, g_list_model_get_item (self->model, i));
}
static void
gtk_sor3_list_model_stop_sorting (GtkSor3ListModel *self)
{
g_clear_handle_id (&self->sorting_cb, g_source_remove);
pivot_stack_set_size (&self->stack, 0);
}
static inline int
compare (GtkSorter *sorter, GObject *a, GObject *b)
{
return gtk_sorter_compare (sorter, a, b);
}
static inline void
swap (SortArray *items, guint i, guint j)
{
GObject *tmp = sort_array_get (items, i);
*sort_array_index (items, i) = sort_array_get (items, j);
*sort_array_index (items, j) = tmp;
}
/* This is pretty much the incremental quicksort that is described
* in the wikipedia article about it. Calling iqs repeatedly for
* each position from 0..k gives you that k smallest elements, in
* order. Which is what we do. As a side-effect, the array ends up
* getting sorted. The nice thing is that we simply remember how
* far we've enumerated (in sorted_to), and interrupt the sorting,
* which is the incremental part.
*/
static guint
partition (SortArray *items, guint first, guint end, GtkSorter *sorter)
{
guint mid;
guint i, j;
GObject *pivot;
mid = first + (end - first) / 2;
if (compare (sorter, sort_array_get (items, mid),
sort_array_get (items, first)) < 0)
swap (items, mid, first);
if (compare (sorter, sort_array_get (items, end),
sort_array_get (items, first)) < 0)
swap (items, end, first);
if (compare (sorter, sort_array_get (items, mid),
sort_array_get (items, end)) < 0)
swap (items, mid, end);
pivot = sort_array_get (items, end);
i = first;
j = end;
while (1)
{
while (compare (sorter, sort_array_get (items, i), pivot) < 0) i++;
while (j > i && compare (sorter, sort_array_get (items, j), pivot) >= 0) j--;
if (i >= j) return j;
swap (items, i, j);
}
return j;
}
static gpointer
iqs (SortArray *items, guint pos, PivotStack *stack, GtkSorter *sorter)
{
guint top;
guint pivot;
top = pivot_stack_top (stack);
if (top == pos)
{
pivot_stack_pop (stack);
return sort_array_get (items, pos);
}
pivot = partition (items, pos, top, sorter);
pivot_stack_push (stack, pivot);
return iqs (items, pos, stack, sorter);
}
static gboolean
gtk_sor3_list_model_sort_cb (gpointer data)
{
GtkSor3ListModel *self = GTK_SOR3_LIST_MODEL (data);
guint start;
guint end;
guint n_items;
guint i;
start = self->sorted_to;
n_items = sort_array_get_size (&self->items);
end = MIN (512, n_items - start);
for (i = 0; i < end; i++)
{
iqs (&self->items, self->sorted_to, &self->stack, self->sorter);
self->sorted_to++;
}
if (self->sorted_to >= n_items)
gtk_sor3_list_model_stop_sorting (self);
g_list_model_items_changed (G_LIST_MODEL (self), start, n_items - start, n_items - start);
return G_SOURCE_CONTINUE;
}
static void
gtk_sor3_list_model_start_sorting (GtkSor3ListModel *self)
{
if (sort_array_get_size (&self->items) == 0)
return;
g_assert (pivot_stack_get_size (&self->stack) == 0);
g_assert (self->sorting_cb == 0);
pivot_stack_push (&self->stack, (guint)sort_array_get_size (&self->items) - 1);
self->sorted_to = 0;
self->sorting_cb = g_idle_add (gtk_sor3_list_model_sort_cb, self);
g_source_set_name_by_id (self->sorting_cb, "[gtk] gtk_sor3_list_model_sort_cb");
}
static void
gtk_sor3_list_model_resort (GtkSor3ListModel *self)
{
gtk_sor3_list_model_stop_sorting (self);
gtk_sor3_list_model_start_sorting (self);
}
static void
gtk_sor3_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSor3ListModel *self)
{
guint n_items;
/* doesn't free() the array */
sort_array_set_size (&self->items, 0);
gtk_sor3_list_model_create_items (self);
gtk_sor3_list_model_resort (self);
n_items = g_list_model_get_n_items (model);
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items - added + removed, n_items);
}
static void
gtk_sor3_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSor3ListModel *self = GTK_SOR3_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_sor3_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_SORTER:
gtk_sor3_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_sor3_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSor3ListModel *self = GTK_SOR3_LIST_MODEL (object);
switch (prop_id)
{
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_sor3_list_model_sorter_changed_cb (GtkSorter *sorter,
int change,
GtkSor3ListModel *self)
{
guint n_items;
if (gtk_sorter_get_order (sorter) == GTK_SORTER_ORDER_NONE)
gtk_sor3_list_model_clear_items (self);
else if (sort_array_is_empty (&self->items))
gtk_sor3_list_model_create_items (self);
gtk_sor3_list_model_resort (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);
}
static void
gtk_sor3_list_model_clear_model (GtkSor3ListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_sor3_list_model_items_changed_cb, self);
g_clear_object (&self->model);
gtk_sor3_list_model_stop_sorting (self);
gtk_sor3_list_model_clear_items (self);
}
static void
gtk_sor3_list_model_clear_sorter (GtkSor3ListModel *self)
{
if (self->sorter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sor3_list_model_sorter_changed_cb, self);
g_clear_object (&self->sorter);
gtk_sor3_list_model_stop_sorting (self);
gtk_sor3_list_model_clear_items (self);
}
static void
gtk_sor3_list_model_dispose (GObject *object)
{
GtkSor3ListModel *self = GTK_SOR3_LIST_MODEL (object);
gtk_sor3_list_model_stop_sorting (self);
gtk_sor3_list_model_clear_model (self);
gtk_sor3_list_model_clear_sorter (self);
G_OBJECT_CLASS (gtk_sor3_list_model_parent_class)->dispose (object);
};
static void
gtk_sor3_list_model_class_init (GtkSor3ListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_sor3_list_model_set_property;
gobject_class->get_property = gtk_sor3_list_model_get_property;
gobject_class->dispose = gtk_sor3_list_model_dispose;
/**
* GtkSor3ListModel: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);
/**
* GtkSor3ListModel: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_sor3_list_model_init (GtkSor3ListModel *self)
{
}
/**
* gtk_sor3_list_model_new:
* @model: (allow-none): 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 #GtkSor3ListModel
**/
GtkSor3ListModel *
gtk_sor3_list_model_new (GListModel *model,
GtkSorter *sorter)
{
GtkSor3ListModel *result;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
result = g_object_new (GTK_TYPE_SOR3_LIST_MODEL,
"model", model,
"sorter", sorter,
NULL);
return result;
}
/**
* gtk_sor3_list_model_set_model:
* @self: a #GtkSor3ListModel
* @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_sor3_list_model_set_model (GtkSor3ListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SOR3_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_sor3_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sor3_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (model);
gtk_sor3_list_model_create_items (self);
gtk_sor3_list_model_resort (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_sor3_list_model_get_model:
* @self: a #GtkSor3ListModel
*
* Gets the model currently sorted or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets sorted
**/
GListModel *
gtk_sor3_list_model_get_model (GtkSor3ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR3_LIST_MODEL (self), NULL);
return self->model;
}
/**
* gtk_sor3_list_model_set_sorter:
* @self: a #GtkSor3ListModel
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Sets a new sorter on @self.
*/
void
gtk_sor3_list_model_set_sorter (GtkSor3ListModel *self,
GtkSorter *sorter)
{
g_return_if_fail (GTK_IS_SOR3_LIST_MODEL (self));
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
gtk_sor3_list_model_clear_sorter (self);
if (sorter)
{
self->sorter = g_object_ref (sorter);
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sor3_list_model_sorter_changed_cb), self);
gtk_sor3_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
}
/**
* gtk_sor3_list_model_get_sorter:
* @self: a #GtkSor3LisTModel
*
* Gets the sorter that is used to sort @self.
*
* Returns: (nullable) (transfer none): the sorter of #self
*/
GtkSorter *
gtk_sor3_list_model_get_sorter (GtkSor3ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR3_LIST_MODEL (self), NULL);
return self->sorter;
}

57
gtk/gtksor3listmodel.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* 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_SOR3_LIST_MODEL_H__
#define __GTK_SOR3_LIST_MODEL_H__
#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 <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_SOR3_LIST_MODEL (gtk_sor3_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSor3ListModel, gtk_sor3_list_model, GTK, SOR3_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSor3ListModel * gtk_sor3_list_model_new (GListModel *model,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
void gtk_sor3_list_model_set_sorter (GtkSor3ListModel *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_sor3_list_model_get_sorter (GtkSor3ListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_sor3_list_model_set_model (GtkSor3ListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_sor3_list_model_get_model (GtkSor3ListModel *self);
G_END_DECLS
#endif /* __GTK_SOR3_LIST_MODEL_H__ */

551
gtk/gtksor4listmodel.c Normal file
View File

@@ -0,0 +1,551 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtksor4listmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
typedef struct _SortItem SortItem;
struct _SortItem
{
GObject *item;
guint position;
};
static void
sort_item_clear (gpointer data)
{
SortItem *si = data;
g_clear_object (&si->item);
}
#define GTK_VECTOR_ELEMENT_TYPE SortItem
#define GTK_VECTOR_TYPE_NAME SortArray
#define GTK_VECTOR_NAME sort_array
#define GTK_VECTOR_FREE_FUNC sort_item_clear
#define GTK_VECTOR_BY_VALUE 1
#include "gtkvectorimpl.c"
/**
* SECTION:gtksor4listmodel
* @title: GtkSor4ListModel
* @short_description: A list model that sorts its items
* @see_also: #GListModel, #GtkSorter
*
* #GtkSor4ListModel is a list model that takes a list model and
* sorts its elements according to a #GtkSorter.
*
* #GtkSor4ListModel 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 #GtkSor4ListModel, it
* is strongly recommended that you write your own sorting list
* model.
*/
enum {
PROP_0,
PROP_MODEL,
PROP_SORTER,
NUM_PROPERTIES
};
struct _GtkSor4ListModel
{
GObject parent_instance;
GListModel *model;
GtkSorter *sorter;
SortArray items; /* empty if known unsorted */
};
struct _GtkSor4ListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_sor4_list_model_get_item_type (GListModel *list)
{
return G_TYPE_OBJECT;
}
static guint
gtk_sor4_list_model_get_n_items (GListModel *list)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (list);
if (self->model == NULL)
return 0;
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_sor4_list_model_get_item (GListModel *list,
guint position)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (list);
if (self->model == NULL)
return NULL;
if (sort_array_is_empty (&self->items))
return g_list_model_get_item (self->model, position);
if (position >= sort_array_get_size (&self->items))
return NULL;
return g_object_ref (sort_array_get (&self->items, position)->item);
}
static void
gtk_sor4_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_sor4_list_model_get_item_type;
iface->get_n_items = gtk_sor4_list_model_get_n_items;
iface->get_item = gtk_sor4_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSor4ListModel, gtk_sor4_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sor4_list_model_model_init))
static void
gtk_sor4_list_model_clear_items (GtkSor4ListModel *self)
{
sort_array_clear (&self->items);
}
static gboolean
gtk_sor4_list_model_should_sort (GtkSor4ListModel *self)
{
return self->sorter != NULL &&
self->model != NULL &&
gtk_sorter_get_order (self->sorter) != GTK_SORTER_ORDER_NONE;
}
static void
gtk_sor4_list_model_create_items (GtkSor4ListModel *self)
{
guint i, n_items;
if (!gtk_sor4_list_model_should_sort (self))
return;
n_items = g_list_model_get_n_items (self->model);
sort_array_reserve (&self->items, n_items);
for (i = 0; i < n_items; i++)
{
sort_array_append (&self->items, &(SortItem) { g_list_model_get_item (self->model, i), i });
}
}
static int
sort_func (gconstpointer a,
gconstpointer b,
gpointer data)
{
SortItem *sa = (SortItem *) a;
SortItem *sb = (SortItem *) b;
return gtk_sorter_compare (data, sa->item, sb->item);
}
static void
gtk_sor4_list_model_resort (GtkSor4ListModel *self)
{
g_qsort_with_data (sort_array_get_data (&self->items),
sort_array_get_size (&self->items),
sizeof (SortItem),
sort_func,
self->sorter);
}
static void
gtk_sor4_list_model_remove_items (GtkSor4ListModel *self,
guint position,
guint removed,
guint added,
guint *unmodified_start,
guint *unmodified_end)
{
guint i, n_items, valid;
guint start, end;
n_items = sort_array_get_size (&self->items);
start = n_items;
end = n_items;
valid = 0;
for (i = 0; i < n_items; i++)
{
SortItem *si = sort_array_index (&self->items, i);
if (si->position >= position + removed)
si->position = si->position - removed + added;
else if (si->position >= position)
{
start = MIN (start, valid);
end = n_items - i - 1;
sort_item_clear (si);
continue;
}
if (valid < i)
*sort_array_index (&self->items, valid) = *sort_array_index (&self->items, i);
valid++;
}
g_assert (valid == n_items - removed);
memset (sort_array_index (&self->items, valid), 0, sizeof (SortItem) * removed);
sort_array_set_size (&self->items, valid);
*unmodified_start = start;
*unmodified_end = end;
}
static void
gtk_sor4_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSor4ListModel *self)
{
guint i, n_items, start, end;
if (removed == 0 && added == 0)
return;
if (!gtk_sor4_list_model_should_sort (self))
{
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
return;
}
gtk_sor4_list_model_remove_items (self, position, removed, added, &start, &end);
if (added > 0)
{
sort_array_reserve (&self->items, sort_array_get_size (&self->items) + added);
for (i = position; i < position + added; i++)
{
sort_array_append (&self->items, &(SortItem) { g_list_model_get_item (self->model, i), i });
}
gtk_sor4_list_model_resort (self);
for (i = 0; i < start; i++)
{
SortItem *si = sort_array_get (&self->items, i);
if (si->position >= position && si->position < position + added)
{
start = i;
break;
}
}
n_items = sort_array_get_size (&self->items);
for (i = 0; i < end; i++)
{
SortItem *si = sort_array_get (&self->items, n_items - i - 1);
if (si->position >= position && si->position < position + added)
{
end = i;
break;
}
}
}
n_items = sort_array_get_size (&self->items) - start - end;
g_list_model_items_changed (G_LIST_MODEL (self), start, n_items - added + removed, n_items);
}
static void
gtk_sor4_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_sor4_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_SORTER:
gtk_sor4_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_sor4_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (object);
switch (prop_id)
{
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_sor4_list_model_sorter_changed_cb (GtkSorter *sorter,
int change,
GtkSor4ListModel *self)
{
guint n_items;
if (gtk_sorter_get_order (sorter) == GTK_SORTER_ORDER_NONE)
gtk_sor4_list_model_clear_items (self);
else if (sort_array_is_empty (&self->items))
gtk_sor4_list_model_create_items (self);
gtk_sor4_list_model_resort (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);
}
static void
gtk_sor4_list_model_clear_model (GtkSor4ListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_sor4_list_model_items_changed_cb, self);
g_clear_object (&self->model);
gtk_sor4_list_model_clear_items (self);
}
static void
gtk_sor4_list_model_clear_sorter (GtkSor4ListModel *self)
{
if (self->sorter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sor4_list_model_sorter_changed_cb, self);
g_clear_object (&self->sorter);
gtk_sor4_list_model_clear_items (self);
}
static void
gtk_sor4_list_model_dispose (GObject *object)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (object);
gtk_sor4_list_model_clear_model (self);
gtk_sor4_list_model_clear_sorter (self);
G_OBJECT_CLASS (gtk_sor4_list_model_parent_class)->dispose (object);
};
static void
gtk_sor4_list_model_class_init (GtkSor4ListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_sor4_list_model_set_property;
gobject_class->get_property = gtk_sor4_list_model_get_property;
gobject_class->dispose = gtk_sor4_list_model_dispose;
/**
* GtkSor4ListModel: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);
/**
* GtkSor4ListModel: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_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_sor4_list_model_init (GtkSor4ListModel *self)
{
}
/**
* gtk_sor4_list_model_new:
* @model: (allow-none): 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 #GtkSor4ListModel
**/
GtkSor4ListModel *
gtk_sor4_list_model_new (GListModel *model,
GtkSorter *sorter)
{
GtkSor4ListModel *result;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
result = g_object_new (GTK_TYPE_SOR4_LIST_MODEL,
"model", model,
"sorter", sorter,
NULL);
return result;
}
/**
* gtk_sor4_list_model_set_model:
* @self: a #GtkSor4ListModel
* @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_sor4_list_model_set_model (GtkSor4ListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SOR4_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_sor4_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sor4_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (model);
gtk_sor4_list_model_create_items (self);
gtk_sor4_list_model_resort (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_sor4_list_model_get_model:
* @self: a #GtkSor4ListModel
*
* Gets the model currently sorted or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets sorted
**/
GListModel *
gtk_sor4_list_model_get_model (GtkSor4ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR4_LIST_MODEL (self), NULL);
return self->model;
}
/**
* gtk_sor4_list_model_set_sorter:
* @self: a #GtkSor4ListModel
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Sets a new sorter on @self.
*/
void
gtk_sor4_list_model_set_sorter (GtkSor4ListModel *self,
GtkSorter *sorter)
{
g_return_if_fail (GTK_IS_SOR4_LIST_MODEL (self));
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
gtk_sor4_list_model_clear_sorter (self);
if (sorter)
{
self->sorter = g_object_ref (sorter);
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sor4_list_model_sorter_changed_cb), self);
gtk_sor4_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self);
}
else
{
guint 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_sor4_list_model_get_sorter:
* @self: a #GtkSor4LisTModel
*
* Gets the sorter that is used to sort @self.
*
* Returns: (nullable) (transfer none): the sorter of #self
*/
GtkSorter *
gtk_sor4_list_model_get_sorter (GtkSor4ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR4_LIST_MODEL (self), NULL);
return self->sorter;
}

57
gtk/gtksor4listmodel.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* 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_SOR4_LIST_MODEL_H__
#define __GTK_SOR4_LIST_MODEL_H__
#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 <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_SOR4_LIST_MODEL (gtk_sor4_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSor4ListModel, gtk_sor4_list_model, GTK, SOR4_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSor4ListModel * gtk_sor4_list_model_new (GListModel *model,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
void gtk_sor4_list_model_set_sorter (GtkSor4ListModel *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_sor4_list_model_get_sorter (GtkSor4ListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_sor4_list_model_set_model (GtkSor4ListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_sor4_list_model_get_model (GtkSor4ListModel *self);
G_END_DECLS
#endif /* __GTK_SOR4_LIST_MODEL_H__ */

546
gtk/gtksor5listmodel.c Normal file
View File

@@ -0,0 +1,546 @@
/*
* Copyright © 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtksor5listmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#define GTK_VECTOR_ELEMENT_TYPE GObject *
#define GTK_VECTOR_TYPE_NAME SortArray
#define GTK_VECTOR_NAME sort_array
#define GTK_VECTOR_FREE_FUNC g_object_unref
#define GTK_VECTOR_PREALLOC 16
#include "gtkvectorimpl.c"
/**
* SECTION:gtksor5listmodel
* @title: GtkSor5ListModel
* @short_description: A list model that sorts its items
* @see_also: #GListModel, #GtkSorter
*
* #GtkSor5ListModel is a list model that takes a list model and
* sorts its elements according to a #GtkSorter.
*
* #GtkSor5ListModel 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 #GtkSor5ListModel, it
* is strongly recommended that you write your own sorting list
* model.
*/
enum {
PROP_0,
PROP_MODEL,
PROP_SORTER,
NUM_PROPERTIES
};
struct _GtkSor5ListModel
{
GObject parent_instance;
GListModel *model;
GtkSorter *sorter;
SortArray items; /* empty if known unsorted */
guint sorting_cb;
guint size;
guint start;
};
struct _GtkSor5ListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_sor5_list_model_get_item_type (GListModel *list)
{
return G_TYPE_OBJECT;
}
static guint
gtk_sor5_list_model_get_n_items (GListModel *list)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (list);
if (self->model == NULL)
return 0;
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_sor5_list_model_get_item (GListModel *list,
guint position)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (list);
if (self->model == NULL)
return NULL;
if (sort_array_is_empty (&self->items))
return g_list_model_get_item (self->model, position);
if (position >= sort_array_get_size (&self->items))
return NULL;
return g_object_ref (sort_array_get (&self->items, position));
}
static void
gtk_sor5_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_sor5_list_model_get_item_type;
iface->get_n_items = gtk_sor5_list_model_get_n_items;
iface->get_item = gtk_sor5_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSor5ListModel, gtk_sor5_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sor5_list_model_model_init))
static void
gtk_sor5_list_model_clear_items (GtkSor5ListModel *self)
{
sort_array_clear (&self->items);
}
static void
gtk_sor5_list_model_create_items (GtkSor5ListModel *self)
{
guint i, n_items;
if (self->sorter == NULL ||
self->model == NULL ||
gtk_sorter_get_order (self->sorter) == GTK_SORTER_ORDER_NONE)
return;
n_items = g_list_model_get_n_items (self->model);
sort_array_reserve (&self->items, n_items);
for (i = 0; i < n_items; i++)
sort_array_append (&self->items, g_list_model_get_item (self->model, i));
}
static void
gtk_sor5_list_model_stop_sorting (GtkSor5ListModel *self)
{
g_clear_handle_id (&self->sorting_cb, g_source_remove);
}
static guint
merge (SortArray *items,
guint start,
guint mid,
guint end,
GtkSorter *sorter)
{
int start2 = mid + 1;
int c = 1;
if (gtk_sorter_compare (sorter,
sort_array_get (items, mid),
sort_array_get (items, start2)) <= 0)
return 1;
while (start <= mid && start2 <= end)
{
c++;
if (gtk_sorter_compare (sorter,
sort_array_get (items, start),
sort_array_get (items, start2)) <= 0)
start++;
else
{
GObject *value = sort_array_get (items, start2);
int index = start2;
while (index != start)
{
c++;
*sort_array_index (items, index) = sort_array_get (items, index - 1);
index--;
}
c++;
*sort_array_index (items, start) = value;
start++;
mid++;
start2++;
}
}
return c;
}
static gboolean
gtk_sor5_list_model_sort_cb (gpointer data)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (data);
guint n_items = sort_array_get_size (&self->items);
guint i;
guint s, e;
s = self->start;
e = self->start;
i = 0;
while (i < 10000)
{
guint mid = MIN (self->start + self->size - 1, n_items - 1);
guint end = MIN (self->start + 2 * self->size - 1, n_items - 1);
i += merge (&self->items, self->start, mid, end, self->sorter);
s = MIN (s, self->start);
e = MAX (e, end);
self->start += 2 * self->size;
if (self->start >= n_items - 1)
{
self->start = 0;
self->size *= 2;
if (self->size >= n_items)
{
gtk_sor5_list_model_stop_sorting (self);
break;
}
}
}
//g_print ("items changed %u:%u\n", s, e - s + 1);
g_list_model_items_changed (G_LIST_MODEL (self), s, e - s + 1, e - s + 1);
return G_SOURCE_CONTINUE;
}
static void
gtk_sor5_list_model_start_sorting (GtkSor5ListModel *self)
{
if (sort_array_get_size (&self->items) == 0)
return;
g_assert (self->sorting_cb == 0);
self->size = 1;
self->start = 0;
self->sorting_cb = g_idle_add (gtk_sor5_list_model_sort_cb, self);
g_source_set_name_by_id (self->sorting_cb, "[gtk] gtk_sor5_list_model_sort_cb");
}
static void
gtk_sor5_list_model_resort (GtkSor5ListModel *self)
{
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_start_sorting (self);
}
static void
gtk_sor5_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSor5ListModel *self)
{
guint n_items;
/* doesn't free() the array */
sort_array_set_size (&self->items, 0);
gtk_sor5_list_model_create_items (self);
gtk_sor5_list_model_resort (self);
n_items = g_list_model_get_n_items (model);
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items - added + removed, n_items);
}
static void
gtk_sor5_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_sor5_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_SORTER:
gtk_sor5_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_sor5_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (object);
switch (prop_id)
{
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_sor5_list_model_sorter_changed_cb (GtkSorter *sorter,
int change,
GtkSor5ListModel *self)
{
guint n_items;
if (gtk_sorter_get_order (sorter) == GTK_SORTER_ORDER_NONE)
gtk_sor5_list_model_clear_items (self);
else if (sort_array_is_empty (&self->items))
gtk_sor5_list_model_create_items (self);
gtk_sor5_list_model_resort (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);
}
static void
gtk_sor5_list_model_clear_model (GtkSor5ListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_sor5_list_model_items_changed_cb, self);
g_clear_object (&self->model);
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_clear_items (self);
}
static void
gtk_sor5_list_model_clear_sorter (GtkSor5ListModel *self)
{
if (self->sorter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sor5_list_model_sorter_changed_cb, self);
g_clear_object (&self->sorter);
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_clear_items (self);
}
static void
gtk_sor5_list_model_dispose (GObject *object)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (object);
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_clear_model (self);
gtk_sor5_list_model_clear_sorter (self);
G_OBJECT_CLASS (gtk_sor5_list_model_parent_class)->dispose (object);
};
static void
gtk_sor5_list_model_class_init (GtkSor5ListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_sor5_list_model_set_property;
gobject_class->get_property = gtk_sor5_list_model_get_property;
gobject_class->dispose = gtk_sor5_list_model_dispose;
/**
* GtkSor5ListModel: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);
/**
* GtkSor5ListModel: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_sor5_list_model_init (GtkSor5ListModel *self)
{
}
/**
* gtk_sor5_list_model_new:
* @model: (allow-none): 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 #GtkSor5ListModel
**/
GtkSor5ListModel *
gtk_sor5_list_model_new (GListModel *model,
GtkSorter *sorter)
{
GtkSor5ListModel *result;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
result = g_object_new (GTK_TYPE_SOR5_LIST_MODEL,
"model", model,
"sorter", sorter,
NULL);
return result;
}
/**
* gtk_sor5_list_model_set_model:
* @self: a #GtkSor5ListModel
* @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_sor5_list_model_set_model (GtkSor5ListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SOR5_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_sor5_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sor5_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (model);
gtk_sor5_list_model_create_items (self);
gtk_sor5_list_model_resort (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_sor5_list_model_get_model:
* @self: a #GtkSor5ListModel
*
* Gets the model currently sorted or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets sorted
**/
GListModel *
gtk_sor5_list_model_get_model (GtkSor5ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR5_LIST_MODEL (self), NULL);
return self->model;
}
/**
* gtk_sor5_list_model_set_sorter:
* @self: a #GtkSor5ListModel
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Sets a new sorter on @self.
*/
void
gtk_sor5_list_model_set_sorter (GtkSor5ListModel *self,
GtkSorter *sorter)
{
g_return_if_fail (GTK_IS_SOR5_LIST_MODEL (self));
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
gtk_sor5_list_model_clear_sorter (self);
if (sorter)
{
self->sorter = g_object_ref (sorter);
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sor5_list_model_sorter_changed_cb), self);
gtk_sor5_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
}
/**
* gtk_sor5_list_model_get_sorter:
* @self: a #GtkSor5LisTModel
*
* Gets the sorter that is used to sort @self.
*
* Returns: (nullable) (transfer none): the sorter of #self
*/
GtkSorter *
gtk_sor5_list_model_get_sorter (GtkSor5ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR5_LIST_MODEL (self), NULL);
return self->sorter;
}

57
gtk/gtksor5listmodel.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* 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_SOR5_LIST_MODEL_H__
#define __GTK_SOR5_LIST_MODEL_H__
#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 <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_SOR5_LIST_MODEL (gtk_sor5_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSor5ListModel, gtk_sor5_list_model, GTK, SOR5_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSor5ListModel * gtk_sor5_list_model_new (GListModel *model,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
void gtk_sor5_list_model_set_sorter (GtkSor5ListModel *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_sor5_list_model_get_sorter (GtkSor5ListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_sor5_list_model_set_model (GtkSor5ListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_sor5_list_model_get_model (GtkSor5ListModel *self);
G_END_DECLS
#endif /* __GTK_SOR5_LIST_MODEL_H__ */

View File

@@ -142,7 +142,8 @@ gtk_sorter_compare (GtkSorter *self,
{
GtkOrdering result;
g_return_val_if_fail (GTK_IS_SORTER (self), GTK_ORDERING_EQUAL);
/* We turn this off because gtk_sorter_compare() is called so much that it's too expensive */
/* 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)

View File

@@ -403,7 +403,7 @@ gtk_sort_list_model_class_init (GtkSortListModelClass *class)
P_("Model"),
P_("The model being sorted"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}

View File

@@ -57,6 +57,12 @@
*/
#define GTK_VECTOR_ELEMENT_TYPE GtkStringObject *
#define GTK_VECTOR_NAME objects
#define GTK_VECTOR_TYPE_NAME Objects
#define GTK_VECTOR_FREE_FUNC g_object_unref
#include "gtkvectorimpl.c"
struct _GtkStringObject
{
GObject parent_instance;
@@ -190,7 +196,7 @@ struct _GtkStringList
{
GObject parent_instance;
GSequence *items;
Objects items;
};
struct _GtkStringListClass
@@ -209,7 +215,7 @@ gtk_string_list_get_n_items (GListModel *list)
{
GtkStringList *self = GTK_STRING_LIST (list);
return g_sequence_get_length (self->items);
return objects_get_size (&self->items);
}
static gpointer
@@ -217,14 +223,11 @@ gtk_string_list_get_item (GListModel *list,
guint position)
{
GtkStringList *self = GTK_STRING_LIST (list);
GSequenceIter *iter;
iter = g_sequence_get_iter_at_pos (self->items, position);
if (g_sequence_iter_is_end (iter))
if (position >= objects_get_size (&self->items))
return NULL;
else
return g_object_ref (g_sequence_get (iter));
return g_object_ref (objects_get (&self->items, position));
}
static void
@@ -331,7 +334,7 @@ item_end_element (GtkBuildableParseContext *context,
g_string_assign (data->string, translated);
}
g_sequence_append (data->list->items, gtk_string_object_new (data->string->str));
gtk_string_list_append (data->list, data->string->str);
}
data->translatable = FALSE;
@@ -411,7 +414,7 @@ gtk_string_list_dispose (GObject *object)
{
GtkStringList *self = GTK_STRING_LIST (object);
g_clear_pointer (&self->items, g_sequence_free);
objects_clear (&self->items);
G_OBJECT_CLASS (gtk_string_list_parent_class)->dispose (object);
}
@@ -427,7 +430,7 @@ gtk_string_list_class_init (GtkStringListClass *class)
static void
gtk_string_list_init (GtkStringList *self)
{
self->items = g_sequence_new (g_object_unref);
objects_init (&self->items);
}
/**
@@ -476,32 +479,19 @@ gtk_string_list_splice (GtkStringList *self,
guint n_removals,
const char * const *additions)
{
GSequenceIter *it;
guint add, n_items;
guint add;
g_return_if_fail (GTK_IS_STRING_LIST (self));
g_return_if_fail (position + n_removals >= position); /* overflow */
g_return_if_fail (position + n_removals <= objects_get_size (&self->items));
n_items = g_sequence_get_length (self->items);
g_return_if_fail (position + n_removals <= n_items);
it = g_sequence_get_iter_at_pos (self->items, position);
if (n_removals)
{
GSequenceIter *end;
end = g_sequence_iter_move (it, n_removals);
g_sequence_remove_range (it, end);
it = end;
}
objects_splice (&self->items, position, n_removals, NULL, 0);
if (additions)
{
for (add = 0; additions[add]; add++)
{
g_sequence_insert_before (it, gtk_string_object_new (additions[add]));
objects_append (&self->items, gtk_string_object_new (additions[add]));
}
}
else
@@ -525,14 +515,11 @@ void
gtk_string_list_append (GtkStringList *self,
const char *string)
{
guint n_items;
g_return_if_fail (GTK_IS_STRING_LIST (self));
n_items = g_sequence_get_length (self->items);
g_sequence_append (self->items, gtk_string_object_new (string));
objects_append (&self->items, gtk_string_object_new (string));
g_list_model_items_changed (G_LIST_MODEL (self), n_items, 0, 1);
g_list_model_items_changed (G_LIST_MODEL (self), objects_get_size (&self->items) - 1, 0, 1);
}
/**
@@ -554,14 +541,11 @@ void
gtk_string_list_take (GtkStringList *self,
char *string)
{
guint n_items;
g_return_if_fail (GTK_IS_STRING_LIST (self));
n_items = g_sequence_get_length (self->items);
g_sequence_append (self->items, gtk_string_object_new_take (string));
objects_append (&self->items, gtk_string_object_new_take (string));
g_list_model_items_changed (G_LIST_MODEL (self), n_items, 0, 1);
g_list_model_items_changed (G_LIST_MODEL (self), objects_get_size (&self->items) - 1, 0, 1);
}
/**
@@ -576,16 +560,9 @@ void
gtk_string_list_remove (GtkStringList *self,
guint position)
{
GSequenceIter *iter;
g_return_if_fail (GTK_IS_STRING_LIST (self));
iter = g_sequence_get_iter_at_pos (self->items, position);
g_return_if_fail (!g_sequence_iter_is_end (iter));
g_sequence_remove (iter);
g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
gtk_string_list_splice (self, position, 1, NULL);
}
/**
@@ -605,20 +582,10 @@ const char *
gtk_string_list_get_string (GtkStringList *self,
guint position)
{
GSequenceIter *iter;
g_return_val_if_fail (GTK_IS_STRING_LIST (self), NULL);
iter = g_sequence_get_iter_at_pos (self->items, position);
if (position >= objects_get_size (&self->items))
return NULL;
if (g_sequence_iter_is_end (iter))
{
return NULL;
}
else
{
GtkStringObject *obj = g_sequence_get (iter);
return obj->string;
}
return objects_get (&self->items, position)->string;
}

283
gtk/gtkvectorimpl.c Normal file
View File

@@ -0,0 +1,283 @@
/*
* Copyright © 2020 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 <glib.h>
G_BEGIN_DECLS
#ifndef GTK_VECTOR_TYPE_NAME
#define GTK_VECTOR_TYPE_NAME GtkVector
#endif
#ifndef GTK_VECTOR_NAME
#define GTK_VECTOR_NAME gtk_vector
#endif
#ifndef GTK_VECTOR_ELEMENT_TYPE
#define GTK_VECTOR_ELEMENT_TYPE gpointer
#endif
#ifdef GTK_VECTOR_PREALLOC
#if GTK_VECTOR_PREALLOC == 0
#undef GTK_VECTOR_PREALLOC
#endif
#endif
#ifdef GTK_VECTOR_NULL_TERMINATED
#define GTK_VECTOR_REAL_SIZE(_size) ((_size) + 1)
#else
#define GTK_VECTOR_REAL_SIZE(_size) (_size)
#endif
/* make this readable */
#define _T_ GTK_VECTOR_ELEMENT_TYPE
#define GtkVector GTK_VECTOR_TYPE_NAME
#define gtk_vector_paste_more(GTK_VECTOR_NAME, func_name) GTK_VECTOR_NAME ## _ ## func_name
#define gtk_vector_paste(GTK_VECTOR_NAME, func_name) gtk_vector_paste_more (GTK_VECTOR_NAME, func_name)
#define gtk_vector(func_name) gtk_vector_paste (GTK_VECTOR_NAME, func_name)
typedef struct GtkVector GtkVector;
struct GtkVector
{
_T_ *start;
_T_ *end;
_T_ *end_allocation;
#ifdef GTK_VECTOR_PREALLOC
_T_ preallocated[GTK_VECTOR_REAL_SIZE(GTK_VECTOR_PREALLOC)];
#endif
};
/* no G_GNUC_UNUSED here, if you don't use an array type, remove it. */
static inline void
gtk_vector(init) (GtkVector *self)
{
#ifdef GTK_VECTOR_PREALLOC
self->start = self->preallocated;
self->end = self->start;
self->end_allocation = self->start + GTK_VECTOR_PREALLOC;
#ifdef GTK_VECTOR_NULL_TERMINATED
*self->start = *(_T_[1]) {};
#endif
#else
self->start = NULL;
self->end = NULL;
self->end_allocation = NULL;
#endif
}
static inline void
gtk_vector(free_elements) (_T_ *start,
_T_ *end)
{
#ifdef GTK_VECTOR_FREE_FUNC
_T_ *e;
for (e = start; e < end; e++)
#ifdef GTK_VECTOR_BY_VALUE
GTK_VECTOR_FREE_FUNC (e);
#else
GTK_VECTOR_FREE_FUNC (*e);
#endif
#endif
}
/* no G_GNUC_UNUSED here */
static inline void
gtk_vector(clear) (GtkVector *self)
{
gtk_vector(free_elements) (self->start, self->end);
#ifdef GTK_VECTOR_PREALLOC
if (self->start != self->preallocated)
g_free (self->start);
#endif
gtk_vector(init) (self);
}
G_GNUC_UNUSED static inline _T_ *
gtk_vector(get_data) (const GtkVector *self)
{
return self->start;
}
G_GNUC_UNUSED static inline _T_ *
gtk_vector(index) (const GtkVector *self,
gsize pos)
{
return self->start + pos;
}
G_GNUC_UNUSED static inline gsize
gtk_vector(get_capacity) (const GtkVector *self)
{
return self->end_allocation - self->start;
}
G_GNUC_UNUSED static inline gsize
gtk_vector(get_size) (const GtkVector *self)
{
return self->end - self->start;
}
G_GNUC_UNUSED static inline gboolean
gtk_vector(is_empty) (const GtkVector *self)
{
return self->end == self->start;
}
G_GNUC_UNUSED static void
gtk_vector(reserve) (GtkVector *self,
gsize n)
{
gsize new_size, size;
if (n <= gtk_vector(get_capacity) (self))
return;
size = gtk_vector(get_size) (self);
new_size = 1 << g_bit_storage (MAX (GTK_VECTOR_REAL_SIZE (n), 16) - 1);
#ifdef GTK_VECTOR_PREALLOC
if (self->start == self->preallocated)
{
self->start = g_new (_T_, new_size);
memcpy (self->start, self->preallocated, sizeof (_T_) * GTK_VECTOR_REAL_SIZE (size));
}
else
#endif
#ifdef GTK_VECTOR_NULL_TERMINATED
if (self->start == NULL)
{
self->start = g_new (_T_, new_size);
*self->start = *(_T_[1]) {};
}
else
#endif
self->start = g_renew (_T_, self->start, new_size);
self->end = self->start + size;
self->end_allocation = self->start + new_size;
#ifdef GTK_VECTOR_NULL_TERMINATED
self->end_allocation--;
#endif
}
G_GNUC_UNUSED static void
gtk_vector(splice) (GtkVector *self,
gsize pos,
gsize removed,
_T_ *additions,
gsize added)
{
gsize size;
gsize remaining;
size = gtk_vector(get_size) (self);
g_assert (pos + removed <= size);
remaining = size - pos - removed;
gtk_vector(free_elements) (gtk_vector(index) (self, pos),
gtk_vector(index) (self, pos + removed));
gtk_vector(reserve) (self, size - removed + added);
if (GTK_VECTOR_REAL_SIZE (remaining) && removed != added)
memmove (gtk_vector(index) (self, pos + added),
gtk_vector(index) (self, pos + removed),
GTK_VECTOR_REAL_SIZE (remaining) * sizeof (_T_));
if (added)
{
if (additions)
memcpy (gtk_vector(index) (self, pos),
additions,
added * sizeof (_T_));
else
memset (gtk_vector(index) (self, pos), 0, added * sizeof (_T_));
}
/* might overflow, but does the right thing */
self->end += added - removed;
}
G_GNUC_UNUSED static void
gtk_vector(set_size) (GtkVector *self,
gsize new_size)
{
gsize old_size = gtk_vector(get_size) (self);
if (new_size > old_size)
gtk_vector(splice) (self, old_size, 0, NULL, new_size - old_size);
else
gtk_vector(splice) (self, new_size, old_size - new_size, NULL, 0);
}
G_GNUC_UNUSED static void
gtk_vector(append) (GtkVector *self,
#ifdef GTK_VECTOR_BY_VALUE
_T_ *value)
#else
_T_ value)
#endif
{
gtk_vector(splice) (self,
gtk_vector(get_size) (self),
0,
#ifdef GTK_VECTOR_BY_VALUE
value,
#else
&value,
#endif
1);
}
#ifdef GTK_VECTOR_BY_VALUE
G_GNUC_UNUSED static _T_ *
gtk_vector(get) (const GtkVector *self,
gsize pos)
{
return gtk_vector(index) (self, pos);
}
#else
G_GNUC_UNUSED static _T_
gtk_vector(get) (const GtkVector *self,
gsize pos)
{
return *gtk_vector(index) (self, pos);
}
#endif
#ifndef GTK_VECTOR_NO_UNDEF
#undef _T_
#undef GtkVector
#undef gtk_vector_paste_more
#undef gtk_vector_paste
#undef gtk_vector
#undef GTK_VECTOR_REAL_SIZE
#undef GTK_VECTOR_BY_VALUE
#undef GTK_VECTOR_ELEMENT_TYPE
#undef GTK_VECTOR_FREE_FUNC
#undef GTK_VECTOR_NAME
#undef GTK_VECTOR_NULL_TERMINATED
#undef GTK_VECTOR_PREALLOC
#undef GTK_VECTOR_TYPE_NAME
#endif

View File

@@ -371,6 +371,10 @@ gtk_public_sources = files([
'gtkslicelistmodel.c',
'gtksnapshot.c',
'gtksorter.c',
'gtksor2listmodel.c',
'gtksor3listmodel.c',
'gtksor4listmodel.c',
'gtksor5listmodel.c',
'gtksortlistmodel.c',
'gtkspinbutton.c',
'gtkspinner.c',
@@ -640,6 +644,10 @@ gtk_public_headers = files([
'gtkslicelistmodel.h',
'gtksnapshot.h',
'gtksorter.h',
'gtksor2listmodel.h',
'gtksor3listmodel.h',
'gtksor4listmodel.h',
'gtksor5listmodel.h',
'gtksortlistmodel.h',
'gtkspinbutton.h',
'gtkspinner.h',

View File

@@ -59,6 +59,7 @@ tests = [
['slicelistmodel'],
['sorter'],
['sortlistmodel'],
['sort-performance'],
['spinbutton'],
['stringlist'],
['templates'],
@@ -75,6 +76,8 @@ tests = [
['typename'],
['displayclose'],
['revealer-size'],
['vector'],
['vector-performance'],
['widgetorder'],
['widget-refcount'],
]

View File

@@ -0,0 +1,302 @@
#include <gtk/gtk.h>
#include <math.h>
#define MAX_SIZE 1024000
#define MAX_TIME (G_USEC_PER_SEC / 2)
static inline guint
quick_random (guint prev)
{
prev ^= prev << 13;
prev ^= prev >> 17;
prev ^= prev << 5;
return prev;
}
static guint comparisons = 0;
static int
compare_string_object (gconstpointer a,
gconstpointer b,
gpointer unused)
{
GtkStringObject *sa = (GtkStringObject *) a;
GtkStringObject *sb = (GtkStringObject *) b;
comparisons++;
return gtk_ordering_from_cmpfunc (strcmp (gtk_string_object_get_string (sa),
gtk_string_object_get_string (sb)));
}
static GtkSorter *
create_sorter (void)
{
return gtk_custom_sorter_new (compare_string_object, NULL, NULL);
}
G_GNUC_UNUSED static void
set_model (const char *testname,
GType type,
GListModel *source,
guint random)
{
gint64 start, end, total;
GtkSliceListModel *slice;
GtkSorter *sorter;
GListModel *sort;
guint size = 1000;
slice = gtk_slice_list_model_new (source, 0, size);
sorter = create_sorter ();
sort = g_object_new (type,
"sorter", sorter,
NULL);
g_object_unref (sorter);
while (TRUE)
{
comparisons = 0;
start = g_get_monotonic_time ();
g_object_set (sort, "model", slice, NULL);
end = g_get_monotonic_time ();
total = (end - start);
g_print ("\"%s\", \"%s\",%8u,%8uus,%8u\n",
testname,
g_type_name (type),
size,
(guint) total,
comparisons);
if (total > MAX_TIME)
break;
size *= 2;
if (4 * total > 2 * MAX_TIME)
break;
gtk_slice_list_model_set_size (slice, size);
g_object_set (sort, "model", NULL, NULL);
}
g_object_unref (sort);
g_object_unref (slice);
}
static void
append (const char *testname,
GType type,
GListModel *source,
guint random,
guint fraction)
{
gint64 start, end, total;
GtkSliceListModel *slice;
GtkSorter *sorter;
GListModel *sort;
guint size = 1000;
slice = gtk_slice_list_model_new (source, 0, (fraction - 1) * size / fraction);
sorter = create_sorter ();
sort = g_object_new (type,
"model", slice,
"sorter", sorter,
NULL);
g_object_unref (sorter);
while (TRUE)
{
gtk_slice_list_model_set_size (slice, (fraction - 1) * size / fraction);
comparisons = 0;
start = g_get_monotonic_time ();
gtk_slice_list_model_set_size (slice, size);
end = g_get_monotonic_time ();
total = (end - start);
g_print ("\"%s\", \"%s\",%8u,%8uus,%8u\n",
testname,
g_type_name (type),
size,
(guint) total,
comparisons);
if (total > MAX_TIME)
break;
size *= 2;
if (4 * total > 2 * MAX_TIME ||
size > MAX_SIZE)
break;
}
g_object_unref (sort);
g_object_unref (slice);
}
static void
append_half (const char *name,
GType type,
GListModel *source,
guint random)
{
append (name, type, source, random, 2);
}
static void
append_10th (const char *name,
GType type,
GListModel *source,
guint random)
{
append (name, type, source, random, 10);
}
static void
append_100th (const char *name,
GType type,
GListModel *source,
guint random)
{
append (name, type, source, random, 100);
}
static void
remove_test (const char *testname,
GType type,
GListModel *source,
guint random,
guint fraction)
{
gint64 start, end, total;
GtkSliceListModel *slice;
GtkSorter *sorter;
GListModel *sort;
guint size = 1000;
slice = gtk_slice_list_model_new (source, 0, size);
sorter = create_sorter ();
sort = g_object_new (type,
"model", slice,
"sorter", sorter,
NULL);
g_object_unref (sorter);
while (TRUE)
{
gtk_slice_list_model_set_size (slice, size);
comparisons = 0;
start = g_get_monotonic_time ();
gtk_slice_list_model_set_size (slice, (fraction - 1) * size / fraction);
end = g_get_monotonic_time ();
total = (end - start);
g_print ("\"%s\", \"%s\",%8u,%8uus,%8u\n",
testname,
g_type_name (type),
size,
(guint) total,
comparisons);
if (total > MAX_TIME)
break;
size *= 2;
if (4 * total > 2 * MAX_TIME ||
size > MAX_SIZE)
break;
}
g_object_unref (sort);
g_object_unref (slice);
}
static void
remove_half (const char *name,
GType type,
GListModel *source,
guint random)
{
remove_test (name, type, source, random, 2);
}
static void
remove_10th (const char *name,
GType type,
GListModel *source,
guint random)
{
remove_test (name, type, source, random, 10);
}
static void
remove_100th (const char *name,
GType type,
GListModel *source,
guint random)
{
remove_test (name, type, source, random, 100);
}
static void
run_test (GtkStringList *source,
const char * const *tests,
const char *test_name,
void (* test_func) (const char *name, GType type, GListModel *source, guint random))
{
GType types[] = {
GTK_TYPE_SORT_LIST_MODEL,
GTK_TYPE_SOR2_LIST_MODEL,
GTK_TYPE_SOR3_LIST_MODEL,
GTK_TYPE_SOR4_LIST_MODEL
};
guint random = g_random_int ();
guint i;
if (tests != NULL && !g_strv_contains (tests, test_name))
return;
for (i = 0; i < G_N_ELEMENTS (types); i++)
{
test_func (test_name, types[i], G_LIST_MODEL (source), random);
}
}
int
main (int argc, char *argv[])
{
GtkStringList *source;
guint random = g_random_int ();
guint i;
const char * const *tests;
gtk_test_init (&argc, &argv);
source = gtk_string_list_new (NULL);
for (i = 0; i < MAX_SIZE; i++)
{
gtk_string_list_take (source, g_strdup_printf ("%u", random));
random = quick_random (random);
}
if (argc < 2)
tests = NULL;
else
tests = (const char **) argv + 1;
g_print ("\"test\",\"model\",\"model size\",\"time\",\"comparisons\"\n");
run_test (source, tests, "set-model", set_model);
run_test (source, tests, "append-half", append_half);
run_test (source, tests, "append-10th", append_10th);
run_test (source, tests, "append-100th", append_100th);
run_test (source, tests, "remove-half", remove_half);
run_test (source, tests, "remove-10th", remove_10th);
run_test (source, tests, "remove-100th", remove_100th);
return g_test_run ();
}

View File

@@ -0,0 +1,443 @@
#include <gtk/gtk.h>
#define GTK_VECTOR_ELEMENT_TYPE gpointer
#define GTK_VECTOR_NAME pointer_vector
#define GTK_VECTOR_TYPE_NAME PointerVector
#include "../../gtk/gtkvectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE gpointer
#define GTK_VECTOR_NAME prealloc_vector
#define GTK_VECTOR_TYPE_NAME PreallocVector
#define GTK_VECTOR_PREALLOC 1024
#include "../../gtk/gtkvectorimpl.c"
static inline guint
quick_random (guint prev)
{
prev ^= prev << 13;
prev ^= prev >> 17;
prev ^= prev << 5;
return prev;
}
typedef struct {
const char *name;
gsize stack_space;
gpointer (* create) (gpointer space, gsize size);
void (* free) (gpointer array);
void (* reserve) (gpointer array, gsize size);
gpointer (* get) (gpointer array, gsize pos);
void (* append) (gpointer array, gpointer data);
void (* insert) (gpointer array, gsize pos, gpointer data);
} Array;
static gpointer
create_ptrarray (gpointer space,
gsize size)
{
return g_ptr_array_sized_new (size);
}
static void
free_ptrarray (gpointer array)
{
g_ptr_array_free (array, TRUE);
}
static void
reserve_ptrarray (gpointer array,
gsize size)
{
gsize length = ((GPtrArray *) array)->len;
if (length >= size)
return;
g_ptr_array_set_size (array, size);
g_ptr_array_set_size (array, length);
}
static gpointer
get_ptrarray (gpointer array,
gsize pos)
{
return g_ptr_array_index ((GPtrArray *) array, pos);
}
static void
append_ptrarray (gpointer array,
gpointer data)
{
g_ptr_array_add (array, data);
}
static void
insert_ptrarray (gpointer array,
gsize pos,
gpointer data)
{
g_ptr_array_insert (array, pos, data);
}
static gpointer
create_vector (gpointer space,
gsize size)
{
pointer_vector_init (space);
if (size)
pointer_vector_reserve ((PointerVector *) space, size);
return space;
}
static void
free_vector (gpointer array)
{
pointer_vector_clear (array);
}
static void
reserve_vector (gpointer array,
gsize size)
{
pointer_vector_reserve (array, size);
}
static gpointer
get_vector (gpointer array,
gsize pos)
{
return pointer_vector_get (array, pos);
}
static void
append_vector (gpointer array,
gpointer data)
{
pointer_vector_append (array, data);
}
static void
insert_vector (gpointer array,
gsize pos,
gpointer data)
{
pointer_vector_splice (array, pos, 0, &data, 1);
}
static gpointer
create_prealloc (gpointer space,
gsize size)
{
prealloc_vector_init (space);
if (size)
prealloc_vector_reserve ((PreallocVector *) space, size);
return space;
}
static void
free_prealloc (gpointer array)
{
prealloc_vector_clear (array);
}
static void
reserve_prealloc (gpointer array,
gsize size)
{
prealloc_vector_reserve (array, size);
}
static gpointer
get_prealloc (gpointer array,
gsize pos)
{
return prealloc_vector_get (array, pos);
}
static void
append_prealloc (gpointer array,
gpointer data)
{
prealloc_vector_append (array, data);
}
static void
insert_prealloc (gpointer array,
gsize pos,
gpointer data)
{
prealloc_vector_splice (array, pos, 0, &data, 1);
}
static void
do_random_access (const Array *klass,
guint random,
gsize size,
gsize max_size)
{
gpointer stack;
gpointer array;
guint i;
guint position;
gint64 start, end;
guint iterations = 10000000;
size = pow (100 * 100 * 100 * 100, (double) size / max_size);
if (klass->stack_space)
stack = g_alloca (klass->stack_space);
else
stack = NULL;
array = klass->create (stack, size);
for (i = 0; i < size; i++)
klass->append (array, GSIZE_TO_POINTER (i));
start = g_get_monotonic_time ();
for (i = 0; i < iterations; i++)
{
position = random % size;
random = quick_random (random);
g_assert_cmpint (position, ==, GPOINTER_TO_SIZE (klass->get (array, position)));
}
end = g_get_monotonic_time ();
g_print ("\"random access\",\"%s\", %zu, %g\n",
klass->name,
size,
((double)(end - start)) / iterations);
klass->free (array);
}
static void
do_linear_access (const Array *klass,
guint random,
gsize size,
gsize max_size)
{
gpointer stack;
gpointer array;
guint i;
gint64 start, end;
guint iterations = 1000000;
size = pow (100 * 100 * 100 * 100, (double) size / max_size);
if (klass->stack_space)
stack = g_alloca (klass->stack_space);
else
stack = NULL;
array = klass->create (stack, size);
for (i = 0; i < size; i++)
klass->append (array, GSIZE_TO_POINTER (i));
start = g_get_monotonic_time ();
for (i = 0; i < iterations; i++)
{
g_assert_cmpint (i % size, ==, GPOINTER_TO_SIZE (klass->get (array, i % size)));
}
end = g_get_monotonic_time ();
g_print ("\"linear access\", \"%s\", %zu, %g\n",
klass->name,
size,
((double)(end - start)) / iterations);
klass->free (array);
}
static void
do_append (const Array *klass,
guint random,
gsize size,
gsize max_size)
{
gpointer stack;
gpointer array;
guint i;
gint64 start, end;
int iterations = 10000;
size = pow (100 * 1000 * 1000, (double) size / max_size);
if (klass->stack_space)
stack = g_alloca (klass->stack_space);
else
stack = NULL;
array = klass->create (stack, size);
for (i = 0; i < size; i++)
klass->append (array, GSIZE_TO_POINTER (i));
start = g_get_monotonic_time ();
for (i = size; i < size + iterations; i++)
{
klass->append (array, GSIZE_TO_POINTER (i));
}
end = g_get_monotonic_time ();
klass->free (array);
g_print ("\"append\", \"%s\", %zu, %g\n",
klass->name,
size,
((double) (end - start)) / iterations);
}
static void
do_insert (const Array *klass,
guint random,
gsize size,
gsize max_size)
{
gpointer stack;
gpointer array;
guint i;
gint64 start, end;
int iterations = 10000;
size = pow (25 * 25 * 25 * 25, (double) size / max_size);
if (klass->stack_space)
stack = g_alloca (klass->stack_space);
else
stack = NULL;
array = klass->create (stack, size);
for (i = 0; i < size; i++)
klass->append (array, GSIZE_TO_POINTER (i));
start = g_get_monotonic_time ();
for (i = size; i < size + iterations; i++)
{
gsize position = random % size;
random = quick_random (random);
klass->insert (array, position, GSIZE_TO_POINTER (i));
}
end = g_get_monotonic_time ();
klass->free (array);
g_print ("\"insert\", \"%s\", %zu, %g\n",
klass->name,
size,
((double) (end - start)) / iterations);
}
static void
do_create (const Array *klass,
guint random,
gsize size,
gsize max_size)
{
gpointer stack;
gpointer array;
gsize i, j;
gint64 start, end;
gsize iterations = 100000;
size = pow (4 * 4 * 4 * 4, (double) size / max_size);
if (klass->stack_space)
stack = g_alloca (klass->stack_space);
else
stack = NULL;
start = g_get_monotonic_time ();
for (i = 0; i < iterations; i++)
{
gsize position = random % size;
random = quick_random (random);
array = klass->create (stack, size);
for (j = 0; j < size; j++)
klass->append (array, GSIZE_TO_POINTER (i));
klass->insert (array, position, GSIZE_TO_POINTER (i));
klass->free (array);
}
end = g_get_monotonic_time ();
g_print ("\"create\", \"%s\", %zu, %g\n",
klass->name,
size,
((double) (end - start)) / iterations);
}
const Array all_arrays[] = {
{
"ptrarray",
0,
create_ptrarray,
free_ptrarray,
reserve_ptrarray,
get_ptrarray,
append_ptrarray,
insert_ptrarray
},
{
"vector",
sizeof (PointerVector),
create_vector,
free_vector,
reserve_vector,
get_vector,
append_vector,
insert_vector
},
{
"preallocated-vector",
sizeof (PreallocVector),
create_prealloc,
free_prealloc,
reserve_prealloc,
get_prealloc,
append_prealloc,
insert_prealloc
}
};
static void
run_test (void (* test_func) (const Array *klass, guint random, gsize size, gsize max_size))
{
int max_size = 4;
int size;
int i;
guint random = g_random_int ();
for (i = 0; i < G_N_ELEMENTS (all_arrays); i++)
{
for (size = 1; size <= max_size; size++)
{
test_func (&all_arrays[i], random, size, max_size);
}
}
}
int
main (int argc, char *argv[])
{
gtk_test_init (&argc, &argv);
g_print ("\"test\",\"model\",\"model size\",\"time\"\n");
run_test (do_random_access);
run_test (do_linear_access);
run_test (do_append);
run_test (do_insert);
run_test (do_create);
return g_test_run ();
}

105
testsuite/gtk/vector.c Normal file
View File

@@ -0,0 +1,105 @@
/*
* Copyright © 2020 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 <locale.h>
#include <gtk/gtk.h>
static void
int_free_func (int data)
{
}
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME int_vector
#define GTK_VECTOR_TYPE_NAME IntVector
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME pre_int_vector
#define GTK_VECTOR_TYPE_NAME PreIntVector
#define GTK_VECTOR_PREALLOC 100
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME free_int_vector
#define GTK_VECTOR_TYPE_NAME FreeIntVector
#define GTK_VECTOR_FREE_FUNC int_free_func
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME pre_free_int_vector
#define GTK_VECTOR_TYPE_NAME PreFreeIntVector
#define GTK_VECTOR_PREALLOC 100
#define GTK_VECTOR_FREE_FUNC int_free_func
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_int_vector
#define GTK_VECTOR_TYPE_NAME NullIntVector
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_pre_int_vector
#define GTK_VECTOR_TYPE_NAME NullPreIntVector
#define GTK_VECTOR_PREALLOC 100
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_free_int_vector
#define GTK_VECTOR_TYPE_NAME NullFreeIntVector
#define GTK_VECTOR_FREE_FUNC int_free_func
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_pre_free_int_vector
#define GTK_VECTOR_TYPE_NAME NullPreFreeIntVector
#define GTK_VECTOR_PREALLOC 100
#define GTK_VECTOR_FREE_FUNC int_free_func
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
setlocale (LC_ALL, "C");
g_test_add_func ("/intvector/simple", int_vector_test_simple);
g_test_add_func ("/intvector/prealloc/simple", pre_int_vector_test_simple);
g_test_add_func ("/intvector/freefunc/simple", free_int_vector_test_simple);
g_test_add_func ("/intvector/prealloc/freefunc/simple", pre_free_int_vector_test_simple);
g_test_add_func ("/intvector/null/simple", null_int_vector_test_simple);
g_test_add_func ("/intvector/null/prealloc/simple", null_pre_int_vector_test_simple);
g_test_add_func ("/intvector/null/freefunc/simple", null_free_int_vector_test_simple);
g_test_add_func ("/intvector/null/prealloc/freefunc/simple", null_pre_free_int_vector_test_simple);
g_test_add_func ("/intvector/splice", int_vector_test_splice);
g_test_add_func ("/intvector/prealloc/splice", pre_int_vector_test_splice);
g_test_add_func ("/intvector/freefunc/splice", free_int_vector_test_splice);
g_test_add_func ("/intvector/prealloc/freefunc/splice", pre_free_int_vector_test_splice);
g_test_add_func ("/intvector/null/splice", null_int_vector_test_splice);
g_test_add_func ("/intvector/null/prealloc/splice", null_pre_int_vector_test_splice);
g_test_add_func ("/intvector/null/freefunc/splice", null_free_int_vector_test_splice);
g_test_add_func ("/intvector/null/prealloc/freefunc/splice", null_pre_free_int_vector_test_splice);
return g_test_run ();
}

117
testsuite/gtk/vectorimpl.c Normal file
View File

@@ -0,0 +1,117 @@
/*
* Copyright © 2020 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 <gtk/gtk.h>
#define GTK_VECTOR_NO_UNDEF
#include "../../gtk/gtkvectorimpl.c"
static void
gtk_vector(test_simple) (void)
{
GtkVector v;
gsize i;
gtk_vector(init) (&v);
for (i = 0; i < 1000; i++)
{
g_assert_cmpint (gtk_vector(get_size) (&v), ==, i);
g_assert_cmpint (gtk_vector(get_size) (&v), <=, gtk_vector(get_capacity) (&v));
gtk_vector(append) (&v, i);
#ifdef GTK_VECTOR_NULL_TERMINATED
g_assert_cmpint (*gtk_vector(index) (&v, gtk_vector(get_size) (&v)), ==, 0);
#endif
}
g_assert_cmpint (gtk_vector(get_size) (&v), ==, i);
g_assert_cmpint (gtk_vector(get_size) (&v), <=, gtk_vector(get_capacity) (&v));
for (i = 0; i < 1000; i++)
{
g_assert_cmpint (gtk_vector(get) (&v, i), ==, i);
}
gtk_vector(clear) (&v);
}
static void
gtk_vector(test_splice) (void)
{
GtkVector v;
gsize i, j, sum;
gsize pos, add, remove;
int additions[4] = { 0, 1, 2, 3 };
gtk_vector(init) (&v);
sum = 0;
for (i = 0; i < 1000; i++)
{
gsize old_size = gtk_vector(get_size) (&v);
pos = g_random_int_range (0, old_size + 1);
g_assert (pos <= old_size);
remove = g_random_int_range (0, 4);
remove = MIN (remove, old_size - pos);
add = g_random_int_range (0, 4);
for (j = 0; j < remove; j++)
sum -= gtk_vector(get) (&v, pos + j);
for (j = 0; j < add; j++)
sum += ++additions[j];
gtk_vector(splice) (&v, pos, remove, additions, add);
{
gsize total = 0;
for (j = 0; j < gtk_vector(get_size) (&v); j++)
total += gtk_vector(get) (&v, j);
g_assert_cmpint (total, ==, sum);
}
g_assert_cmpint (gtk_vector(get_size) (&v), ==, old_size + add - remove);
g_assert_cmpint (gtk_vector(get_size) (&v), <=, gtk_vector(get_capacity) (&v));
#ifdef GTK_VECTOR_NULL_TERMINATED
if (gtk_vector(get_size) (&v))
g_assert_cmpint (*gtk_vector(index) (&v, gtk_vector(get_size) (&v)), ==, 0);
#endif
for (j = 0; j < add; j++)
g_assert_cmpint (gtk_vector(get) (&v, pos + j), ==, additions[j]);
}
for (i = 0; i < gtk_vector(get_size) (&v); i++)
{
sum -= gtk_vector(get) (&v, i);
}
g_assert_cmpint (sum, ==, 0);
}
#undef _T_
#undef GtkVector
#undef gtk_vector_paste_more
#undef gtk_vector_paste
#undef gtk_vector
#undef GTK_VECTOR_REAL_SIZE
#undef GTK_VECTOR_ELEMENT_TYPE
#undef GTK_VECTOR_NAME
#undef GTK_VECTOR_TYPE_NAME
#undef GTK_VECTOR_PREALLOC
#undef GTK_VECTOR_NULL_TERMINATED