Compare commits

...

39 Commits

Author SHA1 Message Date
Matthias Clasen
d233b10559 perf test: Add GtkArrayStore 2020-07-03 19:55:00 -04:00
Matthias Clasen
b4f9f252d7 wip: more perf test work 2020-07-03 19:54:51 -04:00
Matthias Clasen
373c06b148 wip: Add the old stringlist for comparsion 2020-07-03 19:54:44 -04:00
Matthias Clasen
c84cd97fbc wip: benchmarks 2020-07-03 19:54:36 -04:00
Benjamin Otte
6b7d295cfe snapshot: Use GtkVector for the state stack 2020-07-03 04:53:50 +02:00
Benjamin Otte
481d438380 vector: Add a bunch of new features
* GTK_VECTOR_BY_VALUE
   #define this to get GArrray-like behavior
 * gtk_vector_splice (v, 0, 0, NULL, 25)
   Adding items but passing NULL as the items will zero() them.
 * gtk_vector_set_size()
   A nicer way to call gtk_vector_splice()
2020-07-03 04:53:43 +02:00
Benjamin Otte
f4e1057431 icontheme: Use GtkVector 2020-07-03 04:21:07 +02:00
Benjamin Otte
fa0166180e vector: Add null-termination 2020-07-03 03:05:40 +02:00
Benjamin Otte
13d84c202b xxx vector 2020-07-02 23:32:17 +02:00
Benjamin Otte
5846794075 snapshot: Port node list to vector 2020-07-02 23:32:04 +02:00
Benjamin Otte
201da90e23 snapshot: Move structs into .c file
They aren't used anywhere else.
2020-07-02 22:59:40 +02:00
Matthias Clasen
8d4fcabf01 Bring liststore tests over from GLib
Adapt the GListStore tests from GLib to GtkArrayStore.
2020-07-02 22:56:21 +02:00
Benjamin Otte
3f8735a488 Use preallocated array code
Now with GtkVector, we can use that one instead.
2020-07-02 20:52:28 +02:00
Benjamin Otte
a4dc248512 main: Use a GtkVector 2020-07-02 20:52:28 +02:00
Benjamin Otte
ef451b056a cssselector: Use GtkVector 2020-07-02 20:52:28 +02:00
Benjamin Otte
59aaabb161 Add GtkArrayStore
This has roughly the same API as GListStore, it's just an array.
2020-07-02 20:52:28 +02:00
Benjamin Otte
663ca21328 Add GtkVector
This is a scary idea where you #define a bunch of preprocessor values
and then #include "gtkvectorimpl.c" and end up with a dynamic array for
that data type.
2020-07-02 20:34:47 +02:00
Matthias Clasen
77435d31fa gtk-demo: Use a progressbar in the words demo
This looks better and a bit more polished.
2020-07-01 02:47:50 +02:00
Matthias Clasen
6edb8f096f gtk-demo: No selection in the words demo
This demo is about filtering, not about selection,
so use a GtkNoSelection.
2020-07-01 02:46:05 +02:00
Matthias Clasen
c4dfee8860 gtk-demo: Cosmetic fixes for the words demo
Set a window size, and don't put newlines in titles, left align and
ellipsize the label.
2020-07-01 02:46:05 +02:00
Benjamin Otte
8ac1e77c9a demo: Make words listview load async
And add an "Open" button (why are filechooser buttons such a catastrophe
that I can't make them smaller?).
2020-07-01 02:46:05 +02:00
Benjamin Otte
65ceb6c15a stringlist: Call splice() for adding items after construction
This has the benefit of actually allowing NULL to be passed.
2020-07-01 02:46:05 +02:00
Benjamin Otte
f70d10f6ac stringlist: Remove n_additions argument from gtk_string_list_splice()
char ** arrays are null-terminated everywhere, so make sure they are in
splice(), too.

Also fix the argument to be a const char * const * like in the
constructor.
2020-07-01 02:46:05 +02:00
Benjamin Otte
0bbd083d79 stringlist: Clarify docs for gtk_string_list_get_string()
Make sure it's obvious that it behaves like g_list_model_get_item() and
returns NULL for pos >= n_items.
2020-07-01 02:46:05 +02:00
Benjamin Otte
102d2986c6 stringlist: Make one constructor call the other
Simplifies code.
2020-07-01 02:46:05 +02:00
Benjamin Otte
c74201ca87 filterlistmodel: Look at type of change
This way we can avoid refiltering most of an already filtered list when
the change becomes more strict.
2020-07-01 02:46:05 +02:00
Benjamin Otte
b09019a5b4 gtk-demo: Add incremental filtering to words demo 2020-07-01 02:46:05 +02:00
Benjamin Otte
1dd08ad8db filterlistmodel: Add gtk_filter_list_model_get_pending()
This allows tracking if the model is busy filtering.
2020-07-01 02:46:05 +02:00
Benjamin Otte
eb0704855f filterlistmodel: Add incremental filtering 2020-07-01 02:46:05 +02:00
Benjamin Otte
c4c1d4a1b3 bitset: Add gtk_bitset_new_range()
It's a common use.
2020-07-01 02:46:05 +02:00
Benjamin Otte
e6756f605e stringlist: Make property not construct-only
Massively speeds up creation of long stringlists.
2020-07-01 02:46:05 +02:00
Benjamin Otte
1f4b8e089e filterlistmodel: Rewrite with bitset data structure
Bitsets are more powerful, less memory intensive and faster than the old
GtkRbTree version.
2020-07-01 02:46:05 +02:00
Benjamin Otte
c0e08db739 bitset: Add APIs needed for a filterlistmodel 2020-06-30 00:46:56 +02:00
Benjamin Otte
1c337d350d tests: Make testlistview be a list again
The grid conversion was for testing and should never have been
committed.
2020-06-30 00:46:56 +02:00
Benjamin Otte
81e675dfbf gtk-demo: Add a listview demo for filtering strings 2020-06-30 00:36:14 +02:00
Benjamin Otte
8556221429 stringlist: Take a const char const * argument
Sucks that we need to cast a char**, but otherwise we need to cast
{"foo", "bar", "baz" } arrays.
2020-06-30 00:36:14 +02:00
Benjamin Otte
1b4109a7fd scrolledwindow: Expand by default
Use gtk_scrolled_window_set_hexpand/vexpand(FALSE) to make the scrolled
window not expand.
2020-06-30 00:36:13 +02:00
Benjamin Otte
df0786be7a stringfilter: Don't crash if the expression returns "" 2020-06-30 00:36:13 +02:00
Benjamin Otte
3f545da08d a11y: Remove double initialization of variables 2020-06-30 00:36:13 +02:00
37 changed files with 3144 additions and 742 deletions

View File

@@ -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>

View 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;
}

