2007-10-10 Johannes Schmid <jhs@gnome.org>
[anjuta-git-plugin.git] / libegg / eggtreeviewstate.c
blob427b0d7ef82fb3dd057c6507e82052f21774176d
1 #include "eggtreeviewstate.h"
2 #include <string.h>
3 #include <stdlib.h>
5 #define _(x) (x)
7 typedef enum
9 STATE_START,
10 STATE_TREEVIEW_STATE,
11 STATE_TREEVIEW,
12 STATE_COLUMN,
13 STATE_CELL,
14 } ParseState;
16 typedef struct
18 gchar *name;
19 gint column;
20 } CellAttribute;
22 typedef struct
24 GSList *states;
25 GtkTreeView *view;
27 GtkTreeViewColumn *column;
29 GtkCellRenderer *cell;
30 gboolean pack_start;
31 gboolean expand;
32 GSList *cell_attributes;
33 } ParseInfo;
35 static void
36 set_error (GError **err,
37 GMarkupParseContext *context,
38 int error_domain,
39 int error_code,
40 const char *format,
41 ...)
43 int line, ch;
44 va_list args;
45 char *str;
47 g_markup_parse_context_get_position (context, &line, &ch);
49 va_start (args, format);
50 str = g_strdup_vprintf (format, args);
51 va_end (args);
53 g_set_error (err, error_domain, error_code,
54 _("Line %d character %d: %s"),
55 line, ch, str);
57 g_free (str);
60 static ParseState
61 peek_state (ParseInfo *info)
63 g_return_val_if_fail (info->states != NULL, STATE_START);
65 return GPOINTER_TO_INT (info->states->data);
68 static void
69 push_state (ParseInfo *info,
70 ParseState state)
72 info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
75 static void
76 pop_state (ParseInfo *info)
78 g_return_if_fail (info->states != NULL);
80 info->states = g_slist_remove (info->states, info->states->data);
83 static void
84 parse_info_init (ParseInfo *info)
86 info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
87 info->cell_attributes = NULL;
91 #define MAX_REASONABLE 4096
92 static gboolean
93 parse_integer (const char *str,
94 GValue *value,
95 GMarkupParseContext *context,
96 GError **error)
98 char *end;
99 long l;
101 end = NULL;
103 l = strtol (str, &end, 10);
105 if (end == NULL || end == str)
107 set_error (error, context, G_MARKUP_ERROR,
108 G_MARKUP_ERROR_PARSE,
109 _("Could not parse \"%s\" as an integer"),
110 str);
111 return FALSE;
114 if (*end != '\0')
116 set_error (error, context, G_MARKUP_ERROR,
117 G_MARKUP_ERROR_PARSE,
118 _("Did not understand trailing characters \"%s\" in string \"%s\""),
119 end, str);
120 return FALSE;
123 if (l < 0)
125 set_error (error, context, G_MARKUP_ERROR,
126 G_MARKUP_ERROR_PARSE,
127 _("Integer %ld must be positive"), l);
128 return FALSE;
131 if (l > MAX_REASONABLE)
133 set_error (error, context, G_MARKUP_ERROR,
134 G_MARKUP_ERROR_PARSE,
135 _("Integer %ld is too large, current max is %d"),
136 l, MAX_REASONABLE);
137 return FALSE;
140 g_value_init (value, G_TYPE_INT);
141 g_value_set_int (value, (int)l);
143 return TRUE;
146 static gboolean
147 parse_string (const char *str,
148 GValue *value,
149 GMarkupParseContext *context,
150 GError **error)
152 if (!str)
153 return FALSE;
155 g_value_init (value, G_TYPE_STRING);
156 g_value_set_string (value, str);
158 return TRUE;
161 static gboolean
162 parse_boolean (const char *str,
163 GValue *value,
164 GMarkupParseContext *context,
165 GError **error)
167 if (strcmp ("true", str) == 0)
169 g_value_init (value, G_TYPE_BOOLEAN);
170 g_value_set_boolean (value, TRUE);
172 else if (strcmp ("false", str) == 0)
174 g_value_init (value, G_TYPE_BOOLEAN);
175 g_value_set_boolean (value, FALSE);
177 else
179 set_error (error, context, G_MARKUP_ERROR,
180 G_MARKUP_ERROR_PARSE,
181 _("Boolean values must be \"true\" or \"false\" not \"%s\""),
182 str);
183 return FALSE;
186 return TRUE;
189 static gboolean
190 parse_enum (const gchar *str,
191 GValue *value,
192 GParamSpecEnum *pspec,
193 GMarkupParseContext *context,
194 GError **error)
196 GEnumValue *enum_value;
198 enum_value = g_enum_get_value_by_nick (pspec->enum_class, str);
200 if (!enum_value)
202 set_error (error, context, G_MARKUP_ERROR,
203 G_MARKUP_ERROR_PARSE,
204 _("The value \"%s\" is not part of the enum \"%s\""),
205 str,
206 G_ENUM_CLASS_TYPE_NAME (pspec->enum_class));
207 return FALSE;
210 g_value_init (value, G_ENUM_CLASS_TYPE (pspec->enum_class));
211 g_value_set_enum (value, enum_value->value);
213 return TRUE;
217 static gboolean
218 parse_value (const gchar *string,
219 GValue *value,
220 GParamSpec *pspec,
221 GMarkupParseContext *context,
222 GError **error)
224 if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
225 return parse_boolean (string, value, context, error);
226 else if (G_IS_PARAM_SPEC_INT (pspec))
227 return parse_integer (string, value, context, error);
228 else if (G_IS_PARAM_SPEC_STRING (pspec))
229 return parse_string (string, value, context, error);
230 else if (G_IS_PARAM_SPEC_ENUM (pspec))
231 return parse_enum (string, value, G_PARAM_SPEC_ENUM (pspec), context, error);
233 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
234 _("The type \"%s\" can't be parsed from a string"),
235 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
236 return FALSE;
239 static gboolean
240 parse_object_property (GObject *object,
241 const gchar *attribute_name,
242 const gchar *attribute_value,
243 GMarkupParseContext *context,
244 GError **error)
246 GParamSpec *pspec;
247 GValue value = { 0 };
249 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), attribute_name);
251 if (!pspec)
253 set_error (error, context, G_MARKUP_ERROR,
254 G_MARKUP_ERROR_PARSE,
255 _("The property \"%s\" does not exist"),
256 attribute_name);
257 return FALSE;
260 if (!parse_value (attribute_value, &value, pspec, context, error))
261 return FALSE;
263 g_object_set_property (object, attribute_name, &value);
264 g_value_unset (&value);
266 return TRUE;
269 static gboolean
270 parse_object_properties (GObject *object,
271 const gchar **attribute_names,
272 const gchar **attribute_values,
273 GMarkupParseContext *context,
274 GError **error)
276 gint i;
278 for (i = 0; attribute_names[i] != NULL; i++)
280 if (!parse_object_property (object, attribute_names[i], attribute_values[i], context, error))
281 return FALSE;
284 return TRUE;
287 static void
288 parse_cell_element (GMarkupParseContext *context,
289 const gchar **attribute_names,
290 const gchar **attribute_values,
291 ParseInfo *info,
292 GError **error)
294 int i;
295 GtkCellRenderer *cell = NULL;
297 info->pack_start = TRUE;
298 info->expand = TRUE;
300 /* We first need to traverse the attributes looking for a type */
301 for (i = 0; attribute_names[i]; i++)
303 if (strcmp (attribute_names[i], "type") == 0)
305 GType type;
307 if (cell != NULL)
309 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
310 _("The type attribute can only be specified once."));
311 return;
314 type = g_type_from_name (attribute_values[i]);
316 if (type == G_TYPE_INVALID)
318 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
319 _("The type \"%s\" is not a valid type."),
320 attribute_values[i]);
321 return;
324 if (!g_type_is_a (type, GTK_TYPE_CELL_RENDERER))
326 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
327 _("The type \"%s\" is not a cell renderer type."),
328 g_type_name (type));
329 return;
332 cell = g_object_new (type, NULL);
336 if (!cell)
338 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
339 _("No type attribute specified."));
340 return;
343 for (i = 0; attribute_names[i]; i++)
345 if (strcmp (attribute_names[i], "type") == 0)
346 continue;
347 else if (strcmp (attribute_names[i], "pack_start") == 0)
349 GValue value = { 0 };
351 if (!parse_boolean (attribute_values[i], &value, context, error))
352 return;
354 info->pack_start = g_value_get_boolean (&value);
356 else if (strcmp (attribute_names[i], "expand") == 0)
358 GValue value = { 0 };
360 if (!parse_boolean (attribute_values[i], &value, context, error))
361 return;
363 info->expand = g_value_get_boolean (&value);
365 else
367 if (strstr (attribute_values[i], "model:") == attribute_values[i])
369 CellAttribute *attr;
370 GValue value = { 0 };
372 if (!parse_integer (attribute_values[i] + strlen ("model:"), &value, context, error))
373 return;
375 attr = g_new (CellAttribute, 1);
376 attr->name = g_strdup (attribute_names[i]);
377 attr->column = g_value_get_int (&value);
378 g_value_unset (&value);
380 info->cell_attributes = g_slist_prepend (info->cell_attributes, attr);
382 else
384 if (!parse_object_property (G_OBJECT (cell),
385 attribute_names[i],
386 attribute_values[i],
387 context,
388 error))
389 return;
394 push_state (info, STATE_CELL);
395 info->cell = cell;
398 static void
399 parse_column_element (GMarkupParseContext *context,
400 const gchar **attribute_names,
401 const gchar **attribute_values,
402 ParseInfo *info,
403 GError **error)
405 GtkTreeViewColumn *column;
407 column = gtk_tree_view_column_new ();
409 if (!parse_object_properties (G_OBJECT (column),
410 attribute_names,
411 attribute_values,
412 context,
413 error))
414 return;
416 push_state (info, STATE_COLUMN);
418 info->column = column;
421 static void
422 parse_treeview_element (GMarkupParseContext *context,
423 const gchar **attribute_names,
424 const gchar **attribute_values,
425 ParseInfo *info,
426 GError **error)
428 if (!parse_object_properties (G_OBJECT (info->view),
429 attribute_names,
430 attribute_values,
431 context,
432 error))
433 return;
435 push_state (info, STATE_TREEVIEW);
438 static void
439 start_element_handler (GMarkupParseContext *context,
440 const gchar *element_name,
441 const gchar **attribute_names,
442 const gchar **attribute_values,
443 gpointer user_data,
444 GError **error)
446 ParseInfo *info = user_data;
448 switch (peek_state (info))
450 case STATE_START:
451 if (strcmp (element_name, "treeview_state") == 0)
453 push_state (info, STATE_TREEVIEW_STATE);
455 else
456 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
457 _("Outermost element in theme must be <treeview_state> not <%s>"),
458 element_name);
459 break;
460 case STATE_TREEVIEW_STATE:
461 if (strcmp (element_name, "treeview") == 0)
463 parse_treeview_element (context, attribute_names, attribute_values, info, error);
465 else
466 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
467 _("Element inside of <treeview_state> must be <treeview> not <%s>"),
468 element_name);
469 break;
470 case STATE_TREEVIEW:
471 if (strcmp (element_name, "column") == 0)
473 parse_column_element (context, attribute_names, attribute_values, info, error);
475 else
476 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
477 _("Element inside of <treeview> must be <column> not <%s>"),
478 element_name);
480 break;
481 case STATE_COLUMN:
482 if (strcmp (element_name, "cell") == 0)
484 parse_cell_element (context, attribute_names, attribute_values, info, error);
486 else
487 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
488 _("Element inside of <column> must be <cell> not <%s>"),
489 element_name);
491 break;
492 case STATE_CELL:
493 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
494 _("The <cell> element must not have any children."));
495 break;
499 static void
500 end_element_handler (GMarkupParseContext *context,
501 const gchar *element_name,
502 gpointer user_data,
503 GError **error)
505 ParseInfo *info = user_data;
506 GSList *list;
508 switch (peek_state (info))
510 case STATE_START:
511 break;
512 case STATE_TREEVIEW_STATE:
513 pop_state (info);
514 g_assert (peek_state (info) == STATE_START);
515 break;
516 case STATE_TREEVIEW:
517 pop_state (info);
518 g_assert (peek_state (info) == STATE_TREEVIEW_STATE);
519 break;
520 case STATE_COLUMN:
521 g_assert (info->column);
523 gtk_tree_view_append_column (info->view, info->column);
525 pop_state (info);
526 g_assert (peek_state (info) == STATE_TREEVIEW);
527 break;
528 case STATE_CELL:
529 g_assert (info->cell);
531 if (info->pack_start)
532 gtk_tree_view_column_pack_start (info->column, info->cell,
533 info->expand);
534 else
535 gtk_tree_view_column_pack_end (info->column, info->cell,
536 info->expand);
538 for (list = info->cell_attributes; list; list = list->next)
540 CellAttribute *attr = list->data;
542 gtk_tree_view_column_add_attribute (info->column, info->cell,
543 attr->name,
544 attr->column);
545 g_free (attr->name);
546 g_free (attr);
549 g_slist_free (info->cell_attributes);
550 info->cell_attributes = NULL;
552 pop_state (info);
553 g_assert (peek_state (info) == STATE_COLUMN);
558 static GMarkupParser parser =
560 start_element_handler,
561 end_element_handler,
562 NULL,
563 NULL,
566 gboolean
567 egg_tree_view_state_apply_from_string (GtkTreeView *tree_view, const gchar *string, GError **err)
569 GMarkupParseContext *context;
570 GError *error = NULL;
571 gboolean retval;
572 ParseInfo info;
574 parse_info_init (&info);
575 info.view = tree_view;
577 context = g_markup_parse_context_new (&parser, 0, &info, NULL);
579 retval = g_markup_parse_context_parse (context, string, -1, &error);
581 if (!retval)
583 if (err)
584 *err = error;
587 return retval;
590 void
591 egg_tree_view_state_add_cell_renderer_type (GType type)
593 /* Do nothing, this is just to have the types registered */