1/* Copyright 2015 Red Hat, Inc.
2 *
3 * GTK+ is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as
5 * published by the Free Software Foundation; either version 2 of the
6 * License, or (at your option) any later version.
7 *
8 * GLib is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with GTK+; see the file COPYING. If not,
15 * see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Matthias Clasen
18 */
19
20#include <stdlib.h>
21#include <string.h>
22#include <errno.h>
23
24#include <glib/gi18n.h>
25#include <glib/gprintf.h>
26#include <glib/gstdio.h>
27#include <gtk/gtk.h>
28#include "gtkbuilderprivate.h"
29#include "gtk-builder-tool.h"
30
31typedef struct Element Element;
32struct Element {
33 Element *parent;
34 char *element_name;
35 char **attribute_names;
36 char **attribute_values;
37 char *data;
38 GList *children;
39
40 int line_number;
41 int char_number;
42};
43
44static void
45free_element (gpointer data)
46{
47 Element *element = data;
48 g_list_free_full (list: element->children, free_func: free_element);
49 g_free (mem: element->element_name);
50 g_strfreev (str_array: element->attribute_names);
51 g_strfreev (str_array: element->attribute_values);
52 g_free (mem: element->data);
53 g_free (mem: element);
54}
55
56typedef struct {
57 Element *root;
58 Element *current;
59 GString *value;
60 GtkBuilder *builder;
61 const char *input_filename;
62 char *output_filename;
63 FILE *output;
64 gboolean convert3to4;
65 gboolean has_gtk_requires;
66} MyParserData;
67
68static void
69start_element (GMarkupParseContext *context,
70 const char *element_name,
71 const char **attribute_names,
72 const char **attribute_values,
73 gpointer user_data,
74 GError **error)
75{
76 MyParserData *data = user_data;
77 Element *elt;
78
79 elt = g_new0 (Element, 1);
80 elt->parent = data->current;
81 elt->element_name = g_strdup (str: element_name);
82 elt->attribute_names = g_strdupv (str_array: (char **)attribute_names);
83 elt->attribute_values = g_strdupv (str_array: (char **)attribute_values);
84
85 g_markup_parse_context_get_position (context, line_number: &elt->line_number, char_number: &elt->char_number);
86
87 if (data->current)
88 data->current->children = g_list_append (list: data->current->children, data: elt);
89 data->current = elt;
90
91 if (data->root == NULL)
92 data->root = elt;
93
94 g_string_truncate (string: data->value, len: 0);
95}
96
97static void
98end_element (GMarkupParseContext *context,
99 const char *element_name,
100 gpointer user_data,
101 GError **error)
102{
103 MyParserData *data = user_data;
104
105 data->current->data = g_strdup (str: data->value->str);
106
107 data->current = data->current->parent;
108}
109
110static void
111text (GMarkupParseContext *context,
112 const char *text,
113 gsize text_len,
114 gpointer user_data,
115 GError **error)
116{
117 MyParserData *data = user_data;
118
119 if (data->value)
120 {
121 g_string_append_len (string: data->value, val: text, len: text_len);
122 return;
123 }
124}
125
126static GMarkupParser parser = {
127 start_element,
128 end_element,
129 text,
130 NULL,
131 NULL
132};
133
134static const char *
135canonical_boolean_value (MyParserData *data,
136 const char *string)
137{
138 GValue value = G_VALUE_INIT;
139 gboolean b = FALSE;
140
141 if (gtk_builder_value_from_string_type (builder: data->builder, G_TYPE_BOOLEAN, string, value: &value, NULL))
142 b = g_value_get_boolean (value: &value);
143
144 return b ? "1" : "0";
145}
146
147typedef enum {
148 PROP_KIND_OBJECT,
149 PROP_KIND_PACKING,
150 PROP_KIND_CELL_PACKING,
151 PROP_KIND_LAYOUT
152} PropKind;
153
154static PropKind
155get_prop_kind (Element *element)
156{
157 g_assert (g_str_equal (element->element_name, "property"));
158
159 if (g_str_equal (v1: element->parent->element_name, v2: "packing"))
160 return PROP_KIND_PACKING;
161 else if (g_str_equal (v1: element->parent->element_name, v2: "layout"))
162 return PROP_KIND_LAYOUT;
163 else if (g_str_equal (v1: element->parent->element_name, v2: "cell-packing"))
164 return PROP_KIND_CELL_PACKING;
165 else
166 return PROP_KIND_OBJECT;
167}
168
169/* A number of properties unfortunately can't be omitted even
170 * if they are nominally set to their default value. In many
171 * cases, this is due to subclasses not overriding the default
172 * value from the superclass.
173 */
174static gboolean
175needs_explicit_setting (GParamSpec *pspec,
176 PropKind kind)
177{
178 struct _Prop {
179 const char *class;
180 const char *property;
181 PropKind kind;
182 } props[] = {
183 { "GtkAboutDialog", "program-name", PROP_KIND_OBJECT },
184 { "GtkCalendar", "year", PROP_KIND_OBJECT },
185 { "GtkCalendar", "month", PROP_KIND_OBJECT },
186 { "GtkCalendar", "day", PROP_KIND_OBJECT },
187 { "GtkPlacesSidebar", "show-desktop", PROP_KIND_OBJECT },
188 { "GtkRadioButton", "draw-indicator", PROP_KIND_OBJECT },
189 { "GtkWidget", "hexpand", PROP_KIND_OBJECT },
190 { "GtkWidget", "vexpand", PROP_KIND_OBJECT },
191 { "GtkGridLayoutChild", "row", PROP_KIND_LAYOUT },
192 { "GtkGridLayoutChild", "column", PROP_KIND_LAYOUT },
193 };
194 gboolean found;
195 int k;
196 const char *class_name;
197
198 class_name = g_type_name (type: pspec->owner_type);
199
200 found = FALSE;
201 for (k = 0; k < G_N_ELEMENTS (props); k++)
202 {
203 if (strcmp (s1: class_name, s2: props[k].class) == 0 &&
204 strcmp (s1: pspec->name, s2: props[k].property) == 0 &&
205 kind == props[k].kind)
206 {
207 found = TRUE;
208 break;
209 }
210 }
211
212 return found;
213}
214
215static gboolean
216has_attribute (Element *elt,
217 const char *name,
218 const char *value)
219{
220 int i;
221
222 for (i = 0; elt->attribute_names[i]; i++)
223 {
224 if (strcmp (s1: elt->attribute_names[i], s2: name) == 0 &&
225 (value == NULL || strcmp (s1: elt->attribute_values[i], s2: value) == 0))
226 return TRUE;
227 }
228
229 return FALSE;
230}
231
232static gboolean
233is_cdata_property (Element *element)
234{
235 if (g_str_equal (v1: element->element_name, v2: "property") &&
236 has_attribute (elt: element, name: "name", value: "bytes") &&
237 element->parent != NULL &&
238 g_str_equal (v1: element->parent->element_name, v2: "object") &&
239 has_attribute (elt: element->parent, name: "class", value: "GtkBuilderListItemFactory"))
240 return TRUE;
241
242 return FALSE;
243}
244
245static gboolean
246is_pcdata_element (Element *element)
247{
248 /* elements that can contain text */
249 const char *names[] = {
250 "property",
251 "attribute",
252 "action-widget",
253 "pattern",
254 "mime-type",
255 "col",
256 "item",
257 "mark",
258 "lookup",
259 NULL,
260 };
261
262 if (g_str_equal (v1: element->element_name, v2: "property") &&
263 (g_strv_contains (strv: (const char * const *)element->attribute_names, str: "bind-source") ||
264 g_strv_contains (strv: (const char * const *)element->attribute_names, str: "bind_source")))
265 return FALSE;
266
267 if (g_strv_contains (strv: names, str: element->element_name))
268 return TRUE;
269
270 return FALSE;
271}
272
273static gboolean
274is_container_element (Element *element)
275{
276 /* elements that just hold a list of things and
277 * can be omitted when they have no children
278 */
279 const char *names[] = {
280 "packing",
281 "layout",
282 "cell-packing",
283 "attributes",
284 "action-widgets",
285 "patterns",
286 "mime-types",
287 "attributes",
288 "row",
289 "items",
290 NULL
291 };
292
293 if (g_strv_contains (strv: names, str: element->element_name))
294 return TRUE;
295
296 return FALSE;
297}
298
299static void
300canonicalize_key (char *key)
301{
302 char *p;
303
304 for (p = key; *p != 0; p++)
305 {
306 char c = *p;
307
308 /* We may meet something like AtkObject::accessible-name */
309 if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':'))
310 continue;
311
312 if (c != '-' &&
313 (c < '0' || c > '9') &&
314 (c < 'A' || c > 'Z') &&
315 (c < 'a' || c > 'z'))
316 *p = '-';
317 }
318}
319
320static struct {
321 const char *class;
322 const char *layout_manager;
323} layout_managers[] = {
324 { "GtkBox", "GtkBoxLayout" },
325 { "GtkGrid", "GtkGridLayout" },
326 { "GtkFixed", "GtkFixedLayout" },
327 { "GtkFileChooserButton", "GtkBinLayout" },
328 { "GtkFileChooserWidget", "GtkBinLayout" },
329 { "GtkOverlay", "GtkOverlayLayout" }
330};
331
332static GParamSpec *
333get_property_pspec (MyParserData *data,
334 const char *class_name,
335 const char *property_name,
336 PropKind kind)
337{
338 GType type;
339 GObjectClass *class;
340 GParamSpec *pspec;
341 char *canonical_name;
342
343 type = g_type_from_name (name: class_name);
344 if (type == G_TYPE_INVALID)
345 {
346 type = gtk_builder_get_type_from_name (builder: data->builder, type_name: class_name);
347 if (type == G_TYPE_INVALID)
348 return NULL;
349 }
350
351 class = g_type_class_ref (type);
352 canonical_name = g_strdup (str: property_name);
353 canonicalize_key (key: canonical_name);
354 switch (kind)
355 {
356 case PROP_KIND_OBJECT:
357 pspec = g_object_class_find_property (oclass: class, property_name: canonical_name);
358 break;
359
360 case PROP_KIND_PACKING:
361 pspec = NULL;
362 break;
363
364 case PROP_KIND_CELL_PACKING:
365 {
366 GObjectClass *cell_class;
367
368 /* We're just assuming that the cell layout is using a GtkCellAreaBox. */
369 cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX);
370 pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), property_name: canonical_name);
371 g_type_class_unref (g_class: cell_class);
372 }
373 break;
374
375 case PROP_KIND_LAYOUT:
376 {
377 int i;
378 const char *layout_manager = NULL;
379
380 pspec = NULL;
381
382 for (i = 0; i < G_N_ELEMENTS (layout_managers); i++)
383 {
384 if (g_str_equal (v1: layout_managers[i].class, v2: class_name))
385 {
386 layout_manager = layout_managers[i].layout_manager;
387 break;
388 }
389 }
390
391 if (layout_manager)
392 {
393 GtkLayoutManagerClass *layout_manager_class;
394
395 layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (ptr: g_type_class_ref (type: g_type_from_name (name: layout_manager)));
396 if (layout_manager_class->layout_child_type != G_TYPE_INVALID)
397 {
398 GObjectClass *layout_child_class;
399 layout_child_class = g_type_class_ref (type: layout_manager_class->layout_child_type);
400 pspec = g_object_class_find_property (oclass: layout_child_class, property_name: canonical_name);
401 g_type_class_unref (g_class: layout_child_class);
402 }
403 g_type_class_unref (g_class: layout_manager_class);
404 }
405 }
406 break;
407
408 default:
409 g_assert_not_reached ();
410 }
411 g_free (mem: canonical_name);
412 g_type_class_unref (g_class: class);
413
414 return pspec;
415}
416
417static const char *get_class_name (Element *element);
418
419static gboolean
420value_is_default (Element *element,
421 MyParserData *data,
422 GParamSpec *pspec,
423 const char *value_string)
424{
425 GValue value = { 0, };
426 gboolean ret;
427 GError *error = NULL;
428
429 if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
430 return FALSE;
431
432 if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_BOXED))
433 return FALSE;
434
435 if (!value_string)
436 return FALSE;
437
438 if (!gtk_builder_value_from_string (builder: data->builder, pspec, string: value_string, value: &value, error: &error))
439 {
440 g_printerr (_("%s:%d: Couldn’t parse value for property '%s': %s\n"), data->input_filename, element->line_number, pspec->name, error->message);
441 g_error_free (error);
442 ret = FALSE;
443 }
444 else
445 {
446 /* GtkWidget::visible has a 'smart' default */
447 if (pspec->owner_type == GTK_TYPE_WIDGET &&
448 g_str_equal (v1: pspec->name, v2: "visible"))
449 {
450 const char *class_name = get_class_name (element);
451 GType type = g_type_from_name (name: class_name);
452 gboolean default_value;
453
454 if (g_type_is_a (type, GTK_TYPE_ROOT) ||
455 g_type_is_a (type, GTK_TYPE_POPOVER))
456 default_value = FALSE;
457 else
458 default_value = TRUE;
459
460 ret = g_value_get_boolean (value: &value) == default_value;
461 }
462 else if (pspec->owner_type == GTK_TYPE_WINDOW &&
463 (g_str_equal (v1: pspec->name, v2: "default-width") ||
464 g_str_equal (v1: pspec->name, v2: "default-height")))
465 {
466 int default_size;
467
468 default_size = g_value_get_int (value: &value);
469 ret = default_size <= 0;
470 }
471 else
472 ret = g_param_value_defaults (pspec, value: &value);
473 }
474
475 g_value_reset (value: &value);
476
477 return ret;
478}
479
480static const char *
481get_attribute_value (Element *element,
482 const char *name)
483{
484 int i;
485
486 for (i = 0; element->attribute_names[i]; i++)
487 {
488 if (g_str_equal (v1: element->attribute_names[i], v2: name))
489 return element->attribute_values[i];
490 }
491
492 return "";
493}
494
495static void
496set_attribute_value (Element *element,
497 const char *name,
498 const char *value)
499{
500 int i;
501 int len;
502
503 for (i = 0; element->attribute_names[i]; i++)
504 {
505 if (g_str_equal (v1: element->attribute_names[i], v2: name))
506 {
507 g_free (mem: element->attribute_values[i]);
508 element->attribute_values[i] = g_strdup (str: value);
509 return;
510 }
511 }
512
513 len = g_strv_length (str_array: element->attribute_names);
514 element->attribute_names = g_realloc_n (mem: element->attribute_names, n_blocks: len + 2, n_block_bytes: sizeof (char *));
515 element->attribute_values = g_realloc_n (mem: element->attribute_values, n_blocks: len + 2, n_block_bytes: sizeof (char *));
516 element->attribute_names[len] = g_strdup (str: name);
517 element->attribute_values[len] = g_strdup (str: value);
518 element->attribute_names[len + 1] = NULL;
519 element->attribute_values[len + 1] = NULL;
520}
521
522static gboolean
523element_is_object_or_template (Element *element)
524{
525 return g_str_equal (v1: element->element_name, v2: "object") ||
526 g_str_equal (v1: element->element_name, v2: "template");
527}
528
529static const char *
530get_class_name (Element *element)
531{
532 Element *parent = element->parent;
533
534 if (element_is_object_or_template (element))
535 parent = element;
536
537 if (g_str_equal (v1: parent->element_name, v2: "packing"))
538 parent = parent->parent->parent; /* child - object */
539 else if (g_str_equal (v1: parent->element_name, v2: "layout"))
540 parent = parent->parent->parent->parent; /* object - child - object */
541
542
543 if (g_str_equal (v1: parent->element_name, v2: "object"))
544 {
545 return get_attribute_value (element: parent, name: "class");
546 }
547 else if (g_str_equal (v1: parent->element_name, v2: "template"))
548 {
549 if (get_attribute_value (element: parent, name: "parent"))
550 return get_attribute_value (element: parent, name: "parent");
551 else
552 return get_attribute_value (element: parent, name: "class");
553 }
554
555 return "";
556}
557
558static gboolean
559property_is_boolean (Element *element,
560 MyParserData *data)
561{
562 GParamSpec *pspec = NULL;
563 const char *class_name;
564 const char *property_name;
565 int i;
566 PropKind kind;
567
568 kind = get_prop_kind (element);
569 class_name = get_class_name (element);
570 property_name = "";
571
572 for (i = 0; element->attribute_names[i]; i++)
573 {
574 if (strcmp (s1: element->attribute_names[i], s2: "name") == 0)
575 property_name = (const char *)element->attribute_values[i];
576 }
577
578 if (class_name && property_name)
579 pspec = get_property_pspec (data, class_name, property_name, kind);
580 if (pspec)
581 return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN;
582
583 return FALSE;
584}
585
586static gboolean
587property_is_enum (Element *element,
588 MyParserData *data,
589 GType *type)
590{
591 GParamSpec *pspec = NULL;
592 const char *class_name;
593 const char *property_name;
594 int i;
595 PropKind kind;
596
597 kind = get_prop_kind (element);
598 class_name = get_class_name (element);
599 property_name = "";
600
601 for (i = 0; element->attribute_names[i]; i++)
602 {
603 if (strcmp (s1: element->attribute_names[i], s2: "name") == 0)
604 property_name = (const char *)element->attribute_values[i];
605 }
606
607 if (class_name && property_name)
608 pspec = get_property_pspec (data, class_name, property_name, kind);
609 if (pspec && G_TYPE_IS_ENUM (G_PARAM_SPEC_VALUE_TYPE (pspec)))
610 {
611 *type = G_PARAM_SPEC_VALUE_TYPE (pspec);
612 return TRUE;
613 }
614
615 *type = G_TYPE_NONE;
616 return FALSE;
617}
618
619static char *
620canonical_enum_value (MyParserData *data,
621 GType type,
622 const char *string)
623{
624 GValue value = G_VALUE_INIT;
625
626 if (gtk_builder_value_from_string_type (builder: data->builder, type, string, value: &value, NULL))
627 {
628 GEnumClass *eclass = g_type_class_ref (type);
629 GEnumValue *evalue = g_enum_get_value (enum_class: eclass, value: g_value_get_enum (value: &value));
630
631 if (evalue)
632 return g_strdup (str: evalue->value_nick);
633 else
634 return g_strdup_printf (format: "%d", g_value_get_enum (value: &value));
635 }
636
637 return NULL;
638}
639
640static void
641warn_missing_property (Element *element,
642 MyParserData *data,
643 const char *class_name,
644 const char *property_name,
645 PropKind kind)
646{
647 const char *kind_str[] = { "", "Packing ", "Cell ", "Layout " };
648
649 g_printerr (_("%s:%d: %sproperty %s::%s not found\n"),
650 data->input_filename, element->line_number, kind_str[kind], class_name, property_name);
651}
652
653static gboolean
654property_can_be_omitted (Element *element,
655 MyParserData *data)
656{
657 int i;
658 gboolean bound;
659 gboolean translatable;
660 const char *class_name;
661 const char *property_name;
662 const char *value_string;
663 GParamSpec *pspec;
664 PropKind kind;
665
666 kind = get_prop_kind (element);
667 class_name = get_class_name (element);
668 property_name = "";
669 value_string = element->data;
670
671 bound = FALSE;
672 translatable = FALSE;
673 for (i = 0; element->attribute_names[i]; i++)
674 {
675 if (strcmp (s1: element->attribute_names[i], s2: "bind-source") == 0 ||
676 strcmp (s1: element->attribute_names[i], s2: "bind_source") == 0)
677 bound = TRUE;
678 else if (strcmp (s1: element->attribute_names[i], s2: "translatable") == 0)
679 translatable = TRUE;
680 else if (strcmp (s1: element->attribute_names[i], s2: "name") == 0)
681 property_name = (const char *)element->attribute_values[i];
682 }
683
684 if (translatable)
685 return FALSE;
686
687 if (bound)
688 return FALSE;
689
690 pspec = get_property_pspec (data, class_name, property_name, kind);
691 if (pspec == NULL)
692 {
693 warn_missing_property (element, data, class_name, property_name, kind);
694 return FALSE;
695 }
696
697 if (needs_explicit_setting (pspec, kind))
698 return FALSE;
699
700 return value_is_default (element, data, pspec, value_string);
701}
702
703static gboolean
704property_has_been_removed (Element *element,
705 MyParserData *data)
706{
707 const char *class_name;
708 const char *property_name;
709 struct _Prop {
710 const char *class;
711 const char *property;
712 PropKind kind;
713 } props[] = {
714 { "GtkActionBar", "position", PROP_KIND_PACKING },
715 { "GtkButtonBox", "secondary", PROP_KIND_PACKING },
716 { "GtkButtonBox", "non-homogeneous", PROP_KIND_PACKING },
717 { "GtkBox", "position", PROP_KIND_PACKING },
718 { "GtkBox", "pack-type", PROP_KIND_PACKING },
719 { "GtkHeaderBar", "position", PROP_KIND_PACKING },
720 { "GtkPopoverMenu", "position",PROP_KIND_PACKING },
721 { "GtkCheckButton", "draw-indicator", PROP_KIND_OBJECT },
722 };
723 char *canonical_name;
724 gboolean found;
725 int i, k;
726 PropKind kind;
727
728 kind = get_prop_kind (element);
729
730 class_name = get_class_name (element);
731 property_name = "";
732
733 for (i = 0; element->attribute_names[i]; i++)
734 {
735 if (strcmp (s1: element->attribute_names[i], s2: "name") == 0)
736 property_name = (const char *)element->attribute_values[i];
737 }
738
739 canonical_name = g_strdup (str: property_name);
740 g_strdelimit (string: canonical_name, delimiters: "_", new_delimiter: '-');
741
742 found = FALSE;
743 for (k = 0; k < G_N_ELEMENTS (props); k++)
744 {
745 if (strcmp (s1: class_name, s2: props[k].class) == 0 &&
746 strcmp (s1: canonical_name, s2: props[k].property) == 0 &&
747 kind == props[k].kind)
748 {
749 found = TRUE;
750 break;
751 }
752 }
753
754 g_free (mem: canonical_name);
755
756 return found;
757}
758
759static void
760maybe_rename_property (Element *element, MyParserData *data)
761{
762 const char *class_name;
763 const char *property_name;
764 struct _Prop {
765 const char *class;
766 const char *property;
767 GType type;
768 PropKind kind;
769 const char *new_name;
770 const char *alt_names[3];
771 } props[] = {
772 /* the "replacement" property is placed *after* the "added" properties */
773 { "GtkPopover", "modal", GTK_TYPE_POPOVER, PROP_KIND_OBJECT, "autohide", { NULL, NULL, NULL } },
774 { "GtkWidget", "expand", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "vexpand", { "hexpand", NULL, NULL } },
775 { "GtkWidget", "margin", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-bottom", { "margin-start", "margin-end", "margin-top" } },
776 { "GtkWidget", "margin-left", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-start", { NULL, NULL, NULL } },
777 { "GtkWidget", "margin-right", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-end", { NULL, NULL, NULL } },
778 { "GtkHeaderBar", "show-close-button", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "show-title-buttons", { NULL, NULL, NULL } },
779 { "GtkHeaderBar", "custom-title", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "title-widget", { NULL, NULL, NULL } },
780 { "GtkStack", "homogeneous", GTK_TYPE_STACK, PROP_KIND_OBJECT, "hhomogeneous", { "vhomogeneous", NULL, NULL } },
781 { "GtkImage", "pixbuf", GTK_TYPE_IMAGE, PROP_KIND_OBJECT, "file", { NULL, NULL, NULL } },
782 { "GtkWidget", "can-focus", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "focusable", { NULL, NULL, NULL } },
783 };
784 int i, k, l;
785 PropKind kind;
786 int prop_name_index = 0;
787 GType type;
788 char *canonical_name;
789
790 kind = get_prop_kind (element);
791
792 class_name = get_class_name (element);
793 property_name = NULL;
794
795 for (i = 0; element->attribute_names[i]; i++)
796 {
797 if (strcmp (s1: element->attribute_names[i], s2: "name") == 0)
798 {
799 prop_name_index = i;
800 property_name = (const char *)element->attribute_values[i];
801 }
802 }
803
804 if (property_name == NULL)
805 return;
806
807 type = g_type_from_name (name: class_name);
808
809 canonical_name = g_strdup (str: property_name);
810 g_strdelimit (string: canonical_name, delimiters: "_", new_delimiter: '-');
811
812 for (k = 0; k < G_N_ELEMENTS (props); k++)
813 {
814 if (g_type_is_a (type, is_a_type: props[k].type) &&
815 strcmp (s1: canonical_name, s2: props[k].property) == 0 &&
816 kind == props[k].kind)
817 {
818 g_free (mem: element->attribute_values[prop_name_index]);
819 element->attribute_values[prop_name_index] = g_strdup (str: props[k].new_name);
820 for (l = 0; l < 3 && props[k].alt_names[l]; l++)
821 {
822 Element *elt;
823 GList *sibling;
824
825 elt = g_new0 (Element, 1);
826 elt->parent = element->parent;
827 elt->element_name = g_strdup (str: element->element_name);
828 elt->attribute_names = g_strdupv (str_array: (char **) element->attribute_names);
829 elt->attribute_values = g_strdupv (str_array: (char **) element->attribute_values);
830 elt->data = g_strdup (str: element->data);
831
832 g_free (mem: elt->attribute_values[prop_name_index]);
833 elt->attribute_values[prop_name_index] = g_strdup (str: props[k].alt_names[l]);
834
835 sibling = g_list_find (list: element->parent->children, data: element);
836 element->parent->children = g_list_insert_before (list: element->parent->children,
837 sibling,
838 data: elt);
839 }
840 break;
841 }
842 }
843
844 g_free (mem: canonical_name);
845}
846
847static Element *
848rewrite_stack_child (Element *child, MyParserData *data)
849{
850 Element *object = NULL;
851 Element *packing = NULL;
852 Element *new_object;
853 Element *prop;
854 GList *l;
855
856 if (!g_str_equal (v1: child->element_name, v2: "child"))
857 return child;
858
859 for (l = child->children; l; l = l->next)
860 {
861 Element *elt = l->data;
862 if (g_str_equal (v1: elt->element_name, v2: "object"))
863 object = elt;
864 else if (g_str_equal (v1: elt->element_name, v2: "packing"))
865 packing = elt;
866 }
867
868 if (!packing)
869 return child;
870
871 new_object = g_new0 (Element, 1);
872 new_object->element_name = g_strdup (str: "object");
873 new_object->attribute_names = g_new0 (char *, 2);
874 new_object->attribute_names[0] = g_strdup (str: "class");
875 new_object->attribute_values = g_new0 (char *, 2);
876 new_object->attribute_values[0] = g_strdup (str: "GtkStackPage");
877 new_object->children = packing->children;
878 new_object->parent = child;
879 packing->children = NULL;
880
881 prop = g_new0 (Element, 1);
882 prop->element_name = g_strdup (str: "property");
883 prop->attribute_names = g_new0 (char *, 2);
884 prop->attribute_names[0] = g_strdup (str: "name");
885 prop->attribute_values = g_new0 (char *, 2);
886 prop->attribute_values[0] = g_strdup (str: "child");
887 prop->children = g_list_append (list: prop->children, data: object);
888 prop->parent = new_object;
889 new_object->children = g_list_append (list: new_object->children, data: prop);
890
891 g_list_free (list: child->children);
892 child->children = g_list_append (NULL, data: new_object);
893
894 return child;
895}
896
897static void
898rewrite_stack (Element *element,
899 MyParserData *data)
900{
901 GList *l, *new_children;
902
903 new_children = NULL;
904 for (l = element->children; l; l = l->next)
905 {
906 Element *child = l->data;
907 new_children = g_list_append (list: new_children, data: rewrite_stack_child (child, data));
908 }
909
910 g_list_free (list: element->children);
911 element->children = new_children;
912}
913
914static Element *
915rewrite_assistant_child (Element *child, MyParserData *data)
916{
917 Element *object = NULL;
918 Element *packing = NULL;
919 Element *new_object;
920 Element *prop;
921 GList *l;
922
923 if (!g_str_equal (v1: child->element_name, v2: "child"))
924 return child;
925
926 for (l = child->children; l; l = l->next)
927 {
928 Element *elt = l->data;
929 if (g_str_equal (v1: elt->element_name, v2: "object"))
930 object = elt;
931 else if (g_str_equal (v1: elt->element_name, v2: "packing"))
932 packing = elt;
933 }
934
935 if (!packing)
936 return child;
937
938 new_object = g_new0 (Element, 1);
939 new_object->element_name = g_strdup (str: "object");
940 new_object->attribute_names = g_new0 (char *, 2);
941 new_object->attribute_names[0] = g_strdup (str: "class");
942 new_object->attribute_values = g_new0 (char *, 2);
943 new_object->attribute_values[0] = g_strdup (str: "GtkAssistantPage");
944 new_object->children = packing->children;
945 new_object->parent = child;
946 packing->children = NULL;
947
948 prop = g_new0 (Element, 1);
949 prop->element_name = g_strdup (str: "property");
950 prop->attribute_names = g_new0 (char *, 2);
951 prop->attribute_names[0] = g_strdup (str: "name");
952 prop->attribute_values = g_new0 (char *, 2);
953 prop->attribute_values[0] = g_strdup (str: "child");
954 prop->children = g_list_append (list: prop->children, data: object);
955 prop->parent = new_object;
956 new_object->children = g_list_append (list: new_object->children, data: prop);
957
958 g_list_free (list: child->children);
959 child->children = g_list_append (NULL, data: new_object);
960
961 return child;
962}
963
964static void
965rewrite_assistant (Element *element,
966 MyParserData *data)
967{
968 GList *l, *new_children;
969
970 new_children = NULL;
971 for (l = element->children; l; l = l->next)
972 {
973 Element *child = l->data;
974 new_children = g_list_append (list: new_children, data: rewrite_assistant_child (child, data));
975 }
976
977 g_list_free (list: element->children);
978 element->children = new_children;
979}
980
981static Element *
982rewrite_notebook_page (Element *child, Element *tab, MyParserData *data)
983{
984 Element *object = NULL;
985 Element *tab_obj = NULL;
986 Element *packing = NULL;
987 Element *new_object;
988 Element *prop;
989 GList *l;
990
991 if (!g_str_equal (v1: child->element_name, v2: "child"))
992 return child;
993
994 if (has_attribute (elt: child, name: "type", value: "tab") ||
995 has_attribute (elt: child, name: "type", value: "action-start") ||
996 has_attribute (elt: child, name: "type", value: "action-end"))
997 return child;
998
999 for (l = child->children; l; l = l->next)
1000 {
1001 Element *elt = l->data;
1002 if (g_str_equal (v1: elt->element_name, v2: "object"))
1003 object = elt;
1004 else if (g_str_equal (v1: elt->element_name, v2: "packing"))
1005 packing = elt;
1006 else if (g_str_equal (v1: elt->element_name, v2: "placeholder"))
1007 return child;
1008 }
1009
1010 if (!packing && !tab)
1011 return child;
1012
1013 if (tab)
1014 {
1015 for (l = tab->children; l; l = l->next)
1016 {
1017 Element *elt = l->data;
1018 if (g_str_equal (v1: elt->element_name, v2: "object"))
1019 tab_obj = elt;
1020 }
1021 }
1022
1023 new_object = g_new0 (Element, 1);
1024 new_object->element_name = g_strdup (str: "object");
1025 new_object->attribute_names = g_new0 (char *, 2);
1026 new_object->attribute_names[0] = g_strdup (str: "class");
1027 new_object->attribute_values = g_new0 (char *, 2);
1028 new_object->attribute_values[0] = g_strdup (str: "GtkNotebookPage");
1029 new_object->parent = child;
1030 if (packing)
1031 {
1032 new_object->children = packing->children;
1033 packing->children = NULL;
1034 }
1035
1036 prop = g_new0 (Element, 1);
1037 prop->element_name = g_strdup (str: "property");
1038 prop->attribute_names = g_new0 (char *, 2);
1039 prop->attribute_names[0] = g_strdup (str: "name");
1040 prop->attribute_values = g_new0 (char *, 2);
1041 prop->attribute_values[0] = g_strdup (str: "child");
1042 prop->children = g_list_append (list: prop->children, data: object);
1043 prop->parent = new_object;
1044 new_object->children = g_list_append (list: new_object->children, data: prop);
1045
1046 if (tab_obj)
1047 {
1048 prop = g_new0 (Element, 1);
1049 prop->element_name = g_strdup (str: "property");
1050 prop->attribute_names = g_new0 (char *, 2);
1051 prop->attribute_names[0] = g_strdup (str: "name");
1052 prop->attribute_values = g_new0 (char *, 2);
1053 prop->attribute_values[0] = g_strdup (str: "tab");
1054 prop->children = g_list_append (list: prop->children, data: tab_obj);
1055 prop->parent = new_object;
1056 new_object->children = g_list_append (list: new_object->children, data: prop);
1057 }
1058
1059 g_list_free (list: child->children);
1060 child->children = g_list_append (NULL, data: new_object);
1061
1062 return child;
1063}
1064
1065static void
1066rewrite_notebook (Element *element,
1067 MyParserData *data)
1068{
1069 GList *l, *new_children;
1070
1071 new_children = NULL;
1072 for (l = element->children; l; l = l->next)
1073 {
1074 Element *child = l->data;
1075 Element *tab = l->next ? l->next->data : NULL;
1076
1077 if (tab && has_attribute (elt: tab, name: "type", value: "tab"))
1078 {
1079 new_children = g_list_append (list: new_children, data: rewrite_notebook_page (child, tab, data));
1080 l = l->next; /* skip the tab */
1081 }
1082 else
1083 new_children = g_list_append (list: new_children, data: rewrite_notebook_page (child, NULL, data));
1084 }
1085
1086 g_list_free (list: element->children);
1087 element->children = new_children;
1088}
1089
1090static void
1091rewrite_pack_type_child (Element *element,
1092 MyParserData *data)
1093{
1094 Element *pack_type = NULL;
1095 GList *l, *ll;
1096
1097 if (!g_str_equal (v1: element->element_name, v2: "child"))
1098 return;
1099
1100 for (l = element->children; l; l = l->next)
1101 {
1102 Element *elt = l->data;
1103
1104 if (g_str_equal (v1: elt->element_name, v2: "packing"))
1105 {
1106 for (ll = elt->children; ll; ll = ll->next)
1107 {
1108 Element *elt2 = ll->data;
1109
1110 if (g_str_equal (v1: elt2->element_name, v2: "property") &&
1111 has_attribute (elt: elt2, name: "name", value: "pack-type"))
1112 {
1113 pack_type = elt2;
1114 elt->children = g_list_remove (list: elt->children, data: pack_type);
1115 if (elt->children == NULL)
1116 {
1117 element->children = g_list_remove (list: element->children, data: elt);
1118 free_element (data: elt);
1119 }
1120 break;
1121 }
1122 }
1123 }
1124
1125 if (pack_type)
1126 break;
1127 }
1128
1129 if (pack_type)
1130 {
1131 char **attnames = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
1132 char **attvalues = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
1133 int i;
1134
1135 for (i = 0; element->attribute_names[i]; i++)
1136 {
1137 attnames[i] = g_strdup (str: element->attribute_names[i]);
1138 attvalues[i] = g_strdup (str: element->attribute_values[i]);
1139 }
1140
1141 attnames[i] = g_strdup (str: "type");
1142 attvalues[i] = g_strdup (str: pack_type->data);
1143
1144 g_strfreev (str_array: element->attribute_names);
1145 g_strfreev (str_array: element->attribute_values);
1146
1147 element->attribute_names = attnames;
1148 element->attribute_values = attvalues;
1149
1150 free_element (data: pack_type);
1151 }
1152}
1153
1154static void
1155rewrite_pack_type (Element *element,
1156 MyParserData *data)
1157{
1158 GList *l;
1159
1160 for (l = element->children; l; l = l->next)
1161 {
1162 Element *elt = l->data;
1163 if (g_str_equal (v1: elt->element_name, v2: "child"))
1164 rewrite_pack_type_child (element: elt, data);
1165 }
1166}
1167
1168static void
1169rewrite_paned_child (Element *element,
1170 MyParserData *data,
1171 Element *child,
1172 const char *suffix)
1173{
1174 Element *resize = NULL;
1175 Element *shrink = NULL;
1176 GList *l, *ll;
1177
1178 for (l = child->children; l; l = l->next)
1179 {
1180 Element *elt = l->data;
1181
1182 if (g_str_equal (v1: elt->element_name, v2: "packing"))
1183 {
1184 for (ll = elt->children; ll; ll = ll->next)
1185 {
1186 Element *elt2 = ll->data;
1187
1188 if (g_str_equal (v1: elt2->element_name, v2: "property") &&
1189 has_attribute (elt: elt2, name: "name", value: "resize"))
1190 resize = elt2;
1191 else if (g_str_equal (v1: elt2->element_name, v2: "property") &&
1192 has_attribute (elt: elt2, name: "name", value: "shrink"))
1193 shrink = elt2;
1194 }
1195 if (resize)
1196 elt->children = g_list_remove (list: elt->children, data: resize);
1197 if (shrink)
1198 elt->children = g_list_remove (list: elt->children, data: shrink);
1199 if (elt->children == NULL)
1200 {
1201 child->children = g_list_remove (list: child->children, data: elt);
1202 free_element (data: elt);
1203 }
1204 }
1205
1206 if (resize || shrink)
1207 break;
1208 }
1209
1210 if (resize)
1211 {
1212 Element *elt;
1213
1214 elt = g_new0 (Element, 1);
1215 elt->parent = element;
1216 elt->element_name = g_strdup (str: "property");
1217 elt->attribute_names = g_new0 (char *, 2);
1218 elt->attribute_names[0] = g_strdup (str: "name");
1219 elt->attribute_values = g_new0 (char *, 2);
1220 elt->attribute_values[0] = g_strconcat (string1: "resize-", suffix, NULL);
1221 elt->data = g_strdup (str: resize->data);
1222
1223 element->children = g_list_prepend (list: element->children, data: elt);
1224
1225 free_element (data: resize);
1226 }
1227
1228 if (shrink)
1229 {
1230 Element *elt;
1231
1232 elt = g_new0 (Element, 1);
1233 elt->parent = element;
1234 elt->element_name = g_strdup (str: "property");
1235 elt->attribute_names = g_new0 (char *, 2);
1236 elt->attribute_names[0] = g_strdup (str: "name");
1237 elt->attribute_values = g_new0 (char *, 2);
1238 elt->attribute_values[0] = g_strconcat (string1: "shrink-", suffix, NULL);
1239 elt->data = g_strdup (str: shrink->data);
1240
1241 element->children = g_list_prepend (list: element->children, data: elt);
1242
1243 free_element (data: shrink);
1244 }
1245}
1246
1247static void
1248rewrite_paned (Element *element,
1249 MyParserData *data)
1250{
1251 Element *child1 = NULL;
1252 Element *child2 = NULL;
1253 GList *l;
1254
1255 for (l = element->children; l; l = l->next)
1256 {
1257 Element *elt = l->data;
1258
1259 if (g_str_equal (v1: elt->element_name, v2: "child"))
1260 {
1261 if (child1 == NULL)
1262 child1 = elt;
1263 else if (child2 == NULL)
1264 child2 = elt;
1265 else
1266 break;
1267 }
1268 }
1269
1270 if (child1)
1271 rewrite_paned_child (element, data, child: child1, suffix: "start-child");
1272
1273 if (child2)
1274 rewrite_paned_child (element, data, child: child2, suffix: "end-child");
1275}
1276
1277static void
1278rewrite_dialog (Element *element,
1279 MyParserData *data)
1280{
1281 Element *content_area = NULL;
1282 Element *vbox = NULL;
1283 Element *action_area = NULL;
1284 GList *l;
1285
1286 for (l = element->children; l; l = l->next)
1287 {
1288 Element *elt = l->data;
1289
1290 if (g_str_equal (v1: elt->element_name, v2: "child") &&
1291 g_strcmp0 (str1: get_attribute_value (element: elt, name: "internal-child"), str2: "vbox") == 0)
1292 {
1293 content_area = elt;
1294 break;
1295 }
1296 }
1297
1298 if (!content_area || !content_area->children)
1299 return;
1300
1301 vbox = content_area->children->data;
1302
1303 for (l = vbox->children; l; l = l->next)
1304 {
1305 Element *elt = l->data;
1306
1307 if (g_str_equal (v1: elt->element_name, v2: "child") &&
1308 g_strcmp0 (str1: get_attribute_value (element: elt, name: "internal-child"), str2: "action_area") == 0)
1309 {
1310 action_area = elt;
1311 break;
1312 }
1313 }
1314
1315 if (!action_area)
1316 return;
1317
1318 set_attribute_value (element: content_area, name: "internal-child", value: "content_area");
1319 vbox->children = g_list_remove (list: vbox->children, data: action_area);
1320 action_area->parent = element;
1321 element->children = g_list_append (list: element->children, data: action_area);
1322
1323 for (l = action_area->children; l; l = l->next)
1324 {
1325 Element *elt = l->data;
1326
1327 if (g_str_equal (v1: elt->element_name, v2: "packing"))
1328 {
1329 action_area->children = g_list_remove (list: action_area->children, data: elt);
1330 free_element (data: elt);
1331 break;
1332 }
1333 }
1334
1335}
1336
1337static void
1338rewrite_grid_layout_prop (Element *element,
1339 const char *attr_name,
1340 const char *old_value,
1341 const char *new_value)
1342{
1343 if (g_str_equal (v1: element->element_name, v2: "property"))
1344 {
1345 char *canonical_name;
1346
1347 canonical_name = g_strdup (str: old_value);
1348 g_strdelimit (string: canonical_name, delimiters: "_", new_delimiter: '-');
1349
1350 if (has_attribute (elt: element, name: attr_name, value: old_value) ||
1351 has_attribute (elt: element, name: attr_name, value: canonical_name))
1352 {
1353 set_attribute_value (element, name: attr_name, value: new_value);
1354 }
1355
1356 g_free (mem: canonical_name);
1357 }
1358}
1359
1360static void
1361rewrite_grid_layout (Element *element,
1362 MyParserData *data)
1363{
1364 struct _Prop {
1365 const char *attr_name;
1366 const char *old_value;
1367 const char *new_value;
1368 } props[] = {
1369 { "name", "left_attach", "column", },
1370 { "name", "top_attach", "row", },
1371 { "name", "width", "column-span", },
1372 { "name", "height", "row-span", },
1373 };
1374 GList *l, *ll;
1375
1376 for (l = element->children; l; l = l->next)
1377 {
1378 Element *child = l->data;
1379
1380 if (g_str_equal (v1: child->element_name, v2: "child"))
1381 {
1382 Element *object = NULL;
1383 Element *packing = NULL;
1384
1385 for (ll = child->children; ll; ll = ll->next)
1386 {
1387 Element *elt2 = ll->data;
1388
1389 if (g_str_equal (v1: elt2->element_name, v2: "object"))
1390 object = elt2;
1391
1392 if (g_str_equal (v1: elt2->element_name, v2: "packing"))
1393 packing = elt2;
1394 }
1395
1396 if (object && packing)
1397 {
1398 int i;
1399
1400 child->children = g_list_remove (list: child->children, data: packing);
1401
1402 g_free (mem: packing->element_name);
1403 packing->element_name = g_strdup (str: "layout");
1404
1405 packing->parent = object;
1406 object->children = g_list_append (list: object->children, data: packing);
1407
1408 for (ll = packing->children; ll; ll = ll->next)
1409 {
1410 Element *elt = ll->data;
1411
1412 for (i = 0; i < G_N_ELEMENTS (props); i++)
1413 rewrite_grid_layout_prop (element: elt,
1414 attr_name: props[i].attr_name,
1415 old_value: props[i].old_value,
1416 new_value: props[i].new_value);
1417 }
1418 }
1419 }
1420 }
1421}
1422
1423static Element *
1424add_element (Element *parent,
1425 const char *element_name)
1426{
1427 Element *child;
1428
1429 child = g_new0 (Element, 1);
1430 child->parent = parent;
1431 child->element_name = g_strdup (str: element_name);
1432 child->attribute_names = g_new0 (char *, 1);
1433 child->attribute_values = g_new0 (char *, 1);
1434 parent->children = g_list_prepend (list: parent->children, data: child);
1435
1436 return child;
1437}
1438
1439static Element *
1440write_box_prop (Element *element,
1441 Element *parent,
1442 const char *name,
1443 const char *value)
1444{
1445
1446 if (element)
1447 g_free (mem: element->data);
1448 else
1449 {
1450 element = add_element (parent, element_name: "property");
1451 set_attribute_value (element, name: "name", value: name);
1452 }
1453 element->data = g_strdup (str: value);
1454
1455 return element;
1456}
1457
1458static void
1459rewrite_box (Element *element,
1460 MyParserData *data)
1461{
1462 GList *l, *ll;
1463 GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1464
1465 if (g_str_equal (v1: get_class_name (element), v2: "GtkVBox"))
1466 write_box_prop (NULL, parent: element, name: "orientation", value: "vertical");
1467
1468 if (!g_str_equal (v1: get_class_name (element), v2: "GtkBox"))
1469 set_attribute_value (element, name: "class", value: "GtkBox");
1470
1471 for (l = element->children; l; l = l->next)
1472 {
1473 Element *child = l->data;
1474
1475 if (g_str_equal (v1: child->element_name, v2: "property"))
1476 {
1477 if (has_attribute (elt: child, name: "name", value: "orientation"))
1478 {
1479 GValue value = G_VALUE_INIT;
1480
1481 if (gtk_builder_value_from_string_type (builder: data->builder,
1482 type: GTK_TYPE_ORIENTATION,
1483 string: child->data,
1484 value: &value,
1485 NULL))
1486 orientation = g_value_get_enum (value: &value);
1487 }
1488 }
1489 }
1490
1491 for (l = element->children; l; l = l->next)
1492 {
1493 Element *child = l->data;
1494 if (g_str_equal (v1: child->element_name, v2: "child"))
1495 {
1496 Element *object = NULL;
1497 Element *packing = NULL;
1498
1499 for (ll = child->children; ll; ll = ll->next)
1500 {
1501 Element *elt2 = ll->data;
1502
1503 if (g_str_equal (v1: elt2->element_name, v2: "object"))
1504 object = elt2;
1505
1506 if (g_str_equal (v1: elt2->element_name, v2: "packing"))
1507 packing = elt2;
1508 }
1509
1510 if (object && packing)
1511 {
1512 Element *halign = NULL;
1513 Element *hexpand = NULL;
1514 Element *valign = NULL;
1515 Element *vexpand = NULL;
1516
1517 gboolean expand = FALSE;
1518 gboolean fill = TRUE;
1519
1520 for (ll = object->children; ll; ll = ll->next)
1521 {
1522 Element *elt = ll->data;
1523 if (g_str_equal (v1: elt->element_name, v2: "property"))
1524 {
1525 if (has_attribute (elt, name: "name", value: "halign"))
1526 halign = elt;
1527 else if (has_attribute (elt, name: "name", value: "hexpand"))
1528 hexpand = elt;
1529 else if (has_attribute (elt, name: "name", value: "valign"))
1530 valign = elt;
1531 else if (has_attribute (elt, name: "name", value: "vexpand"))
1532 vexpand = elt;
1533 }
1534 }
1535
1536 for (ll = packing->children; ll; ll = ll->next)
1537 {
1538 Element *elt = ll->data;
1539
1540 if (has_attribute (elt, name: "name", value: "expand"))
1541 {
1542 GValue value = G_VALUE_INIT;
1543
1544 if (gtk_builder_value_from_string_type (builder: data->builder,
1545 G_TYPE_BOOLEAN,
1546 string: elt->data,
1547 value: &value,
1548 NULL))
1549 expand = g_value_get_boolean (value: &value);
1550 }
1551
1552 if (has_attribute (elt, name: "name", value: "fill"))
1553 {
1554 GValue value = G_VALUE_INIT;
1555
1556 if (gtk_builder_value_from_string_type (builder: data->builder,
1557 G_TYPE_BOOLEAN,
1558 string: elt->data,
1559 value: &value,
1560 NULL))
1561 fill = g_value_get_boolean (value: &value);
1562 }
1563 }
1564
1565 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1566 {
1567 if (expand)
1568 write_box_prop (element: hexpand, parent: object, name: "hexpand", value: "1");
1569 if (!fill)
1570 write_box_prop (element: halign, parent: object, name: "halign", value: "center");
1571 }
1572 else if (orientation == GTK_ORIENTATION_VERTICAL)
1573 {
1574 if (expand)
1575 write_box_prop (element: vexpand, parent: object, name: "vexpand", value: "1");
1576 if (!fill)
1577 write_box_prop (element: valign, parent: object, name: "valign", value: "center");
1578 }
1579
1580 child->children = g_list_remove (list: child->children, data: packing);
1581 free_element (data: packing);
1582 }
1583 }
1584 }
1585}
1586
1587static void
1588rewrite_bin_child (Element *element,
1589 MyParserData *data)
1590{
1591 GList *l, *ll;
1592 const char *class_name;
1593 GType type;
1594
1595 for (l = element->children; l; l = l->next)
1596 {
1597 Element *child = l->data;
1598 Element *object = NULL;
1599
1600 if (!g_str_equal (v1: child->element_name, v2: "child") ||
1601 has_attribute (elt: child, name: "type", NULL))
1602 continue;
1603
1604 for (ll = child->children; ll; ll = ll->next)
1605 {
1606 Element *elem = ll->data;
1607
1608 if (!g_str_equal (v1: elem->element_name, v2: "object"))
1609 continue;
1610
1611 class_name = get_attribute_value (element: elem, name: "class");
1612 if (!class_name)
1613 continue;
1614
1615 type = g_type_from_name (name: class_name);
1616 if (!g_type_is_a (type, GTK_TYPE_WIDGET))
1617 continue;
1618
1619 object = elem;
1620 }
1621
1622 if (object)
1623 {
1624 g_free (mem: child->element_name);
1625 g_strfreev (str_array: child->attribute_names);
1626 g_strfreev (str_array: child->attribute_values);
1627 child->element_name = g_strdup (str: "property");
1628 child->attribute_names = g_new0 (char *, 2);
1629 child->attribute_names[0] = g_strdup (str: "name");
1630 child->attribute_values = g_new0 (char *, 2);
1631 child->attribute_values[0] = g_strdup (str: "child");
1632 break;
1633 }
1634 }
1635}
1636
1637static gboolean
1638remove_boolean_prop (Element *element,
1639 MyParserData *data,
1640 const char *prop_name,
1641 gboolean *value)
1642{
1643 GList *l;
1644
1645 for (l = element->children; l; l = l->next)
1646 {
1647 Element *child = l->data;
1648
1649 if (g_str_equal (v1: child->element_name, v2: "property") &&
1650 has_attribute (elt: child, name: "name", value: prop_name))
1651 {
1652 *value = strcmp (s1: canonical_boolean_value (data, string: child->data), s2: "1") == 0;
1653 element->children = g_list_remove (list: element->children, data: child);
1654 free_element (data: child);
1655 return TRUE;
1656 }
1657 }
1658
1659 return FALSE;
1660}
1661
1662static void
1663rewrite_radio_button (Element *element,
1664 MyParserData *data)
1665{
1666 gboolean draw_indicator = TRUE;
1667
1668 if (!remove_boolean_prop (element, data, prop_name: "draw-indicator", value: &draw_indicator))
1669 remove_boolean_prop (element, data, prop_name: "draw_indicator", value: &draw_indicator);
1670
1671 if (draw_indicator)
1672 set_attribute_value (element, name: "class", value: "GtkCheckButton");
1673 else
1674 set_attribute_value (element, name: "class", value: "GtkToggleButton");
1675
1676}
1677
1678static gboolean
1679has_prop (Element *element,
1680 MyParserData *data,
1681 const char *prop_name)
1682{
1683 GList *l;
1684
1685 for (l = element->children; l; l = l->next)
1686 {
1687 Element *child = l->data;
1688
1689 if (g_str_equal (v1: child->element_name, v2: "property") &&
1690 has_attribute (elt: child, name: "name", value: prop_name))
1691 return TRUE;
1692 }
1693
1694 return FALSE;
1695}
1696
1697static void
1698rewrite_scale (Element *element,
1699 MyParserData *data)
1700{
1701 if (!has_prop (element, data, prop_name: "draw-value") &&
1702 !has_prop (element, data, prop_name: "draw_value"))
1703 {
1704 Element *child;
1705 child = add_element (parent: element, element_name: "property");
1706 set_attribute_value (element: child, name: "name", value: "draw-value");
1707 child->data = g_strdup (str: "1");
1708 }
1709}
1710
1711static void
1712rewrite_requires (Element *element,
1713 MyParserData *data)
1714{
1715 if (has_attribute (elt: element, name: "lib", value: "gtk+"))
1716 {
1717 set_attribute_value (element, name: "lib", value: "gtk");
1718 set_attribute_value (element, name: "version", value: "4.0");
1719 }
1720}
1721
1722static void
1723rewrite_overlay (Element *element,
1724 MyParserData *data)
1725{
1726 GList *l, *ll;
1727
1728 for (l = element->children; l; l = l->next)
1729 {
1730 Element *child = l->data;
1731
1732 if (g_str_equal (v1: child->element_name, v2: "child"))
1733 {
1734 Element *object = NULL;
1735 Element *packing = NULL;
1736
1737 for (ll = child->children; ll; ll = ll->next)
1738 {
1739 Element *elt2 = ll->data;
1740
1741 if (g_str_equal (v1: elt2->element_name, v2: "object"))
1742 object = elt2;
1743
1744 if (g_str_equal (v1: elt2->element_name, v2: "packing"))
1745 packing = elt2;
1746 }
1747
1748 if (object && packing)
1749 {
1750 child->children = g_list_remove (list: child->children, data: packing);
1751
1752 for (ll = packing->children; ll; ll = ll->next)
1753 {
1754 Element *elt2 = ll->data;
1755
1756 if (g_str_equal (v1: elt2->element_name, v2: "property") &&
1757 (has_attribute (elt: elt2, name: "name", value: "pass-through") ||
1758 has_attribute (elt: elt2, name: "name", value: "pass_through")))
1759 {
1760 const char *b = canonical_boolean_value (data, string: elt2->data);
1761 if (g_str_equal (v1: b, v2: "1"))
1762 {
1763 Element *new_prop;
1764
1765 new_prop = add_element (parent: object, element_name: "property");
1766 set_attribute_value (element: new_prop, name: "name", value: "can-target");
1767 new_prop->data = g_strdup (str: "0");
1768 }
1769 break;
1770 }
1771 }
1772
1773 free_element (data: packing);
1774 }
1775 }
1776 }
1777}
1778
1779static void
1780rewrite_toolbar (Element *element,
1781 MyParserData *data)
1782{
1783 GList *l, *ll;
1784
1785 set_attribute_value (element, name: "class", value: "GtkBox");
1786
1787 for (l = element->children; l; l = l->next)
1788 {
1789 Element *child = l->data;
1790
1791 if (g_str_equal (v1: child->element_name, v2: "property") &&
1792 (has_attribute (elt: child, name: "name", value: "toolbar_style") ||
1793 has_attribute (elt: child, name: "name", value: "toolbar-style")))
1794 {
1795 element->children = g_list_remove (list: element->children, data: child);
1796 free_element (data: child);
1797 break;
1798 }
1799 }
1800
1801 for (l = element->children; l; l = l->next)
1802 {
1803 Element *child = l->data;
1804 Element *object = NULL;
1805 Element *packing = NULL;
1806
1807 if (!g_str_equal (v1: child->element_name, v2: "child"))
1808 continue;
1809
1810 for (ll = child->children; ll; ll = ll->next)
1811 {
1812 Element *elt2 = ll->data;
1813
1814 if (g_str_equal (v1: elt2->element_name, v2: "object"))
1815 object = elt2;
1816
1817 if (g_str_equal (v1: elt2->element_name, v2: "packing"))
1818 packing = elt2;
1819 }
1820
1821 if (object)
1822 {
1823 const char *class_name;
1824
1825 class_name = get_class_name (element: object);
1826
1827 if (g_str_equal (v1: class_name, v2: "GtkToolButton"))
1828 {
1829 set_attribute_value (element: object, name: "class", value: "GtkButton");
1830 }
1831 else if (g_str_equal (v1: class_name, v2: "GtkToggleToolButton") ||
1832 g_str_equal (v1: class_name, v2: "GtkRadioToolButton"))
1833 {
1834 set_attribute_value (element: object, name: "class", value: "GtkToggleButton");
1835 }
1836 else if (g_str_equal (v1: class_name, v2: "GtkSeparatorToolItem"))
1837 {
1838 Element *prop;
1839
1840 set_attribute_value (element: object, name: "class", value: "GtkSeparator");
1841 prop = add_element (parent: object, element_name: "property");
1842 set_attribute_value (element: prop, name: "name", value: "orientation");
1843 prop->data = g_strdup (str: "vertical");
1844 }
1845 }
1846
1847 if (packing)
1848 child->children = g_list_remove (list: child->children, data: packing);
1849 }
1850
1851 {
1852 Element *child;
1853
1854 child = add_element (parent: element, element_name: "property");
1855 set_attribute_value (element: child, name: "name", value: "css-classes");
1856 child->data = g_strdup (str: "toolbar");
1857 }
1858}
1859
1860static void
1861rewrite_fixed (Element *element,
1862 MyParserData *data)
1863{
1864 GList *l, *ll;
1865
1866 for (l = element->children; l; l = l->next)
1867 {
1868 Element *child = l->data;
1869
1870 if (g_str_equal (v1: child->element_name, v2: "child"))
1871 {
1872 Element *object = NULL;
1873 Element *packing = NULL;
1874
1875 for (ll = child->children; ll; ll = ll->next)
1876 {
1877 Element *elt2 = ll->data;
1878
1879 if (g_str_equal (v1: elt2->element_name, v2: "object"))
1880 object = elt2;
1881
1882 if (g_str_equal (v1: elt2->element_name, v2: "packing"))
1883 packing = elt2;
1884 }
1885
1886 if (object && packing)
1887 {
1888 int x = 0;
1889 int y = 0;
1890 Element *layout;
1891 Element *new_prop;
1892 GskTransform *transform;
1893
1894 for (ll = packing->children; ll; ll = ll->next)
1895 {
1896 Element *elt2 = ll->data;
1897 GValue value = G_VALUE_INIT;
1898
1899 if (has_attribute (elt: elt2, name: "name", value: "x"))
1900 {
1901 if (gtk_builder_value_from_string_type (builder: data->builder, G_TYPE_INT, string: elt2->data, value: &value, NULL))
1902 x = g_value_get_int (value: &value);
1903 }
1904 else if (has_attribute (elt: elt2, name: "name", value: "y"))
1905 {
1906 if (gtk_builder_value_from_string_type (builder: data->builder, G_TYPE_INT, string: elt2->data, value: &value, NULL))
1907 y = g_value_get_int (value: &value);
1908 }
1909 }
1910
1911 child->children = g_list_remove (list: child->children, data: packing);
1912 free_element (data: packing);
1913
1914 layout = add_element (parent: object, element_name: "layout");
1915 new_prop = add_element (parent: layout, element_name: "property");
1916 set_attribute_value (element: new_prop, name: "name", value: "transform");
1917
1918 transform = gsk_transform_translate (NULL, point: &GRAPHENE_POINT_INIT (x, y));
1919 new_prop->data = gsk_transform_to_string (self: transform);
1920 gsk_transform_unref (self: transform);
1921 }
1922 }
1923 }
1924}
1925
1926/* returns TRUE to remove the element from the parent */
1927static gboolean
1928simplify_element (Element *element,
1929 MyParserData *data)
1930{
1931 GList *l;
1932 GType type;
1933
1934 if (!is_pcdata_element (element))
1935 {
1936 g_clear_pointer (&element->data, g_free);
1937 }
1938 else if (g_str_equal (v1: element->element_name, v2: "property"))
1939 {
1940 if (property_is_boolean (element, data))
1941 {
1942 const char *b = canonical_boolean_value (data, string: element->data);
1943 g_free (mem: element->data);
1944 element->data = g_strdup (str: b);
1945 }
1946 else if (property_is_enum (element, data, type: &type))
1947 {
1948 char *e = canonical_enum_value (data, type, string: element->data);
1949 g_free (mem: element->data);
1950 element->data = e;
1951 }
1952
1953 for (int i = 0; element->attribute_names[i]; i++)
1954 {
1955 if (g_str_equal (v1: element->attribute_names[i], v2: "translatable"))
1956 {
1957 const char *b = canonical_boolean_value (data, string: element->attribute_values[i]);
1958 g_free (mem: element->attribute_values[i]);
1959 element->attribute_values[i] = g_strdup (str: b);
1960 break;
1961 }
1962 }
1963 }
1964
1965 l = element->children;
1966 while (l)
1967 {
1968 GList *next = l->next;
1969 Element *child = l->data;
1970 if (simplify_element (element: child, data))
1971 {
1972 element->children = g_list_remove (list: element->children, data: child);
1973 free_element (data: child);
1974 }
1975 l = next;
1976 }
1977
1978 if (is_container_element (element) && element->children == NULL)
1979 return TRUE;
1980
1981 if (g_str_equal (v1: element->element_name, v2: "property") &&
1982 property_can_be_omitted (element, data))
1983 return TRUE;
1984
1985 if (g_str_equal (v1: element->element_name, v2: "binding"))
1986 {
1987 const char *property_name = get_attribute_value (element, name: "name");
1988 const char *class_name = get_class_name (element);
1989 if (!get_property_pspec (data, class_name, property_name, kind: PROP_KIND_OBJECT))
1990 warn_missing_property (element, data, class_name, property_name, kind: PROP_KIND_OBJECT);
1991 }
1992
1993 return FALSE;
1994}
1995
1996static void
1997simplify_tree (MyParserData *data)
1998{
1999 simplify_element (element: data->root, data);
2000}
2001
2002static gboolean
2003rewrite_element (Element *element,
2004 MyParserData *data)
2005{
2006 GList *l;
2007
2008 l = element->children;
2009 while (l)
2010 {
2011 GList *next = l->next;
2012 Element *child = l->data;
2013 if (rewrite_element (element: child, data))
2014 {
2015 element->children = g_list_remove (list: element->children, data: child);
2016 free_element (data: child);
2017 }
2018 l = next;
2019 }
2020
2021 if (element_is_object_or_template (element) &&
2022 g_str_equal (v1: get_class_name (element), v2: "GtkStack"))
2023 rewrite_stack (element, data);
2024
2025 if (element_is_object_or_template (element) &&
2026 g_str_equal (v1: get_class_name (element), v2: "GtkAssistant"))
2027 rewrite_assistant (element, data);
2028
2029 if (element_is_object_or_template (element) &&
2030 g_str_equal (v1: get_class_name (element), v2: "GtkNotebook"))
2031 rewrite_notebook (element, data);
2032
2033 if (element_is_object_or_template (element) &&
2034 (g_str_equal (v1: get_class_name (element), v2: "GtkActionBar") ||
2035 g_str_equal (v1: get_class_name (element), v2: "GtkHeaderBar")))
2036 rewrite_pack_type (element, data);
2037
2038 if (element_is_object_or_template (element) &&
2039 g_str_equal (v1: get_class_name (element), v2: "GtkToolbar"))
2040 rewrite_toolbar (element, data);
2041
2042 if (element_is_object_or_template (element) &&
2043 g_str_equal (v1: get_class_name (element), v2: "GtkPaned"))
2044 rewrite_paned (element, data);
2045
2046 if (element_is_object_or_template (element) &&
2047 g_str_equal (v1: get_class_name (element), v2: "GtkDialog"))
2048 rewrite_dialog (element, data);
2049
2050 if (element_is_object_or_template (element) &&
2051 g_str_equal (v1: get_class_name (element), v2: "GtkOverlay"))
2052 rewrite_overlay (element, data);
2053
2054 if (element_is_object_or_template (element) &&
2055 g_str_equal (v1: get_class_name (element), v2: "GtkGrid"))
2056 rewrite_grid_layout (element, data);
2057
2058 if (element_is_object_or_template (element) &&
2059 (g_str_equal (v1: get_class_name (element), v2: "GtkHBox") ||
2060 g_str_equal (v1: get_class_name (element), v2: "GtkVBox") ||
2061 g_str_equal (v1: get_class_name (element), v2: "GtkBox")))
2062 rewrite_box (element, data);
2063
2064 if (element_is_object_or_template (element) &&
2065 g_str_equal (v1: get_class_name (element), v2: "GtkFixed"))
2066 rewrite_fixed (element, data);
2067
2068 if (element_is_object_or_template (element) &&
2069 (g_str_equal (v1: get_class_name (element), v2: "GtkAspectFrame") ||
2070 g_str_equal (v1: get_class_name (element), v2: "GtkComboBox") ||
2071 g_str_equal (v1: get_class_name (element), v2: "GtkComboBoxText") ||
2072 g_str_equal (v1: get_class_name (element), v2: "GtkFlowBoxChild") ||
2073 g_str_equal (v1: get_class_name (element), v2: "GtkFrame") ||
2074 g_str_equal (v1: get_class_name (element), v2: "GtkListBoxRow") ||
2075 g_str_equal (v1: get_class_name (element), v2: "GtkOverlay") ||
2076 g_str_equal (v1: get_class_name (element), v2: "GtkPopover") ||
2077 g_str_equal (v1: get_class_name (element), v2: "GtkPopoverMenu") ||
2078 g_str_equal (v1: get_class_name (element), v2: "GtkRevealer") ||
2079 g_str_equal (v1: get_class_name (element), v2: "GtkScrolledWindow") ||
2080 g_str_equal (v1: get_class_name (element), v2: "GtkSearchBar") ||
2081 g_str_equal (v1: get_class_name (element), v2: "GtkViewport") ||
2082 g_str_equal (v1: get_class_name (element), v2: "GtkWindow")))
2083 rewrite_bin_child (element, data);
2084
2085 if (element_is_object_or_template (element) &&
2086 g_str_equal (v1: get_class_name (element), v2: "GtkRadioButton"))
2087 rewrite_radio_button (element, data);
2088
2089 if (element_is_object_or_template (element) &&
2090 g_str_equal (v1: get_class_name (element), v2: "GtkScale"))
2091 rewrite_scale (element, data);
2092
2093 if (g_str_equal (v1: element->element_name, v2: "property"))
2094 maybe_rename_property (element, data);
2095
2096 if (g_str_equal (v1: element->element_name, v2: "property") &&
2097 property_has_been_removed (element, data))
2098 return TRUE;
2099
2100 if (g_str_equal (v1: element->element_name, v2: "requires"))
2101 rewrite_requires (element, data);
2102
2103 return FALSE;
2104}
2105
2106static void
2107rewrite_tree (MyParserData *data)
2108{
2109 rewrite_element (element: data->root, data);
2110}
2111
2112/* For properties which have changed their default
2113 * value between 3 and 4, we make sure that their
2114 * old default value is present in the tree before
2115 * simplifying it.
2116 *
2117 * So far, this is just GtkWidget::visible,
2118 * changing its default from 0 to 1.
2119 */
2120static void
2121add_old_default_properties (Element *element,
2122 MyParserData *data)
2123{
2124 const char *class_name;
2125 GType type;
2126
2127 if (!g_str_equal (v1: element->element_name, v2: "object"))
2128 return;
2129
2130 class_name = get_class_name (element);
2131 type = g_type_from_name (name: class_name);
2132 if (g_type_is_a (type, GTK_TYPE_WIDGET))
2133 {
2134 GList *l;
2135 gboolean has_visible = FALSE;
2136
2137 for (l = element->children; l; l = l->next)
2138 {
2139 Element *prop = l->data;
2140 const char *name = get_attribute_value (element: prop, name: "name");
2141
2142 if (g_str_equal (v1: prop->element_name, v2: "property") &&
2143 g_str_equal (v1: name, v2: "visible"))
2144 has_visible = TRUE;
2145 }
2146
2147 if (!has_visible)
2148 {
2149 Element *new_prop;
2150
2151 new_prop = add_element (parent: element, element_name: "property");
2152 set_attribute_value (element: new_prop, name: "name", value: "visible");
2153 new_prop->data = g_strdup (str: "0");
2154 }
2155 }
2156}
2157
2158static void
2159enhance_element (Element *element,
2160 MyParserData *data)
2161{
2162 GList *l;
2163
2164 if (strcmp (s1: element->element_name, s2: "requires") == 0 &&
2165 has_attribute (elt: element, name: "lib", value: "gtk+"))
2166 {
2167 data->has_gtk_requires = TRUE;
2168 }
2169
2170 add_old_default_properties (element, data);
2171
2172 for (l = element->children; l; l = l->next)
2173 {
2174 Element *child = l->data;
2175 enhance_element (element: child, data);
2176 }
2177
2178 if (element == data->root && !data->has_gtk_requires)
2179 {
2180 Element *requires = add_element (parent: element, element_name: "requires");
2181 set_attribute_value (element: requires, name: "lib", value: "gtk+");
2182 set_attribute_value (element: requires, name: "version", value: "3.0");
2183 }
2184}
2185
2186static void
2187enhance_tree (MyParserData *data)
2188{
2189 enhance_element (element: data->root, data);
2190}
2191
2192static void
2193dump_element (Element *element,
2194 FILE *output,
2195 int indent)
2196{
2197 g_fprintf (file: output, format: "%*s<%s", indent, "", element->element_name);
2198 if (element->attribute_names[0])
2199 {
2200 int i;
2201 for (i = 0; element->attribute_names[i]; i++)
2202 {
2203 char *escaped = g_markup_escape_text (text: element->attribute_values[i], length: -1);
2204 g_fprintf (file: output, format: " %s=\"%s\"", element->attribute_names[i], escaped);
2205 g_free (mem: escaped);
2206 }
2207 }
2208 if (element->children || element->data)
2209 {
2210 g_fprintf (file: output, format: ">");
2211
2212 if (element->children)
2213 {
2214 GList *l;
2215
2216 g_fprintf (file: output, format: "\n");
2217 for (l = element->children; l; l = l->next)
2218 {
2219 Element *child = l->data;
2220 dump_element (element: child, output, indent: indent + 2);
2221 }
2222 g_fprintf (file: output, format: "%*s", indent, "");
2223 }
2224 else
2225 {
2226 if (is_cdata_property (element))
2227 {
2228 g_fprintf (file: output, format: "<![CDATA[");
2229 g_fprintf (file: output, format: "%s", element->data);
2230 g_fprintf (file: output, format: "]]>");
2231 }
2232 else
2233 {
2234 char *escaped = g_markup_escape_text (text: element->data, length: -1);
2235 g_fprintf (file: output, format: "%s", escaped);
2236 g_free (mem: escaped);
2237 }
2238 }
2239 g_fprintf (file: output, format: "</%s>\n", element->element_name);
2240 }
2241 else
2242 g_fprintf (file: output, format: "/>\n");
2243}
2244
2245static void
2246write_xml_declaration (FILE *output)
2247{
2248 g_fprintf (file: output, format: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2249}
2250
2251static void
2252dump_tree (MyParserData *data)
2253{
2254 write_xml_declaration (output: data->output);
2255 dump_element (element: data->root, output: data->output, indent: 0);
2256}
2257
2258static gboolean
2259simplify_file (const char *filename,
2260 gboolean replace,
2261 gboolean convert3to4)
2262{
2263 GMarkupParseContext *context;
2264 char *buffer;
2265 MyParserData data;
2266 GError *error = NULL;
2267
2268 data.input_filename = filename;
2269 data.output_filename = NULL;
2270 data.convert3to4 = convert3to4;
2271 data.has_gtk_requires = FALSE;
2272
2273 if (replace)
2274 {
2275 int fd;
2276 fd = g_file_open_tmp (tmpl: "gtk-builder-tool-XXXXXX", name_used: &data.output_filename, NULL);
2277 data.output = fdopen (fd: fd, modes: "w");
2278 }
2279 else
2280 {
2281 data.output = stdout;
2282 }
2283
2284 if (!g_file_get_contents (filename, contents: &buffer, NULL, error: &error))
2285 {
2286 g_printerr (_("Can’t load “%s”: %s\n"), filename, error->message);
2287 return FALSE;
2288 }
2289
2290 data.root = NULL;
2291 data.current = NULL;
2292 data.value = g_string_new (init: "");
2293
2294 context = g_markup_parse_context_new (parser: &parser, flags: G_MARKUP_TREAT_CDATA_AS_TEXT, user_data: &data, NULL);
2295 if (!g_markup_parse_context_parse (context, text: buffer, text_len: -1, error: &error))
2296 {
2297 g_printerr (_("Can’t parse “%s”: %s\n"), filename, error->message);
2298 return FALSE;
2299 }
2300
2301 if (!g_markup_parse_context_end_parse (context, error: &error))
2302 {
2303 g_printerr (_("Can’t parse “%s”: %s\n"), filename, error->message);
2304 return FALSE;
2305 }
2306
2307 if (data.root == NULL)
2308 {
2309 g_printerr (_("Can’t parse “%s”\n"), filename);
2310 return FALSE;
2311 }
2312
2313 data.builder = gtk_builder_new ();
2314
2315 if (data.convert3to4)
2316 {
2317 enhance_tree (data: &data);
2318 rewrite_tree (data: &data);
2319 }
2320 simplify_tree (data: &data);
2321
2322 dump_tree (data: &data);
2323
2324 fclose (stream: data.output);
2325
2326 if (data.output_filename)
2327 {
2328 char *content;
2329 gsize length;
2330
2331 if (!g_file_get_contents (filename: data.output_filename, contents: &content, length: &length, error: &error))
2332 {
2333 g_printerr (_("Failed to read “%s”: %s\n"), data.output_filename, error->message);
2334 return FALSE;
2335 }
2336
2337 if (!g_file_set_contents (filename: data.input_filename, contents: content, length, error: &error))
2338 {
2339 g_printerr (_("Failed to write %s: “%s”\n"), data.input_filename, error->message);
2340 return FALSE;
2341 }
2342 }
2343
2344 return TRUE;
2345}
2346
2347void
2348do_simplify (int *argc,
2349 const char ***argv)
2350{
2351 gboolean replace = FALSE;
2352 gboolean convert3to4 = FALSE;
2353 char **filenames = NULL;
2354 GOptionContext *ctx;
2355 const GOptionEntry entries[] = {
2356 { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
2357 { "3to4", 0, 0, G_OPTION_ARG_NONE, &convert3to4, NULL, NULL },
2358 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
2359 { NULL, }
2360 };
2361 GError *error = NULL;
2362 int i;
2363
2364 ctx = g_option_context_new (NULL);
2365 g_option_context_set_help_enabled (context: ctx, FALSE);
2366 g_option_context_add_main_entries (context: ctx, entries, NULL);
2367
2368 if (!g_option_context_parse (context: ctx, argc, argv: (char ***)argv, error: &error))
2369 {
2370 g_printerr (format: "%s\n", error->message);
2371 g_error_free (error);
2372 exit (status: 1);
2373 }
2374
2375 g_option_context_free (context: ctx);
2376
2377 if (filenames == NULL)
2378 {
2379 g_printerr (_("No .ui file specified\n"));
2380 exit (status: 1);
2381 }
2382
2383 if (g_strv_length (str_array: filenames) > 1 && !replace)
2384 {
2385 g_printerr (_("Can only simplify a single .ui file without --replace\n"));
2386 exit (status: 1);
2387 }
2388
2389 for (i = 0; filenames[i]; i++)
2390 {
2391 if (!simplify_file (filename: filenames[i], replace, convert3to4))
2392 exit (status: 1);
2393 }
2394}
2395

source code of gtk/tools/gtk-builder-tool-simplify.c