View File

@@ -49,6 +49,7 @@ demos = files([
'listview_minesweeper.c',
'listview_settings.c',
'listview_weather.c',
'listview_words.c',
'markup.c',
'modelbutton.c',
'overlay.c',

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

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

287
gtk/gtkarraystore.c Normal file
View 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
View 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__ */

View File

@@ -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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

@@ -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 ();
}
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,7 +144,7 @@ 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,
gtk_filter_list_model_run_filter_on_item (GtkFilterListModel *self,
guint position)
{
gpointer item;
@@ -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;
for (i = 0; i < removed; i++)
{
FilterNode *next = gtk_rb_tree_node_get_next (node);
if (node->visible)
filter_removed++;
gtk_rb_tree_remove (self->items, node);
node = next;
}
filter_added = gtk_filter_list_model_add_items (self, node, position, added);
gtk_bitset_slice (self->matches, position, removed, added);
if (self->pending)
gtk_bitset_slice (self->pending, position, removed, 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));
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);
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)
{
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,39 +482,43 @@ 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;
self->strictness = new_strictness;
switch (change)
{
default:
case GTK_FILTER_MATCH_SOME:
gtk_filter_list_model_refilter (self);
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,14 +764,19 @@ 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);
}
}
else
{
self->strictness = GTK_FILTER_MATCH_NONE;
@@ -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))
self->incremental = incremental;
if (!incremental)
{
visible = gtk_filter_list_model_run_filter (self, i);
if (visible == node->visible)
{
if (visible)
{
n_is_visible++;
n_was_visible++;
}
continue;
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);
}
node->visible = visible;
gtk_rb_tree_node_mark_dirty (node);
first_change = MIN (n_is_visible, first_change);
if (visible)
n_is_visible++;
else
n_was_visible++;
last_change = MAX (n_is_visible, last_change);
}
if (first_change <= last_change)
{
g_list_model_items_changed (G_LIST_MODEL (self),
first_change,
last_change - first_change + n_was_visible - n_is_visible,
last_change - first_change);
}
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);
}

