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, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * Author: Ryan Lortie <desrt@desrt.ca>
24 #include "gtkbuilderprivate.h"
39 ParserData *parser_data;
49 gboolean translatable;
50 } GtkBuilderMenuState;
53 gtk_builder_menu_push_frame (GtkBuilderMenuState *state,
59 new = g_slice_new (struct frame);
62 state->frame.menu = menu;
63 state->frame.item = item;
64 state->frame.prev = new;
68 gtk_builder_menu_pop_frame (GtkBuilderMenuState *state)
70 struct frame *prev = state->frame.prev;
72 if (state->frame.item)
74 g_assert (prev->menu != NULL);
75 g_menu_append_item (prev->menu, state->frame.item);
76 g_object_unref (state->frame.item);
81 g_slice_free (struct frame, prev);
85 gtk_builder_menu_start_element (GMarkupParseContext *context,
86 const gchar *element_name,
87 const gchar **attribute_names,
88 const gchar **attribute_values,
92 GtkBuilderMenuState *state = user_data;
94 #define COLLECT(first, ...) \
95 g_markup_collect_attributes (element_name, \
96 attribute_names, attribute_values, error, \
97 first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
98 #define OPTIONAL G_MARKUP_COLLECT_OPTIONAL
99 #define BOOLEAN G_MARKUP_COLLECT_BOOLEAN
100 #define STRING G_MARKUP_COLLECT_STRING
102 if (state->frame.menu)
104 /* Can have '<item>', '<submenu>' or '<section>' here. */
105 if (g_str_equal (element_name, "item"))
109 if (COLLECT (G_MARKUP_COLLECT_INVALID, NULL))
111 item = g_menu_item_new (NULL, NULL);
112 gtk_builder_menu_push_frame (state, NULL, item);
118 else if (g_str_equal (element_name, "submenu"))
122 if (COLLECT (STRING | OPTIONAL, "id", &id))
127 menu = g_menu_new ();
128 item = g_menu_item_new_submenu (NULL, G_MENU_MODEL (menu));
129 gtk_builder_menu_push_frame (state, menu, item);
132 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
138 else if (g_str_equal (element_name, "section"))
142 if (COLLECT (STRING | OPTIONAL, "id", &id))
147 menu = g_menu_new ();
148 item = g_menu_item_new_section (NULL, G_MENU_MODEL (menu));
149 gtk_builder_menu_push_frame (state, menu, item);
152 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
159 if (state->frame.item)
161 /* Can have '<attribute>' or '<link>' here. */
162 if (g_str_equal (element_name, "attribute"))
164 const gchar *typestr;
166 const gchar *context;
168 if (COLLECT (STRING, "name", &name,
169 OPTIONAL | BOOLEAN, "translatable", &state->translatable,
170 OPTIONAL | STRING, "context", &context,
171 OPTIONAL | STRING, "comments", NULL, /* ignore, just for translators */
172 OPTIONAL | STRING, "type", &typestr))
174 if (typestr && !g_variant_type_string_is_valid (typestr))
176 g_set_error (error, G_VARIANT_PARSE_ERROR,
177 G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING,
178 "Invalid GVariant type string '%s'", typestr);
182 state->type = typestr ? g_variant_type_new (typestr) : NULL;
183 state->string = g_string_new (NULL);
184 state->attribute = g_strdup (name);
185 state->context = g_strdup (context);
187 gtk_builder_menu_push_frame (state, NULL, NULL);
193 if (g_str_equal (element_name, "link"))
198 if (COLLECT (STRING, "name", &name,
199 STRING | OPTIONAL, "id", &id))
203 menu = g_menu_new ();
204 g_menu_item_set_link (state->frame.item, name, G_MENU_MODEL (menu));
205 gtk_builder_menu_push_frame (state, menu, NULL);
208 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
216 const GSList *element_stack;
218 element_stack = g_markup_parse_context_get_element_stack (context);
220 if (element_stack->next)
221 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
222 _("Element <%s> not allowed inside <%s>"),
223 element_name, (const gchar *) element_stack->next->data);
226 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
227 _("Element <%s> not allowed at toplevel"), element_name);
232 gtk_builder_menu_end_element (GMarkupParseContext *context,
233 const gchar *element_name,
237 GtkBuilderMenuState *state = user_data;
239 gtk_builder_menu_pop_frame (state);
246 text = g_string_free (state->string, FALSE);
247 state->string = NULL;
249 /* do the translation if necessary */
250 if (state->translatable)
252 const gchar *translated;
255 translated = g_dpgettext2 (state->parser_data->domain, state->context, text);
257 translated = g_dgettext (state->parser_data->domain, text);
259 if (translated != text)
261 /* it's safe because we know that translated != text */
263 text = g_strdup (translated);
267 if (state->type == NULL)
268 /* No type string specified -> it's a normal string. */
269 g_menu_item_set_attribute (state->frame.item, state->attribute, "s", text);
271 /* Else, we try to parse it according to the type string. If
272 * error is set here, it will follow us out, ending the parse.
274 * We still need to free everything, though, so ignore it here.
276 else if ((value = g_variant_parse (state->type, text, NULL, NULL, error)))
278 g_menu_item_set_attribute_value (state->frame.item, state->attribute, value);
279 g_variant_unref (value);
284 g_variant_type_free (state->type);
288 g_free (state->context);
289 state->context = NULL;
291 g_free (state->attribute);
292 state->attribute = NULL;
299 gtk_builder_menu_text (GMarkupParseContext *context,
305 GtkBuilderMenuState *state = user_data;
308 for (i = 0; i < text_len; i++)
309 if (!g_ascii_isspace (text[i]))
312 g_string_append_len (state->string, text, text_len);
315 g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
316 _("text may not appear inside <%s>"),
317 g_markup_parse_context_get_element (context));
323 gtk_builder_menu_error (GMarkupParseContext *context,
327 GtkBuilderMenuState *state = user_data;
329 while (state->frame.prev)
331 struct frame *prev = state->frame.prev;
333 state->frame = *prev;
335 g_slice_free (struct frame, prev);
339 g_string_free (state->string, TRUE);
342 g_variant_type_free (state->type);
344 g_free (state->attribute);
345 g_free (state->context);
347 g_slice_free (GtkBuilderMenuState, state);
350 static GMarkupParser gtk_builder_menu_subparser =
352 gtk_builder_menu_start_element,
353 gtk_builder_menu_end_element,
354 gtk_builder_menu_text,
355 NULL, /* passthrough */
356 gtk_builder_menu_error
360 _gtk_builder_menu_start (ParserData *parser_data,
361 const gchar *element_name,
362 const gchar **attribute_names,
363 const gchar **attribute_values,
366 GtkBuilderMenuState *state;
369 state = g_slice_new0 (GtkBuilderMenuState);
370 state->parser_data = parser_data;
371 g_markup_parse_context_push (parser_data->ctx, >k_builder_menu_subparser, state);
373 if (COLLECT (STRING, "id", &id))
377 menu = g_menu_new ();
378 _gtk_builder_add_object (state->parser_data->builder, id, G_OBJECT (menu));
379 gtk_builder_menu_push_frame (state, menu, NULL);
384 _gtk_builder_menu_end (ParserData *parser_data)
386 GtkBuilderMenuState *state;
388 state = g_markup_parse_context_pop (parser_data->ctx);
389 gtk_builder_menu_pop_frame (state);
391 g_assert (state->frame.prev == NULL);
392 g_assert (state->frame.item == NULL);
393 g_assert (state->frame.menu == NULL);
394 g_slice_free (GtkBuilderMenuState, state);