Compare commits
16 Commits
css-render
...
incrementa
Author | SHA1 | Date | |
---|---|---|---|
|
1e1d8cac0e | ||
|
7f54cf6294 | ||
|
6e0839a956 | ||
|
abd9a19a94 | ||
|
1f0ace7454 | ||
|
802c99d4b4 | ||
|
173534710a | ||
|
347959a112 | ||
|
1ae939cf5a | ||
|
edf523fe30 | ||
|
bfe2970a98 | ||
|
c7749b9098 | ||
|
2226fe0fca | ||
|
cbf9375f97 | ||
|
5297eb36b9 | ||
|
b69e7777cd |
@@ -676,7 +676,7 @@ create_color_grid (void)
|
|||||||
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
|
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
|
||||||
gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
|
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_sor3_list_model_new (gtk_color_list_new (0), NULL));
|
||||||
|
|
||||||
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
|
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
|
||||||
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
|
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
|
||||||
@@ -835,6 +835,54 @@ update_selection_average (GListModel *model,
|
|||||||
g_object_unref (color);
|
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;
|
static GtkWidget *window = NULL;
|
||||||
|
|
||||||
GtkWidget *
|
GtkWidget *
|
||||||
@@ -964,7 +1012,7 @@ do_listview_colors (GtkWidget *do_widget)
|
|||||||
button = gtk_button_new_with_mnemonic ("_Refill");
|
button = gtk_button_new_with_mnemonic ("_Refill");
|
||||||
g_signal_connect (button, "clicked",
|
g_signal_connect (button, "clicked",
|
||||||
G_CALLBACK (refill),
|
G_CALLBACK (refill),
|
||||||
gtk_sort_list_model_get_model (GTK_SORT_LIST_MODEL (model)));
|
gtk_sor3_list_model_get_model (GTK_SOR3_LIST_MODEL (model)));
|
||||||
|
|
||||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
|
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 });
|
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_signal_connect (dropdown, "notify::selected",
|
||||||
G_CALLBACK (limit_changed_cb),
|
G_CALLBACK (limit_changed_cb),
|
||||||
gtk_sort_list_model_get_model (GTK_SORT_LIST_MODEL (model)));
|
gtk_sor3_list_model_get_model (GTK_SOR3_LIST_MODEL (model)));
|
||||||
g_signal_connect (dropdown, "notify::selected",
|
g_signal_connect (dropdown, "notify::selected",
|
||||||
G_CALLBACK (limit_changed_cb2),
|
G_CALLBACK (limit_changed_cb2),
|
||||||
label);
|
label);
|
||||||
@@ -1022,18 +1070,30 @@ do_listview_colors (GtkWidget *do_widget)
|
|||||||
g_list_store_append (sorters, sorter);
|
g_list_store_append (sorters, sorter);
|
||||||
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), 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"));
|
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);
|
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
|
||||||
set_title (sorter, "Green");
|
set_title (sorter, "Green");
|
||||||
g_list_store_append (sorters, sorter);
|
g_list_store_append (sorters, sorter);
|
||||||
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), 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"));
|
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);
|
gtk_numeric_sorter_set_sort_order (GTK_NUMERIC_SORTER (sorter), GTK_SORT_DESCENDING);
|
||||||
set_title (sorter, "Blue");
|
set_title (sorter, "Blue");
|
||||||
g_list_store_append (sorters, sorter);
|
g_list_store_append (sorters, sorter);
|
||||||
gtk_multi_sorter_append (GTK_MULTI_SORTER (multi_sorter), 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");
|
set_title (multi_sorter, "RGB");
|
||||||
g_list_store_append (sorters, multi_sorter);
|
g_list_store_append (sorters, multi_sorter);
|
||||||
g_object_unref (multi_sorter);
|
g_object_unref (multi_sorter);
|
||||||
|
@@ -233,6 +233,8 @@
|
|||||||
#include <gtk/gtkslicelistmodel.h>
|
#include <gtk/gtkslicelistmodel.h>
|
||||||
#include <gtk/gtksnapshot.h>
|
#include <gtk/gtksnapshot.h>
|
||||||
#include <gtk/gtksorter.h>
|
#include <gtk/gtksorter.h>
|
||||||
|
#include <gtk/gtksor2listmodel.h>
|
||||||
|
#include <gtk/gtksor3listmodel.h>
|
||||||
#include <gtk/gtksortlistmodel.h>
|
#include <gtk/gtksortlistmodel.h>
|
||||||
#include <gtk/gtkstacksidebar.h>
|
#include <gtk/gtkstacksidebar.h>
|
||||||
#include <gtk/gtksizegroup.h>
|
#include <gtk/gtksizegroup.h>
|
||||||
|
@@ -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
|
|
@@ -393,7 +393,7 @@ gtk_css_provider_init (GtkCssProvider *css_provider)
|
|||||||
static void
|
static void
|
||||||
verify_tree_match_results (GtkCssProvider *provider,
|
verify_tree_match_results (GtkCssProvider *provider,
|
||||||
GtkCssNode *node,
|
GtkCssNode *node,
|
||||||
GtkArray *tree_rules)
|
GtkCssSelectorMatches *tree_rules)
|
||||||
{
|
{
|
||||||
#ifdef VERIFY_TREE
|
#ifdef VERIFY_TREE
|
||||||
GtkCssProviderPrivate *priv = gtk_css_provider_get_instance_private (provider);
|
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);
|
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;
|
found = TRUE;
|
||||||
break;
|
break;
|
||||||
@@ -459,22 +459,21 @@ gtk_css_style_provider_lookup (GtkStyleProvider *provider,
|
|||||||
GtkCssRuleset *ruleset;
|
GtkCssRuleset *ruleset;
|
||||||
guint j;
|
guint j;
|
||||||
int i;
|
int i;
|
||||||
GtkArray tree_rules_array;
|
GtkCssSelectorMatches tree_rules;
|
||||||
GtkCssRuleset *rules_stack[32];
|
|
||||||
|
|
||||||
if (_gtk_css_selector_tree_is_empty (priv->tree))
|
if (_gtk_css_selector_tree_is_empty (priv->tree))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
gtk_array_init (&tree_rules_array, (void**)rules_stack, 32);
|
gtk_css_selector_matches_init (&tree_rules);
|
||||||
_gtk_css_selector_tree_match_all (priv->tree, filter, node, &tree_rules_array);
|
_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)
|
if (ruleset->styles == NULL)
|
||||||
continue;
|
continue;
|
||||||
@@ -493,9 +492,8 @@ gtk_css_style_provider_lookup (GtkStyleProvider *provider,
|
|||||||
ruleset->styles[j].value);
|
ruleset->styles[j].value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_array_free (&tree_rules_array, NULL);
|
|
||||||
}
|
}
|
||||||
|
gtk_css_selector_matches_clear (&tree_rules);
|
||||||
|
|
||||||
if (change)
|
if (change)
|
||||||
*change = gtk_css_selector_tree_get_change_all (priv->tree, filter, node);
|
*change = gtk_css_selector_tree_get_change_all (priv->tree, filter, node);
|
||||||
|
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include "gtkcssprovider.h"
|
#include "gtkcssprovider.h"
|
||||||
#include "gtkstylecontextprivate.h"
|
#include "gtkstylecontextprivate.h"
|
||||||
#include "gtkarrayimplprivate.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
||||||
@@ -152,14 +151,14 @@ gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_array_insert_sorted (GtkArray *array,
|
gtk_css_selector_matches_insert_sorted (GtkCssSelectorMatches *matches,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
guint i;
|
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)
|
if (data == elem)
|
||||||
return;
|
return;
|
||||||
@@ -168,7 +167,7 @@ gtk_array_insert_sorted (GtkArray *array,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_array_insert (array, i, data);
|
gtk_css_selector_matches_splice (matches, i, 0, (gpointer[1]) { data }, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline gboolean
|
static inline gboolean
|
||||||
@@ -1877,7 +1876,7 @@ gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
|
gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
|
||||||
GtkArray *results)
|
GtkCssSelectorMatches *results)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
gpointer *matches;
|
gpointer *matches;
|
||||||
@@ -1887,7 +1886,7 @@ gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; matches[i] != NULL; i++)
|
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
|
static gboolean
|
||||||
@@ -1895,7 +1894,7 @@ gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
|
|||||||
const GtkCountingBloomFilter *filter,
|
const GtkCountingBloomFilter *filter,
|
||||||
gboolean match_filter,
|
gboolean match_filter,
|
||||||
GtkCssNode *node,
|
GtkCssNode *node,
|
||||||
GtkArray *results)
|
GtkCssSelectorMatches *results)
|
||||||
{
|
{
|
||||||
const GtkCssSelectorTree *prev;
|
const GtkCssSelectorTree *prev;
|
||||||
GtkCssNode *child;
|
GtkCssNode *child;
|
||||||
@@ -1932,7 +1931,7 @@ void
|
|||||||
_gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
|
_gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
|
||||||
const GtkCountingBloomFilter *filter,
|
const GtkCountingBloomFilter *filter,
|
||||||
GtkCssNode *node,
|
GtkCssNode *node,
|
||||||
GtkArray *out_tree_rules)
|
GtkCssSelectorMatches *out_tree_rules)
|
||||||
{
|
{
|
||||||
const GtkCssSelectorTree *iter;
|
const GtkCssSelectorTree *iter;
|
||||||
|
|
||||||
@@ -2117,8 +2116,7 @@ subdivide_infos (GByteArray *array,
|
|||||||
GHashTableIter iter;
|
GHashTableIter iter;
|
||||||
guint max_count;
|
guint max_count;
|
||||||
gpointer key, value;
|
gpointer key, value;
|
||||||
void *exact_matches_stack[8];
|
GtkCssSelectorMatches exact_matches;
|
||||||
GtkArray exact_matches_array;
|
|
||||||
gint32 res;
|
gint32 res;
|
||||||
guint i;
|
guint i;
|
||||||
|
|
||||||
@@ -2160,7 +2158,7 @@ subdivide_infos (GByteArray *array,
|
|||||||
matched_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
|
matched_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
|
||||||
remaining_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++)
|
for (i = 0; i < n_infos; i++)
|
||||||
{
|
{
|
||||||
GtkCssSelectorRuleSetInfo *info = infos[i];
|
GtkCssSelectorRuleSetInfo *info = infos[i];
|
||||||
@@ -2171,7 +2169,7 @@ subdivide_infos (GByteArray *array,
|
|||||||
if (info->current_selector == NULL)
|
if (info->current_selector == NULL)
|
||||||
{
|
{
|
||||||
/* Matches current node */
|
/* 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)
|
if (info->selector_match != NULL)
|
||||||
*info->selector_match = GUINT_TO_POINTER (tree_offset);
|
*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;
|
res = array->len;
|
||||||
g_byte_array_append (array, (guint8 *)gtk_array_get_data (&exact_matches_array),
|
g_byte_array_append (array, (guint8 *) gtk_css_selector_matches_get_data (&exact_matches),
|
||||||
exact_matches_array.len * sizeof (gpointer));
|
gtk_css_selector_matches_get_size (&exact_matches) * sizeof (gpointer));
|
||||||
|
|
||||||
gtk_array_free (&exact_matches_array, NULL);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
|
res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
|
||||||
|
gtk_css_selector_matches_clear (&exact_matches);
|
||||||
get_tree (array, tree_offset)->matches_offset = res;
|
get_tree (array, tree_offset)->matches_offset = res;
|
||||||
|
|
||||||
res = subdivide_infos (array, matched_infos, n_matched, tree_offset);
|
res = subdivide_infos (array, matched_infos, n_matched, tree_offset);
|
||||||
|
@@ -21,7 +21,12 @@
|
|||||||
#include "gtk/gtkcountingbloomfilterprivate.h"
|
#include "gtk/gtkcountingbloomfilterprivate.h"
|
||||||
#include "gtk/gtkcsstypesprivate.h"
|
#include "gtk/gtkcsstypesprivate.h"
|
||||||
#include "gtk/gtkcssparserprivate.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
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@@ -46,7 +51,7 @@ void _gtk_css_selector_tree_free (GtkCssSelectorTree *
|
|||||||
void _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
|
void _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
|
||||||
const GtkCountingBloomFilter *filter,
|
const GtkCountingBloomFilter *filter,
|
||||||
GtkCssNode *node,
|
GtkCssNode *node,
|
||||||
GtkArray *out_tree_rules);
|
GtkCssSelectorMatches *out_tree_rules);
|
||||||
GtkCssChange gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
|
GtkCssChange gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
|
||||||
const GtkCountingBloomFilter *filter,
|
const GtkCountingBloomFilter *filter,
|
||||||
GtkCssNode *node);
|
GtkCssNode *node);
|
||||||
|
@@ -55,6 +55,14 @@
|
|||||||
#include "gdk/gdktextureprivate.h"
|
#include "gdk/gdktextureprivate.h"
|
||||||
#include "gdk/gdkprofilerprivate.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
|
* SECTION:gtkicontheme
|
||||||
* @Short_description: Looking up icons by name
|
* @Short_description: Looking up icons by name
|
||||||
@@ -2276,13 +2284,13 @@ real_choose_icon (GtkIconTheme *self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
icon_name_list_add_icon (GPtrArray *icons,
|
icon_name_list_add_icon (GtkStrvBuilder *icons,
|
||||||
const gchar *dir_suffix,
|
const gchar *dir_suffix,
|
||||||
gchar *icon_name)
|
gchar *icon_name)
|
||||||
{
|
{
|
||||||
if (dir_suffix)
|
if (dir_suffix)
|
||||||
g_ptr_array_add (icons, g_strconcat (icon_name, dir_suffix, NULL));
|
gtk_strv_builder_append (icons, g_strconcat (icon_name, dir_suffix, NULL));
|
||||||
g_ptr_array_add (icons, icon_name);
|
gtk_strv_builder_append (icons, icon_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GtkIconPaintable *
|
static GtkIconPaintable *
|
||||||
@@ -2296,7 +2304,7 @@ choose_icon (GtkIconTheme *self,
|
|||||||
{
|
{
|
||||||
gboolean has_regular = FALSE, has_symbolic = FALSE;
|
gboolean has_regular = FALSE, has_symbolic = FALSE;
|
||||||
GtkIconPaintable *icon;
|
GtkIconPaintable *icon;
|
||||||
GPtrArray *new_names;
|
GtkStrvBuilder new_names;
|
||||||
const gchar *dir_suffix;
|
const gchar *dir_suffix;
|
||||||
guint i;
|
guint i;
|
||||||
|
|
||||||
@@ -2327,73 +2335,70 @@ choose_icon (GtkIconTheme *self,
|
|||||||
|
|
||||||
if ((flags & GTK_ICON_LOOKUP_FORCE_REGULAR) && has_symbolic)
|
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++)
|
for (i = 0; icon_names[i]; i++)
|
||||||
{
|
{
|
||||||
if (icon_name_is_symbolic (icon_names[i], -1))
|
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
|
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++)
|
for (i = 0; icon_names[i]; i++)
|
||||||
{
|
{
|
||||||
if (icon_name_is_symbolic (icon_names[i], -1))
|
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,
|
icon = real_choose_icon (self,
|
||||||
(const gchar **) new_names->pdata,
|
(const char **) gtk_strv_builder_get_data (&new_names),
|
||||||
size,
|
size,
|
||||||
scale,
|
scale,
|
||||||
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
||||||
non_blocking);
|
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)
|
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++)
|
for (i = 0; icon_names[i]; i++)
|
||||||
{
|
{
|
||||||
if (!icon_name_is_symbolic (icon_names[i], -1))
|
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
|
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++)
|
for (i = 0; icon_names[i]; i++)
|
||||||
{
|
{
|
||||||
if (!icon_name_is_symbolic (icon_names[i], -1))
|
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,
|
icon = real_choose_icon (self,
|
||||||
(const gchar **) new_names->pdata,
|
(const char **) gtk_strv_builder_get_data (&new_names),
|
||||||
size,
|
size,
|
||||||
scale,
|
scale,
|
||||||
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
||||||
non_blocking);
|
non_blocking);
|
||||||
|
|
||||||
g_ptr_array_free (new_names, TRUE);
|
gtk_strv_builder_clear (&new_names);
|
||||||
}
|
}
|
||||||
else if (dir_suffix)
|
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++)
|
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,
|
icon = real_choose_icon (self,
|
||||||
(const gchar **) new_names->pdata,
|
(const char **) gtk_strv_builder_get_data (&new_names),
|
||||||
size,
|
size,
|
||||||
scale,
|
scale,
|
||||||
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
||||||
non_blocking);
|
non_blocking);
|
||||||
|
|
||||||
g_ptr_array_free (new_names, TRUE);
|
gtk_strv_builder_clear (&new_names);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -95,7 +95,6 @@
|
|||||||
#include "gdk/gdk-private.h"
|
#include "gdk/gdk-private.h"
|
||||||
#include "gsk/gskprivate.h"
|
#include "gsk/gskprivate.h"
|
||||||
#include "gsk/gskrendernodeprivate.h"
|
#include "gsk/gskrendernodeprivate.h"
|
||||||
#include "gtkarrayimplprivate.h"
|
|
||||||
#include "gtknative.h"
|
#include "gtknative.h"
|
||||||
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
@@ -138,6 +137,13 @@
|
|||||||
#include "a11y/gtkaccessibility.h"
|
#include "a11y/gtkaccessibility.h"
|
||||||
#include "inspector/window.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 GtkWindowGroup *gtk_main_get_window_group (GtkWidget *widget);
|
||||||
|
|
||||||
static gint pre_initialized = FALSE;
|
static gint pre_initialized = FALSE;
|
||||||
@@ -1321,8 +1327,7 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
|
|||||||
double x, y;
|
double x, y;
|
||||||
GtkWidget *prev;
|
GtkWidget *prev;
|
||||||
gboolean seen_ancestor;
|
gboolean seen_ancestor;
|
||||||
GtkArray target_array;
|
GtkWidgetStack target_array;
|
||||||
GtkWidget *stack_targets[16];
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (old_target == new_target)
|
if (old_target == new_target)
|
||||||
@@ -1376,19 +1381,19 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
|
|||||||
widget = _gtk_widget_get_parent (widget);
|
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))
|
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;
|
crossing.direction = GTK_CROSSING_IN;
|
||||||
|
|
||||||
seen_ancestor = FALSE;
|
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)
|
if (i < gtk_widget_stack_get_size (&target_array) - 1)
|
||||||
crossing.new_descendent = gtk_array_index (&target_array, i + 1);
|
crossing.new_descendent = gtk_widget_stack_get (&target_array, i + 1);
|
||||||
else
|
else
|
||||||
crossing.new_descendent = NULL;
|
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_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_array_free (&target_array, NULL);
|
gtk_widget_stack_clear (&target_array);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GtkWidget *
|
static GtkWidget *
|
||||||
@@ -1994,13 +1999,12 @@ gtk_propagate_event_internal (GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
gint handled_event = FALSE;
|
gint handled_event = FALSE;
|
||||||
GtkWidget *target = widget;
|
GtkWidget *target = widget;
|
||||||
GtkArray widget_array;
|
GtkWidgetStack widget_array;
|
||||||
GtkWidget *stack_widgets[16];
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* First, propagate event down */
|
/* First, propagate event down */
|
||||||
gtk_array_init (&widget_array, (void**)stack_widgets, 16);
|
gtk_widget_stack_init (&widget_array);
|
||||||
gtk_array_add (&widget_array, g_object_ref (widget));
|
gtk_widget_stack_append (&widget_array, g_object_ref (widget));
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@@ -2008,16 +2012,16 @@ gtk_propagate_event_internal (GtkWidget *widget,
|
|||||||
if (!widget)
|
if (!widget)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
gtk_array_add (&widget_array, g_object_ref (widget));
|
gtk_widget_stack_append (&widget_array, g_object_ref (widget));
|
||||||
|
|
||||||
if (widget == topmost)
|
if (widget == topmost)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = widget_array.len - 1;
|
i = gtk_widget_stack_get_size (&widget_array) - 1;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
widget = gtk_array_index (&widget_array, i);
|
widget = gtk_widget_stack_get (&widget_array, i);
|
||||||
|
|
||||||
if (!_gtk_widget_is_sensitive (widget))
|
if (!_gtk_widget_is_sensitive (widget))
|
||||||
{
|
{
|
||||||
@@ -2050,9 +2054,9 @@ gtk_propagate_event_internal (GtkWidget *widget,
|
|||||||
* parents can see the button and motion
|
* parents can see the button and motion
|
||||||
* events of the children.
|
* 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
|
/* Scroll events are special cased here because it
|
||||||
* feels wrong when scrolling a GtkViewport, say,
|
* 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;
|
return handled_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -34,6 +34,11 @@
|
|||||||
|
|
||||||
#include "gtk/gskpango.h"
|
#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
|
* SECTION:gtksnapshot
|
||||||
@@ -54,6 +59,85 @@
|
|||||||
* use gtk_snapshot_new().
|
* 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)
|
G_DEFINE_TYPE (GtkSnapshot, gtk_snapshot, GDK_TYPE_SNAPSHOT)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -61,11 +145,11 @@ gtk_snapshot_dispose (GObject *object)
|
|||||||
{
|
{
|
||||||
GtkSnapshot *snapshot = GTK_SNAPSHOT (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));
|
gsk_render_node_unref (gtk_snapshot_to_node (snapshot));
|
||||||
|
|
||||||
g_assert (snapshot->state_stack == NULL);
|
g_assert (gtk_snapshot_states_is_empty (&snapshot->state_stack));
|
||||||
g_assert (snapshot->nodes == NULL);
|
g_assert (gtk_snapshot_nodes_is_empty (&snapshot->nodes));
|
||||||
|
|
||||||
G_OBJECT_CLASS (gtk_snapshot_parent_class)->dispose (object);
|
G_OBJECT_CLASS (gtk_snapshot_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
@@ -112,15 +196,15 @@ gtk_snapshot_push_state (GtkSnapshot *snapshot,
|
|||||||
GskTransform *transform,
|
GskTransform *transform,
|
||||||
GtkSnapshotCollectFunc collect_func)
|
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;
|
GtkSnapshotState *state;
|
||||||
|
|
||||||
g_array_set_size (snapshot->state_stack, n_states + 1);
|
gtk_snapshot_states_set_size (&snapshot->state_stack, n_states + 1);
|
||||||
state = &g_array_index (snapshot->state_stack, GtkSnapshotState, n_states);
|
state = gtk_snapshot_states_get (&snapshot->state_stack, n_states);
|
||||||
|
|
||||||
state->transform = gsk_transform_ref (transform);
|
state->transform = gsk_transform_ref (transform);
|
||||||
state->collect_func = collect_func;
|
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;
|
state->n_nodes = 0;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
@@ -129,17 +213,21 @@ gtk_snapshot_push_state (GtkSnapshot *snapshot,
|
|||||||
static GtkSnapshotState *
|
static GtkSnapshotState *
|
||||||
gtk_snapshot_get_current_state (const GtkSnapshot *snapshot)
|
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 *
|
static GtkSnapshotState *
|
||||||
gtk_snapshot_get_previous_state (const GtkSnapshot *snapshot)
|
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
|
static void
|
||||||
@@ -162,9 +250,8 @@ gtk_snapshot_new (void)
|
|||||||
|
|
||||||
snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL);
|
snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL);
|
||||||
|
|
||||||
snapshot->state_stack = g_array_new (FALSE, TRUE, sizeof (GtkSnapshotState));
|
gtk_snapshot_states_init (&snapshot->state_stack);
|
||||||
g_array_set_clear_func (snapshot->state_stack, (GDestroyNotify)gtk_snapshot_state_clear);
|
gtk_snapshot_nodes_init (&snapshot->nodes);
|
||||||
snapshot->nodes = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_render_node_unref);
|
|
||||||
|
|
||||||
gtk_snapshot_push_state (snapshot,
|
gtk_snapshot_push_state (snapshot,
|
||||||
NULL,
|
NULL,
|
||||||
@@ -1029,30 +1116,28 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
|
|||||||
guint state_index;
|
guint state_index;
|
||||||
GskRenderNode *node;
|
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.");
|
g_warning ("Too many gtk_snapshot_pop() calls.");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
state = gtk_snapshot_get_current_state (snapshot);
|
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)
|
if (state->collect_func)
|
||||||
{
|
{
|
||||||
node = state->collect_func (snapshot,
|
node = state->collect_func (snapshot,
|
||||||
state,
|
state,
|
||||||
(GskRenderNode **) snapshot->nodes->pdata + state->start_node_index,
|
(GskRenderNode **) gtk_snapshot_nodes_index (&snapshot->nodes, state->start_node_index),
|
||||||
state->n_nodes);
|
state->n_nodes);
|
||||||
|
|
||||||
/* The collect func may not modify the state stack... */
|
/* 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 */
|
/* Remove all the state's nodes from the list of nodes */
|
||||||
g_assert (state->start_node_index + state->n_nodes == snapshot->nodes->len);
|
g_assert (state->start_node_index + state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
|
||||||
g_ptr_array_remove_range (snapshot->nodes,
|
gtk_snapshot_nodes_splice (&snapshot->nodes, state->start_node_index, state->n_nodes, NULL, 0);
|
||||||
snapshot->nodes->len - state->n_nodes,
|
|
||||||
state->n_nodes);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1063,10 +1148,10 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
|
|||||||
/* move the nodes to the parent */
|
/* move the nodes to the parent */
|
||||||
previous_state = gtk_snapshot_get_previous_state (snapshot);
|
previous_state = gtk_snapshot_get_previous_state (snapshot);
|
||||||
previous_state->n_nodes += state->n_nodes;
|
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;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -1081,7 +1166,7 @@ gtk_snapshot_append_node_internal (GtkSnapshot *snapshot,
|
|||||||
|
|
||||||
if (current_state)
|
if (current_state)
|
||||||
{
|
{
|
||||||
g_ptr_array_add (snapshot->nodes, node);
|
gtk_snapshot_nodes_append (&snapshot->nodes, node);
|
||||||
current_state->n_nodes ++;
|
current_state->n_nodes ++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1162,16 +1247,14 @@ gtk_snapshot_to_node (GtkSnapshot *snapshot)
|
|||||||
result = gtk_snapshot_pop_internal (snapshot);
|
result = gtk_snapshot_pop_internal (snapshot);
|
||||||
|
|
||||||
/* We should have exactly our initial state */
|
/* 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);
|
gtk_snapshot_states_clear (&snapshot->state_stack);
|
||||||
g_ptr_array_free (snapshot->nodes, TRUE);
|
gtk_snapshot_nodes_clear (&snapshot->nodes);
|
||||||
|
|
||||||
snapshot->state_stack = NULL;
|
|
||||||
snapshot->nodes = NULL;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@@ -24,76 +24,6 @@
|
|||||||
|
|
||||||
G_BEGIN_DECLS
|
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,
|
void gtk_snapshot_append_text (GtkSnapshot *snapshot,
|
||||||
PangoFont *font,
|
PangoFont *font,
|
||||||
PangoGlyphString *glyphs,
|
PangoGlyphString *glyphs,
|
||||||
|
443
gtk/gtksor2listmodel.c
Normal file
443
gtk/gtksor2listmodel.c
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
/*
|
||||||
|
* 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_CONSTRUCT_ONLY | 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
57
gtk/gtksor2listmodel.h
Normal 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
604
gtk/gtksor3listmodel.c
Normal 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
57
gtk/gtksor3listmodel.h
Normal 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__ */
|
@@ -142,7 +142,8 @@ gtk_sorter_compare (GtkSorter *self,
|
|||||||
{
|
{
|
||||||
GtkOrdering result;
|
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);
|
g_return_val_if_fail (item1 && item2, GTK_ORDERING_EQUAL);
|
||||||
|
|
||||||
if (item1 == item2)
|
if (item1 == item2)
|
||||||
|
283
gtk/gtkvectorimpl.c
Normal file
283
gtk/gtkvectorimpl.c
Normal 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
|
@@ -371,6 +371,8 @@ gtk_public_sources = files([
|
|||||||
'gtkslicelistmodel.c',
|
'gtkslicelistmodel.c',
|
||||||
'gtksnapshot.c',
|
'gtksnapshot.c',
|
||||||
'gtksorter.c',
|
'gtksorter.c',
|
||||||
|
'gtksor2listmodel.c',
|
||||||
|
'gtksor3listmodel.c',
|
||||||
'gtksortlistmodel.c',
|
'gtksortlistmodel.c',
|
||||||
'gtkspinbutton.c',
|
'gtkspinbutton.c',
|
||||||
'gtkspinner.c',
|
'gtkspinner.c',
|
||||||
@@ -640,6 +642,8 @@ gtk_public_headers = files([
|
|||||||
'gtkslicelistmodel.h',
|
'gtkslicelistmodel.h',
|
||||||
'gtksnapshot.h',
|
'gtksnapshot.h',
|
||||||
'gtksorter.h',
|
'gtksorter.h',
|
||||||
|
'gtksor2listmodel.h',
|
||||||
|
'gtksor3listmodel.h',
|
||||||
'gtksortlistmodel.h',
|
'gtksortlistmodel.h',
|
||||||
'gtkspinbutton.h',
|
'gtkspinbutton.h',
|
||||||
'gtkspinner.h',
|
'gtkspinner.h',
|
||||||
|
@@ -75,6 +75,8 @@ tests = [
|
|||||||
['typename'],
|
['typename'],
|
||||||
['displayclose'],
|
['displayclose'],
|
||||||
['revealer-size'],
|
['revealer-size'],
|
||||||
|
['vector'],
|
||||||
|
['vector-performance'],
|
||||||
['widgetorder'],
|
['widgetorder'],
|
||||||
['widget-refcount'],
|
['widget-refcount'],
|
||||||
]
|
]
|
||||||
|
443
testsuite/gtk/vector-performance.c
Normal file
443
testsuite/gtk/vector-performance.c
Normal 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
105
testsuite/gtk/vector.c
Normal 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
117
testsuite/gtk/vectorimpl.c
Normal 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
|
||||||
|
|
Reference in New Issue
Block a user