diff --git a/gtk/gtktreestore.c b/gtk/gtktreestore.c index 137f76a215..83c83072c1 100644 --- a/gtk/gtktreestore.c +++ b/gtk/gtktreestore.c @@ -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) * * */ +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); + } }