2 * Copyright © 2011, 2012 Canonical Ltd.
4 * This library is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 * Author: Ryan Lortie <desrt@desrt.ca>
22 #include "gtkbuilderprivate.h"
37 ParserData *parser_data;
47 gboolean translatable;
48 } GtkBuilderMenuState;
51 gtk_builder_menu_push_frame (GtkBuilderMenuState *state,
57 new = g_slice_new (struct frame);
60 state->frame.menu = menu;
61 state->frame.item = item;
62 state->frame.prev = new;
66 gtk_builder_menu_pop_frame (GtkBuilderMenuState *state)
68 struct frame *prev = state->frame.prev;
70 if (state->frame.item)
72 g_assert (prev->menu != NULL);
73 g_menu_append_item (prev->menu, state->frame.item);
74 g_object_unref (state->frame.item);
79 g_slice_free (struct frame, prev);
83 gtk_builder_menu_start_element (GMarkupParseContext *context,
84 const gchar *element_name,
85 const gchar **attribute_names,
86 const gchar **attribute_values,
90 GtkBuilderMenuState *state = user_data;
92 #define COLLECT(first, ...) \
93 g_markup_collect_attributes (element_name, \
94 attribute_names, attribute_values, error, \
95 first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
96 #define OPTIONAL G_MARKUP_COLLECT_OPTIONAL
97 #define BOOLEAN G_MARKUP_COLLECT_BOOLEAN
98 #define STRING G_MARKUP_COLLECT_STRING
100 if (state->frame.menu)
102 /* Can have '<item>', '<submenu>' or '<section>' here. */
103 if (g_str_equal (element_name, "item"))
107 if (COLLECT (G_MARKUP_COLLECT_INVALID, NULL))
109 item = g_menu_item_new (NULL, NULL);
110 gtk_builder_menu_push_frame (state, NULL, item);
116 else if (g_str_equal (element_name, "submenu"))
120 if (COLLECT (STRING | OPTIONAL, "id", &id))
125 menu = g_menu_new ();
126 item = g_menu_item_new_submenu (NULL, G_MENU_MODEL (menu));
127 gtk_builder_menu_push_frame (state, menu, item);
130 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
136 else if (g_str_equal (element_name, "section"))
140 if (COLLECT (STRING | OPTIONAL, "id", &id))
145 menu = g_menu_new ();
146 item = g_menu_item_new_section (NULL, G_MENU_MODEL (menu));
147 gtk_builder_menu_push_frame (state, menu, item);
150 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
157 if (state->frame.item)
159 /* Can have '<attribute>' or '<link>' here. */
160 if (g_str_equal (element_name, "attribute"))
162 const gchar *typestr;
164 const gchar *context;
166 if (COLLECT (STRING, "name", &name,
167 OPTIONAL | BOOLEAN, "translatable", &state->translatable,
168 OPTIONAL | STRING, "context", &context,
169 OPTIONAL | STRING, "comments", NULL, /* ignore, just for translators */
170 OPTIONAL | STRING, "type", &typestr))
172 if (typestr && !g_variant_type_string_is_valid (typestr))
174 g_set_error (error, G_VARIANT_PARSE_ERROR,
175 G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING,
176 "Invalid GVariant type string '%s'", typestr);
180 state->type = typestr ? g_variant_type_new (typestr) : NULL;
181 state->string = g_string_new (NULL);
182 state->attribute = g_strdup (name);
183 state->context = g_strdup (context);
185 gtk_builder_menu_push_frame (state, NULL, NULL);
191 if (g_str_equal (element_name, "link"))
196 if (COLLECT (STRING, "name", &name,
197 STRING | OPTIONAL, "id", &id))
201 menu = g_menu_new ();
202 g_menu_item_set_link (state->frame.item, name, G_MENU_MODEL (menu));
203 gtk_builder_menu_push_frame (state, menu, NULL);
206 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
214 const GSList *element_stack;
216 element_stack = g_markup_parse_context_get_element_stack (context);
218 if (element_stack->next)
219 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
220 _("Element <%s> not allowed inside <%s>"),
221 element_name, (const gchar *) element_stack->next->data);
224 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
225 _("Element <%s> not allowed at toplevel"), element_name);
230 gtk_builder_menu_end_element (GMarkupParseContext *context,
231 const gchar *element_name,
235 GtkBuilderMenuState *state = user_data;
237 gtk_builder_menu_pop_frame (state);
244 text = g_string_free (state->string, FALSE);
245 state->string = NULL;
247 /* do the translation if necessary */
248 if (state->translatable)
250 const gchar *translated;
253 translated = g_dpgettext2 (state->parser_data->domain, state->context, text);
255 translated = g_dgettext (state->parser_data->domain, text);
257 if (translated != text)
259 /* it's safe because we know that translated != text */
261 text = g_strdup (translated);
265 if (state->type == NULL)
266 /* No type string specified -> it's a normal string. */
267 g_menu_item_set_attribute (state->frame.item, state->attribute, "s", text);
269 /* Else, we try to parse it according to the type string. If
270 * error is set here, it will follow us out, ending the parse.
272 * We still need to free everything, though, so ignore it here.
274 else if ((value = g_variant_parse (state->type, text, NULL, NULL, error)))
276 g_menu_item_set_attribute_value (state->frame.item, state->attribute, value);
277 g_variant_unref (value);
282 g_variant_type_free (state->type);
286 g_free (state->context);
287 state->context = NULL;
289 g_free (state->attribute);
290 state->attribute = NULL;
297 gtk_builder_menu_text (GMarkupParseContext *context,
303 GtkBuilderMenuState *state = user_data;
306 for (i = 0; i < text_len; i++)
307 if (!g_ascii_isspace (text[i]))
310 g_string_append_len (state->string, text, text_len);
313 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
314 _("text may not appear inside <%s>"),
315 g_markup_parse_context_get_element (context));
321 gtk_builder_menu_error (GMarkupParseContext *context,
325 GtkBuilderMenuState *state = user_data;
327 while (state->frame.prev)
329 struct frame *prev = state->frame.prev;
331 state->frame = *prev;
333 g_slice_free (struct frame, prev);
337 g_string_free (state->string, TRUE);
340 g_variant_type_free (state->type);
342 g_free (state->attribute);
343 g_free (state->context);
345 g_slice_free (GtkBuilderMenuState, state);
348 static GMarkupParser gtk_builder_menu_subparser =
350 gtk_builder_menu_start_element,
351 gtk_builder_menu_end_element,
352 gtk_builder_menu_text,
353 NULL, /* passthrough */
354 gtk_builder_menu_error
358 _gtk_builder_menu_start (ParserData *parser_data,
359 const gchar *element_name,
360 const gchar **attribute_names,
361 const gchar **attribute_values,
364 GtkBuilderMenuState *state;
367 state = g_slice_new0 (GtkBuilderMenuState);
368 state->parser_data = parser_data;
369 g_markup_parse_context_push (parser_data->ctx, >k_builder_menu_subparser, state);
371 if (COLLECT (STRING, "id", &id))
375 menu = g_menu_new ();
376 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
377 gtk_builder_menu_push_frame (state, menu, NULL);
382 _gtk_builder_menu_end (ParserData *parser_data)
384 GtkBuilderMenuState *state;
386 state = g_markup_parse_context_pop (parser_data->ctx);
387 gtk_builder_menu_pop_frame (state);
389 g_assert (state->frame.prev == NULL);
390 g_assert (state->frame.item == NULL);
391 g_assert (state->frame.menu == NULL);
392 g_slice_free (GtkBuilderMenuState, state);