treestore: Copy liststore buildable implementation

This add support for data, but does not allow
nesting yet.
This commit is contained in:
Matthias Clasen 2022-05-06 07:48:28 -04:00
parent 1a488722fa
commit c2e8604805

View File

@ -177,11 +177,11 @@ static gboolean gtk_tree_store_buildable_custom_tag_start (GtkBuildable *b
const char *tagname,
GtkBuildableParser *parser,
gpointer *data);
static void gtk_tree_store_buildable_custom_finished (GtkBuildable *buildable,
static void gtk_tree_store_buildable_custom_tag_end (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const char *tagname,
gpointer user_data);
gpointer parser_data);
static void gtk_tree_store_move (GtkTreeStore *tree_store,
GtkTreeIter *iter,
@ -275,7 +275,7 @@ void
gtk_tree_store_buildable_init (GtkBuildableIface *iface)
{
iface->custom_tag_start = gtk_tree_store_buildable_custom_tag_start;
iface->custom_finished = gtk_tree_store_buildable_custom_finished;
iface->custom_tag_end = gtk_tree_store_buildable_custom_tag_end;
}
static void
@ -3315,10 +3315,25 @@ validate_gnode (GNode* node)
* <column type="..."/>
* </columns>
*/
typedef struct {
gboolean translatable;
char *context;
int id;
} ColInfo;
typedef struct {
GtkBuilder *builder;
GObject *object;
GSList *column_type_names;
GType *column_types;
GValue *values;
int *colids;
ColInfo **columns;
int last_row;
int n_columns;
int row_column;
gboolean is_data;
const char *domain;
} SubParserData;
static void
@ -3331,7 +3346,76 @@ tree_store_start_element (GtkBuildableParseContext *context,
{
SubParserData *data = (SubParserData*)user_data;
if (strcmp (element_name, "columns") == 0)
if (strcmp (element_name, "col") == 0)
{
int id = -1;
const char *id_str;
const char *msg_context = NULL;
gboolean translatable = FALSE;
ColInfo *info;
GValue val = G_VALUE_INIT;
if (!_gtk_builder_check_parent (data->builder, context, "row", error))
return;
if (data->row_column >= data->n_columns)
{
g_set_error (error,
GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE,
"Too many columns, maximum is %d", data->n_columns - 1);
_gtk_builder_prefix_error (data->builder, context, error);
return;
}
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_STRING, "id", &id_str,
G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL,
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &msg_context,
G_MARKUP_COLLECT_INVALID))
{
_gtk_builder_prefix_error (data->builder, context, error);
return;
}
if (!gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, id_str, &val, error))
{
_gtk_builder_prefix_error (data->builder, context, error);
return;
}
id = g_value_get_int (&val);
if (id < 0 || id >= data->n_columns)
{
g_set_error (error,
GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE,
"id value %d out of range", id);
_gtk_builder_prefix_error (data->builder, context, error);
return;
}
info = g_slice_new0 (ColInfo);
info->translatable = translatable;
info->context = g_strdup (msg_context);
info->id = id;
data->colids[data->row_column] = id;
data->columns[data->row_column] = info;
data->row_column++;
data->is_data = TRUE;
}
else if (strcmp (element_name, "row") == 0)
{
if (!_gtk_builder_check_parent (data->builder, context, "data", error))
return;
if (!g_markup_collect_attributes (element_name, names, values, error,
G_MARKUP_COLLECT_INVALID, NULL, NULL,
G_MARKUP_COLLECT_INVALID))
_gtk_builder_prefix_error (data->builder, context, error);
}
else if (strcmp (element_name, "columns") == 0 ||
strcmp (element_name, "data") == 0)
{
if (!_gtk_builder_check_parent (data->builder, context, "object", error))
return;
@ -3340,7 +3424,6 @@ tree_store_start_element (GtkBuildableParseContext *context,
G_MARKUP_COLLECT_INVALID, NULL, NULL,
G_MARKUP_COLLECT_INVALID))
_gtk_builder_prefix_error (data->builder, context, error);
}
else if (strcmp (element_name, "column") == 0)
{
@ -3359,6 +3442,7 @@ tree_store_start_element (GtkBuildableParseContext *context,
data->column_type_names = g_slist_prepend (data->column_type_names, g_strdup (type));
}
else
{
_gtk_builder_error_unhandled_tag (data->builder, context,
@ -3377,16 +3461,41 @@ tree_store_end_element (GtkBuildableParseContext *context,
g_assert(data->builder);
if (strcmp (element_name, "columns") == 0)
if (strcmp (element_name, "row") == 0)
{
GtkTreeIter iter;
int i;
gtk_tree_store_insert_with_valuesv (GTK_TREE_STORE (data->object),
&iter,
NULL,
data->last_row,
data->colids,
data->values,
data->row_column);
for (i = 0; i < data->row_column; i++)
{
ColInfo *info = data->columns[i];
g_free (info->context);
g_slice_free (ColInfo, info);
data->columns[i] = NULL;
g_value_unset (&data->values[i]);
}
g_free (data->values);
data->values = g_new0 (GValue, data->n_columns);
data->last_row++;
data->row_column = 0;
}
else if (strcmp (element_name, "columns") == 0)
{
GType *column_types;
GSList *l;
GType *types;
int i;
GType type;
data = (SubParserData*)user_data;
data->column_type_names = g_slist_reverse (data->column_type_names);
types = g_new0 (GType, g_slist_length (data->column_type_names));
column_types = g_new0 (GType, g_slist_length (data->column_type_names));
for (l = data->column_type_names, i = 0; l; l = l->next, i++)
{
@ -3398,23 +3507,72 @@ tree_store_end_element (GtkBuildableParseContext *context,
gtk_buildable_get_buildable_id (GTK_BUILDABLE (data->object)));
continue;
}
types[i] = type;
column_types[i] = type;
g_free (l->data);
}
gtk_tree_store_set_column_types (GTK_TREE_STORE (data->object), i, types);
gtk_tree_store_set_column_types (GTK_TREE_STORE (data->object), i, column_types);
g_free (types);
g_free (column_types);
}
else if (strcmp (element_name, "col") == 0)
{
data->is_data = FALSE;
}
}
static const GtkBuildableParser tree_store_parser =
{
tree_store_start_element,
tree_store_end_element
};
static void
tree_store_text (GtkBuildableParseContext *context,
const char *text,
gsize text_len,
gpointer user_data,
GError **error)
{
SubParserData *data = (SubParserData*)user_data;
int i;
char *string;
ColInfo *info;
if (!data->is_data)
return;
i = data->row_column - 1;
info = data->columns[i];
string = g_strndup (text, text_len);
if (info->translatable && text_len)
{
char *translated;
/* FIXME: This will not use the domain set in the .ui file,
* since the parser is not telling the builder about the domain.
* However, it will work for gtk_builder_set_translation_domain() calls.
*/
translated = g_strdup (_gtk_builder_parser_translate (data->domain,
info->context,
string));
g_free (string);
string = translated;
}
if (!gtk_builder_value_from_string_type (data->builder,
data->column_types[info->id],
string,
&data->values[i],
error))
{
_gtk_builder_prefix_error (data->builder, context, error);
}
g_free (string);
}
static const GtkBuildableParser tree_store_parser =
{
tree_store_start_element,
tree_store_end_element,
tree_store_text
};
static gboolean
gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable,
@ -3439,6 +3597,28 @@ gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable,
*parser = tree_store_parser;
*parser_data = data;
return TRUE;
}
else if (strcmp (tagname, "data") == 0)
{
int n_columns = gtk_tree_store_get_n_columns (GTK_TREE_MODEL (buildable));
if (n_columns == 0)
g_error ("Cannot append data to an empty model");
data = g_slice_new0 (SubParserData);
data->builder = builder;
data->object = G_OBJECT (buildable);
data->values = g_new0 (GValue, n_columns);
data->colids = g_new0 (int, n_columns);
data->columns = g_new0 (ColInfo *, n_columns);
data->column_types = GTK_TREE_STORE (buildable)->priv->column_headers;
data->n_columns = n_columns;
data->last_row = 0;
data->domain = gtk_builder_get_translation_domain (builder);
*parser = tree_store_parser;
*parser_data = data;
return TRUE;
}
@ -3446,19 +3626,34 @@ gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable,
}
static void
gtk_tree_store_buildable_custom_finished (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const char *tagname,
gpointer user_data)
gtk_tree_store_buildable_custom_tag_end (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const char *tagname,
gpointer parser_data)
{
SubParserData *data;
SubParserData *data = (SubParserData*)parser_data;
if (strcmp (tagname, "columns"))
return;
data = (SubParserData*)user_data;
g_slist_free (data->column_type_names);
g_slice_free (SubParserData, data);
if (strcmp (tagname, "columns") == 0)
{
g_slist_free (data->column_type_names);
g_slice_free (SubParserData, data);
}
else if (strcmp (tagname, "data") == 0)
{
int i;
for (i = 0; i < data->n_columns; i++)
{
ColInfo *info = data->columns[i];
if (info)
{
g_free (info->context);
g_slice_free (ColInfo, info);
}
}
g_free (data->colids);
g_free (data->columns);
g_free (data->values);
g_slice_free (SubParserData, data);
}
}