View File

@@ -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

View File

@@ -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,
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
{

View File

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

View File

@@ -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

View File

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

View File

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

View File

@@ -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)
{

View File

@@ -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
@@ -470,11 +466,10 @@ void
gtk_string_list_splice (GtkStringList *self,
guint position,
guint n_removals,
const char **additions,
guint n_additions)
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().

View File

@@ -45,7 +45,7 @@ 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,
@@ -63,8 +63,7 @@ GDK_AVAILABLE_IN_ALL
void gtk_string_list_splice (GtkStringList *self,
guint position,
guint n_removals,
const char **additions,
guint n_additions);
const char * const *additions);
GDK_AVAILABLE_IN_ALL
const char * gtk_string_list_get_string (GtkStringList *self,

280
gtk/gtkvectorimpl.c Normal file
View 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

View File

@@ -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',

View File

@@ -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
View 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 ();
}

View 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);
}
}

View 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__ */

View 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 ();
}

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1,105 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include <locale.h>
#include <gtk/gtk.h>
static void
int_free_func (int data)
{
}
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME int_vector
#define GTK_VECTOR_TYPE_NAME IntVector
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME pre_int_vector
#define GTK_VECTOR_TYPE_NAME PreIntVector
#define GTK_VECTOR_PREALLOC 100
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME free_int_vector
#define GTK_VECTOR_TYPE_NAME FreeIntVector
#define GTK_VECTOR_FREE_FUNC int_free_func
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME pre_free_int_vector
#define GTK_VECTOR_TYPE_NAME PreFreeIntVector
#define GTK_VECTOR_PREALLOC 100
#define GTK_VECTOR_FREE_FUNC int_free_func
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_int_vector
#define GTK_VECTOR_TYPE_NAME NullIntVector
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_pre_int_vector
#define GTK_VECTOR_TYPE_NAME NullPreIntVector
#define GTK_VECTOR_PREALLOC 100
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_free_int_vector
#define GTK_VECTOR_TYPE_NAME NullFreeIntVector
#define GTK_VECTOR_FREE_FUNC int_free_func
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
#define GTK_VECTOR_ELEMENT_TYPE int
#define GTK_VECTOR_NAME null_pre_free_int_vector
#define GTK_VECTOR_TYPE_NAME NullPreFreeIntVector
#define GTK_VECTOR_PREALLOC 100
#define GTK_VECTOR_FREE_FUNC int_free_func
#define GTK_VECTOR_NULL_TERMINATED 1
#include "vectorimpl.c"
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
setlocale (LC_ALL, "C");
g_test_add_func ("/intvector/simple", int_vector_test_simple);
g_test_add_func ("/intvector/prealloc/simple", pre_int_vector_test_simple);
g_test_add_func ("/intvector/freefunc/simple", free_int_vector_test_simple);
g_test_add_func ("/intvector/prealloc/freefunc/simple", pre_free_int_vector_test_simple);
g_test_add_func ("/intvector/null/simple", null_int_vector_test_simple);
g_test_add_func ("/intvector/null/prealloc/simple", null_pre_int_vector_test_simple);
g_test_add_func ("/intvector/null/freefunc/simple", null_free_int_vector_test_simple);
g_test_add_func ("/intvector/null/prealloc/freefunc/simple", null_pre_free_int_vector_test_simple);
g_test_add_func ("/intvector/splice", int_vector_test_splice);
g_test_add_func ("/intvector/prealloc/splice", pre_int_vector_test_splice);
g_test_add_func ("/intvector/freefunc/splice", free_int_vector_test_splice);
g_test_add_func ("/intvector/prealloc/freefunc/splice", pre_free_int_vector_test_splice);
g_test_add_func ("/intvector/null/splice", null_int_vector_test_splice);
g_test_add_func ("/intvector/null/prealloc/splice", null_pre_int_vector_test_splice);
g_test_add_func ("/intvector/null/freefunc/splice", null_free_int_vector_test_splice);
g_test_add_func ("/intvector/null/prealloc/freefunc/splice", null_pre_free_int_vector_test_splice);
return g_test_run ();
}

116
testsuite/gtk/vectorimpl.c Normal file
View 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