Compare commits
39 Commits
add-mutter
...
arraystore
Author | SHA1 | Date | |
---|---|---|---|
|
d233b10559 | ||
|
b4f9f252d7 | ||
|
373c06b148 | ||
|
c84cd97fbc | ||
|
6b7d295cfe | ||
|
481d438380 | ||
|
f4e1057431 | ||
|
fa0166180e | ||
|
13d84c202b | ||
|
5846794075 | ||
|
201da90e23 | ||
|
8d4fcabf01 | ||
|
3f8735a488 | ||
|
a4dc248512 | ||
|
ef451b056a | ||
|
59aaabb161 | ||
|
663ca21328 | ||
|
77435d31fa | ||
|
6edb8f096f | ||
|
c4dfee8860 | ||
|
8ac1e77c9a | ||
|
65ceb6c15a | ||
|
f70d10f6ac | ||
|
0bbd083d79 | ||
|
102d2986c6 | ||
|
c74201ca87 | ||
|
b09019a5b4 | ||
|
1dd08ad8db | ||
|
eb0704855f | ||
|
c4c1d4a1b3 | ||
|
e6756f605e | ||
|
1f4b8e089e | ||
|
c0e08db739 | ||
|
1c337d350d | ||
|
81e675dfbf | ||
|
8556221429 | ||
|
1b4109a7fd | ||
|
df0786be7a | ||
|
3f545da08d |
@@ -225,6 +225,7 @@
|
||||
<file>listview_minesweeper.c</file>
|
||||
<file>listview_settings.c</file>
|
||||
<file>listview_weather.c</file>
|
||||
<file>listview_words.c</file>
|
||||
<file>list_store.c</file>
|
||||
<file>markup.c</file>
|
||||
<file>modelbutton.c</file>
|
||||
|
241
demos/gtk-demo/listview_words.c
Normal file
241
demos/gtk-demo/listview_words.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/* Lists/Words
|
||||
*
|
||||
* This demo shows filtering a long list - of words.
|
||||
*
|
||||
* You should have the file `/usr/share/dict/words` installed for
|
||||
* this demo to work.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
static GtkWidget *progress;
|
||||
|
||||
const char *factory_text =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<interface>\n"
|
||||
" <template class='GtkListItem'>\n"
|
||||
" <property name='child'>\n"
|
||||
" <object class='GtkLabel'>\n"
|
||||
" <property name='ellipsize'>end</property>\n"
|
||||
" <property name='xalign'>0</property>\n"
|
||||
" <binding name='label'>\n"
|
||||
" <lookup name='string' type='GtkStringObject'>\n"
|
||||
" <lookup name='item'>GtkListItem</lookup>\n"
|
||||
" </lookup>\n"
|
||||
" </binding>\n"
|
||||
" </object>\n"
|
||||
" </property>\n"
|
||||
" </template>\n"
|
||||
"</interface>\n";
|
||||
|
||||
static void
|
||||
update_title_cb (GtkFilterListModel *model)
|
||||
{
|
||||
guint total;
|
||||
char *title;
|
||||
guint pending;
|
||||
|
||||
total = g_list_model_get_n_items (gtk_filter_list_model_get_model (model));
|
||||
pending = gtk_filter_list_model_get_pending (model);
|
||||
|
||||
title = g_strdup_printf ("%u lines", g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
|
||||
gtk_widget_set_visible (progress, pending != 0);
|
||||
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), (total - pending) / (double) total);
|
||||
gtk_window_set_title (GTK_WINDOW (window), title);
|
||||
g_free (title);
|
||||
}
|
||||
|
||||
static void
|
||||
read_lines_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
GBufferedInputStream *stream = G_BUFFERED_INPUT_STREAM (object);
|
||||
GtkStringList *stringlist = data;
|
||||
GError *error = NULL;
|
||||
gsize size;
|
||||
GPtrArray *lines;
|
||||
gssize n_filled;
|
||||
const char *buffer, *newline;
|
||||
|
||||
n_filled = g_buffered_input_stream_fill_finish (stream, result, &error);
|
||||
if (n_filled < 0)
|
||||
{
|
||||
g_print ("Could not read data: %s\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer = g_buffered_input_stream_peek_buffer (stream, &size);
|
||||
|
||||
if (n_filled == 0)
|
||||
{
|
||||
if (size)
|
||||
gtk_string_list_take (stringlist, g_utf8_make_valid (buffer, size));
|
||||
return;
|
||||
}
|
||||
|
||||
lines = NULL;
|
||||
while ((newline = memchr (buffer, '\n', size)))
|
||||
{
|
||||
if (newline > buffer)
|
||||
{
|
||||
if (lines == NULL)
|
||||
lines = g_ptr_array_new_with_free_func (g_free);
|
||||
g_ptr_array_add (lines, g_utf8_make_valid (buffer, newline - buffer));
|
||||
}
|
||||
if (g_input_stream_skip (G_INPUT_STREAM (stream), newline - buffer + 1, NULL, &error) < 0)
|
||||
{
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
buffer = g_buffered_input_stream_peek_buffer (stream, &size);
|
||||
}
|
||||
if (lines == NULL)
|
||||
{
|
||||
g_buffered_input_stream_set_buffer_size (stream, g_buffered_input_stream_get_buffer_size (stream) + 4096);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ptr_array_add (lines, NULL);
|
||||
gtk_string_list_splice (stringlist, g_list_model_get_n_items (G_LIST_MODEL (stringlist)), 0, (const char **) lines->pdata);
|
||||
g_ptr_array_free (lines, TRUE);
|
||||
}
|
||||
|
||||
g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data);
|
||||
}
|
||||
|
||||
static void
|
||||
file_is_open_cb (GObject *file,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GFileInputStream *file_stream;
|
||||
GBufferedInputStream *stream;
|
||||
|
||||
file_stream = g_file_read_finish (G_FILE (file), result, &error);
|
||||
if (file_stream == NULL)
|
||||
{
|
||||
g_print ("Could not open file: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
stream = G_BUFFERED_INPUT_STREAM (g_buffered_input_stream_new (G_INPUT_STREAM (file_stream)));
|
||||
g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data);
|
||||
g_object_unref (stream);
|
||||
}
|
||||
|
||||
static void
|
||||
load_file (GtkStringList *list,
|
||||
GFile *file)
|
||||
{
|
||||
gtk_string_list_splice (list, 0, g_list_model_get_n_items (G_LIST_MODEL (list)), NULL);
|
||||
g_file_read_async (file, G_PRIORITY_HIGH_IDLE, NULL, file_is_open_cb, list);
|
||||
}
|
||||
|
||||
static void
|
||||
file_selected_cb (GtkWidget *button,
|
||||
GtkStringList *stringlist)
|
||||
{
|
||||
GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (button));
|
||||
|
||||
if (file)
|
||||
{
|
||||
load_file (stringlist, file);
|
||||
g_object_unref (file);
|
||||
}
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_listview_words (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *header, *listview, *sw, *vbox, *search_entry, *open_button, *overlay;
|
||||
GtkFilterListModel *filter_model;
|
||||
GtkNoSelection *selection;
|
||||
GtkStringList *stringlist;
|
||||
GtkFilter *filter;
|
||||
GtkExpression *expression;
|
||||
GFile *file;
|
||||
|
||||
file = g_file_new_for_path ("/usr/share/dict/words");
|
||||
if (g_file_query_exists (file, NULL))
|
||||
{
|
||||
stringlist = gtk_string_list_new (NULL);
|
||||
load_file (stringlist, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
char **words;
|
||||
words = g_strsplit ("lorem ipsum dolor sit amet consectetur adipisci elit sed eiusmod tempor incidunt labore et dolore magna aliqua ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat", " ", -1);
|
||||
stringlist = gtk_string_list_new ((const char **) words);
|
||||
g_strfreev (words);
|
||||
}
|
||||
|
||||
filter = gtk_string_filter_new ();
|
||||
expression = gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string");
|
||||
gtk_string_filter_set_expression (GTK_STRING_FILTER (filter), expression);
|
||||
gtk_expression_unref (expression);
|
||||
filter_model = gtk_filter_list_model_new (G_LIST_MODEL (stringlist), filter);
|
||||
gtk_filter_list_model_set_incremental (filter_model, TRUE);
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 600);
|
||||
|
||||
header = gtk_header_bar_new ();
|
||||
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
|
||||
open_button = gtk_file_chooser_button_new ("_Open", GTK_FILE_CHOOSER_ACTION_OPEN);
|
||||
g_signal_connect (open_button, "file-set", G_CALLBACK (file_selected_cb), stringlist);
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), open_button);
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer*)&window);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_window_set_child (GTK_WINDOW (window), vbox);
|
||||
|
||||
search_entry = gtk_search_entry_new ();
|
||||
g_object_bind_property (search_entry, "text", filter, "search", 0);
|
||||
gtk_box_append (GTK_BOX (vbox), search_entry);
|
||||
|
||||
overlay = gtk_overlay_new ();
|
||||
gtk_box_append (GTK_BOX (vbox), overlay);
|
||||
|
||||
progress = gtk_progress_bar_new ();
|
||||
gtk_widget_set_halign (progress, GTK_ALIGN_FILL);
|
||||
gtk_widget_set_valign (progress, GTK_ALIGN_START);
|
||||
gtk_widget_set_hexpand (progress, TRUE);
|
||||
gtk_overlay_add_overlay (GTK_OVERLAY (overlay), progress);
|
||||
|
||||
sw = gtk_scrolled_window_new ();
|
||||
gtk_overlay_set_child (GTK_OVERLAY (overlay), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_builder_list_item_factory_new_from_bytes (NULL,
|
||||
g_bytes_new_static (factory_text, strlen (factory_text))));
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
|
||||
selection = gtk_no_selection_new (G_LIST_MODEL (filter_model));
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selection));
|
||||
g_object_unref (selection);
|
||||
|
||||
g_signal_connect (filter_model, "items-changed", G_CALLBACK (update_title_cb), progress);
|
||||
g_signal_connect (filter_model, "notify::pending", G_CALLBACK (update_title_cb), progress);
|
||||
update_title_cb (filter_model);
|
||||
|
||||
g_object_unref (filter_model);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
@@ -49,6 +49,7 @@ demos = files([
|
||||
'listview_minesweeper.c',
|
||||
'listview_settings.c',
|
||||
'listview_weather.c',
|
||||
'listview_words.c',
|
||||
'markup.c',
|
||||
'modelbutton.c',
|
||||
'overlay.c',
|
||||
|
@@ -348,6 +348,7 @@ GtkBitset
|
||||
gtk_bitset_ref
|
||||
gtk_bitset_unref
|
||||
gtk_bitset_new_empty
|
||||
gtk_bitset_new_range
|
||||
gtk_bitset_copy
|
||||
<SUBSECTION>
|
||||
gtk_bitset_contains
|
||||
@@ -355,6 +356,9 @@ gtk_bitset_is_empty
|
||||
gtk_bitset_equals
|
||||
gtk_bitset_get_minimum
|
||||
gtk_bitset_get_maximum
|
||||
gtk_bitset_get_size
|
||||
gtk_bitset_get_size_in_range
|
||||
gtk_bitset_get_nth
|
||||
<SUBSECTION>
|
||||
gtk_bitset_remove_all
|
||||
gtk_bitset_add
|
||||
@@ -1540,6 +1544,9 @@ gtk_filter_list_model_set_model
|
||||
gtk_filter_list_model_get_model
|
||||
gtk_filter_list_model_set_filter
|
||||
gtk_filter_list_model_get_filter
|
||||
gtk_filter_list_model_set_incremental
|
||||
gtk_filter_list_model_get_incremental
|
||||
gtk_filter_list_model_get_pending
|
||||
<SUBSECTION Standard>
|
||||
GTK_FILTER_LIST_MODEL
|
||||
GTK_IS_FILTER_LIST_MODEL
|
||||
|
@@ -295,7 +295,6 @@ gtk_cell_accessible_action_do_action (AtkAction *action,
|
||||
GtkCellAccessible *cell = GTK_CELL_ACCESSIBLE (action);
|
||||
GtkCellAccessibleParent *parent;
|
||||
|
||||
cell = GTK_CELL_ACCESSIBLE (action);
|
||||
if (gtk_accessible_get_widget (GTK_ACCESSIBLE (cell)) == NULL)
|
||||
return FALSE;
|
||||
|
||||
|
@@ -65,7 +65,7 @@ static gunichar
|
||||
gtk_password_entry_accessible_get_character_at_offset (AtkText *atk_text,
|
||||
gint offset)
|
||||
{
|
||||
GtkText *text = get_text_widget (GTK_ACCESSIBLE (atk_text));
|
||||
GtkText *text;
|
||||
char *contents, *index;
|
||||
gunichar result;
|
||||
|
||||
|
@@ -44,6 +44,7 @@
|
||||
#include <gtk/gtkappchooserbutton.h>
|
||||
#include <gtk/gtkapplication.h>
|
||||
#include <gtk/gtkapplicationwindow.h>
|
||||
#include <gtk/gtkarraystore.h>
|
||||
#include <gtk/gtkaspectframe.h>
|
||||
#include <gtk/gtkassistant.h>
|
||||
#include <gtk/gtkbinlayout.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
|
287
gtk/gtkarraystore.c
Normal file
287
gtk/gtkarraystore.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* 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 "config.h"
|
||||
|
||||
#include "gtkarraystore.h"
|
||||
|
||||
#define GTK_VECTOR_ELEMENT_TYPE GObject *
|
||||
#define GTK_VECTOR_FREE_FUNC g_object_unref
|
||||
#include "gtkvectorimpl.c"
|
||||
|
||||
/**
|
||||
* SECTION:gtkarraystore
|
||||
* @title: GtkArrayStore
|
||||
* @short_description: A simple array implementation of #GListModel
|
||||
*
|
||||
* #GtkArrayStore is a simple implementation of #GListModel that stores all
|
||||
* items in memory.
|
||||
*
|
||||
* It provides appending, deletions, and lookups in O(1) time and insertions
|
||||
* in O(N) time. it is implemented using an array.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GtkArrayStore:
|
||||
*
|
||||
* #GtkArrayStore is an opaque data structure and can only be accessed
|
||||
* using the following functions.
|
||||
**/
|
||||
|
||||
struct _GtkArrayStore
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GType item_type;
|
||||
GtkVector items;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ITEM_TYPE,
|
||||
N_PROPERTIES
|
||||
};
|
||||
|
||||
static void gtk_array_store_iface_init (GListModelInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkArrayStore, gtk_array_store, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_array_store_iface_init));
|
||||
|
||||
static void
|
||||
gtk_array_store_dispose (GObject *object)
|
||||
{
|
||||
GtkArrayStore *self = GTK_ARRAY_STORE (object);
|
||||
|
||||
gtk_vector_clear (&self->items);
|
||||
|
||||
G_OBJECT_CLASS (gtk_array_store_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_array_store_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkArrayStore *self = GTK_ARRAY_STORE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, self->item_type);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_array_store_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkArrayStore *self = GTK_ARRAY_STORE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_ITEM_TYPE: /* construct-only */
|
||||
g_assert (g_type_is_a (g_value_get_gtype (value), G_TYPE_OBJECT));
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_array_store_class_init (GtkArrayStoreClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_array_store_dispose;
|
||||
object_class->get_property = gtk_array_store_get_property;
|
||||
object_class->set_property = gtk_array_store_set_property;
|
||||
|
||||
/**
|
||||
* GtkArrayStore:item-type:
|
||||
*
|
||||
* The type of items contained in this list self. Items must be
|
||||
* subclasses of #GObject.
|
||||
**/
|
||||
g_object_class_install_property (object_class, PROP_ITEM_TYPE,
|
||||
g_param_spec_gtype ("item-type", "", "", G_TYPE_OBJECT,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_array_store_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkArrayStore *self = GTK_ARRAY_STORE (list);
|
||||
|
||||
return self->item_type;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_array_store_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkArrayStore *self = GTK_ARRAY_STORE (list);
|
||||
|
||||
return gtk_vector_get_size (&self->items);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_array_store_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkArrayStore *self = GTK_ARRAY_STORE (list);
|
||||
|
||||
if (position >= gtk_vector_get_size (&self->items))
|
||||
return NULL;
|
||||
|
||||
return g_object_ref (gtk_vector_get (&self->items, position));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_array_store_iface_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_array_store_get_item_type;
|
||||
iface->get_n_items = gtk_array_store_get_n_items;
|
||||
iface->get_item = gtk_array_store_get_item;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_array_store_init (GtkArrayStore *self)
|
||||
{
|
||||
gtk_vector_init (&self->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_array_store_new:
|
||||
* @item_type: the #GType of items in the list
|
||||
*
|
||||
* Creates a new #GtkArrayStore with items of type @item_type. @item_type
|
||||
* must be a subclass of #GObject.
|
||||
*
|
||||
* Returns: a new #GtkArrayStore
|
||||
*/
|
||||
GtkArrayStore *
|
||||
gtk_array_store_new (GType item_type)
|
||||
{
|
||||
g_return_val_if_fail (g_type_is_a (item_type, G_TYPE_OBJECT), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_ARRAY_STORE,
|
||||
"item-type", item_type,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_array_store_append:
|
||||
* @self: a #GtkArrayStore
|
||||
* @item: (type GObject): the new item
|
||||
*
|
||||
* Appends @item to @self. @item must be of type #GtkArrayStore:item-type.
|
||||
*
|
||||
* This function takes a ref on @item.
|
||||
*
|
||||
* Use gtk_array_store_splice() to append multiple items at the same time
|
||||
* efficiently.
|
||||
*/
|
||||
void
|
||||
gtk_array_store_append (GtkArrayStore *self,
|
||||
gpointer item)
|
||||
{
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_ARRAY_STORE (self));
|
||||
g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (item), self->item_type));
|
||||
|
||||
position = gtk_vector_get_size (&self->items);
|
||||
gtk_vector_append (&self->items, g_object_ref (item));
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_array_store_remove_all:
|
||||
* @self: a #GtkArrayStore
|
||||
*
|
||||
* Removes all items from @self.
|
||||
*
|
||||
* Since: 2.44
|
||||
*/
|
||||
void
|
||||
gtk_array_store_remove_all (GtkArrayStore *self)
|
||||
{
|
||||
guint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_ARRAY_STORE (self));
|
||||
|
||||
n_items = gtk_vector_get_size (&self->items);
|
||||
gtk_vector_clear (&self->items);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_array_store_splice:
|
||||
* @self: a #GtkArrayStore
|
||||
* @position: the position at which to make the change
|
||||
* @n_removals: the number of items to remove
|
||||
* @additions: (array length=n_additions) (element-type GObject): the items to add
|
||||
* @n_additions: the number of items to add
|
||||
*
|
||||
* Changes @self by removing @n_removals items and adding @n_additions
|
||||
* items to it. @additions must contain @n_additions items of type
|
||||
* #GtkArrayStore:item-type. %NULL is not permitted.
|
||||
*
|
||||
* This function is more efficient than gtk_array_store_insert() and
|
||||
* gtk_array_store_remove(), because it only emits
|
||||
* #GListModel::items-changed once for the change.
|
||||
*
|
||||
* This function takes a ref on each item in @additions.
|
||||
*
|
||||
* The parameters @position and @n_removals must be correct (ie:
|
||||
* @position + @n_removals must be less than or equal to the length of
|
||||
* the list at the time this function is called).
|
||||
*
|
||||
* Since: 2.44
|
||||
*/
|
||||
void
|
||||
gtk_array_store_splice (GtkArrayStore *self,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
gpointer *additions,
|
||||
guint n_additions)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (GTK_IS_ARRAY_STORE (self));
|
||||
g_return_if_fail (position + n_removals >= position); /* overflow */
|
||||
g_return_if_fail (position + n_removals <= gtk_vector_get_size (&self->items));
|
||||
|
||||
for (i = 0; i < n_additions; i++)
|
||||
g_object_ref (additions[i]);
|
||||
|
||||
gtk_vector_splice (&self->items, position, n_removals, (GObject **) additions, n_additions);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, n_removals, n_additions);
|
||||
}
|
54
gtk/gtkarraystore.h
Normal file
54
gtk/gtkarraystore.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_ARRAY_STORE_H__
|
||||
#define __GTK_ARRAY_STORE_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_ARRAY_STORE (gtk_array_store_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE(GtkArrayStore, gtk_array_store, GTK, ARRAY_STORE, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkArrayStore * gtk_array_store_new (GType item_type);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_array_store_append (GtkArrayStore *store,
|
||||
gpointer item);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_array_store_remove_all (GtkArrayStore *store);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_array_store_splice (GtkArrayStore *store,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
gpointer *additions,
|
||||
guint n_additions);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_ARRAY_STORE_H__ */
|
@@ -193,12 +193,82 @@ gtk_bitset_get_maximum (const GtkBitset *self)
|
||||
return roaring_bitmap_maximum (&self->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_get_size:
|
||||
* @self: a #GtkBitSet
|
||||
*
|
||||
* Gets the number of values that were added to the set.
|
||||
* For example, if the set is empty, 0 is returned.
|
||||
*
|
||||
* Note that this function returns a #guint64, because when all values are
|
||||
* set, the return value is #G_MAXUINT + 1. Unless you are sure this cannot
|
||||
* happen (it can't with #GListModel), be sure to use a 64bit type.
|
||||
*
|
||||
* Returns: The number of values in the set.
|
||||
**/
|
||||
guint64
|
||||
gtk_bitset_get_size (const GtkBitset *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
|
||||
return roaring_bitmap_get_cardinality (&self->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_get_size_in_range:
|
||||
* @self: a #GtkBitSet
|
||||
* @first: the first element to include
|
||||
* @last: the last element to include
|
||||
*
|
||||
* Gets the number of values that are part of the set from @first to @last
|
||||
* (inclusive).
|
||||
*
|
||||
* Note that this function returns a #guint64, because when all values are
|
||||
* set, the return value is #G_MAXUINT + 1. Unless you are sure this cannot
|
||||
* happen (it can't with #GListModel), be sure to use a 64bit type.
|
||||
*
|
||||
* Returns: The number of values in the set from @first to @last.
|
||||
**/
|
||||
guint64
|
||||
gtk_bitset_get_size_in_range (const GtkBitset *self,
|
||||
guint first,
|
||||
guint last)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
g_return_val_if_fail (last >= first, 0);
|
||||
|
||||
return roaring_bitmap_range_cardinality (&self->roaring, first, ((uint64_t) last) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_get_nth:
|
||||
* @self: a #GtkBitset
|
||||
* @nth: index of the item to get
|
||||
*
|
||||
* Returns the value of the @nth item in self.
|
||||
*
|
||||
* If @nth is >= the size of @self, 0 is returned.
|
||||
*
|
||||
* Returns: the value of the @nth item in @self
|
||||
**/
|
||||
guint
|
||||
gtk_bitset_get_nth (const GtkBitset *self,
|
||||
guint nth)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
if (!roaring_bitmap_select (&self->roaring, nth, &result))
|
||||
return 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_new_empty:
|
||||
*
|
||||
* Creates a new empty bitset.
|
||||
*
|
||||
* Returns: A new empty bitset.
|
||||
* Returns: A new empty bitset
|
||||
**/
|
||||
GtkBitset *
|
||||
gtk_bitset_new_empty (void)
|
||||
@@ -214,6 +284,28 @@ gtk_bitset_new_empty (void)
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_new_range:
|
||||
* @start: first value to add
|
||||
* @n_items: number of consecutive values to add
|
||||
*
|
||||
* Creates a bitset with the given range set.
|
||||
*
|
||||
* Returns: A new bitset
|
||||
**/
|
||||
GtkBitset *
|
||||
gtk_bitset_new_range (guint start,
|
||||
guint n_items)
|
||||
{
|
||||
GtkBitset *self;
|
||||
|
||||
self = gtk_bitset_new_empty ();
|
||||
|
||||
gtk_bitset_add_range (self, start, n_items);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_copy:
|
||||
* @self: a #GtkBitset
|
||||
|
@@ -48,6 +48,15 @@ GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_equals (const GtkBitset *self,
|
||||
const GtkBitset *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint64 gtk_bitset_get_size (const GtkBitset *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint64 gtk_bitset_get_size_in_range (const GtkBitset *self,
|
||||
guint first,
|
||||
guint last);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_bitset_get_nth (const GtkBitset *self,
|
||||
guint nth);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_bitset_get_minimum (const GtkBitset *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_bitset_get_maximum (const GtkBitset *self);
|
||||
@@ -56,6 +65,9 @@ GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_bitset_new_empty (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_bitset_copy (const GtkBitset *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_bitset_new_range (guint start,
|
||||
guint n_items);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_remove_all (GtkBitset *self);
|
||||
|
@@ -391,9 +391,9 @@ gtk_css_provider_init (GtkCssProvider *css_provider)
|
||||
}
|
||||
|
||||
static void
|
||||
verify_tree_match_results (GtkCssProvider *provider,
|
||||
GtkCssNode *node,
|
||||
GtkArray *tree_rules)
|
||||
verify_tree_match_results (GtkCssProvider *provider,
|
||||
GtkCssNode *node,
|
||||
GtkCssSelectorMatches *tree_rules)
|
||||
{
|
||||
#ifdef VERIFY_TREE
|
||||
GtkCssProviderPrivate *priv = gtk_css_provider_get_instance_private (provider);
|
||||
@@ -407,9 +407,9 @@ verify_tree_match_results (GtkCssProvider *provider,
|
||||
|
||||
ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
|
||||
|
||||
for (j = 0; j < tree_rules->len; j++)
|
||||
for (j = 0; j < gtk_css_selector_matches_get_size (tree_rules); j++)
|
||||
{
|
||||
if (ruleset == gtk_array_index (tree_rules, j))
|
||||
if (ruleset == gtk_css_selector_matches_get (tree_rules, j))
|
||||
{
|
||||
found = TRUE;
|
||||
break;
|
||||
@@ -459,22 +459,21 @@ gtk_css_style_provider_lookup (GtkStyleProvider *provider,
|
||||
GtkCssRuleset *ruleset;
|
||||
guint j;
|
||||
int i;
|
||||
GtkArray tree_rules_array;
|
||||
GtkCssRuleset *rules_stack[32];
|
||||
GtkCssSelectorMatches tree_rules;
|
||||
|
||||
if (_gtk_css_selector_tree_is_empty (priv->tree))
|
||||
return;
|
||||
|
||||
gtk_array_init (&tree_rules_array, (void**)rules_stack, 32);
|
||||
_gtk_css_selector_tree_match_all (priv->tree, filter, node, &tree_rules_array);
|
||||
gtk_css_selector_matches_init (&tree_rules);
|
||||
_gtk_css_selector_tree_match_all (priv->tree, filter, node, &tree_rules);
|
||||
|
||||
if (tree_rules_array.len > 0)
|
||||
if (!gtk_css_selector_matches_is_empty (&tree_rules))
|
||||
{
|
||||
verify_tree_match_results (css_provider, node, &tree_rules_array);
|
||||
verify_tree_match_results (css_provider, node, &tree_rules);
|
||||
|
||||
for (i = tree_rules_array.len - 1; i >= 0; i--)
|
||||
for (i = gtk_css_selector_matches_get_size (&tree_rules) - 1; i >= 0; i--)
|
||||
{
|
||||
ruleset = gtk_array_index (&tree_rules_array, i);
|
||||
ruleset = gtk_css_selector_matches_get (&tree_rules, i);
|
||||
|
||||
if (ruleset->styles == NULL)
|
||||
continue;
|
||||
@@ -493,9 +492,8 @@ gtk_css_style_provider_lookup (GtkStyleProvider *provider,
|
||||
ruleset->styles[j].value);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_array_free (&tree_rules_array, NULL);
|
||||
}
|
||||
gtk_css_selector_matches_clear (&tree_rules);
|
||||
|
||||
if (change)
|
||||
*change = gtk_css_selector_tree_get_change_all (priv->tree, filter, node);
|
||||
|
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "gtkcssprovider.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
#include "gtkarrayimplprivate.h"
|
||||
|
||||
#include <errno.h>
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1500
|
||||
@@ -152,14 +151,14 @@ gtk_css_selector_tree_get_matches (const GtkCssSelectorTree *tree)
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_array_insert_sorted (GtkArray *array,
|
||||
gpointer data)
|
||||
gtk_css_selector_matches_insert_sorted (GtkCssSelectorMatches *matches,
|
||||
gpointer data)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < array->len; i++)
|
||||
for (i = 0; i < gtk_css_selector_matches_get_size (matches); i++)
|
||||
{
|
||||
gpointer elem = gtk_array_index (array, i);
|
||||
gpointer elem = gtk_css_selector_matches_get (matches, i);
|
||||
|
||||
if (data == elem)
|
||||
return;
|
||||
@@ -168,7 +167,7 @@ gtk_array_insert_sorted (GtkArray *array,
|
||||
break;
|
||||
}
|
||||
|
||||
gtk_array_insert (array, i, data);
|
||||
gtk_css_selector_matches_splice (matches, i, 0, (gpointer[1]) { data }, 1);
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
@@ -1877,7 +1876,7 @@ gtk_css_selector_tree_get_change (const GtkCssSelectorTree *tree,
|
||||
|
||||
static void
|
||||
gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
|
||||
GtkArray *results)
|
||||
GtkCssSelectorMatches *results)
|
||||
{
|
||||
int i;
|
||||
gpointer *matches;
|
||||
@@ -1887,7 +1886,7 @@ gtk_css_selector_tree_found_match (const GtkCssSelectorTree *tree,
|
||||
return;
|
||||
|
||||
for (i = 0; matches[i] != NULL; i++)
|
||||
gtk_array_insert_sorted (results, matches[i]);
|
||||
gtk_css_selector_matches_insert_sorted (results, matches[i]);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -1895,7 +1894,7 @@ gtk_css_selector_tree_match (const GtkCssSelectorTree *tree,
|
||||
const GtkCountingBloomFilter *filter,
|
||||
gboolean match_filter,
|
||||
GtkCssNode *node,
|
||||
GtkArray *results)
|
||||
GtkCssSelectorMatches *results)
|
||||
{
|
||||
const GtkCssSelectorTree *prev;
|
||||
GtkCssNode *child;
|
||||
@@ -1932,7 +1931,7 @@ void
|
||||
_gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
|
||||
const GtkCountingBloomFilter *filter,
|
||||
GtkCssNode *node,
|
||||
GtkArray *out_tree_rules)
|
||||
GtkCssSelectorMatches *out_tree_rules)
|
||||
{
|
||||
const GtkCssSelectorTree *iter;
|
||||
|
||||
@@ -2117,8 +2116,7 @@ subdivide_infos (GByteArray *array,
|
||||
GHashTableIter iter;
|
||||
guint max_count;
|
||||
gpointer key, value;
|
||||
void *exact_matches_stack[8];
|
||||
GtkArray exact_matches_array;
|
||||
GtkCssSelectorMatches exact_matches;
|
||||
gint32 res;
|
||||
guint i;
|
||||
|
||||
@@ -2160,7 +2158,7 @@ subdivide_infos (GByteArray *array,
|
||||
matched_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
|
||||
remaining_infos = g_alloca (sizeof (GtkCssSelectorRuleSetInfo *) * n_infos);
|
||||
|
||||
gtk_array_init (&exact_matches_array, (void**)exact_matches_stack, 8);
|
||||
gtk_css_selector_matches_init (&exact_matches);
|
||||
for (i = 0; i < n_infos; i++)
|
||||
{
|
||||
GtkCssSelectorRuleSetInfo *info = infos[i];
|
||||
@@ -2171,7 +2169,7 @@ subdivide_infos (GByteArray *array,
|
||||
if (info->current_selector == NULL)
|
||||
{
|
||||
/* Matches current node */
|
||||
gtk_array_add (&exact_matches_array, info->match);
|
||||
gtk_css_selector_matches_append (&exact_matches, info->match);
|
||||
if (info->selector_match != NULL)
|
||||
*info->selector_match = GUINT_TO_POINTER (tree_offset);
|
||||
}
|
||||
@@ -2188,17 +2186,16 @@ subdivide_infos (GByteArray *array,
|
||||
}
|
||||
}
|
||||
|
||||
if (exact_matches_array.len > 0)
|
||||
if (!gtk_css_selector_matches_is_empty (&exact_matches))
|
||||
{
|
||||
gtk_array_add (&exact_matches_array, NULL); /* Null terminate */
|
||||
gtk_css_selector_matches_append (&exact_matches, NULL); /* Null terminate */
|
||||
res = array->len;
|
||||
g_byte_array_append (array, (guint8 *)gtk_array_get_data (&exact_matches_array),
|
||||
exact_matches_array.len * sizeof (gpointer));
|
||||
|
||||
gtk_array_free (&exact_matches_array, NULL);
|
||||
g_byte_array_append (array, (guint8 *) gtk_css_selector_matches_get_data (&exact_matches),
|
||||
gtk_css_selector_matches_get_size (&exact_matches) * sizeof (gpointer));
|
||||
}
|
||||
else
|
||||
res = GTK_CSS_SELECTOR_TREE_EMPTY_OFFSET;
|
||||
gtk_css_selector_matches_clear (&exact_matches);
|
||||
get_tree (array, tree_offset)->matches_offset = res;
|
||||
|
||||
res = subdivide_infos (array, matched_infos, n_matched, tree_offset);
|
||||
|
@@ -21,7 +21,12 @@
|
||||
#include "gtk/gtkcountingbloomfilterprivate.h"
|
||||
#include "gtk/gtkcsstypesprivate.h"
|
||||
#include "gtk/gtkcssparserprivate.h"
|
||||
#include "gtk/gtkarrayimplprivate.h"
|
||||
|
||||
#define GTK_VECTOR_ELEMENT_TYPE gpointer
|
||||
#define GTK_VECTOR_TYPE_NAME GtkCssSelectorMatches
|
||||
#define GTK_VECTOR_NAME gtk_css_selector_matches
|
||||
#define GTK_VECTOR_PREALLOC 32
|
||||
#include "gtk/gtkvectorimpl.c"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -45,8 +50,8 @@ int _gtk_css_selector_compare (const GtkCssSelector *a,
|
||||
void _gtk_css_selector_tree_free (GtkCssSelectorTree *tree);
|
||||
void _gtk_css_selector_tree_match_all (const GtkCssSelectorTree *tree,
|
||||
const GtkCountingBloomFilter *filter,
|
||||
GtkCssNode *node,
|
||||
GtkArray *out_tree_rules);
|
||||
GtkCssNode *node,
|
||||
GtkCssSelectorMatches *out_tree_rules);
|
||||
GtkCssChange gtk_css_selector_tree_get_change_all (const GtkCssSelectorTree *tree,
|
||||
const GtkCountingBloomFilter *filter,
|
||||
GtkCssNode *node);
|
||||
|
@@ -943,7 +943,7 @@ gtk_drop_down_set_from_strings (GtkDropDown *self,
|
||||
|
||||
set_default_factory (self);
|
||||
|
||||
model = G_LIST_MODEL (gtk_string_list_new ((const char **)texts));
|
||||
model = G_LIST_MODEL (gtk_string_list_new (texts));
|
||||
gtk_drop_down_set_model (self, model);
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "gtkfilterlistmodel.h"
|
||||
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
@@ -35,30 +35,22 @@
|
||||
* listmodel.
|
||||
* It hides some elements from the other model according to
|
||||
* criteria given by a #GtkFilter.
|
||||
*
|
||||
* The model can be set up to do incremental searching, so that
|
||||
* filtering long lists doesn't block the UI. See
|
||||
* gtk_filter_list_model_set_incremental() for details.
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FILTER,
|
||||
PROP_INCREMENTAL,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_MODEL,
|
||||
PROP_PENDING,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
typedef struct _FilterNode FilterNode;
|
||||
typedef struct _FilterAugment FilterAugment;
|
||||
|
||||
struct _FilterNode
|
||||
{
|
||||
guint visible : 1;
|
||||
};
|
||||
|
||||
struct _FilterAugment
|
||||
{
|
||||
guint n_items;
|
||||
guint n_visible;
|
||||
};
|
||||
|
||||
struct _GtkFilterListModel
|
||||
{
|
||||
GObject parent_instance;
|
||||
@@ -67,8 +59,11 @@ struct _GtkFilterListModel
|
||||
GListModel *model;
|
||||
GtkFilter *filter;
|
||||
GtkFilterMatch strictness;
|
||||
gboolean incremental;
|
||||
|
||||
GtkRbTree *items; /* NULL if strictness != GTK_FILTER_MATCH_SOME */
|
||||
GtkBitset *matches; /* NULL if strictness != GTK_FILTER_MATCH_SOME */
|
||||
GtkBitset *pending; /* not yet filtered items or NULL if all filtered */
|
||||
guint pending_cb; /* idle callback handle */
|
||||
};
|
||||
|
||||
struct _GtkFilterListModelClass
|
||||
@@ -78,119 +73,6 @@ struct _GtkFilterListModelClass
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_augment (GtkRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FilterNode *node = _node;
|
||||
FilterAugment *aug = _aug;
|
||||
|
||||
aug->n_items = 1;
|
||||
aug->n_visible = node->visible ? 1 : 0;
|
||||
|
||||
if (left)
|
||||
{
|
||||
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_visible += left_aug->n_visible;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_visible += right_aug->n_visible;
|
||||
}
|
||||
}
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth_filtered (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *out_unfiltered)
|
||||
{
|
||||
FilterNode *node, *tmp;
|
||||
guint unfiltered;
|
||||
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
unfiltered = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_visible)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
position -= aug->n_visible;
|
||||
unfiltered += aug->n_items;
|
||||
}
|
||||
|
||||
if (node->visible)
|
||||
{
|
||||
if (position == 0)
|
||||
break;
|
||||
position--;
|
||||
}
|
||||
|
||||
unfiltered++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
if (out_unfiltered)
|
||||
*out_unfiltered = unfiltered;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *out_filtered)
|
||||
{
|
||||
FilterNode *node, *tmp;
|
||||
guint filtered;
|
||||
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
filtered = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
position -= aug->n_items;
|
||||
filtered += aug->n_visible;
|
||||
}
|
||||
|
||||
if (position == 0)
|
||||
break;
|
||||
|
||||
position--;
|
||||
if (node->visible)
|
||||
filtered++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
if (out_filtered)
|
||||
*out_filtered = filtered;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_filter_list_model_get_item_type (GListModel *list)
|
||||
{
|
||||
@@ -203,8 +85,6 @@ static guint
|
||||
gtk_filter_list_model_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
|
||||
FilterAugment *aug;
|
||||
FilterNode *node;
|
||||
|
||||
switch (self->strictness)
|
||||
{
|
||||
@@ -215,18 +95,12 @@ gtk_filter_list_model_get_n_items (GListModel *list)
|
||||
return g_list_model_get_n_items (self->model);
|
||||
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
break;
|
||||
return gtk_bitset_get_size (self->matches);
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = gtk_rb_tree_get_root (self->items);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
aug = gtk_rb_tree_get_augment (self->items, node);
|
||||
return aug->n_visible;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
@@ -246,7 +120,9 @@ gtk_filter_list_model_get_item (GListModel *list,
|
||||
break;
|
||||
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
|
||||
unfiltered = gtk_bitset_get_nth (self->matches, position);
|
||||
if (unfiltered == 0 && position >= gtk_bitset_get_size (self->matches))
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -268,8 +144,8 @@ G_DEFINE_TYPE_WITH_CODE (GtkFilterListModel, gtk_filter_list_model, G_TYPE_OBJEC
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_filter_list_model_model_init))
|
||||
|
||||
static gboolean
|
||||
gtk_filter_list_model_run_filter (GtkFilterListModel *self,
|
||||
guint position)
|
||||
gtk_filter_list_model_run_filter_on_item (GtkFilterListModel *self,
|
||||
guint position)
|
||||
{
|
||||
gpointer item;
|
||||
gboolean visible;
|
||||
@@ -284,26 +160,120 @@ gtk_filter_list_model_run_filter (GtkFilterListModel *self,
|
||||
return visible;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_filter_list_model_add_items (GtkFilterListModel *self,
|
||||
FilterNode *after,
|
||||
guint position,
|
||||
guint n_items)
|
||||
static void
|
||||
gtk_filter_list_model_run_filter (GtkFilterListModel *self,
|
||||
guint n_steps)
|
||||
{
|
||||
FilterNode *node;
|
||||
guint i, n_visible;
|
||||
GtkBitsetIter iter;
|
||||
guint i, pos;
|
||||
gboolean more;
|
||||
|
||||
n_visible = 0;
|
||||
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
if (self->pending == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0, more = gtk_bitset_iter_init_first (&iter, self->pending, &pos);
|
||||
i < n_steps && more;
|
||||
i++, more = gtk_bitset_iter_next (&iter, &pos))
|
||||
{
|
||||
node = gtk_rb_tree_insert_before (self->items, after);
|
||||
node->visible = gtk_filter_list_model_run_filter (self, position + i);
|
||||
if (node->visible)
|
||||
n_visible++;
|
||||
if (gtk_filter_list_model_run_filter_on_item (self, pos))
|
||||
gtk_bitset_add (self->matches, pos);
|
||||
}
|
||||
|
||||
return n_visible;
|
||||
if (more)
|
||||
gtk_bitset_remove_range_closed (self->pending, 0, pos);
|
||||
else
|
||||
g_clear_pointer (&self->pending, gtk_bitset_unref);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PENDING]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_stop_filtering (GtkFilterListModel *self)
|
||||
{
|
||||
gboolean notify_pending = self->pending != NULL;
|
||||
|
||||
g_clear_pointer (&self->pending, gtk_bitset_unref);
|
||||
g_clear_handle_id (&self->pending_cb, g_source_remove);
|
||||
|
||||
if (notify_pending)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PENDING]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_emit_items_changed_for_changes (GtkFilterListModel *self,
|
||||
GtkBitset *old)
|
||||
{
|
||||
GtkBitset *changes;
|
||||
|
||||
changes = gtk_bitset_copy (self->matches);
|
||||
gtk_bitset_difference (changes, old);
|
||||
if (!gtk_bitset_is_empty (changes))
|
||||
{
|
||||
guint min, max;
|
||||
|
||||
min = gtk_bitset_get_minimum (changes);
|
||||
max = gtk_bitset_get_maximum (changes);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self),
|
||||
min > 0 ? gtk_bitset_get_size_in_range (self->matches, 0, min - 1) : 0,
|
||||
gtk_bitset_get_size_in_range (old, min, max),
|
||||
gtk_bitset_get_size_in_range (self->matches, min, max));
|
||||
}
|
||||
gtk_bitset_unref (changes);
|
||||
gtk_bitset_unref (old);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_filter_list_model_run_filter_cb (gpointer data)
|
||||
{
|
||||
GtkFilterListModel *self = data;
|
||||
GtkBitset *old;
|
||||
|
||||
old = gtk_bitset_copy (self->matches);
|
||||
gtk_filter_list_model_run_filter (self, 512);
|
||||
|
||||
if (self->pending == NULL)
|
||||
gtk_filter_list_model_stop_filtering (self);
|
||||
|
||||
gtk_filter_list_model_emit_items_changed_for_changes (self, old);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
/* NB: bitset is (transfer full) */
|
||||
static void
|
||||
gtk_filter_list_model_start_filtering (GtkFilterListModel *self,
|
||||
GtkBitset *items)
|
||||
{
|
||||
if (self->pending)
|
||||
{
|
||||
gtk_bitset_union (self->pending, items);
|
||||
gtk_bitset_unref (items);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PENDING]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gtk_bitset_is_empty (items))
|
||||
{
|
||||
gtk_bitset_unref (items);
|
||||
return;
|
||||
}
|
||||
|
||||
self->pending = items;
|
||||
|
||||
if (!self->incremental)
|
||||
{
|
||||
gtk_filter_list_model_run_filter (self, G_MAXUINT);
|
||||
g_assert (self->pending == NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PENDING]);
|
||||
g_assert (self->pending_cb == 0);
|
||||
self->pending_cb = g_idle_add (gtk_filter_list_model_run_filter_cb, self);
|
||||
g_source_set_name_by_id (self->pending_cb, "[gtk] gtk_filter_list_model_run_filter_cb");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -313,8 +283,7 @@ gtk_filter_list_model_items_changed_cb (GListModel *model,
|
||||
guint added,
|
||||
GtkFilterListModel *self)
|
||||
{
|
||||
FilterNode *node;
|
||||
guint i, filter_position, filter_removed, filter_added;
|
||||
guint filter_removed, filter_added;
|
||||
|
||||
switch (self->strictness)
|
||||
{
|
||||
@@ -332,22 +301,22 @@ gtk_filter_list_model_items_changed_cb (GListModel *model,
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
node = gtk_filter_list_model_get_nth (self->items, position, &filter_position);
|
||||
if (removed > 0)
|
||||
filter_removed = gtk_bitset_get_size_in_range (self->matches, position, position + removed - 1);
|
||||
else
|
||||
filter_removed = 0;
|
||||
|
||||
filter_removed = 0;
|
||||
for (i = 0; i < removed; i++)
|
||||
{
|
||||
FilterNode *next = gtk_rb_tree_node_get_next (node);
|
||||
if (node->visible)
|
||||
filter_removed++;
|
||||
gtk_rb_tree_remove (self->items, node);
|
||||
node = next;
|
||||
}
|
||||
gtk_bitset_slice (self->matches, position, removed, added);
|
||||
if (self->pending)
|
||||
gtk_bitset_slice (self->pending, position, removed, added);
|
||||
|
||||
filter_added = gtk_filter_list_model_add_items (self, node, position, added);
|
||||
gtk_filter_list_model_start_filtering (self, gtk_bitset_new_range (position, added));
|
||||
filter_added = gtk_bitset_get_size_in_range (self->matches, position, position + added - 1);
|
||||
|
||||
if (filter_removed > 0 || filter_added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), filter_position, filter_removed, filter_added);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self),
|
||||
gtk_bitset_get_size_in_range (self->matches, 0, position),
|
||||
filter_removed, filter_added);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -364,6 +333,10 @@ gtk_filter_list_model_set_property (GObject *object,
|
||||
gtk_filter_list_model_set_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_INCREMENTAL:
|
||||
gtk_filter_list_model_set_incremental (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
@@ -392,6 +365,10 @@ gtk_filter_list_model_get_property (GObject *object,
|
||||
g_value_set_object (value, self->filter);
|
||||
break;
|
||||
|
||||
case PROP_INCREMENTAL:
|
||||
g_value_set_boolean (value, self->incremental);
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, self->item_type);
|
||||
break;
|
||||
@@ -400,6 +377,10 @@ gtk_filter_list_model_get_property (GObject *object,
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
case PROP_PENDING:
|
||||
g_value_set_uint (value, gtk_filter_list_model_get_pending (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -412,109 +393,16 @@ gtk_filter_list_model_clear_model (GtkFilterListModel *self)
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
gtk_filter_list_model_stop_filtering (self);
|
||||
g_signal_handlers_disconnect_by_func (self->model, gtk_filter_list_model_items_changed_cb, self);
|
||||
g_clear_object (&self->model);
|
||||
if (self->items)
|
||||
gtk_rb_tree_remove_all (self->items);
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* gtk_filter_list_model_find_filtered:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @start: (out) (caller-allocates): number of unfiltered items
|
||||
* at start of list
|
||||
* @end: (out) (caller-allocates): number of unfiltered items
|
||||
* at end of list
|
||||
* @n_items: (out) (caller-allocates): number of unfiltered items in
|
||||
* list
|
||||
*
|
||||
* Checks if elements in self->items are filtered out and returns
|
||||
* the range that they occupy.
|
||||
* This function is intended to be used for GListModel::items-changed
|
||||
* emissions, so it is called in an intermediate state for @self.
|
||||
*
|
||||
* Returns: %TRUE if elements are filtered out, %FALSE if none are
|
||||
**/
|
||||
static gboolean
|
||||
gtk_filter_list_model_find_filtered (GtkFilterListModel *self,
|
||||
guint *start,
|
||||
guint *end,
|
||||
guint *n_items)
|
||||
{
|
||||
FilterNode *root, *node, *tmp;
|
||||
FilterAugment *aug;
|
||||
|
||||
if (self->items == NULL || self->model == NULL)
|
||||
return FALSE;
|
||||
|
||||
root = gtk_rb_tree_get_root (self->items);
|
||||
if (root == NULL)
|
||||
return FALSE; /* empty parent model */
|
||||
|
||||
aug = gtk_rb_tree_get_augment (self->items, root);
|
||||
if (aug->n_items == aug->n_visible)
|
||||
return FALSE; /* all items visible */
|
||||
|
||||
/* find first filtered */
|
||||
*start = 0;
|
||||
*end = 0;
|
||||
*n_items = aug->n_visible;
|
||||
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*start += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*start)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
/* find last filtered by doing everything the opposite way */
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_right (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*end += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*end)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_left (node);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
if (self->matches)
|
||||
gtk_bitset_remove_all (self->matches);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self);
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self,
|
||||
GtkFilterChange change)
|
||||
{
|
||||
GtkFilterMatch new_strictness;
|
||||
|
||||
@@ -532,8 +420,9 @@ gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
guint n_before = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
g_clear_pointer (&self->matches, gtk_bitset_unref);
|
||||
self->strictness = new_strictness;
|
||||
gtk_filter_list_model_stop_filtering (self);
|
||||
if (n_before > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_before, 0);
|
||||
}
|
||||
@@ -553,16 +442,35 @@ gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
|
||||
gtk_filter_list_model_stop_filtering (self);
|
||||
self->strictness = new_strictness;
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_before))
|
||||
n_after = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
start = gtk_bitset_get_minimum (self->matches);
|
||||
end = gtk_bitset_get_maximum (self->matches);
|
||||
|
||||
n_before = gtk_bitset_get_size (self->matches);
|
||||
if (n_before == n_after)
|
||||
{
|
||||
n_after = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
g_clear_pointer (&self->matches, gtk_bitset_unref);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
GtkBitset *inverse;
|
||||
|
||||
inverse = gtk_bitset_new_range (0, n_after);
|
||||
gtk_bitset_subtract (inverse, self->matches);
|
||||
/* otherwise all items would be visible */
|
||||
g_assert (!gtk_bitset_is_empty (inverse));
|
||||
|
||||
/* find first filtered */
|
||||
start = gtk_bitset_get_minimum (inverse);
|
||||
end = n_after - gtk_bitset_get_maximum (inverse) - 1;
|
||||
|
||||
gtk_bitset_unref (inverse);
|
||||
|
||||
g_clear_pointer (&self->matches, gtk_bitset_unref);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -574,40 +482,44 @@ gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
|
||||
break;
|
||||
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
GtkBitset *old, *pending;
|
||||
|
||||
if (self->matches == NULL)
|
||||
{
|
||||
guint n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_after = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (self->model));
|
||||
if (n_after > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, n_after);
|
||||
if (self->strictness == GTK_FILTER_MATCH_ALL)
|
||||
old = gtk_bitset_new_range (0, g_list_model_get_n_items (self->model));
|
||||
else
|
||||
old = gtk_bitset_new_empty ();
|
||||
}
|
||||
break;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
else
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_before = g_list_model_get_n_items (self->model);
|
||||
gtk_filter_list_model_add_items (self, NULL, 0, n_before);
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_after))
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
old = self->matches;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_refilter (self);
|
||||
break;
|
||||
}
|
||||
self->strictness = new_strictness;
|
||||
switch (change)
|
||||
{
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_CHANGE_DIFFERENT:
|
||||
self->matches = gtk_bitset_new_empty ();
|
||||
pending = gtk_bitset_new_range (0, g_list_model_get_n_items (self->model));
|
||||
break;
|
||||
case GTK_FILTER_CHANGE_LESS_STRICT:
|
||||
self->matches = gtk_bitset_copy (old);
|
||||
pending = gtk_bitset_new_range (0, g_list_model_get_n_items (self->model));
|
||||
gtk_bitset_subtract (pending, self->matches);
|
||||
break;
|
||||
case GTK_FILTER_CHANGE_MORE_STRICT:
|
||||
self->matches = gtk_bitset_new_empty ();
|
||||
pending = gtk_bitset_copy (old);
|
||||
break;
|
||||
}
|
||||
gtk_filter_list_model_start_filtering (self, pending);
|
||||
|
||||
gtk_filter_list_model_emit_items_changed_for_changes (self, old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,7 +528,7 @@ gtk_filter_list_model_filter_changed_cb (GtkFilter *filter,
|
||||
GtkFilterChange change,
|
||||
GtkFilterListModel *self)
|
||||
{
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
gtk_filter_list_model_refilter (self, change);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -636,7 +548,7 @@ gtk_filter_list_model_dispose (GObject *object)
|
||||
|
||||
gtk_filter_list_model_clear_model (self);
|
||||
gtk_filter_list_model_clear_filter (self);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
g_clear_pointer (&self->matches, gtk_bitset_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_filter_list_model_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -662,6 +574,18 @@ gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
|
||||
GTK_TYPE_FILTER,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:incremental:
|
||||
*
|
||||
* If the model should filter items incrementally
|
||||
*/
|
||||
properties[PROP_INCREMENTAL] =
|
||||
g_param_spec_boolean ("incremental",
|
||||
P_("Incremental"),
|
||||
P_("Filer items incrementally"),
|
||||
FALSE,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:item-type:
|
||||
*
|
||||
@@ -686,6 +610,18 @@ gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
|
||||
G_TYPE_LIST_MODEL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:pending:
|
||||
*
|
||||
* Number of items not yet filtered
|
||||
*/
|
||||
properties[PROP_PENDING] =
|
||||
g_param_spec_uint ("pending",
|
||||
P_("Pending"),
|
||||
P_("Number of items not yet filtered"),
|
||||
0, G_MAXUINT, 0,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
@@ -728,7 +664,7 @@ gtk_filter_list_model_new (GListModel *model,
|
||||
*
|
||||
* Creates a new empty filter list model set up to return items of type @item_type.
|
||||
* It is up to the application to set a proper filter and model to ensure
|
||||
* the item type is matched.
|
||||
* the item type is matches.
|
||||
*
|
||||
* Returns: a new #GtkFilterListModel
|
||||
**/
|
||||
@@ -769,7 +705,7 @@ gtk_filter_list_model_set_filter (GtkFilterListModel *self,
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
gtk_filter_list_model_refilter (self, GTK_FILTER_CHANGE_LESS_STRICT);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILTER]);
|
||||
@@ -828,13 +764,18 @@ gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
if (removed == 0)
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
gtk_filter_list_model_refilter (self, GTK_FILTER_CHANGE_LESS_STRICT);
|
||||
added = 0;
|
||||
}
|
||||
else if (self->items)
|
||||
added = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
|
||||
else if (self->matches)
|
||||
{
|
||||
gtk_filter_list_model_start_filtering (self, gtk_bitset_new_range (0, g_list_model_get_n_items (model)));
|
||||
added = gtk_bitset_get_size (self->matches);
|
||||
}
|
||||
else
|
||||
added = g_list_model_get_n_items (model);
|
||||
{
|
||||
added = g_list_model_get_n_items (model);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -864,54 +805,89 @@ gtk_filter_list_model_get_model (GtkFilterListModel *self)
|
||||
return self->model;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self)
|
||||
/**
|
||||
* gtk_filter_list_model_set_incremental:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @incremental: %TRUE to enable incremental filtering
|
||||
*
|
||||
* When incremental filtering is enabled, the filterlistmodel will not run
|
||||
* filters immediately, but will instead queue an idle handler that
|
||||
* incrementally filters the items and adds them to the list. This of course
|
||||
* means that items are not instantly added to the list, but only appear
|
||||
* incrementally.
|
||||
*
|
||||
* When your filter blocks the UI while filtering, you might consider
|
||||
* turning this on. Depending on your model and filters, this may become
|
||||
* interesting around 10,000 to 100,000 items.
|
||||
*
|
||||
* By default, incremental filtering is disabled.
|
||||
**/
|
||||
void
|
||||
gtk_filter_list_model_set_incremental (GtkFilterListModel *self,
|
||||
gboolean incremental)
|
||||
{
|
||||
FilterNode *node;
|
||||
guint i, first_change, last_change;
|
||||
guint n_is_visible, n_was_visible;
|
||||
gboolean visible;
|
||||
|
||||
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
|
||||
|
||||
if (self->items == NULL || self->model == NULL)
|
||||
|
||||
if (self->incremental == incremental)
|
||||
return;
|
||||
|
||||
first_change = G_MAXUINT;
|
||||
last_change = 0;
|
||||
n_is_visible = 0;
|
||||
n_was_visible = 0;
|
||||
for (i = 0, node = gtk_rb_tree_get_first (self->items);
|
||||
node != NULL;
|
||||
i++, node = gtk_rb_tree_node_get_next (node))
|
||||
{
|
||||
visible = gtk_filter_list_model_run_filter (self, i);
|
||||
if (visible == node->visible)
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
n_is_visible++;
|
||||
n_was_visible++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
self->incremental = incremental;
|
||||
|
||||
node->visible = visible;
|
||||
gtk_rb_tree_node_mark_dirty (node);
|
||||
first_change = MIN (n_is_visible, first_change);
|
||||
if (visible)
|
||||
n_is_visible++;
|
||||
else
|
||||
n_was_visible++;
|
||||
last_change = MAX (n_is_visible, last_change);
|
||||
if (!incremental)
|
||||
{
|
||||
GtkBitset *old;
|
||||
gtk_filter_list_model_run_filter (self, G_MAXUINT);
|
||||
|
||||
old = gtk_bitset_copy (self->matches);
|
||||
gtk_filter_list_model_run_filter (self, 512);
|
||||
|
||||
gtk_filter_list_model_stop_filtering (self);
|
||||
|
||||
gtk_filter_list_model_emit_items_changed_for_changes (self, old);
|
||||
}
|
||||
|
||||
if (first_change <= last_change)
|
||||
{
|
||||
g_list_model_items_changed (G_LIST_MODEL (self),
|
||||
first_change,
|
||||
last_change - first_change + n_was_visible - n_is_visible,
|
||||
last_change - first_change);
|
||||
}
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INCREMENTAL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_get_incremental:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Returns whether incremental filtering was enabled via
|
||||
* gtk_filter_list_model_set_incremental().
|
||||
*
|
||||
* Returns: %TRUE if incremental filtering is enabled
|
||||
**/
|
||||
gboolean
|
||||
gtk_filter_list_model_get_incremental (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
|
||||
|
||||
return self->incremental;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_get_pending:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Returns the number of items that have not been filtered yet.
|
||||
*
|
||||
* When incremental filtering is not enabled, this always returns 0.
|
||||
*
|
||||
* You can use this value to check if @self is busy filtering by
|
||||
* comparing the return value to 0 or you can compute the percentage
|
||||
* of the filter remaining by dividing the return value by
|
||||
* g_list_model_get_n_items(gtk_filter_list_model_get_model (self)).
|
||||
*
|
||||
* Returns: The number of items not yet filtered
|
||||
**/
|
||||
guint
|
||||
gtk_filter_list_model_get_pending (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
|
||||
|
||||
if (self->pending == NULL)
|
||||
return 0;
|
||||
|
||||
return gtk_bitset_get_size (self->pending);
|
||||
}
|
||||
|
@@ -52,6 +52,14 @@ void gtk_filter_list_model_set_model (GtkFilterListMo
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_filter_list_model_get_model (GtkFilterListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_set_incremental (GtkFilterListModel *self,
|
||||
gboolean incremental);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_filter_list_model_get_incremental (GtkFilterListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_filter_list_model_get_pending (GtkFilterListModel *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -55,6 +55,13 @@
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
#include "gdk/gdkprofilerprivate.h"
|
||||
|
||||
#define GTK_VECTOR_ELEMENT_TYPE char *
|
||||
#define GTK_VECTOR_NULL_TERMINATED 1
|
||||
#define GTK_VECTOR_FREE_FUNC g_free
|
||||
#define GTK_VECTOR_TYPE_NAME GtkStrvBuilder
|
||||
#define GTK_VECTOR_NAME gtk_strv_builder
|
||||
#include "gtkvectorimpl.c"
|
||||
|
||||
/**
|
||||
* SECTION:gtkicontheme
|
||||
* @Short_description: Looking up icons by name
|
||||
@@ -2276,13 +2283,13 @@ real_choose_icon (GtkIconTheme *self,
|
||||
}
|
||||
|
||||
static void
|
||||
icon_name_list_add_icon (GPtrArray *icons,
|
||||
const gchar *dir_suffix,
|
||||
gchar *icon_name)
|
||||
icon_name_list_add_icon (GtkStrvBuilder *icons,
|
||||
const gchar *dir_suffix,
|
||||
gchar *icon_name)
|
||||
{
|
||||
if (dir_suffix)
|
||||
g_ptr_array_add (icons, g_strconcat (icon_name, dir_suffix, NULL));
|
||||
g_ptr_array_add (icons, icon_name);
|
||||
gtk_strv_builder_append (icons, g_strconcat (icon_name, dir_suffix, NULL));
|
||||
gtk_strv_builder_append (icons, icon_name);
|
||||
}
|
||||
|
||||
static GtkIconPaintable *
|
||||
@@ -2296,7 +2303,7 @@ choose_icon (GtkIconTheme *self,
|
||||
{
|
||||
gboolean has_regular = FALSE, has_symbolic = FALSE;
|
||||
GtkIconPaintable *icon;
|
||||
GPtrArray *new_names;
|
||||
GtkStrvBuilder new_names;
|
||||
const gchar *dir_suffix;
|
||||
guint i;
|
||||
|
||||
@@ -2327,73 +2334,70 @@ choose_icon (GtkIconTheme *self,
|
||||
|
||||
if ((flags & GTK_ICON_LOOKUP_FORCE_REGULAR) && has_symbolic)
|
||||
{
|
||||
new_names = g_ptr_array_new_with_free_func (g_free);
|
||||
gtk_strv_builder_init (&new_names);
|
||||
for (i = 0; icon_names[i]; i++)
|
||||
{
|
||||
if (icon_name_is_symbolic (icon_names[i], -1))
|
||||
icon_name_list_add_icon (new_names, dir_suffix, g_strndup (icon_names[i], strlen (icon_names[i]) - strlen ("-symbolic")));
|
||||
icon_name_list_add_icon (&new_names, dir_suffix, g_strndup (icon_names[i], strlen (icon_names[i]) - strlen ("-symbolic")));
|
||||
else
|
||||
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
}
|
||||
for (i = 0; icon_names[i]; i++)
|
||||
{
|
||||
if (icon_name_is_symbolic (icon_names[i], -1))
|
||||
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
}
|
||||
g_ptr_array_add (new_names, NULL);
|
||||
|
||||
icon = real_choose_icon (self,
|
||||
(const gchar **) new_names->pdata,
|
||||
(const char **) gtk_strv_builder_get_data (&new_names),
|
||||
size,
|
||||
scale,
|
||||
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
||||
non_blocking);
|
||||
|
||||
g_ptr_array_free (new_names, TRUE);
|
||||
gtk_strv_builder_clear (&new_names);
|
||||
}
|
||||
else if ((flags & GTK_ICON_LOOKUP_FORCE_SYMBOLIC) && has_regular)
|
||||
{
|
||||
new_names = g_ptr_array_new_with_free_func (g_free);
|
||||
gtk_strv_builder_init (&new_names);
|
||||
for (i = 0; icon_names[i]; i++)
|
||||
{
|
||||
if (!icon_name_is_symbolic (icon_names[i], -1))
|
||||
icon_name_list_add_icon (new_names, dir_suffix, g_strconcat (icon_names[i], "-symbolic", NULL));
|
||||
icon_name_list_add_icon (&new_names, dir_suffix, g_strconcat (icon_names[i], "-symbolic", NULL));
|
||||
else
|
||||
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
}
|
||||
for (i = 0; icon_names[i]; i++)
|
||||
{
|
||||
if (!icon_name_is_symbolic (icon_names[i], -1))
|
||||
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
}
|
||||
g_ptr_array_add (new_names, NULL);
|
||||
|
||||
icon = real_choose_icon (self,
|
||||
(const gchar **) new_names->pdata,
|
||||
(const char **) gtk_strv_builder_get_data (&new_names),
|
||||
size,
|
||||
scale,
|
||||
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
||||
non_blocking);
|
||||
|
||||
g_ptr_array_free (new_names, TRUE);
|
||||
gtk_strv_builder_clear (&new_names);
|
||||
}
|
||||
else if (dir_suffix)
|
||||
{
|
||||
new_names = g_ptr_array_new_with_free_func (g_free);
|
||||
gtk_strv_builder_init (&new_names);
|
||||
for (i = 0; icon_names[i]; i++)
|
||||
{
|
||||
icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
icon_name_list_add_icon (&new_names, dir_suffix, g_strdup (icon_names[i]));
|
||||
}
|
||||
g_ptr_array_add (new_names, NULL);
|
||||
|
||||
icon = real_choose_icon (self,
|
||||
(const gchar **) new_names->pdata,
|
||||
(const char **) gtk_strv_builder_get_data (&new_names),
|
||||
size,
|
||||
scale,
|
||||
flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC),
|
||||
non_blocking);
|
||||
|
||||
g_ptr_array_free (new_names, TRUE);
|
||||
gtk_strv_builder_clear (&new_names);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -95,7 +95,6 @@
|
||||
#include "gdk/gdk-private.h"
|
||||
#include "gsk/gskprivate.h"
|
||||
#include "gsk/gskrendernodeprivate.h"
|
||||
#include "gtkarrayimplprivate.h"
|
||||
#include "gtknative.h"
|
||||
|
||||
#include <locale.h>
|
||||
@@ -138,6 +137,13 @@
|
||||
#include "a11y/gtkaccessibility.h"
|
||||
#include "inspector/window.h"
|
||||
|
||||
#define GTK_VECTOR_ELEMENT_TYPE GtkWidget *
|
||||
#define GTK_VECTOR_TYPE_NAME GtkWidgetStack
|
||||
#define GTK_VECTOR_NAME gtk_widget_stack
|
||||
#define GTK_VECTOR_FREE_FUNC g_object_unref
|
||||
#define GTK_VECTOR_PREALLOC 16
|
||||
#include "gtkvectorimpl.c"
|
||||
|
||||
static GtkWindowGroup *gtk_main_get_window_group (GtkWidget *widget);
|
||||
|
||||
static gint pre_initialized = FALSE;
|
||||
@@ -1321,8 +1327,7 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
|
||||
double x, y;
|
||||
GtkWidget *prev;
|
||||
gboolean seen_ancestor;
|
||||
GtkArray target_array;
|
||||
GtkWidget *stack_targets[16];
|
||||
GtkWidgetStack target_array;
|
||||
int i;
|
||||
|
||||
if (old_target == new_target)
|
||||
@@ -1376,19 +1381,19 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
|
||||
widget = _gtk_widget_get_parent (widget);
|
||||
}
|
||||
|
||||
gtk_array_init (&target_array, (void**)stack_targets, 16);
|
||||
gtk_widget_stack_init (&target_array);
|
||||
for (widget = new_target; widget; widget = _gtk_widget_get_parent (widget))
|
||||
gtk_array_add (&target_array, widget);
|
||||
gtk_widget_stack_append (&target_array, g_object_ref (widget));
|
||||
|
||||
crossing.direction = GTK_CROSSING_IN;
|
||||
|
||||
seen_ancestor = FALSE;
|
||||
for (i = (int)target_array.len - 1; i >= 0; i--)
|
||||
for (i = gtk_widget_stack_get_size (&target_array) - 1; i >= 0; i--)
|
||||
{
|
||||
widget = gtk_array_index (&target_array, i);
|
||||
widget = gtk_widget_stack_get (&target_array, i);
|
||||
|
||||
if (i < (int)target_array.len - 1)
|
||||
crossing.new_descendent = gtk_array_index (&target_array, i + 1);
|
||||
if (i < gtk_widget_stack_get_size (&target_array) - 1)
|
||||
crossing.new_descendent = gtk_widget_stack_get (&target_array, i + 1);
|
||||
else
|
||||
crossing.new_descendent = NULL;
|
||||
|
||||
@@ -1417,7 +1422,7 @@ gtk_synthesize_crossing_events (GtkRoot *toplevel,
|
||||
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
|
||||
}
|
||||
|
||||
gtk_array_free (&target_array, NULL);
|
||||
gtk_widget_stack_clear (&target_array);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
@@ -1994,13 +1999,12 @@ gtk_propagate_event_internal (GtkWidget *widget,
|
||||
{
|
||||
gint handled_event = FALSE;
|
||||
GtkWidget *target = widget;
|
||||
GtkArray widget_array;
|
||||
GtkWidget *stack_widgets[16];
|
||||
GtkWidgetStack widget_array;
|
||||
int i;
|
||||
|
||||
/* First, propagate event down */
|
||||
gtk_array_init (&widget_array, (void**)stack_widgets, 16);
|
||||
gtk_array_add (&widget_array, g_object_ref (widget));
|
||||
gtk_widget_stack_init (&widget_array);
|
||||
gtk_widget_stack_append (&widget_array, g_object_ref (widget));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
@@ -2008,16 +2012,16 @@ gtk_propagate_event_internal (GtkWidget *widget,
|
||||
if (!widget)
|
||||
break;
|
||||
|
||||
gtk_array_add (&widget_array, g_object_ref (widget));
|
||||
gtk_widget_stack_append (&widget_array, g_object_ref (widget));
|
||||
|
||||
if (widget == topmost)
|
||||
break;
|
||||
}
|
||||
|
||||
i = widget_array.len - 1;
|
||||
i = gtk_widget_stack_get_size (&widget_array) - 1;
|
||||
for (;;)
|
||||
{
|
||||
widget = gtk_array_index (&widget_array, i);
|
||||
widget = gtk_widget_stack_get (&widget_array, i);
|
||||
|
||||
if (!_gtk_widget_is_sensitive (widget))
|
||||
{
|
||||
@@ -2050,9 +2054,9 @@ gtk_propagate_event_internal (GtkWidget *widget,
|
||||
* parents can see the button and motion
|
||||
* events of the children.
|
||||
*/
|
||||
for (i = 0; i < widget_array.len; i++)
|
||||
for (i = 0; i < gtk_widget_stack_get_size (&widget_array); i++)
|
||||
{
|
||||
widget = gtk_array_index (&widget_array, i);
|
||||
widget = gtk_widget_stack_get (&widget_array, i);
|
||||
|
||||
/* Scroll events are special cased here because it
|
||||
* feels wrong when scrolling a GtkViewport, say,
|
||||
@@ -2071,7 +2075,7 @@ gtk_propagate_event_internal (GtkWidget *widget,
|
||||
}
|
||||
}
|
||||
|
||||
gtk_array_free (&widget_array, g_object_unref);
|
||||
gtk_widget_stack_clear (&widget_array);
|
||||
return handled_event;
|
||||
}
|
||||
|
||||
|
@@ -546,19 +546,8 @@ gtk_scrolled_window_compute_expand (GtkWidget *widget,
|
||||
gboolean *hexpand,
|
||||
gboolean *vexpand)
|
||||
{
|
||||
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
|
||||
GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
|
||||
|
||||
if (priv->child)
|
||||
{
|
||||
*hexpand = gtk_widget_compute_expand (priv->child, GTK_ORIENTATION_HORIZONTAL);
|
||||
*vexpand = gtk_widget_compute_expand (priv->child, GTK_ORIENTATION_VERTICAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
*hexpand = FALSE;
|
||||
*vexpand = FALSE;
|
||||
}
|
||||
*hexpand = TRUE;
|
||||
*vexpand = TRUE;
|
||||
}
|
||||
|
||||
static GtkSizeRequestMode
|
||||
|
@@ -34,6 +34,11 @@
|
||||
|
||||
#include "gtk/gskpango.h"
|
||||
|
||||
#define GTK_VECTOR_NAME gtk_snapshot_nodes
|
||||
#define GTK_VECTOR_TYPE_NAME GtkSnapshotNodes
|
||||
#define GTK_VECTOR_ELEMENT_TYPE GskRenderNode *
|
||||
#define GTK_VECTOR_FREE_FUNC gsk_render_node_unref
|
||||
#include "gtkvectorimpl.c"
|
||||
|
||||
/**
|
||||
* SECTION:gtksnapshot
|
||||
@@ -54,6 +59,85 @@
|
||||
* use gtk_snapshot_new().
|
||||
*/
|
||||
|
||||
typedef struct _GtkSnapshotState GtkSnapshotState;
|
||||
|
||||
typedef GskRenderNode * (* GtkSnapshotCollectFunc) (GtkSnapshot *snapshot,
|
||||
GtkSnapshotState *state,
|
||||
GskRenderNode **nodes,
|
||||
guint n_nodes);
|
||||
|
||||
struct _GtkSnapshotState {
|
||||
guint start_node_index;
|
||||
guint n_nodes;
|
||||
|
||||
GskTransform * transform;
|
||||
|
||||
GtkSnapshotCollectFunc collect_func;
|
||||
union {
|
||||
struct {
|
||||
double opacity;
|
||||
} opacity;
|
||||
struct {
|
||||
double radius;
|
||||
} blur;
|
||||
struct {
|
||||
graphene_matrix_t matrix;
|
||||
graphene_vec4_t offset;
|
||||
} color_matrix;
|
||||
struct {
|
||||
graphene_rect_t bounds;
|
||||
graphene_rect_t child_bounds;
|
||||
} repeat;
|
||||
struct {
|
||||
graphene_rect_t bounds;
|
||||
} clip;
|
||||
struct {
|
||||
GskRoundedRect bounds;
|
||||
} rounded_clip;
|
||||
struct {
|
||||
gsize n_shadows;
|
||||
GskShadow *shadows;
|
||||
GskShadow a_shadow; /* Used if n_shadows == 1 */
|
||||
} shadow;
|
||||
struct {
|
||||
GskBlendMode blend_mode;
|
||||
GskRenderNode *bottom_node;
|
||||
} blend;
|
||||
struct {
|
||||
double progress;
|
||||
GskRenderNode *start_node;
|
||||
} cross_fade;
|
||||
struct {
|
||||
char *message;
|
||||
} debug;
|
||||
} data;
|
||||
};
|
||||
|
||||
static void gtk_snapshot_state_clear (GtkSnapshotState *state);
|
||||
|
||||
#define GTK_VECTOR_NAME gtk_snapshot_states
|
||||
#define GTK_VECTOR_TYPE_NAME GtkSnapshotStates
|
||||
#define GTK_VECTOR_ELEMENT_TYPE GtkSnapshotState
|
||||
#define GTK_VECTOR_FREE_FUNC gtk_snapshot_state_clear
|
||||
#define GTK_VECTOR_BY_VALUE 1
|
||||
#include "gtkvectorimpl.c"
|
||||
|
||||
/* This is a nasty little hack. We typedef GtkSnapshot to the fake object GdkSnapshot
|
||||
* so that we don't need to typecast between them.
|
||||
* After all, the GdkSnapshot only exist so poor language bindings don't trip. Hardcore
|
||||
* C code can just blatantly ignore such layering violations with a typedef.
|
||||
*/
|
||||
struct _GdkSnapshot {
|
||||
GObject parent_instance; /* it's really GdkSnapshot, but don't tell anyone! */
|
||||
|
||||
GtkSnapshotStates state_stack;
|
||||
GtkSnapshotNodes nodes;
|
||||
};
|
||||
|
||||
struct _GtkSnapshotClass {
|
||||
GObjectClass parent_class; /* it's really GdkSnapshotClass, but don't tell anyone! */
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkSnapshot, gtk_snapshot, GDK_TYPE_SNAPSHOT)
|
||||
|
||||
static void
|
||||
@@ -61,11 +145,11 @@ gtk_snapshot_dispose (GObject *object)
|
||||
{
|
||||
GtkSnapshot *snapshot = GTK_SNAPSHOT (object);
|
||||
|
||||
if (snapshot->state_stack)
|
||||
if (!gtk_snapshot_states_is_empty (&snapshot->state_stack))
|
||||
gsk_render_node_unref (gtk_snapshot_to_node (snapshot));
|
||||
|
||||
g_assert (snapshot->state_stack == NULL);
|
||||
g_assert (snapshot->nodes == NULL);
|
||||
g_assert (gtk_snapshot_states_is_empty (&snapshot->state_stack));
|
||||
g_assert (gtk_snapshot_nodes_is_empty (&snapshot->nodes));
|
||||
|
||||
G_OBJECT_CLASS (gtk_snapshot_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -112,15 +196,15 @@ gtk_snapshot_push_state (GtkSnapshot *snapshot,
|
||||
GskTransform *transform,
|
||||
GtkSnapshotCollectFunc collect_func)
|
||||
{
|
||||
const gsize n_states = snapshot->state_stack->len;
|
||||
const gsize n_states = gtk_snapshot_states_get_size (&snapshot->state_stack);
|
||||
GtkSnapshotState *state;
|
||||
|
||||
g_array_set_size (snapshot->state_stack, n_states + 1);
|
||||
state = &g_array_index (snapshot->state_stack, GtkSnapshotState, n_states);
|
||||
gtk_snapshot_states_set_size (&snapshot->state_stack, n_states + 1);
|
||||
state = gtk_snapshot_states_get (&snapshot->state_stack, n_states);
|
||||
|
||||
state->transform = gsk_transform_ref (transform);
|
||||
state->collect_func = collect_func;
|
||||
state->start_node_index = snapshot->nodes->len;
|
||||
state->start_node_index = gtk_snapshot_nodes_get_size (&snapshot->nodes);
|
||||
state->n_nodes = 0;
|
||||
|
||||
return state;
|
||||
@@ -129,17 +213,21 @@ gtk_snapshot_push_state (GtkSnapshot *snapshot,
|
||||
static GtkSnapshotState *
|
||||
gtk_snapshot_get_current_state (const GtkSnapshot *snapshot)
|
||||
{
|
||||
g_assert (snapshot->state_stack->len > 0);
|
||||
gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
|
||||
|
||||
return &g_array_index (snapshot->state_stack, GtkSnapshotState, snapshot->state_stack->len - 1);
|
||||
g_assert (size > 0);
|
||||
|
||||
return gtk_snapshot_states_get (&snapshot->state_stack, size - 1);
|
||||
}
|
||||
|
||||
static GtkSnapshotState *
|
||||
gtk_snapshot_get_previous_state (const GtkSnapshot *snapshot)
|
||||
{
|
||||
g_assert (snapshot->state_stack->len > 1);
|
||||
gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
|
||||
|
||||
return &g_array_index (snapshot->state_stack, GtkSnapshotState, snapshot->state_stack->len - 2);
|
||||
g_assert (size > 1);
|
||||
|
||||
return gtk_snapshot_states_get (&snapshot->state_stack, size - 2);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -162,9 +250,8 @@ gtk_snapshot_new (void)
|
||||
|
||||
snapshot = g_object_new (GTK_TYPE_SNAPSHOT, NULL);
|
||||
|
||||
snapshot->state_stack = g_array_new (FALSE, TRUE, sizeof (GtkSnapshotState));
|
||||
g_array_set_clear_func (snapshot->state_stack, (GDestroyNotify)gtk_snapshot_state_clear);
|
||||
snapshot->nodes = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_render_node_unref);
|
||||
gtk_snapshot_states_init (&snapshot->state_stack);
|
||||
gtk_snapshot_nodes_init (&snapshot->nodes);
|
||||
|
||||
gtk_snapshot_push_state (snapshot,
|
||||
NULL,
|
||||
@@ -1029,30 +1116,28 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
|
||||
guint state_index;
|
||||
GskRenderNode *node;
|
||||
|
||||
if (snapshot->state_stack->len == 0)
|
||||
if (gtk_snapshot_states_is_empty (&snapshot->state_stack))
|
||||
{
|
||||
g_warning ("Too many gtk_snapshot_pop() calls.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state = gtk_snapshot_get_current_state (snapshot);
|
||||
state_index = snapshot->state_stack->len - 1;
|
||||
state_index = gtk_snapshot_states_get_size (&snapshot->state_stack) - 1;
|
||||
|
||||
if (state->collect_func)
|
||||
{
|
||||
node = state->collect_func (snapshot,
|
||||
state,
|
||||
(GskRenderNode **) snapshot->nodes->pdata + state->start_node_index,
|
||||
(GskRenderNode **) gtk_snapshot_nodes_index (&snapshot->nodes, state->start_node_index),
|
||||
state->n_nodes);
|
||||
|
||||
/* The collect func may not modify the state stack... */
|
||||
g_assert (state_index == snapshot->state_stack->len - 1);
|
||||
g_assert (state_index == gtk_snapshot_states_get_size (&snapshot->state_stack) - 1);
|
||||
|
||||
/* Remove all the state's nodes from the list of nodes */
|
||||
g_assert (state->start_node_index + state->n_nodes == snapshot->nodes->len);
|
||||
g_ptr_array_remove_range (snapshot->nodes,
|
||||
snapshot->nodes->len - state->n_nodes,
|
||||
state->n_nodes);
|
||||
g_assert (state->start_node_index + state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
|
||||
gtk_snapshot_nodes_splice (&snapshot->nodes, state->start_node_index, state->n_nodes, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1063,10 +1148,10 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
|
||||
/* move the nodes to the parent */
|
||||
previous_state = gtk_snapshot_get_previous_state (snapshot);
|
||||
previous_state->n_nodes += state->n_nodes;
|
||||
g_assert (previous_state->start_node_index + previous_state->n_nodes == snapshot->nodes->len);
|
||||
g_assert (previous_state->start_node_index + previous_state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
|
||||
}
|
||||
|
||||
g_array_remove_index (snapshot->state_stack, state_index);
|
||||
gtk_snapshot_states_splice (&snapshot->state_stack, state_index, 1, NULL, 0);
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -1081,7 +1166,7 @@ gtk_snapshot_append_node_internal (GtkSnapshot *snapshot,
|
||||
|
||||
if (current_state)
|
||||
{
|
||||
g_ptr_array_add (snapshot->nodes, node);
|
||||
gtk_snapshot_nodes_append (&snapshot->nodes, node);
|
||||
current_state->n_nodes ++;
|
||||
}
|
||||
else
|
||||
@@ -1162,16 +1247,14 @@ gtk_snapshot_to_node (GtkSnapshot *snapshot)
|
||||
result = gtk_snapshot_pop_internal (snapshot);
|
||||
|
||||
/* We should have exactly our initial state */
|
||||
if (snapshot->state_stack->len > 0)
|
||||
if (!gtk_snapshot_states_is_empty (&snapshot->state_stack))
|
||||
{
|
||||
g_warning ("Too many gtk_snapshot_push() calls. %u states remaining.", snapshot->state_stack->len);
|
||||
g_warning ("Too many gtk_snapshot_push() calls. %zu states remaining.",
|
||||
gtk_snapshot_states_get_size (&snapshot->state_stack));
|
||||
}
|
||||
|
||||
g_array_free (snapshot->state_stack, TRUE);
|
||||
g_ptr_array_free (snapshot->nodes, TRUE);
|
||||
|
||||
snapshot->state_stack = NULL;
|
||||
snapshot->nodes = NULL;
|
||||
gtk_snapshot_states_clear (&snapshot->state_stack);
|
||||
gtk_snapshot_nodes_clear (&snapshot->nodes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@@ -24,76 +24,6 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkSnapshotState GtkSnapshotState;
|
||||
|
||||
typedef GskRenderNode * (* GtkSnapshotCollectFunc) (GtkSnapshot *snapshot,
|
||||
GtkSnapshotState *state,
|
||||
GskRenderNode **nodes,
|
||||
guint n_nodes);
|
||||
|
||||
struct _GtkSnapshotState {
|
||||
guint start_node_index;
|
||||
guint n_nodes;
|
||||
|
||||
GskTransform * transform;
|
||||
|
||||
GtkSnapshotCollectFunc collect_func;
|
||||
union {
|
||||
struct {
|
||||
double opacity;
|
||||
} opacity;
|
||||
struct {
|
||||
double radius;
|
||||
} blur;
|
||||
struct {
|
||||
graphene_matrix_t matrix;
|
||||
graphene_vec4_t offset;
|
||||
} color_matrix;
|
||||
struct {
|
||||
graphene_rect_t bounds;
|
||||
graphene_rect_t child_bounds;
|
||||
} repeat;
|
||||
struct {
|
||||
graphene_rect_t bounds;
|
||||
} clip;
|
||||
struct {
|
||||
GskRoundedRect bounds;
|
||||
} rounded_clip;
|
||||
struct {
|
||||
gsize n_shadows;
|
||||
GskShadow *shadows;
|
||||
GskShadow a_shadow; /* Used if n_shadows == 1 */
|
||||
} shadow;
|
||||
struct {
|
||||
GskBlendMode blend_mode;
|
||||
GskRenderNode *bottom_node;
|
||||
} blend;
|
||||
struct {
|
||||
double progress;
|
||||
GskRenderNode *start_node;
|
||||
} cross_fade;
|
||||
struct {
|
||||
char *message;
|
||||
} debug;
|
||||
} data;
|
||||
};
|
||||
|
||||
/* This is a nasty little hack. We typedef GtkSnapshot to the fake object GdkSnapshot
|
||||
* so that we don't need to typecast between them.
|
||||
* After all, the GdkSnapshot only exist so poor language bindings don't trip. Hardcore
|
||||
* C code can just blatantly ignore such layering violations with a typedef.
|
||||
*/
|
||||
struct _GdkSnapshot {
|
||||
GObject parent_instance; /* it's really GdkSnapshot, but don't tell anyone! */
|
||||
|
||||
GArray *state_stack;
|
||||
GPtrArray *nodes;
|
||||
};
|
||||
|
||||
struct _GtkSnapshotClass {
|
||||
GObjectClass parent_class; /* it's really GdkSnapshotClass, but don't tell anyone! */
|
||||
};
|
||||
|
||||
void gtk_snapshot_append_text (GtkSnapshot *snapshot,
|
||||
PangoFont *font,
|
||||
PangoGlyphString *glyphs,
|
||||
|
@@ -110,9 +110,9 @@ gtk_string_filter_match (GtkFilter *filter,
|
||||
!gtk_expression_evaluate (self->expression, item, &value))
|
||||
return FALSE;
|
||||
s = g_value_get_string (&value);
|
||||
if (s == NULL)
|
||||
return FALSE;
|
||||
prepared = gtk_string_filter_prepare (self, s);
|
||||
if (prepared == NULL)
|
||||
return FALSE;
|
||||
|
||||
switch (self->match_mode)
|
||||
{
|
||||
|
@@ -138,20 +138,13 @@ gtk_string_object_class_init (GtkStringObjectClass *class)
|
||||
|
||||
pspec = g_param_spec_string ("string", "String", "String",
|
||||
NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_READABLE |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_property (object_class, PROP_STRING, pspec);
|
||||
|
||||
}
|
||||
|
||||
static GtkStringObject *
|
||||
gtk_string_object_new (const char *string)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_STRING_OBJECT, "string", string, NULL);
|
||||
}
|
||||
|
||||
static GtkStringObject *
|
||||
gtk_string_object_new_take (char *string)
|
||||
{
|
||||
@@ -163,6 +156,12 @@ gtk_string_object_new_take (char *string)
|
||||
return obj;
|
||||
}
|
||||
|
||||
static GtkStringObject *
|
||||
gtk_string_object_new (const char *string)
|
||||
{
|
||||
return gtk_string_object_new_take (g_strdup (string));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_string_object_get_string:
|
||||
* @self: a #GtkStringObject
|
||||
@@ -432,15 +431,13 @@ gtk_string_list_init (GtkStringList *self)
|
||||
* Returns: a new #GtkStringList
|
||||
*/
|
||||
GtkStringList *
|
||||
gtk_string_list_new (const char **strings)
|
||||
gtk_string_list_new (const char * const *strings)
|
||||
{
|
||||
GtkStringList *self;
|
||||
guint i;
|
||||
|
||||
self = g_object_new (GTK_TYPE_STRING_LIST, NULL);
|
||||
|
||||
for (i = 0; strings[i]; i++)
|
||||
g_sequence_append (self->items, gtk_string_object_new (strings[i]));
|
||||
gtk_string_list_splice (self, 0, 0, strings);
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -450,11 +447,10 @@ gtk_string_list_new (const char **strings)
|
||||
* @self: a #GtkStringList
|
||||
* @position: the position at which to make the change
|
||||
* @n_removals: the number of strings to remove
|
||||
* @additions: (array length=n_additions): the strings to add
|
||||
* @n_additions: the number of items to add
|
||||
* @additions: (array zero-terminated=1) (nullable): The strings to add
|
||||
*
|
||||
* Changes @self by removing @n_removals strings and adding @n_additions
|
||||
* strings to it.
|
||||
* Changes @self by removing @n_removals strings and adding @additions
|
||||
* to it.
|
||||
*
|
||||
* This function is more efficient than gtk_string_list_insert() and
|
||||
* gtk_string_list_remove(), because it only emits
|
||||
@@ -467,14 +463,13 @@ gtk_string_list_new (const char **strings)
|
||||
* of the list at the time this function is called).
|
||||
*/
|
||||
void
|
||||
gtk_string_list_splice (GtkStringList *self,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
const char **additions,
|
||||
guint n_additions)
|
||||
gtk_string_list_splice (GtkStringList *self,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
const char * const *additions)
|
||||
{
|
||||
GSequenceIter *it;
|
||||
guint n_items;
|
||||
guint add, n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_STRING_LIST (self));
|
||||
g_return_if_fail (position + n_removals >= position); /* overflow */
|
||||
@@ -494,17 +489,18 @@ gtk_string_list_splice (GtkStringList *self,
|
||||
it = end;
|
||||
}
|
||||
|
||||
if (n_additions)
|
||||
if (additions)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < n_additions; i++)
|
||||
for (add = 0; additions[add]; add++)
|
||||
{
|
||||
g_sequence_insert_before (it, gtk_string_object_new (additions[i]));
|
||||
g_sequence_insert_before (it, gtk_string_object_new (additions[add]));
|
||||
}
|
||||
}
|
||||
else
|
||||
add = 0;
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, n_removals, n_additions);
|
||||
if (n_removals || add)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, n_removals, add);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -589,8 +585,8 @@ gtk_string_list_remove (GtkStringList *self,
|
||||
* @self: a #GtkStringList
|
||||
* @position: the position to get the string for
|
||||
*
|
||||
* Gets the string that is at @position in @self. @position
|
||||
* must be smaller than the current length of the list.
|
||||
* Gets the string that is at @position in @self. If @self
|
||||
* does not contain @position items, %NULL is returned.
|
||||
*
|
||||
* This function returns the const char *. To get the
|
||||
* object wrapping it, use g_list_model_get_item().
|
||||
|
@@ -45,30 +45,29 @@ GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkStringList, gtk_string_list, GTK, STRING_LIST, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkStringList * gtk_string_list_new (const char **strings);
|
||||
GtkStringList * gtk_string_list_new (const char * const *strings);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list_append (GtkStringList *self,
|
||||
const char *string);
|
||||
void gtk_string_list_append (GtkStringList *self,
|
||||
const char *string);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list_take (GtkStringList *self,
|
||||
char *string);
|
||||
void gtk_string_list_take (GtkStringList *self,
|
||||
char *string);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list_remove (GtkStringList *self,
|
||||
guint position);
|
||||
void gtk_string_list_remove (GtkStringList *self,
|
||||
guint position);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list_splice (GtkStringList *self,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
const char **additions,
|
||||
guint n_additions);
|
||||
void gtk_string_list_splice (GtkStringList *self,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
const char * const *additions);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_string_list_get_string (GtkStringList *self,
|
||||
guint position);
|
||||
const char * gtk_string_list_get_string (GtkStringList *self,
|
||||
guint position);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
280
gtk/gtkvectorimpl.c
Normal file
280
gtk/gtkvectorimpl.c
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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 (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;
|
||||
}
|
||||
|
||||
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, old_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
|
@@ -160,6 +160,7 @@ gtk_public_sources = files([
|
||||
'gtkappchooserwidget.c',
|
||||
'gtkapplication.c',
|
||||
'gtkapplicationwindow.c',
|
||||
'gtkarraystore.c',
|
||||
'gtkaspectframe.c',
|
||||
'gtkassistant.c',
|
||||
'gtkbinlayout.c',
|
||||
@@ -447,6 +448,7 @@ gtk_public_headers = files([
|
||||
'gtkappchooserwidget.h',
|
||||
'gtkapplication.h',
|
||||
'gtkapplicationwindow.h',
|
||||
'gtkarraystore.h',
|
||||
'gtkaspectframe.h',
|
||||
'gtkassistant.h',
|
||||
'gtkbinlayout.h',
|
||||
|
@@ -618,7 +618,7 @@ main (int argc, char *argv[])
|
||||
gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (search_entry), sw);
|
||||
gtk_box_append (GTK_BOX (vbox), sw);
|
||||
|
||||
listview = gtk_grid_view_new_with_factory (
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_widget,
|
||||
NULL,
|
||||
NULL, NULL));
|
||||
@@ -645,7 +645,7 @@ main (int argc, char *argv[])
|
||||
selectionmodel = file_info_selection_new (G_LIST_MODEL (filter));
|
||||
g_object_unref (filter);
|
||||
|
||||
gtk_grid_view_set_model (GTK_GRID_VIEW (listview), G_LIST_MODEL (selectionmodel));
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selectionmodel));
|
||||
|
||||
statusbar = gtk_statusbar_new ();
|
||||
gtk_widget_add_tick_callback (statusbar, (GtkTickCallback) update_statusbar, NULL, NULL);
|
||||
|
683
testsuite/gtk/arraystore.c
Normal file
683
testsuite/gtk/arraystore.c
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* Copyright 2015 Lars Uebernickel
|
||||
*
|
||||
* 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: Lars Uebernickel <lars@uebernic.de>
|
||||
*/
|
||||
|
||||
#include <gtk.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Wrapper around g_list_model_get_item() and g_list_model_get_object() which
|
||||
* checks they return the same thing. */
|
||||
static gpointer
|
||||
list_model_get (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
GObject *item = g_list_model_get_item (model, position);
|
||||
GObject *object = g_list_model_get_object (model, position);
|
||||
|
||||
g_assert_true (item == object);
|
||||
|
||||
g_clear_object (&object);
|
||||
return g_steal_pointer (&item);
|
||||
}
|
||||
|
||||
/* Test that constructing/getting/setting properties on a #GtkArrayStore works. */
|
||||
static void
|
||||
test_store_properties (void)
|
||||
{
|
||||
GtkArrayStore *store = NULL;
|
||||
GType item_type;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_MENU_ITEM);
|
||||
g_object_get (store, "item-type", &item_type, NULL);
|
||||
g_assert_cmpint (item_type, ==, G_TYPE_MENU_ITEM);
|
||||
|
||||
g_clear_object (&store);
|
||||
}
|
||||
|
||||
/* Test that #GtkArrayStore rejects non-GObject item types. */
|
||||
static void
|
||||
test_store_non_gobjects (void)
|
||||
{
|
||||
if (g_test_subprocess ())
|
||||
{
|
||||
/* We have to use g_object_new() since gtk_array_store_new() checks the item
|
||||
* type. We want to check the property setter code works properly. */
|
||||
g_object_new (GTK_TYPE_ARRAY_STORE, "item-type", G_TYPE_LONG, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
g_test_trap_subprocess (NULL, 0, 0);
|
||||
g_test_trap_assert_failed ();
|
||||
g_test_trap_assert_stderr ("*WARNING*value * of type 'GType' is invalid or "
|
||||
"out of range for property 'item-type'*");
|
||||
}
|
||||
|
||||
#define gtk_array_store_insert(store,position,item) \
|
||||
gtk_array_store_splice (store, position, 0, (gpointer *)&item, 1)
|
||||
|
||||
#define gtk_array_store_remove(store,position) \
|
||||
gtk_array_store_splice (store, position, 1, NULL, 0)
|
||||
|
||||
static void
|
||||
test_store_boundaries (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GMenuItem *item;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_MENU_ITEM);
|
||||
|
||||
item = g_menu_item_new (NULL, NULL);
|
||||
|
||||
/* remove an item from an empty list */
|
||||
g_test_expect_message ("Gtk", G_LOG_LEVEL_CRITICAL, "*position*");
|
||||
gtk_array_store_remove (store, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
/* don't allow inserting an item past the end ... */
|
||||
g_test_expect_message ("Gtk", G_LOG_LEVEL_CRITICAL, "*position*");
|
||||
gtk_array_store_insert (store, 1, item);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
/* ... except exactly at the end */
|
||||
gtk_array_store_insert (store, 0, item);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, 1);
|
||||
|
||||
/* remove a non-existing item at exactly the end of the list */
|
||||
g_test_expect_message ("Gtk", G_LOG_LEVEL_CRITICAL, "*position*");
|
||||
gtk_array_store_remove (store, 1);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
gtk_array_store_remove (store, 0);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, 0);
|
||||
|
||||
/* splice beyond the end of the list */
|
||||
g_test_expect_message ("Gtk", G_LOG_LEVEL_CRITICAL, "*position*");
|
||||
gtk_array_store_splice (store, 1, 0, NULL, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
/* remove items from an empty list */
|
||||
g_test_expect_message ("Gtk", G_LOG_LEVEL_CRITICAL, "*position*");
|
||||
gtk_array_store_splice (store, 0, 1, NULL, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
|
||||
gtk_array_store_append (store, item);
|
||||
gtk_array_store_splice (store, 0, 1, (gpointer *) &item, 1);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, 1);
|
||||
|
||||
/* remove more items than exist */
|
||||
g_test_expect_message ("Gtk", G_LOG_LEVEL_CRITICAL, "*position*");
|
||||
gtk_array_store_splice (store, 0, 5, NULL, 0);
|
||||
g_test_assert_expected_messages ();
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, 1);
|
||||
|
||||
g_object_unref (store);
|
||||
g_assert_finalize_object (item);
|
||||
}
|
||||
|
||||
static void
|
||||
test_store_refcounts (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GMenuItem *items[10];
|
||||
GMenuItem *tmp;
|
||||
guint i;
|
||||
guint n_items;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_MENU_ITEM);
|
||||
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, 0);
|
||||
g_assert_null (list_model_get (G_LIST_MODEL (store), 0));
|
||||
|
||||
n_items = G_N_ELEMENTS (items);
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
items[i] = g_menu_item_new (NULL, NULL);
|
||||
g_object_add_weak_pointer (G_OBJECT (items[i]), (gpointer *) &items[i]);
|
||||
gtk_array_store_append (store, items[i]);
|
||||
|
||||
g_object_unref (items[i]);
|
||||
g_assert_nonnull (items[i]);
|
||||
}
|
||||
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, n_items);
|
||||
g_assert_null (list_model_get (G_LIST_MODEL (store), n_items));
|
||||
|
||||
tmp = list_model_get (G_LIST_MODEL (store), 3);
|
||||
g_assert_true (tmp == items[3]);
|
||||
g_object_unref (tmp);
|
||||
|
||||
gtk_array_store_remove (store, 4);
|
||||
g_assert_null (items[4]);
|
||||
n_items--;
|
||||
g_assert_cmpuint (g_list_model_get_n_items (G_LIST_MODEL (store)), ==, n_items);
|
||||
g_assert_null (list_model_get (G_LIST_MODEL (store), n_items));
|
||||
|
||||
g_object_unref (store);
|
||||
for (i = 0; i < G_N_ELEMENTS (items); i++)
|
||||
g_assert_null (items[i]);
|
||||
}
|
||||
|
||||
/* Test that using splice() to replace the middle element in a list store works. */
|
||||
static void
|
||||
test_store_splice_replace_middle (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GAction *item;
|
||||
GPtrArray *array;
|
||||
|
||||
g_test_bug ("795307");
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
array = g_ptr_array_new_full (0, g_object_unref);
|
||||
g_ptr_array_add (array, g_simple_action_new ("1", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("2", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("3", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("4", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("5", NULL));
|
||||
|
||||
/* Add three items through splice */
|
||||
gtk_array_store_splice (store, 0, 0, array->pdata, 3);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 3);
|
||||
|
||||
item = list_model_get (model, 0);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "1");
|
||||
g_object_unref (item);
|
||||
item = list_model_get (model, 1);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "2");
|
||||
g_object_unref (item);
|
||||
item = list_model_get (model, 2);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "3");
|
||||
g_object_unref (item);
|
||||
|
||||
/* Replace the middle one with two new items */
|
||||
gtk_array_store_splice (store, 1, 1, array->pdata + 3, 2);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 4);
|
||||
|
||||
item = list_model_get (model, 0);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "1");
|
||||
g_object_unref (item);
|
||||
item = list_model_get (model, 1);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "4");
|
||||
g_object_unref (item);
|
||||
item = list_model_get (model, 2);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "5");
|
||||
g_object_unref (item);
|
||||
item = list_model_get (model, 3);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "3");
|
||||
g_object_unref (item);
|
||||
|
||||
g_ptr_array_unref (array);
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Test that using splice() to replace the whole list store works. */
|
||||
static void
|
||||
test_store_splice_replace_all (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GPtrArray *array;
|
||||
GAction *item;
|
||||
|
||||
g_test_bug ("795307");
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
array = g_ptr_array_new_full (0, g_object_unref);
|
||||
g_ptr_array_add (array, g_simple_action_new ("1", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("2", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("3", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("4", NULL));
|
||||
|
||||
/* Add the first two */
|
||||
gtk_array_store_splice (store, 0, 0, array->pdata, 2);
|
||||
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 2);
|
||||
item = list_model_get (model, 0);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "1");
|
||||
g_object_unref (item);
|
||||
item = list_model_get (model, 1);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "2");
|
||||
g_object_unref (item);
|
||||
|
||||
/* Replace all with the last two */
|
||||
gtk_array_store_splice (store, 0, 2, array->pdata + 2, 2);
|
||||
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 2);
|
||||
item = list_model_get (model, 0);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "3");
|
||||
g_object_unref (item);
|
||||
item = list_model_get (model, 1);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "4");
|
||||
g_object_unref (item);
|
||||
|
||||
g_ptr_array_unref (array);
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Test that using splice() without removing or adding anything works */
|
||||
static void
|
||||
test_store_splice_noop (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GAction *item;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
/* splice noop with an empty list */
|
||||
gtk_array_store_splice (store, 0, 0, NULL, 0);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 0);
|
||||
|
||||
/* splice noop with a non-empty list */
|
||||
item = G_ACTION (g_simple_action_new ("1", NULL));
|
||||
gtk_array_store_append (store, item);
|
||||
g_object_unref (item);
|
||||
|
||||
gtk_array_store_splice (store, 0, 0, NULL, 0);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 1);
|
||||
|
||||
gtk_array_store_splice (store, 1, 0, NULL, 0);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 1);
|
||||
|
||||
item = list_model_get (model, 0);
|
||||
g_assert_cmpstr (g_action_get_name (item), ==, "1");
|
||||
g_object_unref (item);
|
||||
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
model_array_equal (GListModel *model, GPtrArray *array)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (g_list_model_get_n_items (model) != array->len)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < array->len; i++)
|
||||
{
|
||||
GObject *ptr;
|
||||
gboolean ptrs_equal;
|
||||
|
||||
ptr = list_model_get (model, i);
|
||||
ptrs_equal = (g_ptr_array_index (array, i) == ptr);
|
||||
g_object_unref (ptr);
|
||||
if (!ptrs_equal)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Test that using splice() to remove multiple items at different
|
||||
* positions works */
|
||||
static void
|
||||
test_store_splice_remove_multiple (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GPtrArray *array;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
array = g_ptr_array_new_full (0, g_object_unref);
|
||||
g_ptr_array_add (array, g_simple_action_new ("1", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("2", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("3", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("4", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("5", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("6", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("7", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("8", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("9", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("10", NULL));
|
||||
|
||||
/* Add all */
|
||||
gtk_array_store_splice (store, 0, 0, array->pdata, array->len);
|
||||
g_assert_true (model_array_equal (model, array));
|
||||
|
||||
/* Remove the first two */
|
||||
gtk_array_store_splice (store, 0, 2, NULL, 0);
|
||||
g_assert_false (model_array_equal (model, array));
|
||||
g_ptr_array_remove_range (array, 0, 2);
|
||||
g_assert_true (model_array_equal (model, array));
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 8);
|
||||
|
||||
/* Remove two in the middle */
|
||||
gtk_array_store_splice (store, 2, 2, NULL, 0);
|
||||
g_assert_false (model_array_equal (model, array));
|
||||
g_ptr_array_remove_range (array, 2, 2);
|
||||
g_assert_true (model_array_equal (model, array));
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 6);
|
||||
|
||||
/* Remove two at the end */
|
||||
gtk_array_store_splice (store, 4, 2, NULL, 0);
|
||||
g_assert_false (model_array_equal (model, array));
|
||||
g_ptr_array_remove_range (array, 4, 2);
|
||||
g_assert_true (model_array_equal (model, array));
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 4);
|
||||
|
||||
g_ptr_array_unref (array);
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Test that using splice() to add multiple items at different
|
||||
* positions works */
|
||||
static void
|
||||
test_store_splice_add_multiple (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GPtrArray *array;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
array = g_ptr_array_new_full (0, g_object_unref);
|
||||
g_ptr_array_add (array, g_simple_action_new ("1", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("2", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("3", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("4", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("5", NULL));
|
||||
g_ptr_array_add (array, g_simple_action_new ("6", NULL));
|
||||
|
||||
/* Add two at the beginning */
|
||||
gtk_array_store_splice (store, 0, 0, array->pdata, 2);
|
||||
|
||||
/* Add two at the end */
|
||||
gtk_array_store_splice (store, 2, 0, array->pdata + 4, 2);
|
||||
|
||||
/* Add two in the middle */
|
||||
gtk_array_store_splice (store, 2, 0, array->pdata + 2, 2);
|
||||
|
||||
g_assert_true (model_array_equal (model, array));
|
||||
|
||||
g_ptr_array_unref (array);
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Test that get_item_type() returns the right type */
|
||||
static void
|
||||
test_store_item_type (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GType gtype;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
gtype = g_list_model_get_item_type (G_LIST_MODEL (store));
|
||||
g_assert (gtype == G_TYPE_SIMPLE_ACTION);
|
||||
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Test that remove_all() removes all items */
|
||||
static void
|
||||
test_store_remove_all (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GSimpleAction *item;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
/* Test with an empty list */
|
||||
gtk_array_store_remove_all (store);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 0);
|
||||
|
||||
/* Test with a non-empty list */
|
||||
item = g_simple_action_new ("42", NULL);
|
||||
gtk_array_store_append (store, item);
|
||||
gtk_array_store_append (store, item);
|
||||
g_object_unref (item);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 2);
|
||||
gtk_array_store_remove_all (store);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 0);
|
||||
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Test that splice() logs an error when passed the wrong item type */
|
||||
static void
|
||||
test_store_splice_wrong_type (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
|
||||
g_test_expect_message (G_LOG_DOMAIN,
|
||||
G_LOG_LEVEL_CRITICAL,
|
||||
"*GtkArrayStore instead of a GSimpleAction*");
|
||||
gtk_array_store_splice (store, 0, 0, (gpointer)&store, 1);
|
||||
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Test the cases where the item store tries to speed up item access by caching
|
||||
* the last iter/position */
|
||||
static void
|
||||
test_store_get_item_cache (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GSimpleAction *item1, *item2, *temp;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
/* Add two */
|
||||
item1 = g_simple_action_new ("1", NULL);
|
||||
gtk_array_store_append (store, item1);
|
||||
item2 = g_simple_action_new ("2", NULL);
|
||||
gtk_array_store_append (store, item2);
|
||||
|
||||
/* Clear the cache */
|
||||
g_assert_null (list_model_get (model, 42));
|
||||
|
||||
/* Access the same position twice */
|
||||
temp = list_model_get (model, 1);
|
||||
g_assert (temp == item2);
|
||||
g_object_unref (temp);
|
||||
temp = list_model_get (model, 1);
|
||||
g_assert (temp == item2);
|
||||
g_object_unref (temp);
|
||||
|
||||
g_assert_null (list_model_get (model, 42));
|
||||
|
||||
/* Access forwards */
|
||||
temp = list_model_get (model, 0);
|
||||
g_assert (temp == item1);
|
||||
g_object_unref (temp);
|
||||
temp = list_model_get (model, 1);
|
||||
g_assert (temp == item2);
|
||||
g_object_unref (temp);
|
||||
|
||||
g_assert_null (list_model_get (model, 42));
|
||||
|
||||
/* Access backwards */
|
||||
temp = list_model_get (model, 1);
|
||||
g_assert (temp == item2);
|
||||
g_object_unref (temp);
|
||||
temp = list_model_get (model, 0);
|
||||
g_assert (temp == item1);
|
||||
g_object_unref (temp);
|
||||
|
||||
g_object_unref (item1);
|
||||
g_object_unref (item2);
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
struct ItemsChangedData
|
||||
{
|
||||
guint position;
|
||||
guint removed;
|
||||
guint added;
|
||||
gboolean called;
|
||||
};
|
||||
|
||||
static void
|
||||
expect_items_changed (struct ItemsChangedData *expected,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added)
|
||||
{
|
||||
expected->position = position;
|
||||
expected->removed = removed;
|
||||
expected->added = added;
|
||||
expected->called = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_items_changed (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
struct ItemsChangedData *expected)
|
||||
{
|
||||
g_assert_false (expected->called);
|
||||
g_assert_cmpuint (expected->position, ==, position);
|
||||
g_assert_cmpuint (expected->removed, ==, removed);
|
||||
g_assert_cmpuint (expected->added, ==, added);
|
||||
expected->called = TRUE;
|
||||
}
|
||||
|
||||
/* Test that all operations on the list emit the items-changed signal */
|
||||
static void
|
||||
test_store_signal_items_changed (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GSimpleAction *item;
|
||||
struct ItemsChangedData expected = {0};
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
g_object_connect (model, "signal::items-changed",
|
||||
on_items_changed, &expected, NULL);
|
||||
|
||||
/* Emit the signal manually */
|
||||
expect_items_changed (&expected, 0, 0, 0);
|
||||
g_list_model_items_changed (model, 0, 0, 0);
|
||||
g_assert_true (expected.called);
|
||||
|
||||
/* Append an item */
|
||||
expect_items_changed (&expected, 0, 0, 1);
|
||||
item = g_simple_action_new ("2", NULL);
|
||||
gtk_array_store_append (store, item);
|
||||
g_object_unref (item);
|
||||
g_assert_true (expected.called);
|
||||
|
||||
/* Insert an item */
|
||||
expect_items_changed (&expected, 1, 0, 1);
|
||||
item = g_simple_action_new ("1", NULL);
|
||||
gtk_array_store_insert (store, 1, item);
|
||||
g_object_unref (item);
|
||||
g_assert_true (expected.called);
|
||||
|
||||
/* Insert an item */
|
||||
expect_items_changed (&expected, 1, 0, 1);
|
||||
item = g_simple_action_new ("3", NULL);
|
||||
gtk_array_store_insert (store, 1, item);
|
||||
g_object_unref (item);
|
||||
g_assert_true (expected.called);
|
||||
|
||||
/* Remove an item */
|
||||
expect_items_changed (&expected, 1, 1, 0);
|
||||
gtk_array_store_remove (store, 1);
|
||||
g_assert_true (expected.called);
|
||||
|
||||
/* Splice */
|
||||
expect_items_changed (&expected, 0, 2, 1);
|
||||
item = g_simple_action_new ("4", NULL);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), >=, 2);
|
||||
gtk_array_store_splice (store, 0, 2, (gpointer)&item, 1);
|
||||
g_object_unref (item);
|
||||
g_assert_true (expected.called);
|
||||
|
||||
g_print ("remove all\n");
|
||||
/* Remove all */
|
||||
expect_items_changed (&expected, 0, 1, 0);
|
||||
g_assert_cmpuint (g_list_model_get_n_items (model), ==, 1);
|
||||
gtk_array_store_remove_all (store);
|
||||
g_assert_true (expected.called);
|
||||
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
/* Due to an overflow in the list store last-iter optimization,
|
||||
* the sequence 'lookup 0; lookup MAXUINT' was returning the
|
||||
* same item twice, and not NULL for the second lookup.
|
||||
* See #1639.
|
||||
*/
|
||||
static void
|
||||
test_store_past_end (void)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
GListModel *model;
|
||||
GSimpleAction *item;
|
||||
|
||||
store = gtk_array_store_new (G_TYPE_SIMPLE_ACTION);
|
||||
model = G_LIST_MODEL (store);
|
||||
|
||||
item = g_simple_action_new ("2", NULL);
|
||||
gtk_array_store_append (store, item);
|
||||
g_object_unref (item);
|
||||
|
||||
g_assert_cmpint (g_list_model_get_n_items (model), ==, 1);
|
||||
item = g_list_model_get_item (model, 0);
|
||||
g_assert_nonnull (item);
|
||||
g_object_unref (item);
|
||||
item = g_list_model_get_item (model, G_MAXUINT);
|
||||
g_assert_null (item);
|
||||
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_bug_base ("https://bugzilla.gnome.org/");
|
||||
|
||||
g_test_add_func ("/arraystore/properties", test_store_properties);
|
||||
g_test_add_func ("/arraystore/non-gobjects", test_store_non_gobjects);
|
||||
g_test_add_func ("/arraystore/boundaries", test_store_boundaries);
|
||||
g_test_add_func ("/arraystore/refcounts", test_store_refcounts);
|
||||
g_test_add_func ("/arraystore/splice-replace-middle", test_store_splice_replace_middle);
|
||||
g_test_add_func ("/arraystore/splice-replace-all", test_store_splice_replace_all);
|
||||
g_test_add_func ("/arraystore/splice-noop", test_store_splice_noop);
|
||||
g_test_add_func ("/arraystore/splice-remove-multiple", test_store_splice_remove_multiple);
|
||||
g_test_add_func ("/arraystore/splice-add-multiple", test_store_splice_add_multiple);
|
||||
g_test_add_func ("/arraystore/splice-wrong-type", test_store_splice_wrong_type);
|
||||
g_test_add_func ("/arraystore/item-type", test_store_item_type);
|
||||
g_test_add_func ("/arraystore/remove-all", test_store_remove_all);
|
||||
g_test_add_func ("/arraystore/get-item-cache", test_store_get_item_cache);
|
||||
g_test_add_func ("/arraystore/items-changed", test_store_signal_items_changed);
|
||||
g_test_add_func ("/arraystore/past-end", test_store_past_end);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
222
testsuite/gtk/gtkstringlist.c
Normal file
222
testsuite/gtk/gtkstringlist.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright © 2020 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gtk.h>
|
||||
#include "gtkstringlist.h"
|
||||
|
||||
struct _GtkStringList2
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GSequence *items;
|
||||
};
|
||||
|
||||
struct _GtkStringList2Class
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType
|
||||
gtk_string_list2_get_item_type (GListModel *list)
|
||||
{
|
||||
return G_TYPE_OBJECT;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_string_list2_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkStringList2 *self = GTK_STRING_LIST2 (list);
|
||||
|
||||
return g_sequence_get_length (self->items);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_string_list2_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkStringList2 *self = GTK_STRING_LIST2 (list);
|
||||
GSequenceIter *iter;
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->items, position);
|
||||
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
return NULL;
|
||||
else
|
||||
return g_object_ref (g_sequence_get (iter));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_string_list2_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_string_list2_get_item_type;
|
||||
iface->get_n_items = gtk_string_list2_get_n_items;
|
||||
iface->get_item = gtk_string_list2_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkStringList2, gtk_string_list2, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
gtk_string_list2_model_init))
|
||||
|
||||
static void
|
||||
gtk_string_list2_dispose (GObject *object)
|
||||
{
|
||||
GtkStringList2 *self = GTK_STRING_LIST2 (object);
|
||||
|
||||
g_clear_pointer (&self->items, g_sequence_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_string_list2_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_string_list2_class_init (GtkStringList2Class *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->dispose = gtk_string_list2_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_string_list2_init (GtkStringList2 *self)
|
||||
{
|
||||
self->items = g_sequence_new (g_object_unref);
|
||||
}
|
||||
|
||||
GtkStringList2 *
|
||||
gtk_string_list2_new (const char * const *strings)
|
||||
{
|
||||
GtkStringList2 *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_STRING_LIST2, NULL);
|
||||
|
||||
gtk_string_list2_splice (self, 0, 0, strings);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GObject obj;
|
||||
char *str;
|
||||
} StrObj;
|
||||
|
||||
static GtkStringObject *
|
||||
string_object_new (const char *string)
|
||||
{
|
||||
GtkStringObject *s;
|
||||
|
||||
s = g_object_new (GTK_TYPE_STRING_OBJECT, NULL);
|
||||
((StrObj*)s)->str = g_strdup (string);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_string_list2_splice (GtkStringList2 *self,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
const char * const *additions)
|
||||
{
|
||||
guint n_items;
|
||||
guint add;
|
||||
GSequenceIter *it;
|
||||
|
||||
g_return_if_fail (GTK_IS_STRING_LIST2 (self));
|
||||
g_return_if_fail (position + n_removals >= position); /* overflow */
|
||||
|
||||
n_items = g_sequence_get_length (self->items);
|
||||
g_return_if_fail (position + n_removals <= n_items);
|
||||
|
||||
it = g_sequence_get_iter_at_pos (self->items, position);
|
||||
|
||||
if (n_removals)
|
||||
{
|
||||
GSequenceIter *end;
|
||||
|
||||
end = g_sequence_iter_move (it, n_removals);
|
||||
g_sequence_remove_range (it, end);
|
||||
|
||||
it = end;
|
||||
}
|
||||
|
||||
if (additions)
|
||||
{
|
||||
for (add = 0; additions[add]; add++)
|
||||
{
|
||||
g_sequence_insert_before (it, string_object_new (additions[add]));
|
||||
}
|
||||
}
|
||||
else
|
||||
add = 0;
|
||||
|
||||
if (n_removals || add)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, n_removals, add);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_string_list2_append (GtkStringList2 *self,
|
||||
const char *string)
|
||||
{
|
||||
guint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_STRING_LIST2 (self));
|
||||
|
||||
n_items = g_sequence_get_length (self->items);
|
||||
g_sequence_append (self->items, string_object_new (string));
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), n_items, 0, 1);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_string_list2_remove (GtkStringList2 *self,
|
||||
guint position)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
|
||||
g_return_if_fail (GTK_IS_STRING_LIST2 (self));
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->items, position);
|
||||
g_return_if_fail (!g_sequence_iter_is_end (iter));
|
||||
|
||||
g_sequence_remove (iter);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
|
||||
}
|
||||
|
||||
const char *
|
||||
gtk_string_list2_get_string (GtkStringList2 *self,
|
||||
guint position)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_STRING_LIST (self), NULL);
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->items, position);
|
||||
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkStringObject *obj = g_sequence_get (iter);
|
||||
|
||||
return gtk_string_object_get_string (obj);
|
||||
}
|
||||
}
|
62
testsuite/gtk/gtkstringlist.h
Normal file
62
testsuite/gtk/gtkstringlist.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright © 2020 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_STRING_LIST2_H__
|
||||
#define __GTK_STRING_LIST2_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
/* for GDK_AVAILABLE_IN_ALL */
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_STRING_LIST2 (gtk_string_list2_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkStringList2, gtk_string_list2, GTK, STRING_LIST2, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkStringList2 * gtk_string_list2_new (const char * const *strings);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list2_append (GtkStringList2 *self,
|
||||
const char *string);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list2_take (GtkStringList2 *self,
|
||||
char *string);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list2_remove (GtkStringList2 *self,
|
||||
guint position);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_string_list2_splice (GtkStringList2 *self,
|
||||
guint position,
|
||||
guint n_removals,
|
||||
const char * const *additions);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_string_list2_get_string (GtkStringList2 *self,
|
||||
guint position);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_STRING_LIST2_H__ */
|
382
testsuite/gtk/listmodel-performance.c
Normal file
382
testsuite/gtk/listmodel-performance.c
Normal file
@@ -0,0 +1,382 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkstringlist.h"
|
||||
|
||||
typedef struct {
|
||||
GObject obj;
|
||||
char *str;
|
||||
} StrObj;
|
||||
|
||||
static GtkStringObject *
|
||||
get_object (const char *string)
|
||||
{
|
||||
GtkStringObject *s;
|
||||
|
||||
s = g_object_new (GTK_TYPE_STRING_OBJECT, NULL);
|
||||
((StrObj*)s)->str = g_strdup (string);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
make_list_store (guint n_items)
|
||||
{
|
||||
GListStore *store;
|
||||
guint i;
|
||||
|
||||
store = g_list_store_new (GTK_TYPE_STRING_OBJECT);
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
char *string;
|
||||
gpointer obj;
|
||||
|
||||
string = g_strdup_printf ("item %d", i);
|
||||
obj = get_object (string);
|
||||
g_list_store_append (store, obj);
|
||||
g_object_unref (obj);
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
make_array_store (guint n_items)
|
||||
{
|
||||
GtkArrayStore *store;
|
||||
guint i;
|
||||
|
||||
store = gtk_array_store_new (GTK_TYPE_STRING_OBJECT);
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
char *string;
|
||||
gpointer obj;
|
||||
|
||||
string = g_strdup_printf ("item %d", i);
|
||||
obj = get_object (string);
|
||||
gtk_array_store_append (store, obj);
|
||||
g_object_unref (obj);
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
make_string_list2 (guint n_items)
|
||||
{
|
||||
GtkStringList2 *store;
|
||||
guint i;
|
||||
|
||||
store = gtk_string_list2_new (NULL);
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
char *string;
|
||||
|
||||
string = g_strdup_printf ("item %d", i);
|
||||
gtk_string_list2_append (store, string);
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
make_string_list (guint n_items)
|
||||
{
|
||||
GtkStringList *store;
|
||||
guint i;
|
||||
|
||||
store = gtk_string_list_new (NULL);
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
char *string;
|
||||
|
||||
string = g_strdup_printf ("item %d", i);
|
||||
gtk_string_list_append (store, string);
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static void
|
||||
do_random_access (const char *kind,
|
||||
guint size)
|
||||
{
|
||||
GListModel *model;
|
||||
guint i;
|
||||
guint position;
|
||||
GtkStringObject *obj;
|
||||
gint64 start, end;
|
||||
guint iterations = 10000000;
|
||||
|
||||
if (strcmp (kind, "liststore") == 0)
|
||||
model = make_list_store (size);
|
||||
else if (strcmp (kind, "arraystore") == 0)
|
||||
model = make_array_store (size);
|
||||
else if (strcmp (kind, "stringlist") == 0)
|
||||
model = make_string_list2 (size);
|
||||
else if (strcmp (kind, "array stringlist") == 0)
|
||||
model = make_string_list (size);
|
||||
else
|
||||
g_error ("unsupported: %s", kind);
|
||||
|
||||
start = g_get_monotonic_time ();
|
||||
|
||||
for (i = 0; i < iterations; i++)
|
||||
{
|
||||
position = g_random_int_range (0, size);
|
||||
obj = g_list_model_get_item (model, position);
|
||||
if (g_getenv ("PRINT_ACCESS"))
|
||||
g_print ("%s", gtk_string_object_get_string (obj));
|
||||
g_object_unref (obj);
|
||||
}
|
||||
|
||||
end = g_get_monotonic_time ();
|
||||
|
||||
g_print ("\"random access\",\"%s\", %u, %g\n",
|
||||
kind,
|
||||
size,
|
||||
((double)(end - start)) / iterations);
|
||||
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
static void
|
||||
do_linear_access (const char *kind,
|
||||
guint size)
|
||||
{
|
||||
GListModel *model;
|
||||
guint i;
|
||||
GtkStringObject *obj;
|
||||
gint64 start, end;
|
||||
guint iterations = 10000000;
|
||||
|
||||
if (strcmp (kind, "liststore") == 0)
|
||||
model = make_list_store (size);
|
||||
else if (strcmp (kind, "arraystore") == 0)
|
||||
model = make_array_store (size);
|
||||
else if (strcmp (kind, "stringlist") == 0)
|
||||
model = make_string_list2 (size);
|
||||
else if (strcmp (kind, "array stringlist") == 0)
|
||||
model = make_string_list (size);
|
||||
else
|
||||
g_error ("unsupported: %s", kind);
|
||||
|
||||
start = g_get_monotonic_time ();
|
||||
|
||||
for (i = 0; i < iterations; i++)
|
||||
{
|
||||
obj = g_list_model_get_item (model, i % size);
|
||||
if (g_getenv ("PRINT_ACCESS"))
|
||||
g_print ("%s", gtk_string_object_get_string (obj));
|
||||
g_object_unref (obj);
|
||||
}
|
||||
|
||||
end = g_get_monotonic_time ();
|
||||
|
||||
g_print ("\"linear access\", \"%s\", %u, %g\n",
|
||||
kind,
|
||||
size,
|
||||
((double)(end - start)) / iterations);
|
||||
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
static void
|
||||
do_append (const char *kind,
|
||||
guint size)
|
||||
{
|
||||
GListModel *model;
|
||||
guint i, j;
|
||||
gint64 start, end;
|
||||
int iterations = 5;
|
||||
gint64 total;
|
||||
|
||||
total = 0;
|
||||
|
||||
for (i = 0; i < iterations; i++)
|
||||
{
|
||||
if (strcmp (kind, "liststore") == 0)
|
||||
model = make_list_store (0);
|
||||
else if (strcmp (kind, "arraystore") == 0)
|
||||
model = make_array_store (0);
|
||||
else if (strcmp (kind, "stringlist") == 0)
|
||||
model = make_string_list2 (0);
|
||||
else if (strcmp (kind, "array stringlist") == 0)
|
||||
model = make_string_list (0);
|
||||
else
|
||||
g_error ("unsupported: %s", kind);
|
||||
|
||||
start = g_get_monotonic_time ();
|
||||
|
||||
for (j = 0; j < size; j++)
|
||||
{
|
||||
char *string = g_strdup_printf ("item %d", j);
|
||||
|
||||
if (strcmp (kind, "liststore") == 0)
|
||||
{
|
||||
gpointer obj = get_object (string);
|
||||
g_list_store_append (G_LIST_STORE (model), obj);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
else if (strcmp (kind, "arraystore") == 0)
|
||||
{
|
||||
gpointer obj = get_object (string);
|
||||
gtk_array_store_append (GTK_ARRAY_STORE (model), obj);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
else if (strcmp (kind, "stringlist") == 0)
|
||||
gtk_string_list2_append (GTK_STRING_LIST2 (model), string);
|
||||
else if (strcmp (kind, "array stringlist") == 0)
|
||||
gtk_string_list_append (GTK_STRING_LIST (model), string);
|
||||
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
end = g_get_monotonic_time ();
|
||||
total += end - start;
|
||||
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
g_print ("\"append\", \"%s\", %u, %g\n", kind, size, ((double)total) / iterations);
|
||||
}
|
||||
|
||||
#define gtk_array_store_insert(store,position,item) \
|
||||
gtk_array_store_splice (store, position, 0, (gpointer *)&item, 1)
|
||||
|
||||
static void
|
||||
do_insert (const char *kind,
|
||||
guint size)
|
||||
{
|
||||
GListModel *model;
|
||||
guint i, j;
|
||||
gint64 start, end;
|
||||
int iterations = 5;
|
||||
gint64 total;
|
||||
guint position;
|
||||
|
||||
total = 0;
|
||||
|
||||
for (i = 0; i < iterations; i++)
|
||||
{
|
||||
if (strcmp (kind, "liststore") == 0)
|
||||
model = make_list_store (1);
|
||||
else if (strcmp (kind, "arraystore") == 0)
|
||||
model = make_array_store (1);
|
||||
else if (strcmp (kind, "stringlist") == 0)
|
||||
model = make_string_list2 (1);
|
||||
else if (strcmp (kind, "array stringlist") == 0)
|
||||
model = make_string_list (1);
|
||||
else
|
||||
g_error ("unsupported: %s", kind);
|
||||
|
||||
start = g_get_monotonic_time ();
|
||||
|
||||
for (j = 1; j < size; j++)
|
||||
{
|
||||
char *string = g_strdup_printf ("item %d", j);
|
||||
position = g_random_int_range (0, j);
|
||||
|
||||
if (strcmp (kind, "liststore") == 0)
|
||||
{
|
||||
gpointer obj = get_object (string);
|
||||
g_list_store_insert (G_LIST_STORE (model), position, obj);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
else if (strcmp (kind, "arraystore") == 0)
|
||||
{
|
||||
gpointer obj = get_object (string);
|
||||
gtk_array_store_insert (GTK_ARRAY_STORE (model), position, obj);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
else if (strcmp (kind, "stringlist") == 0)
|
||||
gtk_string_list2_splice (GTK_STRING_LIST2 (model), position, 0,
|
||||
(const char * const []){string, NULL});
|
||||
else if (strcmp (kind, "array stringlist") == 0)
|
||||
gtk_string_list_splice (GTK_STRING_LIST (model), position, 0,
|
||||
(const char * const []){string, NULL});
|
||||
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
end = g_get_monotonic_time ();
|
||||
total += end - start;
|
||||
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
g_print ("\"insert\", \"%s\", %u, %g\n", kind, size, ((double)total) / iterations);
|
||||
}
|
||||
|
||||
static void
|
||||
random_access (void)
|
||||
{
|
||||
const char *kind[] = { "liststore", "arraystore", "stringlist", "array stringlist" };
|
||||
int sizes = 22;
|
||||
int size;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (kind); i++)
|
||||
for (j = 0, size = 2; j < sizes; j++, size *= 2)
|
||||
do_random_access (kind[i], size);
|
||||
}
|
||||
|
||||
static void
|
||||
linear_access (void)
|
||||
{
|
||||
const char *kind[] = { "liststore", "arraystore", "stringlist", "array stringlist" };
|
||||
int sizes = 22;
|
||||
int size;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (kind); i++)
|
||||
for (j = 0, size = 2; j < sizes; j++, size *= 2)
|
||||
do_linear_access (kind[i], size);
|
||||
}
|
||||
|
||||
static void
|
||||
append (void)
|
||||
{
|
||||
const char *kind[] = { "liststore", "arraystore", "stringlist", "array stringlist" };
|
||||
int sizes = 22;
|
||||
int size;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (kind); i++)
|
||||
for (j = 0, size = 2; j < sizes; j++, size *= 2)
|
||||
do_append (kind[i], size);
|
||||
}
|
||||
|
||||
static void
|
||||
insert (void)
|
||||
{
|
||||
const char *kind[] = { "liststore", "arraystore", "stringlist", "array stringlist" };
|
||||
int sizes = 22;
|
||||
int size;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (kind); i++)
|
||||
for (j = 0, size = 2; j < sizes; j++, size *= 2)
|
||||
do_insert (kind[i], size);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv);
|
||||
|
||||
g_print ("\"test\",\"model\",\"model size\",\"time\"");
|
||||
g_test_add_func ("/model/random-access", random_access);
|
||||
g_test_add_func ("/model/linear-access", linear_access);
|
||||
g_test_add_func ("/model/append", append);
|
||||
g_test_add_func ("/model/insert", insert);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
@@ -12,6 +12,7 @@ tests = [
|
||||
['accessible'],
|
||||
['action'],
|
||||
['adjustment'],
|
||||
['arraystore'],
|
||||
['bitset'],
|
||||
['bitmask', ['../../gtk/gtkallocatedbitmask.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
|
||||
['builder', [], [], gtk_tests_export_dynamic_ldflag],
|
||||
@@ -74,8 +75,10 @@ tests = [
|
||||
['typename'],
|
||||
['displayclose'],
|
||||
['revealer-size'],
|
||||
['vector'],
|
||||
['widgetorder'],
|
||||
['widget-refcount'],
|
||||
['listmodel-performance', ['listmodel-performance.c','gtkstringlist.c']]
|
||||
]
|
||||
|
||||
# Tests that are expected to fail
|
||||
|
@@ -184,7 +184,7 @@ test_splice (void)
|
||||
|
||||
assert_model (list, "a b c d e");
|
||||
|
||||
gtk_string_list_splice (list, 2, 2, (const char *[]){ "x", "y", "z" }, 3);
|
||||
gtk_string_list_splice (list, 2, 2, (const char *[]){ "x", "y", "z", NULL });
|
||||
|
||||
assert_model (list, "a b x y z e");
|
||||
assert_changes (list, "2-2+3");
|
||||
|
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 ();
|
||||
}
|
116
testsuite/gtk/vectorimpl.c
Normal file
116
testsuite/gtk/vectorimpl.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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
|
||||
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