From 4d638bf0c15c363f2c28cafc1485cf4066885383 Mon Sep 17 00:00:00 2001 From: Johan Dahlin Date: Fri, 15 Jun 2007 17:53:46 +0000 Subject: [PATCH] Add GtkBuilder, fixes #172535 2007-06-15 Johan Dahlin * demos/gtk-demo/Makefile.am: * demos/gtk-demo/builder.c: (quit_activate), (about_activate), (do_builder): * demos/gtk-demo/demo.ui: * docs/reference/gtk/gtk-docs.sgml: * docs/reference/gtk/gtk-sections.txt: * docs/reference/gtk/gtk.types: * docs/reference/gtk/tmpl/gtkbuildable.sgml: * docs/reference/gtk/tmpl/gtkbuilder.sgml: * gtk/Makefile.am: * gtk/gtk.h: * gtk/gtk.symbols: * gtk/gtkaction.c: (gtk_action_buildable_init), (gtk_action_buildable_set_name), (gtk_action_buildable_get_name): * gtk/gtkactiongroup.c: (gtk_action_group_get_type), (gtk_action_group_buildable_init), (gtk_action_group_buildable_add), (gtk_action_group_buildable_set_name), (gtk_action_group_buildable_get_name): * gtk/gtkbuildable.c: (gtk_buildable_get_type), (gtk_buildable_set_name), (gtk_buildable_get_name), (gtk_buildable_add), (gtk_buildable_set_property), (gtk_buildable_parser_finished), (gtk_buildable_construct_child), (gtk_buildable_custom_tag_start), (gtk_buildable_custom_tag_end), (gtk_buildable_custom_finished), (gtk_buildable_get_internal_child): * gtk/gtkbuildable.h: * gtk/gtkbuilder.c: (gtk_builder_class_init), (gtk_builder_init), (gtk_builder_finalize), (gtk_builder_set_property), (gtk_builder_get_property), (_gtk_builder_resolve_type_lazily), (gtk_builder_real_get_type_from_name), (gtk_builder_get_parameters), (gtk_builder_get_internal_child), (_gtk_builder_construct), (_gtk_builder_add), (apply_delayed_properties), (_gtk_builder_finish), (gtk_builder_new), (gtk_builder_add_from_file), (gtk_builder_add_from_string), (gtk_builder_get_object), (object_add_to_list), (gtk_builder_get_objects), (gtk_builder_set_translation_domain), (gtk_builder_get_translation_domain), (gtk_builder_connect_signals_default), (gtk_builder_connect_signals), (gtk_builder_connect_signals_full), (gtk_builder_value_from_string), (gtk_builder_value_from_string_type), (_gtk_builder_enum_from_string), (_gtk_builder_flags_from_string), (gtk_builder_get_type_from_name), (gtk_builder_error_quark): * gtk/gtkbuilder.h: * gtk/gtkbuilderparser.c: (state_push), (state_peek), (state_pop), (error_missing_attribute), (error_invalid_attribute), (error_invalid_tag), (builder_construct), (parse_object), (free_object_info), (_get_type_by_symbol), (parse_child), (free_child_info), (parse_property), (free_property_info), (parse_signal), (_free_signal_info), (parse_interface), (create_subparser), (free_subparser), (subparser_start), (subparser_end), (parse_custom), (start_element), (end_element), (text), (_gtk_builder_parser_parse_buffer): * gtk/gtkbuilderprivate.h: * gtk/gtkcelllayout.c: (attributes_start_element), (attributes_text_element), (_gtk_cell_layout_buildable_custom_tag_start), (_gtk_cell_layout_buildable_custom_tag_end), (_gtk_cell_layout_buildable_add): * gtk/gtkcelllayout.h: * gtk/gtkcellview.c: (gtk_cell_view_buildable_init), (gtk_cell_view_buildable_custom_tag_start), (gtk_cell_view_buildable_custom_tag_end): * gtk/gtkcolorseldialog.c: (gtk_color_selection_dialog_buildable_interface_init), (gtk_color_selection_dialog_buildable_get_internal_child): * gtk/gtkcombobox.c: (gtk_combo_box_buildable_init), (gtk_combo_box_buildable_custom_tag_start), (gtk_combo_box_buildable_custom_tag_end): * gtk/gtkcomboboxentry.c: (gtk_combo_box_entry_buildable_interface_init), (gtk_combo_box_entry_buildable_get_internal_child): * gtk/gtkcontainer.c: (gtk_container_get_type), (gtk_container_buildable_init), (gtk_container_buildable_add), (gtk_container_buildable_set_child_property), (attributes_start_element), (attributes_text_element), (gtk_container_buildable_custom_tag_start), (gtk_container_buildable_custom_tag_end): * gtk/gtkdebug.h: * gtk/gtkdialog.c: (gtk_dialog_buildable_interface_init), (gtk_dialog_buildable_get_internal_child), (attributes_start_element), (attributes_text_element), (gtk_dialog_buildable_custom_tag_start), (gtk_dialog_buildable_custom_finished): * gtk/gtkentrycompletion.c: (gtk_entry_completion_buildable_init): * gtk/gtkexpander.c: (gtk_expander_buildable_add), (gtk_expander_buildable_init): * gtk/gtkfontsel.c: (gtk_font_selection_dialog_buildable_interface_init), (gtk_font_selection_dialog_buildable_get_internal_child): * gtk/gtkframe.c: (gtk_frame_buildable_init), (gtk_frame_buildable_add): * gtk/gtkiconview.c: (gtk_icon_view_buildable_init), (gtk_icon_view_buildable_custom_tag_start), (gtk_icon_view_buildable_custom_tag_end): * gtk/gtkliststore.c: (gtk_list_store_buildable_init), (list_store_start_element), (list_store_end_element), (list_store_text), (gtk_list_store_buildable_custom_tag_start), (gtk_list_store_buildable_custom_tag_end): * gtk/gtkmain.c: * gtk/gtknotebook.c: (gtk_notebook_buildable_init), (gtk_notebook_buildable_add): * gtk/gtksizegroup.c: (gtk_size_group_buildable_init), (size_group_start_element), (gtk_size_group_buildable_custom_tag_start), (gtk_size_group_buildable_custom_finished): * gtk/gtktreestore.c: (gtk_tree_store_buildable_init), (tree_model_start_element), (gtk_tree_store_buildable_custom_tag_start), (gtk_tree_store_buildable_custom_finished): * gtk/gtktreeview.c: (gtk_tree_view_buildable_init), (gtk_tree_view_buildable_add): * gtk/gtktreeviewcolumn.c: (gtk_tree_view_column_buildable_init): * gtk/gtkuimanager.c: (gtk_ui_manager_buildable_init), (gtk_ui_manager_buildable_add), (gtk_ui_manager_buildable_construct_child), (gtk_ui_manager_buildable_custom_tag_start), (gtk_ui_manager_buildable_custom_tag_end): * gtk/gtkwidget.c: (gtk_widget_get_type), (gtk_widget_buildable_interface_init), (gtk_widget_buildable_set_name), (gtk_widget_buildable_get_name), (gtk_widget_buildable_set_property), (gtk_widget_buildable_parser_finshed), (accel_group_start_element), (gtk_widget_buildable_custom_tag_start), (gtk_widget_buildable_custom_finshed): * gtk/gtkwindow.c: (gtk_window_buildable_interface_init), (gtk_window_buildable_set_property), (gtk_window_buildable_parser_finished): * tests/Makefile.am: * tests/buildertest.c: (builder_new_from_string), (test_parser), (signal_normal), (signal_after), (signal_object), (signal_object_after), (signal_first), (signal_second), (signal_extra), (signal_extra2), (test_connect_signals), (test_uimanager_simple), (test_domain), (test_translation), (test_sizegroup), (test_list_store), (test_tree_store), (test_types), (test_spin_button), (test_notebook), (test_construct_only_property), (test_children), (test_child_properties), (test_treeview_column), (test_icon_view), (test_combo_box), (test_combo_box_entry), (test_cell_view), (test_dialog), (test_accelerators), (test_widget), (main): Add GtkBuilder, fixes #172535 svn path=/trunk/; revision=18141 --- ChangeLog | 149 +++ demos/gtk-demo/Makefile.am | 7 +- demos/gtk-demo/builder.c | 60 + demos/gtk-demo/demo.ui | 227 ++++ docs/reference/gtk/gtk-docs.sgml | 8 + docs/reference/gtk/gtk-sections.txt | 67 + docs/reference/gtk/gtk.types | 2 + docs/reference/gtk/tmpl/gtkbuildable.sgml | 151 +++ docs/reference/gtk/tmpl/gtkbuilder.sgml | 181 +++ gtk/Makefile.am | 6 + gtk/gtk.h | 1 + gtk/gtk.symbols | 36 + gtk/gtkaction.c | 40 +- gtk/gtkactiongroup.c | 55 +- gtk/gtkbuildable.c | 372 ++++++ gtk/gtkbuildable.h | 123 ++ gtk/gtkbuilder.c | 1267 ++++++++++++++++++ gtk/gtkbuilder.h | 122 ++ gtk/gtkbuilderparser.c | 851 ++++++++++++ gtk/gtkbuilderprivate.h | 111 ++ gtk/gtkcelllayout.c | 112 ++ gtk/gtkcelllayout.h | 18 +- gtk/gtkcellview.c | 63 +- gtk/gtkcolorseldialog.c | 39 +- gtk/gtkcombobox.c | 60 +- gtk/gtkcomboboxentry.c | 27 +- gtk/gtkcontainer.c | 190 +++ gtk/gtkdebug.h | 3 +- gtk/gtkdialog.c | 172 ++- gtk/gtkentrycompletion.c | 16 +- gtk/gtkexpander.c | 34 +- gtk/gtkfontsel.c | 37 +- gtk/gtkframe.c | 32 +- gtk/gtkiconview.c | 61 +- gtk/gtkliststore.c | 276 +++- gtk/gtkmain.c | 5 +- gtk/gtknotebook.c | 42 +- gtk/gtksizegroup.c | 123 +- gtk/gtktreestore.c | 133 +- gtk/gtktreeview.c | 28 +- gtk/gtktreeviewcolumn.c | 15 +- gtk/gtkuimanager.c | 118 +- gtk/gtkwidget.c | 203 +++ gtk/gtkwindow.c | 50 +- tests/Makefile.am | 7 +- tests/buildertest.c | 1462 +++++++++++++++++++++ 46 files changed, 7132 insertions(+), 30 deletions(-) create mode 100644 demos/gtk-demo/builder.c create mode 100644 demos/gtk-demo/demo.ui create mode 100644 docs/reference/gtk/tmpl/gtkbuildable.sgml create mode 100644 docs/reference/gtk/tmpl/gtkbuilder.sgml create mode 100644 gtk/gtkbuildable.c create mode 100644 gtk/gtkbuildable.h create mode 100644 gtk/gtkbuilder.c create mode 100644 gtk/gtkbuilder.h create mode 100644 gtk/gtkbuilderparser.c create mode 100644 gtk/gtkbuilderprivate.h create mode 100644 tests/buildertest.c diff --git a/ChangeLog b/ChangeLog index 8e9869c709..d97a89f3d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,152 @@ +2007-06-15 Johan Dahlin + + reviewed by: Matthias Clasen + + * demos/gtk-demo/Makefile.am: + * demos/gtk-demo/builder.c: (quit_activate), (about_activate), + (do_builder): + * demos/gtk-demo/demo.ui: + * docs/reference/gtk/gtk-docs.sgml: + * docs/reference/gtk/gtk-sections.txt: + * docs/reference/gtk/gtk.types: + * docs/reference/gtk/tmpl/gtkbuildable.sgml: + * docs/reference/gtk/tmpl/gtkbuilder.sgml: + * gtk/Makefile.am: + * gtk/gtk.h: + * gtk/gtk.symbols: + * gtk/gtkaction.c: (gtk_action_buildable_init), + (gtk_action_buildable_set_name), (gtk_action_buildable_get_name): + * gtk/gtkactiongroup.c: (gtk_action_group_get_type), + (gtk_action_group_buildable_init), + (gtk_action_group_buildable_add), + (gtk_action_group_buildable_set_name), + (gtk_action_group_buildable_get_name): + * gtk/gtkbuildable.c: (gtk_buildable_get_type), + (gtk_buildable_set_name), (gtk_buildable_get_name), + (gtk_buildable_add), (gtk_buildable_set_property), + (gtk_buildable_parser_finished), (gtk_buildable_construct_child), + (gtk_buildable_custom_tag_start), (gtk_buildable_custom_tag_end), + (gtk_buildable_custom_finished), + (gtk_buildable_get_internal_child): + * gtk/gtkbuildable.h: + * gtk/gtkbuilder.c: (gtk_builder_class_init), (gtk_builder_init), + (gtk_builder_finalize), (gtk_builder_set_property), + (gtk_builder_get_property), (_gtk_builder_resolve_type_lazily), + (gtk_builder_real_get_type_from_name), + (gtk_builder_get_parameters), (gtk_builder_get_internal_child), + (_gtk_builder_construct), (_gtk_builder_add), + (apply_delayed_properties), (_gtk_builder_finish), + (gtk_builder_new), (gtk_builder_add_from_file), + (gtk_builder_add_from_string), (gtk_builder_get_object), + (object_add_to_list), (gtk_builder_get_objects), + (gtk_builder_set_translation_domain), + (gtk_builder_get_translation_domain), + (gtk_builder_connect_signals_default), + (gtk_builder_connect_signals), (gtk_builder_connect_signals_full), + (gtk_builder_value_from_string), + (gtk_builder_value_from_string_type), + (_gtk_builder_enum_from_string), (_gtk_builder_flags_from_string), + (gtk_builder_get_type_from_name), (gtk_builder_error_quark): + * gtk/gtkbuilder.h: + * gtk/gtkbuilderparser.c: (state_push), (state_peek), (state_pop), + (error_missing_attribute), (error_invalid_attribute), + (error_invalid_tag), (builder_construct), (parse_object), + (free_object_info), (_get_type_by_symbol), (parse_child), + (free_child_info), (parse_property), (free_property_info), + (parse_signal), (_free_signal_info), (parse_interface), + (create_subparser), (free_subparser), (subparser_start), + (subparser_end), (parse_custom), (start_element), (end_element), + (text), (_gtk_builder_parser_parse_buffer): + * gtk/gtkbuilderprivate.h: + * gtk/gtkcelllayout.c: (attributes_start_element), + (attributes_text_element), + (_gtk_cell_layout_buildable_custom_tag_start), + (_gtk_cell_layout_buildable_custom_tag_end), + (_gtk_cell_layout_buildable_add): + * gtk/gtkcelllayout.h: + * gtk/gtkcellview.c: (gtk_cell_view_buildable_init), + (gtk_cell_view_buildable_custom_tag_start), + (gtk_cell_view_buildable_custom_tag_end): + * gtk/gtkcolorseldialog.c: + (gtk_color_selection_dialog_buildable_interface_init), + (gtk_color_selection_dialog_buildable_get_internal_child): + * gtk/gtkcombobox.c: (gtk_combo_box_buildable_init), + (gtk_combo_box_buildable_custom_tag_start), + (gtk_combo_box_buildable_custom_tag_end): + * gtk/gtkcomboboxentry.c: + (gtk_combo_box_entry_buildable_interface_init), + (gtk_combo_box_entry_buildable_get_internal_child): + * gtk/gtkcontainer.c: (gtk_container_get_type), + (gtk_container_buildable_init), (gtk_container_buildable_add), + (gtk_container_buildable_set_child_property), + (attributes_start_element), (attributes_text_element), + (gtk_container_buildable_custom_tag_start), + (gtk_container_buildable_custom_tag_end): + * gtk/gtkdebug.h: + * gtk/gtkdialog.c: (gtk_dialog_buildable_interface_init), + (gtk_dialog_buildable_get_internal_child), + (attributes_start_element), (attributes_text_element), + (gtk_dialog_buildable_custom_tag_start), + (gtk_dialog_buildable_custom_finished): + * gtk/gtkentrycompletion.c: (gtk_entry_completion_buildable_init): + * gtk/gtkexpander.c: (gtk_expander_buildable_add), + (gtk_expander_buildable_init): + * gtk/gtkfontsel.c: + (gtk_font_selection_dialog_buildable_interface_init), + (gtk_font_selection_dialog_buildable_get_internal_child): + * gtk/gtkframe.c: (gtk_frame_buildable_init), + (gtk_frame_buildable_add): + * gtk/gtkiconview.c: (gtk_icon_view_buildable_init), + (gtk_icon_view_buildable_custom_tag_start), + (gtk_icon_view_buildable_custom_tag_end): + * gtk/gtkliststore.c: (gtk_list_store_buildable_init), + (list_store_start_element), (list_store_end_element), + (list_store_text), (gtk_list_store_buildable_custom_tag_start), + (gtk_list_store_buildable_custom_tag_end): + * gtk/gtkmain.c: + * gtk/gtknotebook.c: (gtk_notebook_buildable_init), + (gtk_notebook_buildable_add): + * gtk/gtksizegroup.c: (gtk_size_group_buildable_init), + (size_group_start_element), + (gtk_size_group_buildable_custom_tag_start), + (gtk_size_group_buildable_custom_finished): + * gtk/gtktreestore.c: (gtk_tree_store_buildable_init), + (tree_model_start_element), + (gtk_tree_store_buildable_custom_tag_start), + (gtk_tree_store_buildable_custom_finished): + * gtk/gtktreeview.c: (gtk_tree_view_buildable_init), + (gtk_tree_view_buildable_add): + * gtk/gtktreeviewcolumn.c: (gtk_tree_view_column_buildable_init): + * gtk/gtkuimanager.c: (gtk_ui_manager_buildable_init), + (gtk_ui_manager_buildable_add), + (gtk_ui_manager_buildable_construct_child), + (gtk_ui_manager_buildable_custom_tag_start), + (gtk_ui_manager_buildable_custom_tag_end): + * gtk/gtkwidget.c: (gtk_widget_get_type), + (gtk_widget_buildable_interface_init), + (gtk_widget_buildable_set_name), (gtk_widget_buildable_get_name), + (gtk_widget_buildable_set_property), + (gtk_widget_buildable_parser_finshed), (accel_group_start_element), + (gtk_widget_buildable_custom_tag_start), + (gtk_widget_buildable_custom_finshed): + * gtk/gtkwindow.c: (gtk_window_buildable_interface_init), + (gtk_window_buildable_set_property), + (gtk_window_buildable_parser_finished): + * tests/Makefile.am: + * tests/buildertest.c: (builder_new_from_string), (test_parser), + (signal_normal), (signal_after), (signal_object), + (signal_object_after), (signal_first), (signal_second), + (signal_extra), (signal_extra2), (test_connect_signals), + (test_uimanager_simple), (test_domain), (test_translation), + (test_sizegroup), (test_list_store), (test_tree_store), + (test_types), (test_spin_button), (test_notebook), + (test_construct_only_property), (test_children), + (test_child_properties), (test_treeview_column), (test_icon_view), + (test_combo_box), (test_combo_box_entry), (test_cell_view), + (test_dialog), (test_accelerators), (test_widget), (main): + + Add GtkBuilder, fixes #172535 + 2007-06-15 Hans Breuer * gtk/makefile.msc.in tests/makefile.msc : updated diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index f043ee0f4e..ccd941a0b8 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -7,6 +7,7 @@ democodedir=$(datadir)/gtk-2.0/demo demos = \ appwindow.c \ assistant.c \ + builder.c \ button_box.c \ changedisplay.c \ clipboard.c \ @@ -60,7 +61,8 @@ bin_PROGRAMS = gtk-demo BUILT_SOURCES = demos.h EXTRA_DIST = \ - $(IMAGEFILES) + $(IMAGEFILES) \ + demo.ui demos.h: @REBUILD@ $(demos) geninclude.pl (here=`pwd` ; cd $(srcdir) && $(PERL) $$here/geninclude.pl $(demos)) > demos.h @@ -73,6 +75,7 @@ gtk_demo_SOURCES = \ gtk_demo_DEPENDENCIES = $(DEPS) gtk_demo_LDADD = $(LDADDS) +gtk_demo_LDFLAGS = -export-dynamic IMAGEFILES= alphatest.png \ apple-red.png \ @@ -89,6 +92,6 @@ IMAGEFILES= alphatest.png \ gnu-keys.png \ gtk-logo-rgb.gif -democode_DATA = $(demos) $(IMAGEFILES) +democode_DATA = $(demos) $(IMAGEFILES) demo.ui DISTCLEANFILES = demos.h diff --git a/demos/gtk-demo/builder.c b/demos/gtk-demo/builder.c new file mode 100644 index 0000000000..3363969945 --- /dev/null +++ b/demos/gtk-demo/builder.c @@ -0,0 +1,60 @@ +/* Builder + * + * Demonstrates an interface loaded from a XML description. + */ + +#include +#include "demo-common.h" + +void +quit_activate (GtkAction *action) +{ +} + +void +about_activate (GtkAction *action) +{ + GtkWidget *about_dlg; + + about_dlg = gtk_about_dialog_new (); + gtk_about_dialog_set_name (GTK_ABOUT_DIALOG (about_dlg), "GtkBuilder demo"); + gtk_dialog_run (GTK_DIALOG (about_dlg)); + gtk_widget_destroy (about_dlg); +} + +GtkWidget * +do_builder (GtkWidget *do_widget) +{ + static GtkWidget *window = NULL; + GtkBuilder *builder; + GError *err = NULL; + gchar *filename; + + if (!window) + { + builder = gtk_builder_new (); + filename = demo_find_file ("demo.ui", NULL); + gtk_builder_add_from_file (builder, filename, &err); + g_free (filename); + if (err) + { + g_error ("ERROR: %s\n", err->message); + return NULL; + } + gtk_builder_connect_signals (builder, NULL); + window = GTK_WIDGET (gtk_builder_get_object (builder, "window1")); + } + + if (!GTK_WIDGET_VISIBLE (window)) + { + gtk_widget_show_all (window); + } + else + { + gtk_widget_destroy (window); + window = NULL; + } + + + return window; +} diff --git a/demos/gtk-demo/demo.ui b/demos/gtk-demo/demo.ui new file mode 100644 index 0000000000..9830ed96c8 --- /dev/null +++ b/demos/gtk-demo/demo.ui @@ -0,0 +1,227 @@ + + + + + + + + + + + John + Doe + 25 + + + Mary + Dole + 50 + + + + + + + + + Copy + Copy selected object into the clipboard + gtk-copy + + + + + Cut + Cut selected object into the clipboard + gtk-cut + + + + + EditMenu + _Edit + + + + + FileMenu + _File + + + + + New + Create a new file + gtk-new + + + + + Open + Open a file + gtk-open + + + + + Paste + Paste object from the Clipboard + gtk-paste + + + + + Quit + Quit the program + gtk-quit + + + + + + Save + True + Save a file + gtk-save + + + + + SaveAs + Save with a different name + gtk-save-as + + + + + Help + _Help + + + + + About + gtk-about + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 250 + 440 + + + + True + + + True + + + False + + + + + True + + + False + 1 + + + + + automatic + in + True + automatic + + + True + liststore1 + + + Name + + + + 0 + + + + + + + Surname + + + + 1 + + + + + + + Age + + + + 2 + + + + + + + + + 2 + + + + + True + + + False + 3 + + + + + + diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index dfa7bd4cda..f50d843836 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -14,6 +14,8 @@ + + @@ -588,6 +590,12 @@ that is, GUI components such as #GtkButton or #GtkTextView. &GtkRecentFilter; + + Interface builder + &GtkBuildable; + &GtkBuilder; + + Deprecated &GtkCList; diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt index 5aed8ff235..b41ab7e3b2 100644 --- a/docs/reference/gtk/gtk-sections.txt +++ b/docs/reference/gtk/gtk-sections.txt @@ -431,6 +431,73 @@ GTK_BOX_GET_CLASS gtk_box_get_type +
+gtkvscrollbar +GtkVScrollbar +GtkVScrollbar +gtk_vscrollbar_new + +GTK_VSCROLLBAR +GTK_IS_VSCROLLBAR +GTK_TYPE_VSCROLLBAR +gtk_vscrollbar_get_type +GTK_VSCROLLBAR_CLASS +GTK_IS_VSCROLLBAR_CLASS +GTK_VSCROLLBAR_GET_CLASS +
+ +
+gtkbuildable +GtkBuildable +GtkBuildableIface +gtk_buildable_set_name +gtk_buildable_get_name +gtk_buildable_add +gtk_buildable_set_property +gtk_buildable_construct_child +gtk_buildable_custom_tag_start +gtk_buildable_custom_tag_end +gtk_buildable_custom_finished +gtk_buildable_parser_finished +gtk_buildable_get_internal_child + +GTK_BUILDABLE +GTK_IS_BUILDABLE +GTK_TYPE_BUILDABLE +gtk_buildable_get_type +GTK_BUILDABLE_CLASS +GTK_BUILDABLE_GET_IFACE +
+ +
+gtkbuilder +GtkBuilder +GtkBuilder +GtkBuilderConnectFunc +gtk_builder_new +gtk_builder_add_from_file +gtk_builder_add_from_string +gtk_builder_get_object +gtk_builder_get_objects +gtk_builder_connect_signals +gtk_builder_connect_signals_full +gtk_builder_set_translation_domain +gtk_builder_get_translation_domain +gtk_builder_get_type_from_name +gtk_builder_value_from_string +gtk_builder_value_from_string_type +GTK_BUILDER_WARN_INVALID_CHILD_TYPE + +GTK_BUILDER +GTK_IS_BUILDER +GTK_TYPE_BUILDER +GTK_BUILDER_CLASS +GTK_IS_BUILDER_CLASS +GTK_BUILDER_GET_CLASS + +gtk_builder_get_type +
+
gtkbutton GtkButton diff --git a/docs/reference/gtk/gtk.types b/docs/reference/gtk/gtk.types index 5ad47ff551..fde19eae17 100644 --- a/docs/reference/gtk/gtk.types +++ b/docs/reference/gtk/gtk.types @@ -18,6 +18,8 @@ gtk_aspect_frame_get_type gtk_assistant_get_type gtk_bin_get_type gtk_box_get_type +gtk_builder_get_type +gtk_buildable_get_type gtk_button_box_get_type gtk_button_get_type gtk_calendar_get_type diff --git a/docs/reference/gtk/tmpl/gtkbuildable.sgml b/docs/reference/gtk/tmpl/gtkbuildable.sgml new file mode 100644 index 0000000000..8080455758 --- /dev/null +++ b/docs/reference/gtk/tmpl/gtkbuildable.sgml @@ -0,0 +1,151 @@ + +GtkBuildable + + +SHORT + + + +LONG + + + + +SEE ALSO + + + + + + + + + + + + + + + + +@g_iface: +@set_name: +@get_name: +@add: +@set_property: +@construct_child: +@custom_tag_start: +@custom_tag_end: +@custom_finished: +@parser_finished: +@get_internal_child: + + + + + + +@buildable: +@name: + + + + + + + +@buildable: +@Returns: + + + + + + + +@buildable: +@builder: +@child: +@type: + + + + + + + +@buildable: +@builder: +@name: +@value: + + + + + + + +@buildable: +@builder: +@name: +@Returns: + + + + + + + +@buildable: +@builder: +@child: +@tagname: +@parser: +@data: +@Returns: + + + + + + + +@buildable: +@builder: +@child: +@tagname: +@data: + + + + + + + +@buildable: +@builder: +@child: +@tagname: +@data: + + + + + + + +@buildable: +@builder: + + + + + + + +@buildable: +@builder: +@childname: +@Returns: + + diff --git a/docs/reference/gtk/tmpl/gtkbuilder.sgml b/docs/reference/gtk/tmpl/gtkbuilder.sgml new file mode 100644 index 0000000000..34b8a2da88 --- /dev/null +++ b/docs/reference/gtk/tmpl/gtkbuilder.sgml @@ -0,0 +1,181 @@ + +GtkBuilder + + +Build an interface from a UI definition description. + + + +This object represents an `instantiation' of an UI definition description. +When one of these objects is created, the XML file is read, and the +interface is created. The GtkBuilder object then provides an interface +for accessing the widgets in the interface by the names assigned to +them inside the UI description. + +The GtkBuilder object can also be used to connect handlers to the named +signals in the description. GtkBuilder also provides an interface by +which it can look up the signal handler names in the program's symbol +table and automatically connect as many handlers up as it can that way. + + + + +SEE ALSO + + + + + + + + + + + + + + + + + + + + + +@builder: +@object: +@signal_name: +@handler_name: +@connect_object: +@flags: +@user_data: + + + + + + + +@Returns: + + + + + + + +@builder: +@filename: +@error: +@Returns: + + + + + + + +@builder: +@buffer: +@length: +@error: +@Returns: + + + + + + + +@builder: +@name: +@Returns: + + + + + + + +@builder: +@Returns: + + + + + + + +@builder: +@user_data: + + + + + + + +@builder: +@func: +@user_data: + + + + + + + +@builder: +@domain: + + + + + + + +@builder: +@Returns: + + + + + + + +@builder: +@typename: +@Returns: + + + + + + + +@pspec: +@string: +@value: +@Returns: + + + + + + + +@type: +@string: +@value: +@Returns: + + + + + + + +@object: +@type: + + diff --git a/gtk/Makefile.am b/gtk/Makefile.am index a1acb56cdc..92a78b8537 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -143,6 +143,9 @@ gtk_public_h_sources = \ gtkbin.h \ gtkbindings.h \ gtkbox.h \ + gtkbuilder.h \ + gtkbuildable.h \ + gtkbuilderprivate.h \ gtkbutton.h \ gtkcalendar.h \ gtkcelleditable.h \ @@ -405,6 +408,9 @@ gtk_base_c_sources = \ gtkbin.c \ gtkbindings.c \ gtkbox.c \ + gtkbuildable.c \ + gtkbuilder.c \ + gtkbuilderparser.c \ gtkbutton.c \ gtkcalendar.c \ gtkcelleditable.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 1e9436fb5f..70f04b7984 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 4cff2b5640..0ad3de6148 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -254,6 +254,7 @@ gtk_arg_flags_get_type G_GNUC_CONST gtk_arrow_type_get_type G_GNUC_CONST gtk_assistant_page_type_get_type G_GNUC_CONST gtk_attach_options_get_type G_GNUC_CONST +gtk_builder_error_get_type G_GNUC_CONST gtk_button_action_get_type G_GNUC_CONST gtk_buttons_type_get_type G_GNUC_CONST gtk_calendar_display_options_get_type G_GNUC_CONST @@ -420,6 +421,41 @@ gtk_box_set_spacing #endif #endif +#if IN_HEADER(__GTK_BUILDABLE_H__) +#if IN_FILE(__GTK_BUILDABLE_C__) +gtk_buildable_add +gtk_buildable_construct_child +gtk_buildable_custom_tag_start +gtk_buildable_custom_tag_end +gtk_buildable_custom_finished +gtk_buildable_get_internal_child +gtk_buildable_get_name +gtk_buildable_get_type G_GNUC_CONST +gtk_buildable_parser_finished +gtk_buildable_set_name +gtk_buildable_set_property +#endif +#endif + +#if IN_HEADER(__GTK_BUILDER_H__) +#if IN_FILE(__GTK_BUILDER_C__) +gtk_builder_add_from_file +gtk_builder_add_from_string +gtk_builder_error_quark +gtk_builder_get_object +gtk_builder_get_objects +gtk_builder_get_translation_domain +gtk_builder_get_type G_GNUC_CONST +gtk_builder_get_type_from_name +gtk_builder_new +gtk_builder_set_translation_domain +gtk_builder_connect_signals +gtk_builder_connect_signals_full +gtk_builder_value_from_string +gtk_builder_value_from_string_type +#endif +#endif + #if IN_HEADER(__GTK_BUTTON_BOX_H__) #if IN_FILE(__GTK_BUTTON_BOX_C__) #ifndef GTK_DISABLE_DEPRECATED diff --git a/gtk/gtkaction.c b/gtk/gtkaction.c index eb8173ef8b..f1b161820e 100644 --- a/gtk/gtkaction.c +++ b/gtk/gtkaction.c @@ -46,6 +46,7 @@ #include "gtktoolbutton.h" #include "gtktoolbar.h" #include "gtkprivate.h" +#include "gtkbuildable.h" #include "gtkalias.h" @@ -107,14 +108,22 @@ enum PROP_ACTION_GROUP }; +/* GtkBuildable */ +static void gtk_action_buildable_init (GtkBuildableIface *iface); +static void gtk_action_buildable_set_name (GtkBuildable *buildable, + const gchar *name); +static const gchar* gtk_action_buildable_get_name (GtkBuildable *buildable); + +G_DEFINE_TYPE_WITH_CODE (GtkAction, gtk_action, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_action_buildable_init)) + static GQuark accel_path_id = 0; static GQuark quark_gtk_action_proxy = 0; static const gchar accel_path_key[] = "GtkAction::accel_path"; static const gchar gtk_action_proxy_key[] = "gtk-action"; -G_DEFINE_TYPE (GtkAction, gtk_action, G_TYPE_OBJECT) - static void gtk_action_finalize (GObject *object); static void gtk_action_set_property (GObject *object, guint prop_id, @@ -378,6 +387,33 @@ gtk_action_init (GtkAction *action) action->private_data->proxies = NULL; } +static void +gtk_action_buildable_init (GtkBuildableIface *iface) +{ + iface->set_name = gtk_action_buildable_set_name; + iface->get_name = gtk_action_buildable_get_name; +} + +static void +gtk_action_buildable_set_name (GtkBuildable *buildable, + const gchar *name) +{ + gchar *tmp; + GtkAction *action = GTK_ACTION (buildable); + + tmp = action->private_data->name; + action->private_data->name = g_strdup (name); + g_free (tmp); +} + +static const gchar * +gtk_action_buildable_get_name (GtkBuildable *buildable) +{ + GtkAction *action = GTK_ACTION (buildable); + + return action->private_data->name; +} + /** * gtk_action_new: * @name: A unique name for the action diff --git a/gtk/gtkactiongroup.c b/gtk/gtkactiongroup.c index 26243f3197..c976b46e11 100644 --- a/gtk/gtkactiongroup.c +++ b/gtk/gtkactiongroup.c @@ -31,6 +31,7 @@ #include #include "gtkactiongroup.h" +#include "gtkbuildable.h" #include "gtkiconfactory.h" #include "gtkicontheme.h" #include "gtkstock.h" @@ -87,6 +88,15 @@ static void gtk_action_group_get_property (GObject *object, static GtkAction *gtk_action_group_real_get_action (GtkActionGroup *self, const gchar *name); +/* GtkBuildable */ +static void gtk_action_group_buildable_init (GtkBuildableIface *iface); +static void gtk_action_group_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); +static void gtk_action_group_buildable_set_name (GtkBuildable *buildable, + const gchar *name); +static const gchar* gtk_action_group_buildable_get_name (GtkBuildable *buildable); GType gtk_action_group_get_type (void) @@ -108,10 +118,20 @@ gtk_action_group_get_type (void) (GInstanceInitFunc) gtk_action_group_init, }; + static const GInterfaceInfo buildable_info = + { + (GInterfaceInitFunc) gtk_action_group_buildable_init, + NULL, + NULL + }; + type = g_type_register_static (G_TYPE_OBJECT, I_("GtkActionGroup"), &type_info, 0); - } + g_type_add_interface_static (type, + GTK_TYPE_BUILDABLE, + &buildable_info); + } return type; } @@ -272,6 +292,39 @@ gtk_action_group_init (GtkActionGroup *self) self->private_data->translate_notify = NULL; } +static void +gtk_action_group_buildable_init (GtkBuildableIface *iface) +{ + iface->add = gtk_action_group_buildable_add; + iface->set_name = gtk_action_group_buildable_set_name; + iface->get_name = gtk_action_group_buildable_get_name; +} + +static void +gtk_action_group_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + gtk_action_group_add_action (GTK_ACTION_GROUP (buildable), + GTK_ACTION (child)); +} + +static void +gtk_action_group_buildable_set_name (GtkBuildable *buildable, + const gchar *name) +{ + GtkActionGroup *self = GTK_ACTION_GROUP (buildable); + self->private_data->name = g_strdup (name); +} + +static const gchar * +gtk_action_group_buildable_get_name (GtkBuildable *buildable) +{ + GtkActionGroup *self = GTK_ACTION_GROUP (buildable); + return self->private_data->name; +} + /** * gtk_action_group_new: * @name: the name of the action group. diff --git a/gtk/gtkbuildable.c b/gtk/gtkbuildable.c new file mode 100644 index 0000000000..ee3934ec27 --- /dev/null +++ b/gtk/gtkbuildable.c @@ -0,0 +1,372 @@ +/* gtkbuildable.c + * Copyright (C) 2006-2007 Async Open Source, + * Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include +#include "gtkbuildable.h" +#include "gtktypeutils.h" +#include "gtkintl.h" +#include "gtkalias.h" + +GType +gtk_buildable_get_type (void) +{ + static GType buildable_type = 0; + + if (!buildable_type) + buildable_type = + g_type_register_static_simple (G_TYPE_INTERFACE, I_("GtkBuildable"), + sizeof (GtkBuildableIface), + NULL, 0, NULL, 0); + + return buildable_type; +} + +/** + * gtk_buildable_set_name: + * @buildable: a #GtkBuildable + * @name: name to set + * + * Sets the name of the buildable object, it's used to synchronize the name + * if the object already has it's own concept of name. + * + * #GtkWidget implements this to map the buildable name to the widget name + * + * Since: 2.12 + **/ +void +gtk_buildable_set_name (GtkBuildable *buildable, + const gchar *name) +{ + GtkBuildableIface *iface; + + g_return_if_fail (GTK_IS_BUILDABLE (buildable)); + g_return_if_fail (name != NULL); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + + if (iface->set_name) + (* iface->set_name) (buildable, name); + else + g_object_set_data_full (G_OBJECT (buildable), + "gtk-builder-name", + g_strdup (name), + g_free); +} + +/** + * gtk_buildable_get_name: + * @buildable: a #GtkBuildable + * + * Returns: the buildable name, the name which was set in + * the GtkBuilder UI definition used to + * construct the @buildable. + * + * #GtkWidget implements this to map the buildable name to the widget name + * + * Since: 2.12 + **/ +const gchar * +gtk_buildable_get_name (GtkBuildable *buildable) +{ + GtkBuildableIface *iface; + + g_return_val_if_fail (GTK_IS_BUILDABLE (buildable), NULL); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + + if (iface->get_name) + return (* iface->get_name) (buildable); + else + return (const gchar*)g_object_get_data (G_OBJECT (buildable), + "gtk-builder-name"); +} + +/** + * gtk_buildable_add: + * @buildable: a #GtkBuildable + * @builder: a #GtkBuilder + * @child: child to add + * @type: kind of child or %NULL + * + * Add a child to a buildable. type is an optional string + * describing how the child should be added. + * + * #GtkContainer implements this to be able to add a child widget + * to the container. #GtkNotebook uses the @type to distinguish between + * page labels (@type = "page-label") and normal children. + * + * Since: 2.12 + **/ +void +gtk_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + GtkBuildableIface *iface; + + g_return_if_fail (GTK_IS_BUILDABLE (buildable)); + g_return_if_fail (GTK_IS_BUILDER (builder)); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + g_return_if_fail (iface->add != NULL); + + (* iface->add) (buildable, builder, child, type); +} + +/** + * gtk_buildable_set_property: + * @buildable: a #GtkBuildable + * @builder: a #GtkBuilder + * @name: name of property + * @value: value of property + * + * Sets the property name @name to @value on the buildable object @buildable + * which is created by the @builder. + * + * This is optional to implement and is normally not needed. + * g_object_set_property() is used as a fallback. + * + * #GtkWindow implements this to delay showing (::visible) itself until + * the whole interface is fully created. + * + * Since: 2.12 + **/ +void +gtk_buildable_set_property (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name, + const GValue *value) +{ + GtkBuildableIface *iface; + + g_return_if_fail (GTK_IS_BUILDABLE (buildable)); + g_return_if_fail (GTK_IS_BUILDER (builder)); + g_return_if_fail (name != NULL); + g_return_if_fail (value != NULL); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + if (iface->set_property) + (* iface->set_property) (buildable, builder, name, value); + else + g_object_set_property (G_OBJECT (buildable), name, value); +} + +/** + * gtk_buildable_parser_finished: + * @buildable: a #GtkBuildable + * @builder: a #GtkBuilder + * + * Finish the parsing of a GtkBuilder UI definition + * snippet. Note that this will be called once for each time gtk_builder_add_from_file or + * gtk_builder_add_from_string is called on a builder. + * + * #GtkWindow implements this to delay showing (::visible) itself until + * the whole interface is fully created. + * + * Since: 2.12 + **/ +void +gtk_buildable_parser_finished (GtkBuildable *buildable, + GtkBuilder *builder) +{ + GtkBuildableIface *iface; + + g_return_if_fail (GTK_IS_BUILDABLE (buildable)); + g_return_if_fail (GTK_IS_BUILDER (builder)); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + if (iface->parser_finished) + (* iface->parser_finished) (buildable, builder); +} + +/** + * gtk_buildable_construct_child + * @buildable: A #GtkBuildable + * @builder: #GtkBuilder used to construct this object + * @name: name of child to construct + * + * Construct a child of @buildable with the name @name. + * + * #GtkUIManager implements this to reference to a widget created in a <ui> tag + * which is outside of the normal GtkBuilder UI definition + * object hierarchy. + * + * Since: 2.12 + **/ +GObject * +gtk_buildable_construct_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name) +{ + GtkBuildableIface *iface; + + g_return_val_if_fail (GTK_IS_BUILDABLE (buildable), NULL); + g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); + g_return_val_if_fail (name != NULL, NULL); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + g_return_val_if_fail (iface->construct_child != NULL, NULL); + + return (* iface->construct_child) (buildable, builder, name); +} + +/** + * gtk_buildable_custom_tag_start + * @buildable: a #GtkBuildable + * @builder: a #GtkBuilder used to construct this object + * @child: child object or %NULL for non-child tags + * @tagname: name of tag + * @parser: a #GMarkupParser structure + * @data: user data that will be passed in to parser functions + * + * This is called when an unknown tag under <child> tag is found. + * + * Called when an unknown tag is present under a <child> tag. + * If the buildable implementation wishes to handle the tag it should + * return %TRUE and fill in the @parser structure. Remember to either + * implement custom_tag_end or custom_tag_finish to free + * the user data allocated here. + * + * #GtkWidget implements this and parsers all <accelerator> tags to + * keyboard accelerators. + * #GtkContainer implements this to map properties defined under + * <packing> tag to child properties. + * + * Returns: %TRUE if a object has a custom implementation, %FALSE + * if it doesn't. + * + * Since: 2.12 + **/ +gboolean +gtk_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + GtkBuildableIface *iface; + + g_return_val_if_fail (GTK_IS_BUILDABLE (buildable), FALSE); + g_return_val_if_fail (GTK_IS_BUILDER (builder), FALSE); + g_return_val_if_fail (tagname != NULL, FALSE); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + g_return_val_if_fail (iface->custom_tag_start != NULL, FALSE); + + return (* iface->custom_tag_start) (buildable, builder, child, + tagname, parser, data); +} + +/** + * gtk_buildable_custom_tag_end + * @buildable: A #GtkBuildable + * @builder: #GtkBuilder used to construct this object + * @child: child object or %NULL for non-child tags + * @tagname: name of tag + * @data: user data that will be passed in to parser functions + * + * This is called for each custom tag handled by the buildable. + * It will be called when the end of the tag is reached. + * + * Since: 2.12 + **/ +void +gtk_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + GtkBuildableIface *iface; + + g_return_if_fail (GTK_IS_BUILDABLE (buildable)); + g_return_if_fail (GTK_IS_BUILDER (builder)); + g_return_if_fail (tagname != NULL); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + if (iface->custom_tag_end) + (* iface->custom_tag_end) (buildable, builder, child, tagname, data); +} + +/** + * gtk_buildable_custom_finished: + * @buildable: a #GtkBuildable + * @builder: a #GtkBuilder + * @child: child object or %NULL for non-child tags + * @tagname: the name of the tag + * @data: user data created in custom_tag_start + * + * This is similar to gtk_buildable_parser_finished() but is + * called once for each custom tag handled by the @buildable. + * + * Since: 2.12 + **/ +void +gtk_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data) +{ + GtkBuildableIface *iface; + + g_return_if_fail (GTK_IS_BUILDABLE (buildable)); + g_return_if_fail (GTK_IS_BUILDER (builder)); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + if (iface->custom_finished) + (* iface->custom_finished) (buildable, builder, child, tagname, data); +} + +/** + * gtk_buildable_get_internal_child + * @buildable: a #GtkBuildable + * @builder: a #GtkBuilder + * @childname: name of child + * + * Get the internal child called @child of the @buildable object. + * + * Return: the internal child of the buildable object + * + * Since: 2.12 + **/ +GObject * +gtk_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname) +{ + GtkBuildableIface *iface; + + g_return_val_if_fail (GTK_IS_BUILDABLE (buildable), NULL); + g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); + g_return_val_if_fail (childname != NULL, NULL); + + iface = GTK_BUILDABLE_GET_IFACE (buildable); + if (!iface->get_internal_child) + return NULL; + + return (* iface->get_internal_child) (buildable, builder, childname); +} + +#define __GTK_BUILDABLE_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkbuildable.h b/gtk/gtkbuildable.h new file mode 100644 index 0000000000..397e4e8745 --- /dev/null +++ b/gtk/gtkbuildable.h @@ -0,0 +1,123 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2006-2007 Async Open Source, + * Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_BUILDABLE_H__ +#define __GTK_BUILDABLE_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_BUILDABLE (gtk_buildable_get_type ()) +#define GTK_BUILDABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_BUILDABLE, GtkBuildable)) +#define GTK_BUILDABLE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GTK_TYPE_BUILDABLE, GtkBuildableIface)) +#define GTK_IS_BUILDABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_BUILDABLE)) +#define GTK_BUILDABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_BUILDABLE, GtkBuildableIface)) + +typedef struct _GtkBuildable GtkBuildable; /* Dummy typedef */ +typedef struct _GtkBuildableIface GtkBuildableIface; + +struct _GtkBuildableIface +{ + GTypeInterface g_iface; + + /* virtual table */ + void (* set_name) (GtkBuildable *buildable, + const gchar *name); + const gchar * (* get_name) (GtkBuildable *buildable); + void (* add) (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); + void (* set_property) (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name, + const GValue *value); + GObject * (* construct_child) (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name); + gboolean (* custom_tag_start) (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); + void (* custom_tag_end) (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + void (* custom_finished) (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data); + void (* parser_finished) (GtkBuildable *buildable, + GtkBuilder *builder); + + GObject * (* get_internal_child) (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname); +}; + + +GType gtk_buildable_get_type (void) G_GNUC_CONST; + +void gtk_buildable_set_name (GtkBuildable *buildable, + const gchar *name); +const gchar * gtk_buildable_get_name (GtkBuildable *buildable); +void gtk_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); +void gtk_buildable_set_property (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name, + const GValue *value); +GObject * gtk_buildable_construct_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name); +gboolean gtk_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +void gtk_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); +void gtk_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data); +void gtk_buildable_parser_finished (GtkBuildable *buildable, + GtkBuilder *builder); +GObject * gtk_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname); + +G_END_DECLS + +#endif /* __GTK_BUILDABLE_H__ */ diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c new file mode 100644 index 0000000000..a4017c3ce4 --- /dev/null +++ b/gtk/gtkbuilder.c @@ -0,0 +1,1267 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1998-2002 James Henstridge + * Copyright (C) 2006-2007 Async Open Source, + * Johan Dahlin , + * Henrique Romano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include /* tolower, toupper */ +#include /* errno */ +#include /* strtol, strtoul */ +#include /* strlen */ + +#include "gtkbuilder.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" +#include "gtkmain.h" +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtktypebuiltins.h" +#include "gtkalias.h" + +static void gtk_builder_class_init (GtkBuilderClass *klass); +static void gtk_builder_init (GtkBuilder *builder); +static void gtk_builder_finalize (GObject *object); +static void gtk_builder_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_builder_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static GType gtk_builder_real_get_type_from_name (GtkBuilder *builder, + const char *typename); +static gint _gtk_builder_enum_from_string (GType type, const char *string); + + +enum { + PROP_0, + PROP_TRANSLATION_DOMAIN, +}; + +struct _GtkBuilderPrivate +{ + gchar *domain; + GHashTable *objects; + GHashTable *delayed_properties; + GSList *signals; + gchar *current_toplevel; +}; + +G_DEFINE_TYPE (GtkBuilder, gtk_builder, G_TYPE_OBJECT) + +static void +gtk_builder_class_init (GtkBuilderClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gtk_builder_finalize; + gobject_class->set_property = gtk_builder_set_property; + gobject_class->get_property = gtk_builder_get_property; + + klass->get_type_from_name = gtk_builder_real_get_type_from_name; + + g_object_class_install_property (gobject_class, + PROP_TRANSLATION_DOMAIN, + g_param_spec_string ("translation-domain", + P_("Translation Domain"), + P_("The translation domain used by gettext"), + NULL, + GTK_PARAM_READWRITE)); + + g_type_class_add_private (gobject_class, sizeof (GtkBuilderPrivate)); +} + +static void +gtk_builder_init (GtkBuilder *builder) +{ + builder->priv = G_TYPE_INSTANCE_GET_PRIVATE (builder, GTK_TYPE_BUILDER, + GtkBuilderPrivate); + builder->priv->domain = NULL; + builder->priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + builder->priv->delayed_properties = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, NULL); +} + + +/* + * GObject virtual methods + */ + +static void +gtk_builder_finalize (GObject *object) +{ + GtkBuilder *builder = GTK_BUILDER (object); + + g_free (builder->priv->domain); + + g_free (builder->priv->current_toplevel); + g_hash_table_destroy (builder->priv->delayed_properties); + builder->priv->delayed_properties = NULL; + g_slist_foreach (builder->priv->signals, (GFunc)_free_signal_info, NULL); + g_slist_free (builder->priv->signals); + g_hash_table_destroy (builder->priv->objects); +} + +static void +gtk_builder_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkBuilder *builder = GTK_BUILDER (object); + + switch (prop_id) + { + case PROP_TRANSLATION_DOMAIN: + gtk_builder_set_translation_domain (builder, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_builder_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkBuilder *builder = GTK_BUILDER (object); + + switch (prop_id) + { + case PROP_TRANSLATION_DOMAIN: + g_value_set_string (value, builder->priv->domain); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +/* + * Try to map a type name to a _get_type function + * and call it, eg: + * + * GtkWindow -> gtk_window_get_type + * GtkHBox -> gtk_hbox_get_type + * GtkUIManager -> gtk_ui_manager_get_type + * + */ +static GType +_gtk_builder_resolve_type_lazily (const gchar *name) +{ + static GModule *module = NULL; + GTypeGetFunc func; + GString *symbol_name = g_string_new (""); + char c, *symbol; + int i; + GType gtype = G_TYPE_INVALID; + + if (!module) + module = g_module_open (NULL, 0); + + for (i = 0; name[i] != '\0'; i++) + { + c = name[i]; + /* skip if uppercase, first or previous is uppercase */ + if ((c == toupper (c) && + i > 0 && name[i-1] != toupper (name[i-1])) || + (i > 2 && name[i] == toupper (name[i]) && + name[i-1] == toupper (name[i-1]) && + name[i-2] == toupper (name[i-2]))) + g_string_append_c (symbol_name, '_'); + g_string_append_c (symbol_name, tolower (c)); + } + g_string_append (symbol_name, "_get_type"); + + symbol = g_string_free (symbol_name, FALSE); + + if (g_module_symbol (module, symbol, (gpointer)&func)) + gtype = func (); + + g_free (symbol); + + return gtype; +} + +/* + * GtkBuilder virtual methods + */ + +static GType +gtk_builder_real_get_type_from_name (GtkBuilder *builder, const char *typename) +{ + GType gtype; + + gtype = g_type_from_name (typename); + if (gtype != G_TYPE_INVALID) + return gtype; + + return _gtk_builder_resolve_type_lazily (typename); +} + +typedef struct +{ + gchar *object; + gchar *name; + gchar *value; +} DelayedProperty; + +static void +gtk_builder_get_parameters (GtkBuilder *builder, + GType object_type, + const gchar *object_name, + GSList *properties, + GArray **parameters, + GArray **construct_parameters) +{ + GSList *l; + GParamSpec *pspec; + GObjectClass *oclass; + DelayedProperty *property; + + oclass = g_type_class_ref (object_type); + g_assert (oclass != NULL); + + *parameters = g_array_new (FALSE, FALSE, sizeof (GParameter)); + *construct_parameters = g_array_new (FALSE, FALSE, sizeof (GParameter)); + + for (l = properties; l; l = l->next) + { + PropertyInfo *prop = (PropertyInfo*)l->data; + GParameter parameter = { NULL }; + + pspec = g_object_class_find_property (G_OBJECT_CLASS (oclass), + prop->name); + if (!pspec) + { + g_warning ("Unknown property: %s.%s\n", + g_type_name (object_type), prop->name); + continue; + } + + parameter.name = prop->name; + + if (G_IS_PARAM_SPEC_OBJECT (pspec)) + { + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) + { + GObject *object; + object = gtk_builder_get_object (builder, prop->data); + if (!object) + { + g_warning ("failed to get constuct only property %s of %s " + "with value `%s'", + prop->name, object_name, prop->data); + continue; + } + g_value_init (¶meter.value, G_OBJECT_TYPE (object)); + g_value_set_object (¶meter.value, g_object_ref (object)); + } + else + { + GSList *delayed_properties; + + delayed_properties = g_hash_table_lookup (builder->priv->delayed_properties, + builder->priv->current_toplevel); + property = g_slice_new (DelayedProperty); + property->object = g_strdup (object_name); + property->name = g_strdup (prop->name); + property->value = g_strdup (prop->data); + delayed_properties = g_slist_prepend (delayed_properties, property); + g_hash_table_insert (builder->priv->delayed_properties, + g_strdup (builder->priv->current_toplevel), + delayed_properties); + continue; + } + } + else if (!gtk_builder_value_from_string (pspec, prop->data, ¶meter.value)) + { + g_warning ("failed to set property %s.%s to %s", + g_type_name (object_type), prop->name, prop->data); + continue; + } + + if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) + g_array_append_val (*construct_parameters, parameter); + else + g_array_append_val (*parameters, parameter); + } + + g_type_class_unref (oclass); +} + +static GObject * +gtk_builder_get_internal_child (GtkBuilder *builder, + ObjectInfo *info, + const gchar *childname) +{ + GObject *obj = NULL; + + while (!obj) + { + if (!info->parent) + break; + + info = (ObjectInfo*)((ChildInfo*)info->parent)->parent; + if (!info) + break; + + GTK_NOTE (BUILDER, + g_print ("Trying to get internal child %s from %s\n", + childname, + gtk_buildable_get_name (GTK_BUILDABLE (info->object)))); + + if (GTK_IS_BUILDABLE (info->object)) + obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object), + builder, + childname); + }; + + if (!obj) + g_error ("Unknown internal child: %s\n", childname); + + return obj; +} + +GObject * +_gtk_builder_construct (GtkBuilder *builder, + ObjectInfo *info) +{ + GArray *parameters, *construct_parameters; + GType object_type; + GObject *obj; + int i; + GtkBuildableIface *iface; + gboolean custom_set_property; + GtkBuildable *buildable; + + g_assert (info->class_name != NULL); + object_type = gtk_builder_get_type_from_name (builder, info->class_name); + if (object_type == G_TYPE_INVALID) + g_error ("Invalid type: %s", info->class_name); + + gtk_builder_get_parameters (builder, object_type, + info->id, + info->properties, + ¶meters, + &construct_parameters); + + if (info->constructor) + { + GObject *constructor; + + constructor = gtk_builder_get_object (builder, info->constructor); + if (constructor == NULL) + g_error ("Unknown constructor for %s: %s\n", info->id, + info->constructor); + + obj = gtk_buildable_construct_child (GTK_BUILDABLE (constructor), + builder, + info->id); + g_assert (obj != NULL); + if (construct_parameters->len) + g_warning ("Can't pass in construct-only parameters to %s", info->id); + + } + else if (info->parent && ((ChildInfo*)info->parent)->internal_child != NULL) + { + gchar *childname = ((ChildInfo*)info->parent)->internal_child; + obj = gtk_builder_get_internal_child (builder, info, childname); + if (construct_parameters->len) + g_warning ("Can't pass in construct-only parameters to %s", childname); + } + else + { + obj = g_object_newv (object_type, + construct_parameters->len, + (GParameter *)construct_parameters->data); + + GTK_NOTE (BUILDER, + g_print ("created %s of type %s\n", info->id, info->class_name)); + + for (i = 0; i < construct_parameters->len; i++) + { + GParameter *param = &g_array_index (construct_parameters, + GParameter, i); + g_value_unset (¶m->value); + } + } + g_array_free (construct_parameters, TRUE); + + custom_set_property = FALSE; + buildable = NULL; + iface = NULL; + if (GTK_IS_BUILDABLE (obj)) + { + buildable = GTK_BUILDABLE (obj); + iface = GTK_BUILDABLE_GET_IFACE (obj); + if (iface->set_property) + custom_set_property = TRUE; + } + + for (i = 0; i < parameters->len; i++) + { + GParameter *param = &g_array_index (parameters, GParameter, i); + if (custom_set_property) + iface->set_property (buildable, builder, param->name, ¶m->value); + else + g_object_set_property (obj, param->name, ¶m->value); + +#if G_ENABLE_DEBUG + if (gtk_debug_flags & GTK_DEBUG_BUILDER) + { + gchar *str = g_strdup_value_contents ((const GValue*)¶m->value); + g_print ("set %s: %s = %s\n", info->id, param->name, str); + g_free (str); + } +#endif + g_value_unset (¶m->value); + } + g_array_free (parameters, TRUE); + + if (GTK_IS_BUILDABLE (obj)) + gtk_buildable_set_name (buildable, info->id); + else + g_object_set_data_full (obj, + "gtk-builder-name", + g_strdup (info->id), + g_free); + + if (!info->parent) + { + g_free (builder->priv->current_toplevel); + builder->priv->current_toplevel = g_strdup (info->id); + } + g_hash_table_insert (builder->priv->objects, g_strdup (info->id), obj); + + builder->priv->signals = g_slist_concat (builder->priv->signals, + g_slist_copy (info->signals)); + return obj; +} + + +void +_gtk_builder_add (GtkBuilder *builder, + ChildInfo *child_info) +{ + GObject *object; + GObject *parent; + + /* Internal children are already added + * Also prevent us from being called twice. + */ + if (!child_info || + child_info->internal_child || + child_info->added) + return; + + object = child_info->object; + if (!object) + return; + + if (!child_info->parent) + { + g_warning ("%s: Not adding, No parent\n", + gtk_buildable_get_name (GTK_BUILDABLE (object))); + return; + } + + g_assert (object != NULL); + + parent = ((ObjectInfo*)child_info->parent)->object; + g_assert (GTK_IS_BUILDABLE (parent)); + + GTK_NOTE (BUILDER, + g_print ("adding %s to %s\n", + gtk_buildable_get_name (GTK_BUILDABLE (object)), + gtk_buildable_get_name (GTK_BUILDABLE (parent)))); + + gtk_buildable_add (GTK_BUILDABLE (parent), builder, object, + child_info->type); + + child_info->added = TRUE; +} + +static void +apply_delayed_properties (const gchar *window_name, + GSList *props, + GtkBuilder *builder) +{ + GSList *l; + DelayedProperty *property; + GObject *object; + GType object_type; + GObjectClass *oclass; + GParamSpec *pspec; + + g_assert (props != NULL); + props = g_slist_reverse (props); + for (l = props; l; l = l->next) + { + property = (DelayedProperty*)l->data; + object = g_hash_table_lookup (builder->priv->objects, property->object); + g_assert (object != NULL); + + object_type = G_OBJECT_TYPE (object); + g_assert (object_type != G_TYPE_INVALID); + + oclass = g_type_class_ref (object_type); + g_assert (oclass != NULL); + + pspec = g_object_class_find_property (G_OBJECT_CLASS (oclass), + property->name); + if (!pspec) + g_warning ("Unknown property: %s.%s\n", g_type_name (object_type), + property->name); + else + { + GObject *obj; + + obj = g_hash_table_lookup (builder->priv->objects, property->value); + if (!obj) + g_warning ("No object called: %s\n", property->object); + else + g_object_set (object, property->name, obj, NULL); + } + g_free (property->value); + g_free (property->object); + g_free (property->name); + g_slice_free (DelayedProperty, property); + g_type_class_unref (oclass); + } + g_slist_free (props); +} + +void +_gtk_builder_finish (GtkBuilder *builder) +{ + if (builder->priv->delayed_properties) + g_hash_table_foreach (builder->priv->delayed_properties, + (GHFunc)apply_delayed_properties, builder); +} + +/** + * gtk_builder_new: + * + * Creates a new builder object. + * + * Return value: a new builder object. + * + * Since: 2.12 + **/ +GtkBuilder * +gtk_builder_new (void) +{ + return g_object_new (GTK_TYPE_BUILDER, NULL); +} + +/** + * gtk_builder_add_from_file: + * @builder: a #GtkBuilder + * @filename: the name of the file to parse + * @error: return location for an error + * + * Parses a string containing a GtkBuilder UI definition and + * merges it with the current contents of @builder. + * + * Returns: A positive value on success, 0 if an error occurred + * + * Since: 2.12 + **/ +guint +gtk_builder_add_from_file (GtkBuilder *builder, + const gchar *filename, + GError **error) +{ + char *buffer; + unsigned length; + GError *tmp_error; + + g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); + g_return_val_if_fail (filename != NULL, 0); + + tmp_error = NULL; + + if (!g_file_get_contents (filename, &buffer, &length, &tmp_error)) + { + g_propagate_error (error, tmp_error); + return 0; + } + + _gtk_builder_parser_parse_buffer (builder, filename, + buffer, length, + &tmp_error); + + if (tmp_error != NULL) + { + g_propagate_error (error, tmp_error); + return 0; + } + + g_free (buffer); + + return 1; +} + +/** + * gtk_builder_add_from_string: + * @builder: a #GtkBuilder + * @buffer: the string to parse + * @length: the length of @buffer (may be -1 if @buffer is nul-terminated) + * @error: return location for an error + * + * Parses a file containing a GtkBuilder UI definition and + * merges it with the current contents of @builder. + * + * Returns: A positive value on success, 0 if an error occurred + * + * Since: 2.12 + **/ +guint +gtk_builder_add_from_string (GtkBuilder *builder, + const gchar *buffer, + gsize length, + GError **error) +{ + GError *tmp_error; + + g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); + g_return_val_if_fail (buffer != NULL, 0); + + tmp_error = NULL; + + _gtk_builder_parser_parse_buffer (builder, "", + buffer, length, + &tmp_error); + if (tmp_error != NULL) + { + g_propagate_error (error, tmp_error); + return 0; + } + + return 1; +} + +/** + * gtk_builder_get_object: + * @builder: a #GtkBuilder + * @name: name of object to get + * + * Return value: GObject or %NULL if it could not be found in the object tree. + * + * Since: 2.12 + **/ +GObject * +gtk_builder_get_object (GtkBuilder *builder, + const gchar *name) +{ + g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return g_hash_table_lookup (builder->priv->objects, name); +} + +static void +object_add_to_list (gchar *object_id, + GObject *object, + GSList **list) +{ + *list = g_slist_prepend (*list, object); +} + +/** + * gtk_builder_get_objects: + * @builder: a #GtkBuilder + * + * Return value: a newly-allocated #GSList containing all the objects + * constructed by GtkBuilder instance. + * + * Since: 2.12 + **/ +GSList * +gtk_builder_get_objects (GtkBuilder *builder) +{ + GSList *objects = NULL; + + g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); + + g_hash_table_foreach (builder->priv->objects, (GHFunc)object_add_to_list, &objects); + + return g_slist_reverse (objects); +} + +/** + * gtk_builder_set_translation_domain: + * @builder: a #GtkBuilder + * @domain: the translation domain or %NULL + * + * Sets the translation domain and uses dgettext() for translating the + * property values marked as translatable from an interface description. + * You can also pass in %NULL to this method to use gettext() instead of + * dgettext(). + * + * Since: 2.12 + **/ +void +gtk_builder_set_translation_domain (GtkBuilder *builder, + const gchar *domain) +{ + gchar *new_domain; + + g_return_if_fail (GTK_IS_BUILDER (builder)); + + new_domain = g_strdup (domain); + g_free (builder->priv->domain); + builder->priv->domain = new_domain; + + g_object_notify (G_OBJECT (builder), "translation-domain"); +} + +/** + * gtk_builder_get_translation_domain: + * @builder: a #GtkBuilder + * + * Return value : the translation domain. This string is owned + * by the builder object and must not be modified or freed. + * + * Since: 2.12 + **/ +const gchar * +gtk_builder_get_translation_domain (GtkBuilder *builder) +{ + g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); + + return builder->priv->domain; +} + +typedef struct { + GModule *module; + gpointer data; +} connect_args; + +static void +gtk_builder_connect_signals_default (GtkBuilder *builder, + GObject *object, + const gchar *signal_name, + const gchar *handler_name, + GObject *connect_object, + GConnectFlags flags, + gpointer user_data) +{ + GCallback func; + connect_args *args = (connect_args*)user_data; + + if (!g_module_symbol (args->module, handler_name, (gpointer)&func)) + { + g_warning ("could not find signal handler '%s'", handler_name); + return; + } + + if (connect_object) + g_signal_connect_object (object, signal_name, func, connect_object, flags); + else + g_signal_connect_data (object, signal_name, func, args->data, NULL, flags); +} + + +/** + * gtk_builder_connect_signals: + * @builder: a #GtkBuilder + * @user_data: a pointer to a structure sent in as user data to all signals + * + * This method is a simpler variation of gtk_builder_connect_signals_full(). + * It uses #GModule's introspective features (by opening the module %NULL) to + * look at the application's symbol table. From here it tries to match + * the signal handler names given in the interface description with + * symbols in the application and connects the signals. + * + * Note that this function will not work correctly if #GModule is not + * supported on the platform. + * + * Since: 2.12 + **/ +void +gtk_builder_connect_signals (GtkBuilder *builder, + gpointer user_data) +{ + connect_args *args; + + g_return_if_fail (GTK_IS_BUILDER (builder)); + + if (!g_module_supported ()) + g_error ("gtk_builder_connect_signals requires working GModule"); + + args = g_slice_new0 (connect_args); + args->module = g_module_open (NULL, G_MODULE_BIND_LAZY); + args->data = user_data; + + gtk_builder_connect_signals_full (builder, + gtk_builder_connect_signals_default, + args); + g_module_close (args->module); + + g_slice_free (connect_args, args); +} + +/** + * GtkBuilderConnectFunc: + * @builder: a #GtkBuilder + * @object: a GObject subclass to connect a signal to + * @signal_name: name of the signal + * @handler_name: name of the handler + * @connect_object: GObject, if non-%NULL, use g_signal_connect_object. + * @flags: #GConnectFlags to use + * @user_data: user data + * + * This is the signature of a function used to connect signals. It is used + * by the gtk_builder_connect_signals() and gtk_builder_connect_signals_full() + * methods. It is mainly intended for interpreted language bindings, but + * could be useful where the programmer wants more control over the signal + * connection process. + * + * Since: 2.12 + */ + +/** + * gtk_builder_connect_signals_full: + * @builder: a #GtkBuilder + * @func: the function used to connect the signals. + * @user_data: arbitrary data that will be passed to the connection function. + * + * This function can be thought of the interpreted language binding + * version of gtk_builder_signal_autoconnect(), except that it does not + * require gmodule to function correctly. + * + * Since: 2.12 + */ +void +gtk_builder_connect_signals_full (GtkBuilder *builder, + GtkBuilderConnectFunc func, + gpointer user_data) +{ + GSList *l; + GObject *object; + GObject *connect_object; + + g_return_if_fail (GTK_IS_BUILDER (builder)); + g_return_if_fail (func != NULL); + + if (!builder->priv->signals) + return; + + builder->priv->signals = g_slist_reverse (builder->priv->signals); + for (l = builder->priv->signals; l; l = l->next) + { + SignalInfo *signal = (SignalInfo*)l->data; + + g_assert (signal != NULL); + g_assert (signal->name != NULL); + + object = g_hash_table_lookup (builder->priv->objects, + signal->object_name); + g_assert (object != NULL); + + connect_object = NULL; + + if (signal->connect_object_name) + { + connect_object = g_hash_table_lookup (builder->priv->objects, + signal->connect_object_name); + if (!connect_object) + g_warning ("could not lookup object %s on signal %s of object %s", + signal->connect_object_name, signal->name, + signal->object_name); + } + + func (builder, object, signal->name, signal->handler, + connect_object, signal->flags, user_data); + } + + g_slist_foreach (builder->priv->signals, (GFunc)_free_signal_info, NULL); + g_slist_free (builder->priv->signals); + builder->priv->signals = NULL; +} + +/** + * gtk_builder_value_from_string + * @pspec: the GParamSpec for the property + * @string: the string representation of the value. + * @value: the GValue to store the result in. + * + * This function demarshals a value from a string. This function + * calls g_value_init() on the @value argument, so it need not be + * initialised beforehand. + * + * This function can handle char, uchar, boolean, int, uint, long, + * ulong, enum, flags, float, double, string, GdkColor and + * GtkAdjustment type values. Support for GtkWidget type values is + * still to come. + * + * Returns: %TRUE on success. + * + * Since: 2.12 + */ +gboolean +gtk_builder_value_from_string (GParamSpec *pspec, + const gchar *string, + GValue *value) +{ + /* + * GParamSpecUnichar has the internal type G_TYPE_UINT, + * so we cannot handle this in the switch, do it separately + */ + if (G_IS_PARAM_SPEC_UNICHAR (pspec)) + { + gunichar c; + g_value_init (value, G_TYPE_UINT); + c = g_utf8_get_char_validated (string, strlen (string)); + if (c > 0) + g_value_set_uint (value, c); + return TRUE; + } + + return gtk_builder_value_from_string_type (G_PARAM_SPEC_VALUE_TYPE (pspec), + string, value); +} + +/** + * gtk_builder_value_from_string_type + * @type: the GType of the value + * @string: the string representation of the value. + * @value: the GValue to store the result in. + * + * Like gtk_builder_value_from_string(), but takes a #GType instead of #GParamSpec. + * + * Returns: %TRUE on success. + * + * Since: 2.12 + */ +gboolean +gtk_builder_value_from_string_type (GType type, + const gchar *string, + GValue *value) +{ + gboolean ret = TRUE; + + g_return_val_if_fail (type != G_TYPE_INVALID, FALSE); + g_return_val_if_fail (string != NULL, FALSE); + + g_value_init (value, type); + + switch (G_TYPE_FUNDAMENTAL (type)) + { + case G_TYPE_CHAR: + g_value_set_char (value, string[0]); + break; + case G_TYPE_UCHAR: + g_value_set_uchar (value, (guchar)string[0]); + break; + case G_TYPE_BOOLEAN: + { + gboolean b; + + if (g_ascii_tolower (string[0]) == 't') + b = TRUE; + else if (g_ascii_tolower (string[0]) == 'y') + b = FALSE; + else { + errno = 0; + b = strtol (string, NULL, 0); + if (errno) { + g_warning ("could not parse int `%s'", string); + break; + } + } + g_value_set_boolean (value, b); + break; + } + case G_TYPE_INT: + case G_TYPE_LONG: + { + long l; + errno = 0; + l = strtol (string, NULL, 0); + if (errno) { + g_warning ("could not parse long `%s'", string); + break; + } + if (G_VALUE_HOLDS_INT (value)) + g_value_set_int (value, l); + else + g_value_set_long (value, l); + break; + } + case G_TYPE_UINT: + case G_TYPE_ULONG: + { + ulong ul; + errno = 0; + ul = strtoul (string, NULL, 0); + if (errno) + { + g_warning ("could not parse ulong `%s'", string); + break; + } + if (G_VALUE_HOLDS_UINT (value)) + g_value_set_uint (value, strtoul (string, NULL, 0)); + else + g_value_set_ulong (value, strtoul (string, NULL, 0)); + break; + } + case G_TYPE_ENUM: + g_value_set_enum (value, _gtk_builder_enum_from_string (type, string)); + break; + case G_TYPE_FLAGS: + g_value_set_flags (value, _gtk_builder_flags_from_string (type, string)); + break; + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + { + double d; + errno = 0; + d = g_ascii_strtod (string, NULL); + if (errno) + { + g_warning ("could not parse double `%s'", string); + break; + } + if (G_VALUE_HOLDS_FLOAT (value)) + g_value_set_float (value, d); + else + g_value_set_double (value, d); + break; + } + case G_TYPE_STRING: + g_value_set_string (value, string); + break; + case G_TYPE_BOXED: + if (G_VALUE_HOLDS (value, GDK_TYPE_COLOR)) + { + GdkColor colour = { 0, }; + + if (gdk_color_parse (string, &colour) && + gdk_colormap_alloc_color (gtk_widget_get_default_colormap (), + &colour, FALSE, TRUE)) + g_value_set_boxed (value, &colour); + else + { + g_warning ("could not parse colour name `%s'", string); + ret = FALSE; + } + } + else if (G_VALUE_HOLDS (value, G_TYPE_STRV)) + { + char **vector = g_strsplit (string, "\n", 0); + g_value_take_boxed (value, vector); + } + else + ret = FALSE; + break; + case G_TYPE_OBJECT: +#if 0 + if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF)) + { + gchar *filename; + GError *error = NULL; + GdkPixbuf *pixbuf; + + filename = gtk_xml_relative_file (xml, string); + pixbuf = gdk_pixbuf_new_from_file (filename, &error); + if (pixbuf) + { + g_value_set_object (value, pixbuf); + g_object_unref (G_OBJECT (pixbuf)); + } + else + { + g_warning ("Error loading image: %s", error->message); + g_error_free (error); + ret = FALSE; + } + g_free (filename); + } + else +#endif + ret = FALSE; + break; + default: + ret = FALSE; + break; + } + + return ret; +} + +static gint +_gtk_builder_enum_from_string (GType type, const char *string) +{ + GEnumClass *eclass; + GEnumValue *ev; + gchar *endptr; + gint ret = 0; + + g_return_val_if_fail (G_TYPE_IS_ENUM (type), 0); + g_return_val_if_fail (string != NULL, 0); + + ret = strtoul (string, &endptr, 0); + if (endptr != string) /* parsed a number */ + return ret; + + eclass = g_type_class_ref (type); + ev = g_enum_get_value_by_name (eclass, string); + if (!ev) + ev = g_enum_get_value_by_nick (eclass, string); + + if (ev) + ret = ev->value; + + g_type_class_unref (eclass); + + return ret; +} + +guint +_gtk_builder_flags_from_string (GType type, const char *string) +{ + GFlagsClass *fclass; + gchar *endptr, *prevptr; + guint i, j, ret; + char *flagstr; + GFlagsValue *fv; + const char *flag; + gunichar ch; + gboolean eos; + + g_return_val_if_fail (G_TYPE_IS_FLAGS (type), 0); + g_return_val_if_fail (string != 0, 0); + + ret = strtoul (string, &endptr, 0); + if (endptr != string) /* parsed a number */ + return ret; + + fclass = g_type_class_ref (type); + + flagstr = g_strdup (string); + for (ret = i = j = 0; ; i++) + { + + eos = flagstr[i] == '\0'; + + if (!eos && flagstr[i] != '|') + continue; + + flag = &flagstr[j]; + endptr = &flagstr[i]; + + if (!eos) + { + flagstr[i++] = '\0'; + j = i; + } + + /* trim spaces */ + for (;;) + { + ch = g_utf8_get_char (flag); + if (!g_unichar_isspace (ch)) + break; + flag = g_utf8_next_char (flag); + } + + while (endptr > flag) + { + prevptr = g_utf8_prev_char (endptr); + ch = g_utf8_get_char (prevptr); + if (!g_unichar_isspace (ch)) + break; + endptr = prevptr; + } + + if (endptr > flag) + { + *endptr = '\0'; + fv = g_flags_get_value_by_name (fclass, flag); + + if (!fv) + fv = g_flags_get_value_by_nick (fclass, flag); + + if (fv) + ret |= fv->value; + else + g_warning ("Unknown flag: '%s'", flag); + } + + if (eos) + break; + } + + g_free (flagstr); + + g_type_class_unref (fclass); + + return ret; +} + +/** + * gtk_builder_get_type_from_name: + * @builder: a #GtkBuilder + * @typename: Type name to lookup. + * + * This method is used to lookup a type. It can be implemented in a subclass to + * override the #GType of an object created by the builder. + * + * Since 2.12 + */ +GType +gtk_builder_get_type_from_name (GtkBuilder *builder, const gchar *typename) +{ + g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID); + g_return_val_if_fail (typename != NULL, G_TYPE_INVALID); + + return GTK_BUILDER_GET_CLASS (builder)->get_type_from_name (builder, typename); +} + +/** + * gtk_builder_error_quark: + * + * Registers an error quark for #GtkBuilder if necessary. + * + * Return value: The error quark used for #GtkBuilder errors. + * + * Since: 2.12 + **/ +GQuark +gtk_builder_error_quark (void) +{ + return g_quark_from_static_string ("gtk-builder-error-quark"); +} + + +#define __GTK_BUILDER_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkbuilder.h b/gtk/gtkbuilder.h new file mode 100644 index 0000000000..81ea9a7250 --- /dev/null +++ b/gtk/gtkbuilder.h @@ -0,0 +1,122 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2006-2007 Async Open Source, + * Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GTK_BUILDER_H__ +#define __GTK_BUILDER_H__ + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_BUILDER (gtk_builder_get_type ()) +#define GTK_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_BUILDER, GtkBuilder)) +#define GTK_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_BUILDER, GtkBuilderClass)) +#define GTK_IS_BUILDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_BUILDER)) +#define GTK_IS_BUILDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_BUILDER)) +#define GTK_BUILDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_BUILDER, GtkBuilderClass)) + +#define GTK_BUILDER_ERROR (gtk_builder_error_quark ()) + +typedef struct _GtkBuilder GtkBuilder; +typedef struct _GtkBuilderClass GtkBuilderClass; +typedef struct _GtkBuilderPrivate GtkBuilderPrivate; + +typedef enum +{ + GTK_BUILDER_ERROR_INVALID_TYPE_FUNCTION, + GTK_BUILDER_ERROR_UNHANDLED_TAG, + GTK_BUILDER_ERROR_MISSING_ATTRIBUTE, + GTK_BUILDER_ERROR_INVALID_ATTRIBUTE, + GTK_BUILDER_ERROR_INVALID_TAG +} GtkBuilderError; + +GQuark gtk_builder_error_quark (void); + +struct _GtkBuilder +{ + GObject parent_instance; + + GtkBuilderPrivate *priv; +}; + +struct _GtkBuilderClass +{ + GObjectClass parent_class; + + GType (* get_type_from_name) (GtkBuilder *builder, + const char *typename); + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); + void (*_gtk_reserved5) (void); + void (*_gtk_reserved6) (void); + void (*_gtk_reserved7) (void); + void (*_gtk_reserved8) (void); +}; + +typedef void (*GtkBuilderConnectFunc) (GtkBuilder *builder, + GObject *object, + const gchar *signal_name, + const gchar *handler_name, + GObject *connect_object, + GConnectFlags flags, + gpointer user_data); + +GType gtk_builder_get_type (void) G_GNUC_CONST; +GtkBuilder* gtk_builder_new (void); + +guint gtk_builder_add_from_file (GtkBuilder *builder, + const gchar *filename, + GError **error); +guint gtk_builder_add_from_string (GtkBuilder *builder, + const gchar *buffer, + gsize length, + GError **error); +GObject* gtk_builder_get_object (GtkBuilder *builder, + const gchar *name); +GSList* gtk_builder_get_objects (GtkBuilder *builder); +void gtk_builder_connect_signals (GtkBuilder *builder, + gpointer user_data); +void gtk_builder_connect_signals_full (GtkBuilder *builder, + GtkBuilderConnectFunc func, + gpointer user_data); +void gtk_builder_set_translation_domain (GtkBuilder *builder, + const gchar *domain); +const gchar* gtk_builder_get_translation_domain (GtkBuilder *builder); +GType gtk_builder_get_type_from_name (GtkBuilder *builder, + const char *typename); + +gboolean gtk_builder_value_from_string (GParamSpec *pspec, + const gchar *string, + GValue *value); +gboolean gtk_builder_value_from_string_type (GType type, + const gchar *string, + GValue *value); +guint _gtk_builder_flags_from_string (GType type, + const char *string); + +#define GTK_BUILDER_WARN_INVALID_CHILD_TYPE(object, type) \ + g_warning ("'%s' is not a valid child type of '%s'", type, g_type_name (G_OBJECT_TYPE (type))) + +G_END_DECLS + +#endif /* __GTK_BUILDER_H__ */ diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c new file mode 100644 index 0000000000..6d82b35126 --- /dev/null +++ b/gtk/gtkbuilderparser.c @@ -0,0 +1,851 @@ +/* gtkbuilderparser.c + * Copyright (C) 2006-2007 Async Open Source, + * Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gtkbuilderprivate.h" +#include "gtkbuilder.h" +#include "gtkbuildable.h" +#include "gtkdebug.h" +#include "gtktypeutils.h" +#include "gtkalias.h" + +static void free_property_info (PropertyInfo *info, + gpointer user_data); + +static inline void +state_push (ParserData *data, gpointer info) +{ + data->stack = g_slist_prepend (data->stack, info); +} + +static inline gpointer +state_peek (ParserData *data) +{ + if (!data->stack) + return NULL; + + return data->stack->data; +} + +static inline gpointer +state_pop (ParserData *data) +{ + gpointer old = NULL; + + if (!data->stack) + return NULL; + + old = data->stack->data; + data->stack = g_slist_delete_link (data->stack, data->stack); + return old; +} +#define state_peek_info(data, st) ((st*)state_peek(data)) +#define state_pop_info(data, st) ((st*)state_pop(data)) + +static void +error_missing_attribute (ParserData *data, + const gchar *tag, + const gchar *attribute, + GError **error) +{ + gint line_number, char_number; + + g_markup_parse_context_get_position (data->ctx, + &line_number, + &char_number); + + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_MISSING_ATTRIBUTE, + "%s:%d:%d <%s> requires attribute \"%s\"", + data->filename, + line_number, char_number, tag, attribute); +} + +static void +error_invalid_attribute (ParserData *data, + const gchar *tag, + const gchar *attribute, + GError **error) +{ + gint line_number, char_number; + + g_markup_parse_context_get_position (data->ctx, + &line_number, + &char_number); + + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_ATTRIBUTE, + "%s:%d:%d '%s' is not a valid attribute of <%s>", + data->filename, + line_number, char_number, attribute, tag); +} + +static void +error_invalid_tag (ParserData *data, + const gchar *tag, + const gchar *expected, + GError **error) +{ + gint line_number, char_number; + + g_markup_parse_context_get_position (data->ctx, + &line_number, + &char_number); + + if (expected) + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_TAG, + "%s:%d:%d '%s' is not a valid tag here, expected a '%s' tag", + data->filename, + line_number, char_number, tag, expected); + else + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_TAG, + "%s:%d:%d '%s' is not a valid tag here", + data->filename, + line_number, char_number, tag); +} + +static GObject * +builder_construct (ParserData *data, + ObjectInfo *object_info) +{ + GObject *object; + + g_assert (object_info != NULL); + + if (object_info->object) + return object_info->object; + + object_info->properties = g_slist_reverse (object_info->properties); + + object = _gtk_builder_construct (data->builder, object_info); + g_assert (G_IS_OBJECT (object)); + + object_info->object = object; + + return object; +} + +static void +parse_object (ParserData *data, + const gchar *element_name, + const gchar **names, + const gchar **values, + GError **error) +{ + ObjectInfo *object_info; + ChildInfo* child_info; + int i; + gchar *object_class = NULL; + gchar *object_id = NULL; + gchar *constructor = NULL; + + child_info = state_peek_info (data, ChildInfo); + if (child_info && strcmp (child_info->tag.name, "object") == 0) + { + error_invalid_tag (data, element_name, NULL, error); + return; + } + + for (i = 0; names[i] != NULL; i++) + { + if (strcmp (names[i], "class") == 0) + object_class = g_strdup (values[i]); + else if (strcmp (names[i], "id") == 0) + object_id = g_strdup (values[i]); + else if (strcmp (names[i], "constructor") == 0) + constructor = g_strdup (values[i]); + else + { + error_invalid_attribute (data, element_name, values[i], error); + return; + } + } + + if (!object_class) + { + error_missing_attribute (data, element_name, "class", error); + return; + } + + if (!object_id) + { + error_missing_attribute (data, element_name, "id", error); + return; + } + + object_info = g_slice_new0 (ObjectInfo); + object_info->class_name = object_class; + object_info->id = object_id; + object_info->constructor = constructor; + state_push (data, object_info); + g_assert (state_peek (data) != NULL); + object_info->tag.name = element_name; + + if (child_info) + object_info->parent = (CommonInfo*)child_info; +} + +static void +free_object_info (ObjectInfo *info) +{ + /* Do not free the signal items, which GtkBuilder takes ownership of */ + g_slist_free (info->signals); + g_slist_foreach (info->properties, + (GFunc)free_property_info, NULL); + g_slist_free (info->properties); + g_free (info->constructor); + g_free (info->class_name); + g_free (info->id); + g_slice_free (ObjectInfo, info); +} + +static gchar * +_get_type_by_symbol (const gchar* symbol) +{ + static GModule *module = NULL; + GTypeGetFunc func; + GType type; + + if (!module) + module = g_module_open (NULL, 0); + + if (!g_module_symbol (module, symbol, (gpointer)&func)) + return NULL; + + type = func (); + if (type == G_TYPE_INVALID) + return NULL; + + return g_strdup (g_type_name (type)); +} + +static void +parse_child (ParserData *data, + const gchar *element_name, + const gchar **names, + const gchar **values, + GError **error) + +{ + ObjectInfo* object_info; + ChildInfo *child_info; + guint i; + + object_info = state_peek_info (data, ObjectInfo); + if (!object_info || strcmp (object_info->tag.name, "object") != 0) + { + error_invalid_tag (data, element_name, "object", error); + return; + } + + child_info = g_slice_new0 (ChildInfo); + state_push (data, child_info); + g_assert (state_peek (data) != NULL); + child_info->tag.name = element_name; + for (i = 0; names[i]; i++) + { + if (strcmp (names[i], "type") == 0) + child_info->type = g_strdup (values[i]); + else if (strcmp (names[i], "internal-child") == 0) + child_info->internal_child = g_strdup (values[i]); + else if (strcmp (names[i], "type-func") == 0) + { + child_info->type = _get_type_by_symbol (values[i]); + if (!child_info->type) + { + g_set_error (error, GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_TYPE_FUNCTION, + _("Invalid type function: `%s'"), + values[i]); + return; + } + } + else + error_invalid_attribute (data, element_name, values[i], error); + } + + child_info->parent = (CommonInfo*)object_info; + + object_info->object = builder_construct (data, object_info); +} + +static void +free_child_info (ChildInfo *info) +{ + g_free (info->type); + g_free (info->internal_child); + g_slice_free (ChildInfo, info); +} + +static void +parse_property (ParserData *data, + const gchar *element_name, + const gchar **names, + const gchar **values, + GError **error) +{ + PropertyInfo *info; + gchar *name = NULL; + gboolean translatable = FALSE; + int i; + + g_assert (data->stack != NULL); + + for (i = 0; names[i] != NULL; i++) + { + if (strcmp (names[i], "name") == 0) + name = g_strdelimit (g_strdup (values[i]), "_", '-'); + else if (strcmp (names[i], "translatable") == 0) + translatable = strcmp (values[i], "yes") == 0; + else + { + error_invalid_attribute (data, element_name, values[i], error); + return; + } + } + + if (!name) + { + error_missing_attribute (data, element_name, "name", error); + return; + } + + info = g_slice_new0 (PropertyInfo); + info->name = name; + info->translatable = translatable; + state_push (data, info); + + info->tag.name = element_name; +} + +static void +free_property_info (PropertyInfo *info, + gpointer user_data) +{ + g_free (info->data); + g_free (info->name); + g_slice_free (PropertyInfo, info); +} + +static void +parse_signal (ParserData *data, + const gchar *element_name, + const gchar **names, + const gchar **values, + GError **error) +{ + SignalInfo *info; + gchar *name = NULL; + gchar *handler = NULL; + gchar *object = NULL; + gboolean after = FALSE; + gboolean swapped = FALSE; + gboolean swapped_set = FALSE; + int i; + + g_assert (data->stack != NULL); + + for (i = 0; names[i] != NULL; i++) + { + if (strcmp (names[i], "name") == 0) + name = g_strdup (values[i]); + else if (strcmp (names[i], "handler") == 0) + handler = g_strdup (values[i]); + else if (strcmp (names[i], "after") == 0) + after = strcmp (values[i], "yes") == 0; + else if (strcmp (names[i], "swapped") == 0) + { + swapped = strcmp (values[i], "yes") == 0; + swapped_set = TRUE; + } + else if (strcmp (names[i], "object") == 0) + object = g_strdup (values[i]); + else + { + error_invalid_attribute (data, element_name, values[i], error); + return; + } + } + + if (!name) + { + error_missing_attribute (data, element_name, "name", error); + return; + } + else if (!handler) + { + error_missing_attribute (data, element_name, "handler", error); + return; + } + + /* Swapped defaults to FALSE except when object is set */ + if (object && !swapped_set) + swapped = TRUE; + + info = g_slice_new0 (SignalInfo); + info->name = name; + info->handler = handler; + if (after) + info->flags |= G_CONNECT_AFTER; + if (swapped) + info->flags |= G_CONNECT_SWAPPED; + info->connect_object_name = object; + state_push (data, info); + + info->tag.name = element_name; +} + +/* Called by GtkBuilder */ +void +_free_signal_info (SignalInfo *info, + gpointer user_data) +{ + g_free (info->name); + g_free (info->handler); + g_free (info->connect_object_name); + g_free (info->object_name); + g_slice_free (SignalInfo, info); +} + +static void +parse_interface (ParserData *data, + const gchar *element_name, + const gchar **names, + const gchar **values, + GError **error) +{ + int i; + + for (i = 0; names[i] != NULL; i++) + { + if (strcmp (names[i], "domain") == 0 && !data->domain) + { + data->domain = g_strdup (values[i]); + break; + } + else + error_invalid_attribute (data, "interface", values[i], error); + } +} + +static SubParser * +create_subparser (GObject *object, + GObject *child, + const gchar *element_name, + GMarkupParser *parser, + gpointer user_data) +{ + SubParser *subparser; + + subparser = g_slice_new0 (SubParser); + subparser->object = object; + subparser->child = child; + subparser->tagname = g_strdup (element_name); + subparser->start = element_name; + subparser->parser = g_memdup (parser, sizeof (GMarkupParser)); + subparser->data = user_data; + + return subparser; +} + +static void +free_subparser (SubParser *subparser) +{ + g_free (subparser->tagname); + g_slice_free (SubParser, subparser); +} + +static gboolean +subparser_start (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + ParserData *data, + GError **error) +{ + SubParser *subparser = data->subparser; + + if (!subparser->start && + strcmp (element_name, subparser->tagname) == 0) + subparser->start = element_name; + + if (subparser->start) + { + if (subparser->parser->start_element) + subparser->parser->start_element (context, + element_name, names, values, + subparser->data, error); + return FALSE; + } + return TRUE; +} + +static void +subparser_end (GMarkupParseContext *context, + const gchar *element_name, + ParserData *data, + GError **error) +{ + if (data->subparser->parser->end_element) + data->subparser->parser->end_element (context, element_name, + data->subparser->data, error); + + if (!strcmp (data->subparser->start, element_name) == 0) + return; + + gtk_buildable_custom_tag_end (GTK_BUILDABLE (data->subparser->object), + data->builder, + data->subparser->child, + element_name, + data->subparser->data); + g_free (data->subparser->parser); + + if (GTK_BUILDABLE_GET_IFACE (data->subparser->object)->custom_finished) + data->custom_finalizers = g_slist_prepend (data->custom_finalizers, + data->subparser); + else + free_subparser (data->subparser); + + data->subparser = NULL; +} + +static gboolean +parse_custom (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + ParserData *data, + GError **error) +{ + CommonInfo* parent_info; + GMarkupParser parser; + gpointer *subparser_data; + GObject *object; + GObject *child; + + parent_info = state_peek_info (data, CommonInfo); + if (!parent_info) + return FALSE; + + if (strcmp (parent_info->tag.name, "object") == 0) + { + ObjectInfo* object_info = (ObjectInfo*)parent_info; + if (!object_info->object) + object_info->object = _gtk_builder_construct (data->builder, + object_info); + g_assert (object_info->object); + object = object_info->object; + child = NULL; + } + else if (strcmp (parent_info->tag.name, "child") == 0) + { + ChildInfo* child_info = (ChildInfo*)parent_info; + + _gtk_builder_add (data->builder, child_info); + + object = ((ObjectInfo*)child_info->parent)->object; + child = child_info->object; + } + else + return FALSE; + + if (!gtk_buildable_custom_tag_start (GTK_BUILDABLE (object), + data->builder, + child, + element_name, + &parser, + (gpointer*)&subparser_data)) + return FALSE; + + data->subparser = create_subparser (object, child, element_name, + &parser, subparser_data); + + if (parser.start_element) + parser.start_element (context, + element_name, names, values, + subparser_data, error); + return TRUE; +} + +static void +start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + ParserData *data = (ParserData*)user_data; + +#ifdef GTK_ENABLE_DEBUG + if (gtk_debug_flags & GTK_DEBUG_BUILDER) + { + GString *tags = g_string_new (""); + int i; + for (i = 0; names[i]; i++) + g_string_append_printf (tags, "%s=\"%s\" ", names[i], values[i]); + + if (i) + { + g_string_insert_c (tags, 0, ' '); + g_string_truncate (tags, tags->len - 1); + } + g_print ("<%s%s>\n", element_name, tags->str); + g_string_free (tags, TRUE); + } +#endif + + if (!data->last_element && strcmp (element_name, "interface") != 0) + { + g_set_error (error, GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_UNHANDLED_TAG, + _("Invalid root element: '%s'"), + element_name); + return; + } + data->last_element = element_name; + + if (data->subparser) + if (!subparser_start (context, element_name, names, values, + data, error)) + return; + + if (strcmp (element_name, "object") == 0) + parse_object (data, element_name, names, values, error); + else if (strcmp (element_name, "child") == 0) + parse_child (data, element_name, names, values, error); + else if (strcmp (element_name, "property") == 0) + parse_property (data, element_name, names, values, error); + else if (strcmp (element_name, "signal") == 0) + parse_signal (data, element_name, names, values, error); + else if (strcmp (element_name, "interface") == 0) + parse_interface (data, element_name, names, values, error); + else if (strcmp (element_name, "placeholder") == 0) + { + /* placeholder has no special treatmeant, but it needs an + * if clause to avoid an error below. + */ + } + else + if (!parse_custom (context, element_name, names, values, + data, error)) + g_set_error (error, GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_UNHANDLED_TAG, + _("Unhandled tag: '%s'"), + element_name); +} + +/* Called for close tags */ +static void +end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParserData *data = (ParserData*)user_data; + + GTK_NOTE (BUILDER, g_print ("\n", element_name)); + + if (data->subparser && data->subparser->start) + { + subparser_end (context, element_name, data, error); + return; + } + + if (strcmp (element_name, "object") == 0) + { + ObjectInfo *object_info = state_pop_info (data, ObjectInfo); + ChildInfo* child_info = state_peek_info (data, ChildInfo); + + object_info->object = builder_construct (data, object_info); + + if (child_info) + child_info->object = object_info->object; + + if (GTK_IS_BUILDABLE (object_info->object) && + GTK_BUILDABLE_GET_IFACE (object_info->object)->parser_finished) + data->finalizers = g_slist_prepend (data->finalizers, object_info->object); + free_object_info (object_info); + } + else if (strcmp (element_name, "property") == 0) + { + PropertyInfo *prop_info = state_pop_info (data, PropertyInfo); + CommonInfo *info = state_peek_info (data, CommonInfo); + + /* Normal properties */ + if (strcmp (info->tag.name, "object") == 0) + { + ObjectInfo *object_info = (ObjectInfo*)info; + object_info->properties = + g_slist_prepend (object_info->properties, prop_info); + } + else + g_assert_not_reached (); + } + else if (strcmp (element_name, "child") == 0) + { + ChildInfo *child_info = state_pop_info (data, ChildInfo); + + _gtk_builder_add (data->builder, child_info); + + free_child_info (child_info); + } + else if (strcmp (element_name, "signal") == 0) + { + SignalInfo *signal_info = state_pop_info (data, SignalInfo); + ObjectInfo *object_info = (ObjectInfo*)state_peek_info (data, CommonInfo); + signal_info->object_name = g_strdup (object_info->id); + object_info->signals = + g_slist_prepend (object_info->signals, signal_info); + } + else if (strcmp (element_name, "interface") == 0) + { + } + else if (strcmp (element_name, "placeholder") == 0) + { + } + else + { + g_assert_not_reached (); + } +} + +/* Called for character data */ +/* text is not nul-terminated */ +static void +text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ParserData *data = (ParserData*)user_data; + CommonInfo *info; + + if (data->subparser && data->subparser->start) + { + if (data->subparser->parser->text) + data->subparser->parser->text (context, text, text_len, + data->subparser->data, error); + return; + } + + if (!data->stack) + return; + + info = state_peek_info (data, CommonInfo); + g_assert (info != NULL); + + if (strcmp (g_markup_parse_context_get_element (context), "property") == 0) + { + PropertyInfo *prop_info = (PropertyInfo*)info; + + if (prop_info->translatable && text_len) + { + if (data->domain) + text = dgettext (text, data->domain); + else + text = gettext (text); + } + prop_info->data = g_strndup (text, text_len); + } +} + +static const GMarkupParser parser = { + start_element, + end_element, + text, + NULL, + NULL +}; + +void +_gtk_builder_parser_parse_buffer (GtkBuilder *builder, + const gchar *filename, + const gchar *buffer, + gsize length, + GError **error) +{ + ParserData *data; + GSList *l; + + data = g_new0 (ParserData, 1); + data->builder = builder; + data->filename = filename; + data->domain = g_strdup (gtk_builder_get_translation_domain (builder)); + + data->ctx = g_markup_parse_context_new ( + &parser, G_MARKUP_TREAT_CDATA_AS_TEXT, data, NULL); + + if (!g_markup_parse_context_parse (data->ctx, buffer, length, error)) + goto out; + + gtk_builder_set_translation_domain (data->builder, data->domain); + _gtk_builder_finish (builder); + + /* Custom parser_finished */ + data->custom_finalizers = g_slist_reverse (data->custom_finalizers); + for (l = data->custom_finalizers; l; l = l->next) + { + SubParser *sub = (SubParser*)l->data; + + gtk_buildable_custom_finished (GTK_BUILDABLE (sub->object), + builder, + sub->child, + sub->tagname, + sub->data); + free_subparser (sub); + } + + /* Common parser_finished, for all created objects */ + data->finalizers = g_slist_reverse (data->finalizers); + for (l = data->finalizers; l; l = l->next) + { + GtkBuildable *buildable = (GtkBuildable*)l->data; + gtk_buildable_parser_finished (GTK_BUILDABLE (buildable), builder); + } + + out: + g_markup_parse_context_free (data->ctx); + + g_slist_free (data->stack); + g_slist_free (data->custom_finalizers); + g_slist_free (data->finalizers); + g_free (data->domain); + g_free (data); +} diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h new file mode 100644 index 0000000000..1a65d48cc4 --- /dev/null +++ b/gtk/gtkbuilderprivate.h @@ -0,0 +1,111 @@ +/* gtkbuilderprivate.h + * Copyright (C) 2006-2007 Async Open Source, + * Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_BUILDER_PRIVATE_H__ +#define __GTK_BUILDER_PRIVATE_H__ + +#include +#include + +#include +#include "gtkbuilder.h" + +typedef struct { + const gchar *name; +} TagInfo; + +typedef struct { + TagInfo tag; +} CommonInfo; + +typedef struct { + TagInfo tag; + gchar *class_name; + gchar *id; + gchar *constructor; + GSList *properties; + GSList *signals; + GObject *object; + CommonInfo *parent; +} ObjectInfo; + +typedef struct { + TagInfo tag; + GSList *packing_properties; + GObject *object; + CommonInfo *parent; + gchar *type; + gchar *internal_child; + gboolean added; +} ChildInfo; + +typedef struct { + TagInfo tag; + gchar *name; + gchar *data; + gboolean translatable; +} PropertyInfo; + +typedef struct { + TagInfo tag; + gchar *object_name; + gchar *name; + gchar *handler; + GConnectFlags flags; + gchar *connect_object_name; +} SignalInfo; + +typedef struct { + GMarkupParser *parser; + gchar *tagname; + const gchar *start; + gpointer data; + GObject *object; + GObject *child; +} SubParser; + +typedef struct { + const gchar *last_element; + GtkBuilder *builder; + gchar *domain; + GSList *stack; + SubParser *subparser; + GMarkupParseContext *ctx; + const gchar *filename; + GSList *finalizers; + GSList *custom_finalizers; +} ParserData; + +typedef GType (*GTypeGetFunc) (void); + +void _gtk_builder_parser_parse_buffer (GtkBuilder *builder, + const gchar *filename, + const gchar *buffer, + gsize length, + GError **error); +GObject * _gtk_builder_construct (GtkBuilder *builder, + ObjectInfo *info); +void _gtk_builder_add (GtkBuilder *builder, + ChildInfo *child_info); +void _gtk_builder_finish (GtkBuilder *builder); +void _free_signal_info (SignalInfo *info, + gpointer user_data); + +#endif /* __GTK_BUILDER_PRIVATE_H__ */ diff --git a/gtk/gtkcelllayout.c b/gtk/gtkcelllayout.c index 10cf3c4267..89f0a1ab03 100644 --- a/gtk/gtkcelllayout.c +++ b/gtk/gtkcelllayout.c @@ -17,6 +17,8 @@ * Boston, MA 02111-1307, USA. */ +#include +#include #include #include "gtkcelllayout.h" #include "gtkintl.h" @@ -309,5 +311,115 @@ gtk_cell_layout_get_cells (GtkCellLayout *cell_layout) return NULL; } +typedef struct { + GtkCellLayout *cell_layout; + GtkCellRenderer *renderer; + gchar *attr_name; +} AttributesSubParserData; + +static void +attributes_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data; + guint i; + + if (strcmp (element_name, "attribute") == 0) + for (i = 0; names[i]; i++) + if (strcmp (names[i], "name") == 0) + parser_data->attr_name = g_strdup (values[i]); + else if (strcmp (element_name, "attributes") == 0) + return; + else + g_warning ("Unsupported tag for GtkCellLayout: %s\n", element_name); +} + +static void +attributes_text_element (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data; + + if (!parser_data->attr_name) + return; + gtk_cell_layout_add_attribute (parser_data->cell_layout, + parser_data->renderer, + parser_data->attr_name, atoi (text)); + g_free (parser_data->attr_name); + parser_data->attr_name = NULL; +} + +static const GMarkupParser attributes_parser = + { + attributes_start_element, + NULL, + attributes_text_element, + }; + +gboolean +_gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + AttributesSubParserData *parser_data; + + if (!child) + return FALSE; + + if (strcmp (tagname, "attributes") == 0) + { + parser_data = g_slice_new0 (AttributesSubParserData); + parser_data->cell_layout = GTK_CELL_LAYOUT (buildable); + parser_data->renderer = GTK_CELL_RENDERER (child); + parser_data->attr_name = NULL; + + *parser = attributes_parser; + *data = parser_data; + return TRUE; + } + + return FALSE; +} + +void +_gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + AttributesSubParserData *parser_data; + + parser_data = (AttributesSubParserData*)data; + g_assert (!parser_data->attr_name); + g_slice_free (AttributesSubParserData, parser_data); +} + +void +_gtk_cell_layout_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + GtkCellLayoutIface *iface; + + g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable)); + g_return_if_fail (GTK_IS_CELL_RENDERER (child)); + + iface = GTK_CELL_LAYOUT_GET_IFACE (buildable); + g_return_if_fail (iface->pack_end != NULL); + iface->pack_end (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE); +} + #define __GTK_CELL_LAYOUT_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkcelllayout.h b/gtk/gtkcelllayout.h index 520f35dbc1..8e2e272cb2 100644 --- a/gtk/gtkcelllayout.h +++ b/gtk/gtkcelllayout.h @@ -24,6 +24,8 @@ #include #include +#include +#include G_BEGIN_DECLS @@ -97,7 +99,21 @@ void gtk_cell_layout_clear_attributes (GtkCellLayout *cell_layout, void gtk_cell_layout_reorder (GtkCellLayout *cell_layout, GtkCellRenderer *cell, gint position); - +gboolean _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +void _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); +void _gtk_cell_layout_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); G_END_DECLS diff --git a/gtk/gtkcellview.c b/gtk/gtkcellview.c index 1cd3fc7065..f9d49896ad 100644 --- a/gtk/gtkcellview.c +++ b/gtk/gtkcellview.c @@ -18,6 +18,7 @@ */ #include +#include #include "gtkcellview.h" #include "gtkcelllayout.h" #include "gtkintl.h" @@ -26,6 +27,7 @@ #include "gtkcellrendererpixbuf.h" #include "gtkprivate.h" #include +#include "gtkbuildable.h" #include "gtkalias.h" typedef struct _GtkCellViewCellInfo GtkCellViewCellInfo; @@ -105,6 +107,22 @@ static void gtk_cell_view_cell_layout_reorder (GtkCellLayout gint position); static GList * gtk_cell_view_cell_layout_get_cells (GtkCellLayout *layout); +/* buildable */ +static void gtk_cell_view_buildable_init (GtkBuildableIface *iface); +static gboolean gtk_cell_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + +static GtkBuildableIface *parent_buildable_iface; + #define GTK_CELL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CELL_VIEW, GtkCellViewPrivate)) enum @@ -118,7 +136,9 @@ enum G_DEFINE_TYPE_WITH_CODE (GtkCellView, gtk_cell_view, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_cell_view_cell_layout_init)) + gtk_cell_view_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_cell_view_buildable_init)) static void gtk_cell_view_class_init (GtkCellViewClass *klass) @@ -174,6 +194,15 @@ gtk_cell_view_class_init (GtkCellViewClass *klass) g_type_class_add_private (gobject_class, sizeof (GtkCellViewPrivate)); } +static void +gtk_cell_view_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add = _gtk_cell_layout_buildable_add; + iface->custom_tag_start = gtk_cell_view_buildable_custom_tag_start; + iface->custom_tag_end = gtk_cell_view_buildable_custom_tag_end; +} + static void gtk_cell_view_cell_layout_init (GtkCellLayoutIface *iface) { @@ -1068,5 +1097,37 @@ gtk_cell_view_cell_layout_get_cells (GtkCellLayout *layout) } +static gboolean +gtk_cell_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + if (parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, + tagname, parser, data); +} + +static void +gtk_cell_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + if (strcmp (tagname, "attributes") == 0) + _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, + data); + else + parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, + data); +} + + #define __GTK_CELL_VIEW_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkcolorseldialog.c b/gtk/gtkcolorseldialog.c index 1f427ca43e..d1b7c00603 100644 --- a/gtk/gtkcolorseldialog.c +++ b/gtk/gtkcolorseldialog.c @@ -24,6 +24,7 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include +#include #include #include "gtkcolorseldialog.h" #include "gtkframe.h" @@ -31,6 +32,7 @@ #include "gtkbutton.h" #include "gtkstock.h" #include "gtkintl.h" +#include "gtkbuildable.h" #include "gtkalias.h" @@ -38,7 +40,17 @@ /* GtkColorSelectionDialog */ /***************************/ -G_DEFINE_TYPE (GtkColorSelectionDialog, gtk_color_selection_dialog, GTK_TYPE_DIALOG) +static void gtk_color_selection_dialog_buildable_interface_init (GtkBuildableIface *iface); +static GObject * gtk_color_selection_dialog_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname); + +G_DEFINE_TYPE_WITH_CODE (GtkColorSelectionDialog, gtk_color_selection_dialog, + GTK_TYPE_DIALOG, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_color_selection_dialog_buildable_interface_init)) + +static GtkBuildableIface *parent_buildable_iface; static void gtk_color_selection_dialog_class_init (GtkColorSelectionDialogClass *klass) @@ -106,5 +118,30 @@ gtk_color_selection_dialog_new (const gchar *title) return GTK_WIDGET (colorseldiag); } +static void +gtk_color_selection_dialog_buildable_interface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->get_internal_child = gtk_color_selection_dialog_buildable_get_internal_child; +} + +static GObject * +gtk_color_selection_dialog_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname) +{ + if (strcmp(childname, "ok_button") == 0) + return G_OBJECT (GTK_COLOR_SELECTION_DIALOG (buildable)->ok_button); + else if (strcmp(childname, "cancel_button") == 0) + return G_OBJECT (GTK_COLOR_SELECTION_DIALOG (buildable)->cancel_button); + else if (strcmp(childname, "help_button") == 0) + return G_OBJECT (GTK_COLOR_SELECTION_DIALOG(buildable)->help_button); + else if (strcmp(childname, "color_selection") == 0) + return G_OBJECT (GTK_COLOR_SELECTION_DIALOG(buildable)->colorsel); + + return parent_buildable_iface->get_internal_child (buildable, builder, childname); +} + + #define __GTK_COLOR_SELECTION_DIALOG_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c index 14dfbe34da..742de106e5 100644 --- a/gtk/gtkcombobox.c +++ b/gtk/gtkcombobox.c @@ -438,6 +438,21 @@ static void gtk_combo_box_child_show (GtkWidget *w static void gtk_combo_box_child_hide (GtkWidget *widget, GtkComboBox *combo_box); +/* GtkBuildable method implementation */ +static GtkBuildableIface *parent_buildable_iface; + +static void gtk_combo_box_buildable_init (GtkBuildableIface *iface); +static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); /* GtkCellEditable method implementations */ static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable, @@ -448,7 +463,10 @@ G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN, G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, gtk_combo_box_cell_layout_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, - gtk_combo_box_cell_editable_init)) + gtk_combo_box_cell_editable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_combo_box_buildable_init)) + /* common */ static void @@ -813,6 +831,15 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass) g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate)); } +static void +gtk_combo_box_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add = _gtk_cell_layout_buildable_add; + iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start; + iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end; +} + static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface) { @@ -5583,5 +5610,36 @@ gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box) } +static gboolean +gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + if (parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, + tagname, parser, data); +} + +static void +gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + if (strcmp (tagname, "attributes") == 0) + _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, + data); + else + parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, + data); +} + #define __GTK_COMBO_BOX_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkcomboboxentry.c b/gtk/gtkcomboboxentry.c index f8b698a849..eff22d5c44 100644 --- a/gtk/gtkcomboboxentry.c +++ b/gtk/gtkcomboboxentry.c @@ -18,6 +18,7 @@ */ #include +#include #include "gtkcomboboxentry.h" #include "gtkcelllayout.h" @@ -26,6 +27,7 @@ #include "gtkprivate.h" #include "gtkintl.h" +#include "gtkbuildable.h" #include "gtkalias.h" #define GTK_COMBO_BOX_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX_ENTRY, GtkComboBoxEntryPrivate)) @@ -60,6 +62,10 @@ static void gtk_combo_box_entry_grab_focus (GtkWidget *widget); static void has_frame_changed (GtkComboBoxEntry *entry_box, GParamSpec *pspec, gpointer data); +static void gtk_combo_box_entry_buildable_interface_init (GtkBuildableIface *iface); +static GObject * gtk_combo_box_entry_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname); enum { @@ -67,7 +73,9 @@ enum PROP_TEXT_COLUMN }; -G_DEFINE_TYPE (GtkComboBoxEntry, gtk_combo_box_entry, GTK_TYPE_COMBO_BOX) +G_DEFINE_TYPE_WITH_CODE (GtkComboBoxEntry, gtk_combo_box_entry, GTK_TYPE_COMBO_BOX, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_combo_box_entry_buildable_interface_init)) static void gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass) @@ -129,6 +137,23 @@ gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box) g_signal_connect (entry_box, "notify::has-frame", G_CALLBACK (has_frame_changed), NULL); } +static void +gtk_combo_box_entry_buildable_interface_init (GtkBuildableIface *iface) +{ + iface->get_internal_child = gtk_combo_box_entry_buildable_get_internal_child; +} + +static GObject * +gtk_combo_box_entry_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname) +{ + if (strcmp (childname, "entry") == 0) + return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable))); + + return NULL; +} + static void gtk_combo_box_entry_set_property (GObject *object, guint prop_id, diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c index 2040503d95..9305db57d8 100644 --- a/gtk/gtkcontainer.c +++ b/gtk/gtkcontainer.c @@ -30,6 +30,7 @@ #include #include "gtkcontainer.h" +#include "gtkbuildable.h" #include "gtkprivate.h" #include "gtkmain.h" #include "gtkmarshalers.h" @@ -99,6 +100,24 @@ static void gtk_container_unmap (GtkWidget *widget); static gchar* gtk_container_child_default_composite_name (GtkContainer *container, GtkWidget *child); +/* GtkBuildable */ +static void gtk_container_buildable_init (GtkBuildableIface *iface); +static void gtk_container_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); +static gboolean gtk_container_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_container_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + /* --- variables --- */ static const gchar vadjustment_key[] = "gtk-vadjustment"; @@ -110,6 +129,7 @@ static guint container_signals[LAST_SIGNAL] = { 0 }; static GtkWidgetClass *parent_class = NULL; extern GParamSpecPool *_gtk_widget_child_property_pool; extern GObjectNotifyContext *_gtk_widget_child_property_notify_context; +static GtkBuildableIface *parent_buildable_iface; /* --- functions --- */ @@ -134,9 +154,21 @@ gtk_container_get_type (void) NULL, /* value_table */ }; + static const GInterfaceInfo buildable_info = + { + (GInterfaceInitFunc) gtk_container_buildable_init, + NULL, + NULL + }; + container_type = g_type_register_static (GTK_TYPE_WIDGET, I_("GtkContainer"), &container_info, G_TYPE_FLAG_ABSTRACT); + + g_type_add_interface_static (container_type, + GTK_TYPE_BUILDABLE, + &buildable_info); + } return container_type; @@ -260,6 +292,164 @@ gtk_container_class_init (GtkContainerClass *class) GTK_TYPE_WIDGET); } +static void +gtk_container_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add = gtk_container_buildable_add; + iface->custom_tag_start = gtk_container_buildable_custom_tag_start; + iface->custom_tag_end = gtk_container_buildable_custom_tag_end; +} + +static void +gtk_container_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + g_return_if_fail (GTK_IS_WIDGET (child)); + + gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); +} + +static void +gtk_container_buildable_set_child_property (GtkContainer *container, + GtkWidget *child, + gchar *name, + const gchar *value) +{ + GParamSpec *pspec; + GValue gvalue = { 0, }; + + pspec = gtk_container_class_find_child_property + (G_OBJECT_GET_CLASS (container), name); + if (!pspec) + { + g_warning ("%s does not have a property called %s", + g_type_name (G_OBJECT_TYPE (container)), name); + return; + } + + if (!gtk_builder_value_from_string (pspec, value, &gvalue)) + { + g_warning ("Could not read property %s:%s with value %s of type %s", + g_type_name (G_OBJECT_TYPE (container)), + name, + value, + g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec))); + return; + } + + gtk_container_child_set_property (container, child, name, &gvalue); + g_value_unset (&gvalue); +} + +typedef struct { + GtkBuilder *builder; + GtkContainer *container; + GtkWidget *child; + gchar *child_prop_name; +} PackingPropertiesData; + +static void +attributes_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + PackingPropertiesData *parser_data = (PackingPropertiesData*)user_data; + guint i; + + if (strcmp (element_name, "property") == 0) + for (i = 0; names[i]; i++) + if (strcmp (names[i], "name") == 0) + parser_data->child_prop_name = g_strdup (values[i]); + else if (strcmp (element_name, "packing") == 0) + return; + else + g_warning ("Unsupported tag for GtkContainer: %s\n", element_name); +} + +static void +attributes_text_element (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + PackingPropertiesData *parser_data = (PackingPropertiesData*)user_data; + + if (!parser_data->child_prop_name) + return; + + gtk_container_buildable_set_child_property (parser_data->container, + parser_data->child, + parser_data->child_prop_name, + text); + + g_free (parser_data->child_prop_name); + parser_data->child_prop_name = NULL; +} + +static const GMarkupParser attributes_parser = + { + attributes_start_element, + NULL, + attributes_text_element, + }; + +static gboolean +gtk_container_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + PackingPropertiesData *parser_data; + + if (parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + if (child && strcmp (tagname, "packing") == 0) + { + parser_data = g_slice_new0 (PackingPropertiesData); + parser_data->builder = builder; + parser_data->container = GTK_CONTAINER (buildable); + parser_data->child = GTK_WIDGET (child); + parser_data->child_prop_name = NULL; + + *parser = attributes_parser; + *data = parser_data; + return TRUE; + } + + return FALSE; +} + +static void +gtk_container_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + if (strcmp (tagname, "packing") == 0) + { + g_slice_free (PackingPropertiesData, (gpointer)data); + return; + + } + + if (parent_buildable_iface->custom_tag_end) + parent_buildable_iface->custom_tag_end (buildable, builder, + child, tagname, data); + +} + /** * gtk_container_child_type: * @container: a #GtkContainer diff --git a/gtk/gtkdebug.h b/gtk/gtkdebug.h index c70c61768a..5b148c8862 100644 --- a/gtk/gtkdebug.h +++ b/gtk/gtkdebug.h @@ -42,7 +42,8 @@ typedef enum { GTK_DEBUG_MODULES = 1 << 7, GTK_DEBUG_GEOMETRY = 1 << 8, GTK_DEBUG_ICONTHEME = 1 << 9, - GTK_DEBUG_PRINTING = 1 << 10 + GTK_DEBUG_PRINTING = 1 << 10, + GTK_DEBUG_BUILDER = 1 << 11 } GtkDebugFlag; #ifdef G_ENABLE_DEBUG diff --git a/gtk/gtkdialog.c b/gtk/gtkdialog.c index 93c6ce70f9..2419689487 100644 --- a/gtk/gtkdialog.c +++ b/gtk/gtkdialog.c @@ -24,6 +24,8 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include +#include #include #include "gtkbutton.h" #include "gtkdialog.h" @@ -37,6 +39,7 @@ #include "gtkintl.h" #include "gtkbindings.h" #include "gtkprivate.h" +#include "gtkbuildable.h" #include "gtkalias.h" #define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_DIALOG, GtkDialogPrivate)) @@ -76,6 +79,22 @@ static void gtk_dialog_close (GtkDialog *dialog); static ResponseData* get_response_data (GtkWidget *widget, gboolean create); +static void gtk_dialog_buildable_interface_init (GtkBuildableIface *iface); +static GObject * gtk_dialog_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname); +static gboolean gtk_dialog_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_dialog_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data); + enum { PROP_0, @@ -90,7 +109,9 @@ enum { static guint dialog_signals[LAST_SIGNAL]; -G_DEFINE_TYPE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW) +G_DEFINE_TYPE_WITH_CODE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_dialog_buildable_interface_init)) static void gtk_dialog_class_init (GtkDialogClass *class) @@ -235,6 +256,26 @@ gtk_dialog_init (GtkDialog *dialog) gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT); } +static void +gtk_dialog_buildable_interface_init (GtkBuildableIface *iface) +{ + iface->get_internal_child = gtk_dialog_buildable_get_internal_child; + iface->custom_tag_start = gtk_dialog_buildable_custom_tag_start; + iface->custom_finished = gtk_dialog_buildable_custom_finished; +} + +static GObject * +gtk_dialog_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname) +{ + if (strcmp (childname, "vbox") == 0) + return G_OBJECT (GTK_DIALOG (buildable)->vbox); + else if (strcmp (childname, "action_area") == 0) + return G_OBJECT (GTK_DIALOG (buildable)->action_area); + + return NULL; +} static void gtk_dialog_set_property (GObject *object, @@ -1216,5 +1257,134 @@ gtk_dialog_set_alternative_button_order_from_array (GtkDialog *dialog, } } +typedef struct { + gchar *widget_name; + gchar *response_id; +} ActionWidgetInfo; + +typedef struct { + GtkDialog *dialog; + GtkBuilder *builder; + GSList *items; + gchar *response; +} ActionWidgetsSubParserData; + +static void +attributes_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data; + guint i; + + if (strcmp (element_name, "action-widget") == 0) + for (i = 0; names[i]; i++) + if (strcmp (names[i], "response") == 0) + parser_data->response = g_strdup (values[i]); + else if (strcmp (element_name, "action-widgets") == 0) + return; + else + g_warning ("Unsupported tag for GtkDialog: %s\n", element_name); +} + +static void +attributes_text_element (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data; + ActionWidgetInfo *item; + + if (!parser_data->response) + return; + + item = g_new (ActionWidgetInfo, 1); + item->widget_name = g_strndup (text, text_len); + item->response_id = parser_data->response; + parser_data->items = g_slist_prepend (parser_data->items, item); + parser_data->response = NULL; +} + +static const GMarkupParser attributes_parser = + { + attributes_start_element, + NULL, + attributes_text_element, + }; + +gboolean +gtk_dialog_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + ActionWidgetsSubParserData *parser_data; + + if (child) + return FALSE; + + if (strcmp (tagname, "action-widgets") == 0) + { + parser_data = g_slice_new0 (ActionWidgetsSubParserData); + parser_data->dialog = GTK_DIALOG (buildable); + parser_data->items = NULL; + + *parser = attributes_parser; + *data = parser_data; + return TRUE; + } + + return FALSE; +} + +static void +gtk_dialog_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data) +{ + GSList *l; + ActionWidgetsSubParserData *parser_data; + GObject *object; + ResponseData* data; + + if (strcmp (tagname, "action-widgets")) + return; + + parser_data = (ActionWidgetsSubParserData*)user_data; + parser_data->items = g_slist_reverse (parser_data->items); + + for (l = parser_data->items; l; l = l->next) + { + ActionWidgetInfo *item = l->data; + + object = gtk_builder_get_object (builder, item->widget_name); + if (!object) + { + g_warning ("Unknown object %s specified in action-widgets %s", + item->widget_name, + gtk_buildable_get_name (GTK_BUILDABLE (object))); + continue; + } + + data = get_response_data (GTK_WIDGET (object), TRUE); + data->response_id = atoi (item->response_id); + + g_free (item->widget_name); + g_free (item->response_id); + g_free (item); + } + g_slist_free (parser_data->items); + g_slice_free (ActionWidgetsSubParserData, parser_data); +} + #define __GTK_DIALOG_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c index 8df785c158..53b87748b9 100644 --- a/gtk/gtkentrycompletion.c +++ b/gtk/gtkentrycompletion.c @@ -149,9 +149,15 @@ static void gtk_entry_completion_insert_completion_text (GtkEntryCompletion static guint entry_completion_signals[LAST_SIGNAL] = { 0 }; +/* GtkBuildable */ +static void gtk_entry_completion_buildable_init (GtkBuildableIface *iface); + G_DEFINE_TYPE_WITH_CODE (GtkEntryCompletion, gtk_entry_completion, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_entry_completion_cell_layout_init)) + gtk_entry_completion_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_entry_completion_buildable_init)) + static void gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) @@ -386,6 +392,14 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate)); } +static void +gtk_entry_completion_buildable_init (GtkBuildableIface *iface) +{ + iface->add = _gtk_cell_layout_buildable_add; + iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start; + iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end; +} + static void gtk_entry_completion_cell_layout_init (GtkCellLayoutIface *iface) { diff --git a/gtk/gtkexpander.c b/gtk/gtkexpander.c index 7d99da960e..62c08cd356 100644 --- a/gtk/gtkexpander.c +++ b/gtk/gtkexpander.c @@ -22,10 +22,11 @@ */ #include - +#include #include "gtkexpander.h" #include "gtklabel.h" +#include "gtkbuildable.h" #include "gtkcontainer.h" #include "gtkmarshalers.h" #include "gtkmain.h" @@ -126,7 +127,16 @@ static void gtk_expander_activate (GtkExpander *expander); static void get_expander_bounds (GtkExpander *expander, GdkRectangle *rect); -G_DEFINE_TYPE (GtkExpander, gtk_expander, GTK_TYPE_BIN) +/* GtkBuildable */ +static void gtk_expander_buildable_init (GtkBuildableIface *iface); +static void gtk_expander_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); + +G_DEFINE_TYPE_WITH_CODE (GtkExpander, gtk_expander, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_expander_buildable_init)) static void gtk_expander_class_init (GtkExpanderClass *klass) @@ -277,6 +287,26 @@ gtk_expander_init (GtkExpander *expander) gtk_drag_dest_set_track_motion (GTK_WIDGET (expander), TRUE); } +static void +gtk_expander_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (!type) + gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); + else if (strcmp (type, "label") == 0) + gtk_expander_set_label_widget (GTK_EXPANDER (buildable), GTK_WIDGET (child)); + else + GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_EXPANDER (buildable), type); +} + +static void +gtk_expander_buildable_init (GtkBuildableIface *iface) +{ + iface->add = gtk_expander_buildable_add; +} + static void gtk_expander_set_property (GObject *object, guint prop_id, diff --git a/gtk/gtkfontsel.c b/gtk/gtkfontsel.c index 095f216ab6..5be9b7cf0b 100644 --- a/gtk/gtkfontsel.c +++ b/gtk/gtkfontsel.c @@ -59,6 +59,7 @@ #include "gtkintl.h" #include "gtkaccessible.h" #include "gtkprivate.h" +#include "gtkbuildable.h" #include "gtkalias.h" /* We don't enable the font and style entries because they don't add @@ -1291,7 +1292,17 @@ gtk_font_selection_set_preview_text (GtkFontSelection *fontsel, * GtkFontSelectionDialog *****************************************************************************/ -G_DEFINE_TYPE (GtkFontSelectionDialog, gtk_font_selection_dialog, GTK_TYPE_DIALOG); +static void gtk_font_selection_dialog_buildable_interface_init (GtkBuildableIface *iface); +static GObject * gtk_font_selection_dialog_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname); + +G_DEFINE_TYPE_WITH_CODE (GtkFontSelectionDialog, gtk_font_selection_dialog, + GTK_TYPE_DIALOG, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_font_selection_dialog_buildable_interface_init)) + +static GtkBuildableIface *parent_buildable_iface; static void gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass) @@ -1365,6 +1376,30 @@ gtk_font_selection_dialog_new (const gchar *title) return GTK_WIDGET (fontseldiag); } +static void +gtk_font_selection_dialog_buildable_interface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->get_internal_child = gtk_font_selection_dialog_buildable_get_internal_child; +} + +static GObject * +gtk_font_selection_dialog_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname) +{ + if (strcmp(childname, "ok_button") == 0) + return G_OBJECT (GTK_FONT_SELECTION_DIALOG(buildable)->ok_button); + else if (strcmp(childname, "cancel_button") == 0) + return G_OBJECT (GTK_FONT_SELECTION_DIALOG (buildable)->cancel_button); + else if (strcmp(childname, "apply_button") == 0) + return G_OBJECT (GTK_FONT_SELECTION_DIALOG(buildable)->apply_button); + else if (strcmp(childname, "font_selection") == 0) + return G_OBJECT (GTK_FONT_SELECTION_DIALOG(buildable)->fontsel); + + return parent_buildable_iface->get_internal_child (buildable, builder, childname); +} + /** * gtk_font_selection_dialog_get_font_name: * @fsd: a #GtkFontSelectionDialog diff --git a/gtk/gtkframe.c b/gtk/gtkframe.c index f5974022fc..fba55df934 100644 --- a/gtk/gtkframe.c +++ b/gtk/gtkframe.c @@ -30,6 +30,7 @@ #include "gtklabel.h" #include "gtkprivate.h" #include "gtkintl.h" +#include "gtkbuildable.h" #include "gtkalias.h" #define LABEL_PAD 1 @@ -73,7 +74,16 @@ static void gtk_frame_compute_child_allocation (GtkFrame *frame, static void gtk_frame_real_compute_child_allocation (GtkFrame *frame, GtkAllocation *child_allocation); -G_DEFINE_TYPE (GtkFrame, gtk_frame, GTK_TYPE_BIN) +/* GtkBuildable */ +static void gtk_frame_buildable_init (GtkBuildableIface *iface); +static void gtk_frame_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); + +G_DEFINE_TYPE_WITH_CODE (GtkFrame, gtk_frame, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_frame_buildable_init)) static void gtk_frame_class_init (GtkFrameClass *class) @@ -149,6 +159,26 @@ gtk_frame_class_init (GtkFrameClass *class) class->compute_child_allocation = gtk_frame_real_compute_child_allocation; } +static void +gtk_frame_buildable_init (GtkBuildableIface *iface) +{ + iface->add = gtk_frame_buildable_add; +} + +static void +gtk_frame_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (type && strcmp (type, "label") == 0) + gtk_frame_set_label_widget (GTK_FRAME (buildable), GTK_WIDGET (child)); + else if (!type) + gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); + else + GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_FRAME (buildable), type); +} + static void gtk_frame_init (GtkFrame *frame) { diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 46db615363..ea7a1933b0 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -450,11 +450,28 @@ static void clear_source_info (GtkIconView *icon_view); static void adjust_wrap_width (GtkIconView *icon_view, GtkIconViewItem *item); +/* GtkBuildable */ +static GtkBuildableIface *parent_buildable_iface; +static void gtk_icon_view_buildable_init (GtkBuildableIface *iface); +static gboolean gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + static guint icon_view_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE_WITH_CODE (GtkIconView, gtk_icon_view, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_icon_view_cell_layout_init)) + gtk_icon_view_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_icon_view_buildable_init)) static void gtk_icon_view_class_init (GtkIconViewClass *klass) @@ -895,6 +912,15 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) GTK_MOVEMENT_VISUAL_POSITIONS, -1); } +static void +gtk_icon_view_buildable_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add = _gtk_cell_layout_buildable_add; + iface->custom_tag_start = gtk_icon_view_buildable_custom_tag_start; + iface->custom_tag_end = gtk_icon_view_buildable_custom_tag_end; +} + static void gtk_icon_view_cell_layout_init (GtkCellLayoutIface *iface) { @@ -9236,5 +9262,38 @@ gtk_icon_view_get_accessible (GtkWidget *widget) return (* GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->get_accessible) (widget); } +static gboolean +gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + if (parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, + tagname, parser, data); +} + +static void +gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + if (strcmp (tagname, "attributes") == 0) + _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, + data); + else + parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, + data); +} + + + #define __GTK_ICON_VIEW_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c index dc5ebbfa9a..c012afbda3 100644 --- a/gtk/gtkliststore.c +++ b/gtk/gtkliststore.c @@ -18,6 +18,8 @@ */ #include +#include +#include #include #include #include "gtktreemodel.h" @@ -25,6 +27,7 @@ #include "gtktreedatalist.h" #include "gtktreednd.h" #include "gtkintl.h" +#include "gtkbuildable.h" #include "gtkalias.h" #define GTK_LIST_STORE_IS_SORTED(list) (((GtkListStore*)(list))->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) @@ -34,6 +37,7 @@ static void gtk_list_store_tree_model_init (GtkTreeModelIface *iface); static void gtk_list_store_drag_source_init(GtkTreeDragSourceIface *iface); static void gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface); static void gtk_list_store_sortable_init (GtkTreeSortableIface *iface); +static void gtk_list_store_buildable_init (GtkBuildableIface *iface); static void gtk_list_store_finalize (GObject *object); static GtkTreeModelFlags gtk_list_store_get_flags (GtkTreeModel *tree_model); static gint gtk_list_store_get_n_columns (GtkTreeModel *tree_model); @@ -114,6 +118,19 @@ static void gtk_list_store_set_default_sort_func (GtkTreeSortable *so static gboolean gtk_list_store_has_default_sort_func (GtkTreeSortable *sortable); +/* buildable */ +static gboolean gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + G_DEFINE_TYPE_WITH_CODE (GtkListStore, gtk_list_store, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, gtk_list_store_tree_model_init) @@ -122,7 +139,10 @@ G_DEFINE_TYPE_WITH_CODE (GtkListStore, gtk_list_store, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, gtk_list_store_drag_dest_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - gtk_list_store_sortable_init)) + gtk_list_store_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_list_store_buildable_init)) + static void gtk_list_store_class_init (GtkListStoreClass *class) @@ -176,6 +196,13 @@ gtk_list_store_sortable_init (GtkTreeSortableIface *iface) iface->has_default_sort_func = gtk_list_store_has_default_sort_func; } +void +gtk_list_store_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_list_store_buildable_custom_tag_start; + iface->custom_tag_end = gtk_list_store_buildable_custom_tag_end; +} + static void gtk_list_store_init (GtkListStore *list_store) { @@ -2008,5 +2035,252 @@ gtk_list_store_insert_with_valuesv (GtkListStore *list_store, gtk_tree_path_free (path); } +/* GtkBuildable custom tag implementation + * + * + * + * + * + */ +typedef struct { + GtkBuilder *builder; + GObject *object; + GSList *column_type_names; + GType *column_types; + GValue *values; + gint *columns; + gint last_row; + gint n_columns; + gint row_column; + GQuark error_quark; + gboolean is_data; +} SubParserData; + +static void +list_store_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + guint i; + SubParserData *data = (SubParserData*)user_data; + + if (strcmp (element_name, "col") == 0) + { + int i, id = -1; + + if (data->row_column >= data->n_columns) + g_set_error (error, data->error_quark, 0, + "Too many columns, maximum is %d\n", data->n_columns - 1); + + for (i = 0; names[i]; i++) + if (strcmp (names[i], "id") == 0) + { + errno = 0; + id = atoi (values[i]); + if (errno) + g_set_error (error, data->error_quark, 0, + "the id tag %s could not be converted to an integer", values[i]); + } + + if (id == -1) + g_set_error (error, data->error_quark, 0, + " needs an id attribute"); + + data->columns[data->row_column] = id; + data->row_column++; + data->is_data = TRUE; + } + else if (strcmp (element_name, "row") == 0) + ; + else if (strcmp (element_name, "column") == 0) + for (i = 0; names[i]; i++) + if (strcmp (names[i], "type") == 0) + data->column_type_names = g_slist_prepend (data->column_type_names, + g_strdup (values[i])); + else if (strcmp (element_name, "columns") == 0) + ; + else if (strcmp (element_name, "data") == 0) + ; + else + g_set_error (error, data->error_quark, 0, + "Unknown start tag: %s", element_name); +} + +static void +list_store_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + + g_assert (data->builder); + + if (strcmp (element_name, "row") == 0) + { + GtkTreeIter iter; + int i; + + gtk_list_store_insert_with_valuesv (GTK_LIST_STORE (data->object), + &iter, + data->last_row, + data->columns, + data->values, + data->row_column); + for (i = 0; i < data->row_column; i++) + g_value_unset (&data->values[i]); + g_free (data->values); + data->values = g_new0 (GValue, data->n_columns); + data->last_row++; + data->row_column = 0; + } + else if (strcmp (element_name, "columns") == 0) + { + GType *column_types; + GSList *l; + int i; + GType type; + + data->column_type_names = g_slist_reverse (data->column_type_names); + column_types = g_new0 (GType, g_slist_length (data->column_type_names)); + + for (l = data->column_type_names, i = 0; l; l = l->next, i++) + { + type = gtk_builder_get_type_from_name (data->builder, l->data); + if (type == G_TYPE_INVALID) + { + g_warning ("Unknown type %s specified in treemodel %s", + (const gchar*)l->data, + gtk_buildable_get_name (GTK_BUILDABLE (data->object))); + continue; + } + column_types[i] = type; + + g_free (l->data); + } + + gtk_list_store_set_column_types (GTK_LIST_STORE (data->object), i, + column_types); + + g_free (column_types); + } + else if (strcmp (element_name, "col") == 0) + data->is_data = FALSE; + else if (strcmp (element_name, "data") == 0) + ; + else if (strcmp (element_name, "column") == 0) + ; + else + g_set_error (error, data->error_quark, 0, + "Unknown end tag: %s", element_name); +} + +static void +list_store_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + SubParserData *data = (SubParserData*)user_data; + gint i; + + if (!data->is_data) + return; + + i = data->row_column - 1; + + if (!gtk_builder_value_from_string_type (data->column_types[i], + text, + &data->values[i])) + g_error ("Could not convert '%s' to type %s\n", + text, g_type_name (data->column_types[i])); +} + +static const GMarkupParser list_store_parser = + { + list_store_start_element, + list_store_end_element, + list_store_text + }; + +static gboolean +gtk_list_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + SubParserData *parser_data; + + if (child) + return FALSE; + + if (strcmp (tagname, "columns") == 0) + { + + parser_data = g_slice_new0 (SubParserData); + parser_data->builder = builder; + parser_data->object = G_OBJECT (buildable); + parser_data->column_type_names = NULL; + + *parser = list_store_parser; + *data = parser_data; + return TRUE; + } + else if (strcmp (tagname, "data") == 0) + { + gint n_columns = gtk_list_store_get_n_columns (GTK_TREE_MODEL (buildable)); + if (n_columns == 0) + g_error ("Cannot append data to an empty model"); + + parser_data = g_slice_new0 (SubParserData); + parser_data->builder = builder; + parser_data->object = G_OBJECT (buildable); + parser_data->values = g_new0 (GValue, n_columns); + parser_data->columns = g_new0 (gint, n_columns); + parser_data->column_types = GTK_LIST_STORE (buildable)->column_headers; + parser_data->n_columns = n_columns; + parser_data->last_row = 0; + parser_data->error_quark = g_quark_from_static_string ("GtkListStore"); + + *parser = list_store_parser; + *data = parser_data; + return TRUE; + } + else + g_warning ("Unknown custom list store tag: %s", tagname); + + return FALSE; +} + +static void +gtk_list_store_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + SubParserData *sub = (SubParserData*)data; + + if (strcmp (tagname, "columns") == 0) + { + g_slist_free (sub->column_type_names); + g_slice_free (SubParserData, sub); + } + else if (strcmp (tagname, "data") == 0) + { + g_free (sub->columns); + g_free (sub->values); + g_slice_free (SubParserData, sub); + } + else + g_warning ("Unknown custom list store tag: %s", tagname); +} + #define __GTK_LIST_STORE_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index d5e9fc20b9..ca9e874bc2 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -162,7 +162,8 @@ static const GDebugKey gtk_debug_keys[] = { {"modules", GTK_DEBUG_MODULES}, {"geometry", GTK_DEBUG_GEOMETRY}, {"icontheme", GTK_DEBUG_ICONTHEME}, - {"printing", GTK_DEBUG_PRINTING} + {"printing", GTK_DEBUG_PRINTING}, + {"builder", GTK_DEBUG_BUILDER} }; #endif /* G_ENABLE_DEBUG */ @@ -658,7 +659,7 @@ do_post_parse_initialization (int *argc, } gtk_type_init (0); - _gtk_accel_map_init (); + _gtk_accel_map_init (); _gtk_rc_init (); /* Set the 'initialized' flag. diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 227a4972e7..4297456f1f 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -26,6 +26,7 @@ */ #include +#include #include "gtknotebook.h" #include "gtkmain.h" #include "gtkmenu.h" @@ -38,6 +39,7 @@ #include "gtkbindings.h" #include "gtkprivate.h" #include "gtkdnd.h" +#include "gtkbuildable.h" #include "gtkalias.h" #define SCROLL_DELAY_FACTOR 5 @@ -421,6 +423,12 @@ static void do_detach_tab (GtkNotebook *from, gint x, gint y); +/* GtkBuildable */ +static void gtk_notebook_buildable_init (GtkBuildableIface *iface); +static void gtk_notebook_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); static GtkNotebookWindowCreationFunc window_creation_hook = NULL; static gpointer window_creation_hook_data; @@ -428,7 +436,9 @@ static GDestroyNotify window_creation_hook_destroy = NULL; static guint notebook_signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER) +G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_notebook_buildable_init)) static void add_tab_bindings (GtkBindingSet *binding_set, @@ -1095,6 +1105,36 @@ gtk_notebook_init (GtkNotebook *notebook) gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE); } +static void +gtk_notebook_buildable_init (GtkBuildableIface *iface) +{ + iface->add = gtk_notebook_buildable_add; +} + +static void +gtk_notebook_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + GtkNotebook *notebook = GTK_NOTEBOOK (buildable); + + if (type && strcmp (type, "tab") == 0) + { + GtkWidget * page; + + page = gtk_notebook_get_nth_page (notebook, -1); + /* To set the tab label widget, we must have already a child + * inside the tab container. */ + g_assert (page != NULL); + gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child)); + } + else if (!type) + gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL); + else + GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type); +} + static gboolean gtk_notebook_select_page (GtkNotebook *notebook, gboolean move_focus) diff --git a/gtk/gtksizegroup.c b/gtk/gtksizegroup.c index af404f0a7c..477c45f5f1 100644 --- a/gtk/gtksizegroup.c +++ b/gtk/gtksizegroup.c @@ -19,10 +19,12 @@ */ #include +#include #include "gtkcontainer.h" #include "gtkintl.h" #include "gtkprivate.h" #include "gtksizegroup.h" +#include "gtkbuildable.h" #include "gtkalias.h" enum { @@ -49,6 +51,20 @@ static void add_widget_to_closure (GtkWidget *widget, GSList **groups, GSList **widgets); +/* GtkBuildable */ +static void gtk_size_group_buildable_init (GtkBuildableIface *iface); +static gboolean gtk_size_group_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_size_group_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data); + static GQuark size_groups_quark; static const gchar size_groups_tag[] = "gtk-size-groups"; @@ -310,7 +326,16 @@ gtk_size_group_init (GtkSizeGroup *size_group) size_group->ignore_hidden = 0; } -G_DEFINE_TYPE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT) +static void +gtk_size_group_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start; + iface->custom_finished = gtk_size_group_buildable_custom_finished; +} + +G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_size_group_buildable_init)) static void gtk_size_group_set_property (GObject *object, @@ -813,5 +838,101 @@ _gtk_size_group_queue_resize (GtkWidget *widget) queue_resize_on_widget (widget, TRUE); } +typedef struct { + GObject *object; + GSList *items; +} GSListSubParserData; + +static void +size_group_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + guint i; + GSListSubParserData *data = (GSListSubParserData*)user_data; + + if (strcmp (element_name, "widget") == 0) + for (i = 0; names[i]; i++) + if (strcmp (names[i], "name") == 0) + data->items = g_slist_prepend (data->items, g_strdup (values[i])); + else if (strcmp (element_name, "widgets") == 0) + return; + else + g_warning ("Unsupported type tag for GtkSizeGroup: %s\n", + element_name); + +} + +static const GMarkupParser size_group_parser = + { + size_group_start_element + }; + +static gboolean +gtk_size_group_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + GSListSubParserData *parser_data; + + if (child) + return FALSE; + + if (strcmp (tagname, "widgets") == 0) + { + parser_data = g_slice_new0 (GSListSubParserData); + parser_data->items = NULL; + parser_data->object = G_OBJECT (buildable); + + *parser = size_group_parser; + *data = parser_data; + return TRUE; + } + + return FALSE; +} + +static void +gtk_size_group_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data) +{ + GSList *l; + GSListSubParserData *data; + GObject *object; + + if (strcmp (tagname, "widgets")) + return; + + data = (GSListSubParserData*)user_data; + data->items = g_slist_reverse (data->items); + + for (l = data->items; l; l = l->next) + { + object = gtk_builder_get_object (builder, l->data); + if (!object) + { + g_warning ("Unknown object %s specified in sizegroup %s", + (const gchar*)l->data, + gtk_buildable_get_name (GTK_BUILDABLE (data->object))); + continue; + } + gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object), + GTK_WIDGET (object)); + g_free (l->data); + } + g_slist_free (data->items); + g_slice_free (GSListSubParserData, data); +} + + #define __GTK_SIZE_GROUP_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtktreestore.c b/gtk/gtktreestore.c index 4379af70bb..730c942f5b 100644 --- a/gtk/gtktreestore.c +++ b/gtk/gtktreestore.c @@ -24,6 +24,7 @@ #include "gtktreestore.h" #include "gtktreedatalist.h" #include "gtktreednd.h" +#include "gtkbuildable.h" #include "gtkintl.h" #include "gtkalias.h" @@ -35,6 +36,7 @@ static void gtk_tree_store_tree_model_init (GtkTreeModelIface *iface); static void gtk_tree_store_drag_source_init(GtkTreeDragSourceIface *iface); static void gtk_tree_store_drag_dest_init (GtkTreeDragDestIface *iface); static void gtk_tree_store_sortable_init (GtkTreeSortableIface *iface); +static void gtk_tree_store_buildable_init (GtkBuildableIface *iface); static void gtk_tree_store_finalize (GObject *object); static GtkTreeModelFlags gtk_tree_store_get_flags (GtkTreeModel *tree_model); static gint gtk_tree_store_get_n_columns (GtkTreeModel *tree_model); @@ -115,6 +117,21 @@ static void gtk_tree_store_set_default_sort_func (GtkTreeSortable * GtkDestroyNotify destroy); static gboolean gtk_tree_store_has_default_sort_func (GtkTreeSortable *sortable); + +/* buildable */ + +static gboolean gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_tree_store_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data); + static void validate_gnode (GNode *node); static void gtk_tree_store_move (GtkTreeStore *tree_store, @@ -142,7 +159,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkTreeStore, gtk_tree_store, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, gtk_tree_store_drag_dest_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - gtk_tree_store_sortable_init)) + gtk_tree_store_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_tree_store_buildable_init)) static void gtk_tree_store_class_init (GtkTreeStoreClass *class) @@ -196,6 +215,13 @@ gtk_tree_store_sortable_init (GtkTreeSortableIface *iface) iface->has_default_sort_func = gtk_tree_store_has_default_sort_func; } +void +gtk_tree_store_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_tree_store_buildable_custom_tag_start; + iface->custom_finished = gtk_tree_store_buildable_custom_finished; +} + static void gtk_tree_store_init (GtkTreeStore *tree_store) { @@ -3173,5 +3199,110 @@ validate_gnode (GNode* node) } } +/* GtkBuildable custom tag implementation + * + * + * + * + * + */ +typedef struct { + GObject *object; + GSList *items; +} GSListSubParserData; + +static void +tree_model_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + guint i; + GSListSubParserData *data = (GSListSubParserData*)user_data; + + for (i = 0; names[i]; i++) + { + if (strcmp (names[i], "type") == 0) + data->items = g_slist_prepend (data->items, g_strdup (values[i])); + } +} + +static const GMarkupParser tree_model_parser = + { + tree_model_start_element + }; + + +static gboolean +gtk_tree_store_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + GSListSubParserData *parser_data; + + if (child) + return FALSE; + + if (strcmp (tagname, "columns") == 0) + { + parser_data = g_slice_new0 (GSListSubParserData); + parser_data->items = NULL; + parser_data->object = G_OBJECT (buildable); + + *parser = tree_model_parser; + *data = parser_data; + return TRUE; + } + + return FALSE; +} + +static void +gtk_tree_store_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data) +{ + GSList *l; + GSListSubParserData *data; + GType *types; + int i; + GType type; + + if (strcmp (tagname, "columns")) + return; + + data = (GSListSubParserData*)user_data; + data->items = g_slist_reverse (data->items); + types = g_new0 (GType, g_slist_length (data->items)); + + for (l = data->items, i = 0; l; l = l->next, i++) + { + type = gtk_builder_get_type_from_name (builder, l->data); + if (type == G_TYPE_INVALID) + { + g_warning ("Unknown type %s specified in treemodel %s", + (const gchar*)l->data, + gtk_buildable_get_name (GTK_BUILDABLE (data->object))); + continue; + } + types[i] = type; + + g_free (l->data); + } + + gtk_tree_store_set_column_types (GTK_TREE_STORE (data->object), i, types); + + g_free (types); + g_slist_free (data->items); + g_slice_free (GSListSubParserData, data); +} + #define __GTK_TREE_STORE_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 268389808f..474c2f0387 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -29,6 +29,7 @@ #include "gtkcellrenderer.h" #include "gtkmain.h" #include "gtkmarshalers.h" +#include "gtkbuildable.h" #include "gtkbutton.h" #include "gtkalignment.h" #include "gtklabel.h" @@ -460,6 +461,14 @@ static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView *tree GtkTreeViewColumn *column, gint drop_position); +/* GtkBuildable */ +static void gtk_tree_view_buildable_add (GtkBuildable *tree_view, + GtkBuilder *builder, + GObject *child, + const gchar *type); +static void gtk_tree_view_buildable_init (GtkBuildableIface *iface); + + static gboolean scroll_row_timeout (gpointer data); static void add_scroll_timeout (GtkTreeView *tree_view); static void remove_scroll_timeout (GtkTreeView *tree_view); @@ -471,7 +480,9 @@ static guint tree_view_signals [LAST_SIGNAL] = { 0 }; /* GType Methods */ -G_DEFINE_TYPE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER) +G_DEFINE_TYPE_WITH_CODE (GtkTreeView, gtk_tree_view, GTK_TYPE_CONTAINER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_tree_view_buildable_init)) static void gtk_tree_view_class_init (GtkTreeViewClass *class) @@ -1281,6 +1292,12 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) g_type_class_add_private (o_class, sizeof (GtkTreeViewPrivate)); } +static void +gtk_tree_view_buildable_init (GtkBuildableIface *iface) +{ + iface->add = gtk_tree_view_buildable_add; +} + static void gtk_tree_view_init (GtkTreeView *tree_view) { @@ -1489,6 +1506,15 @@ gtk_tree_view_finalize (GObject *object) +static void +gtk_tree_view_buildable_add (GtkBuildable *tree_view, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), GTK_TREE_VIEW_COLUMN (child)); +} + /* GtkObject Methods */ diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c index f2d2b9a421..86be0371b7 100644 --- a/gtk/gtktreeviewcolumn.c +++ b/gtk/gtktreeviewcolumn.c @@ -151,12 +151,17 @@ static GList *gtk_tree_view_column_cell_prev (GtkTreeViewColum GList *current); static void gtk_tree_view_column_clear_attributes_by_info (GtkTreeViewColumn *tree_column, GtkTreeViewColumnCellInfo *info); +/* GtkBuildable implementation */ +static void gtk_tree_view_column_buildable_init (GtkBuildableIface *iface); static guint tree_column_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, GTK_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, - gtk_tree_view_column_cell_layout_init)) + gtk_tree_view_column_cell_layout_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_tree_view_column_buildable_init)) + static void gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class) @@ -323,6 +328,14 @@ gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class) } +static void +gtk_tree_view_column_buildable_init (GtkBuildableIface *iface) +{ + iface->add = _gtk_cell_layout_buildable_add; + iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start; + iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end; +} + static void gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface) { diff --git a/gtk/gtkuimanager.c b/gtk/gtkuimanager.c index 529c296035..d1a5ce4906 100644 --- a/gtk/gtkuimanager.c +++ b/gtk/gtkuimanager.c @@ -31,6 +31,7 @@ #include #include +#include "gtkbuildable.h" #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkmenu.h" @@ -142,6 +143,28 @@ static void node_prepend_ui_reference (GNode *node, static void node_remove_ui_reference (GNode *node, guint merge_id); +/* GtkBuildable */ +static void gtk_ui_manager_buildable_init (GtkBuildableIface *iface); +static void gtk_ui_manager_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); +static GObject* gtk_ui_manager_buildable_construct_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name); +static gboolean gtk_ui_manager_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_ui_manager_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + + enum { @@ -163,7 +186,9 @@ enum static guint ui_manager_signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkUIManager, gtk_ui_manager, G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_CODE (GtkUIManager, gtk_ui_manager, GTK_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_ui_manager_buildable_init)) static void gtk_ui_manager_class_init (GtkUIManagerClass *klass) @@ -400,6 +425,52 @@ gtk_ui_manager_finalize (GObject *object) G_OBJECT_CLASS (gtk_ui_manager_parent_class)->finalize (object); } +static void +gtk_ui_manager_buildable_init (GtkBuildableIface *iface) +{ + iface->add = gtk_ui_manager_buildable_add; + iface->construct_child = gtk_ui_manager_buildable_construct_child; + iface->custom_tag_start = gtk_ui_manager_buildable_custom_tag_start; + iface->custom_tag_end = gtk_ui_manager_buildable_custom_tag_end; +} + +static void +gtk_ui_manager_buildable_add (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + GtkUIManager *self = GTK_UI_MANAGER (buildable); + guint pos; + + g_return_if_fail (GTK_IS_ACTION_GROUP (child)); + + pos = g_list_length (self->private_data->action_groups); + + g_object_ref (child); + gtk_ui_manager_insert_action_group (self, + GTK_ACTION_GROUP (child), + pos); +} + +static GObject * +gtk_ui_manager_buildable_construct_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *id) +{ + GtkWidget *widget; + char *name; + + name = g_strdup_printf ("ui/%s", id); + widget = gtk_ui_manager_get_widget (GTK_UI_MANAGER (buildable), name); + if (!widget) + g_error ("Unknown ui manager child: %s\n", name); + + g_free (name); + + return G_OBJECT (widget); +} + static void gtk_ui_manager_set_property (GObject *object, guint prop_id, @@ -1479,7 +1550,7 @@ add_ui_from_string (GtkUIManager *self, queue_update (self); - g_object_notify (G_OBJECT (self), "ui"); + g_object_notify (G_OBJECT (self), "ui"); return ctx.merge_id; @@ -2818,6 +2889,49 @@ print_node (GtkUIManager *self, g_string_append_printf (buffer, close_fmt, indent_level, ""); } +static gboolean +gtk_ui_manager_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + if (child) + return FALSE; + + if (strcmp (tagname, "ui") == 0) + { + ParseContext *ctx; + + ctx = g_new0 (ParseContext, 1); + ctx->state = STATE_START; + ctx->self = GTK_UI_MANAGER (buildable); + ctx->current = NULL; + ctx->merge_id = gtk_ui_manager_new_merge_id (GTK_UI_MANAGER (buildable)); + + *data = ctx; + *parser = ui_parser; + + return TRUE; + } + + return FALSE; + +} + +static void +gtk_ui_manager_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + queue_update (GTK_UI_MANAGER (buildable)); + g_object_notify (G_OBJECT (buildable), "ui"); + g_free (data); +} + /** * gtk_ui_manager_get_ui: * @self: a #GtkUIManager diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 40917ed5f8..1d47c8c315 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -52,6 +52,7 @@ #include "gtktooltips.h" #include "gtktooltip.h" #include "gtkinvisible.h" +#include "gtkbuildable.h" #include "gtkalias.h" #define WIDGET_CLASS(w) GTK_WIDGET_GET_CLASS (w) @@ -245,6 +246,28 @@ static gboolean gtk_widget_real_can_activate_accel (GtkWidget *widg static void gtk_widget_set_has_tooltip (GtkWidget *widget, gboolean has_tooltip, gboolean force); +static void gtk_widget_buildable_interface_init (GtkBuildableIface *iface); +static void gtk_widget_buildable_set_name (GtkBuildable *buildable, + const gchar *name); +static const gchar * gtk_widget_buildable_get_name (GtkBuildable *buildable); +static void gtk_widget_buildable_set_property (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name, + const GValue *value); +static gboolean gtk_widget_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_widget_buildable_custom_finshed (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data); +static void gtk_widget_buildable_parser_finshed (GtkBuildable *buildable, + GtkBuilder *builder); + static void gtk_widget_set_usize_internal (GtkWidget *widget, gint width, @@ -311,11 +334,20 @@ gtk_widget_get_type (void) NULL /* interface data */ }; + const GInterfaceInfo buildable_info = + { + (GInterfaceInitFunc) gtk_widget_buildable_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL /* interface data */ + }; + widget_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkWidget", &widget_info, G_TYPE_FLAG_ABSTRACT); g_type_add_interface_static (widget_type, ATK_TYPE_IMPLEMENTOR, &accessibility_info) ; + g_type_add_interface_static (widget_type, GTK_TYPE_BUILDABLE, + &buildable_info) ; } @@ -8360,6 +8392,176 @@ gtk_widget_ref_accessible (AtkImplementor *implementor) return accessible; } +/* + * GtkBuildable implementation + */ +static GQuark quark_builder_has_default = 0; +static GQuark quark_builder_has_focus = 0; + +static void +gtk_widget_buildable_interface_init (GtkBuildableIface *iface) +{ + quark_builder_has_default = g_quark_from_static_string ("gtk-builder-has-default"); + quark_builder_has_focus = g_quark_from_static_string ("gtk-builder-has-focus"); + + iface->set_name = gtk_widget_buildable_set_name; + iface->get_name = gtk_widget_buildable_get_name; + iface->set_property = gtk_widget_buildable_set_property; + iface->parser_finished = gtk_widget_buildable_parser_finshed; + iface->custom_tag_start = gtk_widget_buildable_custom_tag_start; + iface->custom_finished = gtk_widget_buildable_custom_finshed; +} + +static void +gtk_widget_buildable_set_name (GtkBuildable *buildable, + const gchar *name) +{ + gtk_widget_set_name (GTK_WIDGET (buildable), name); +} + +static const gchar * +gtk_widget_buildable_get_name (GtkBuildable *buildable) +{ + return gtk_widget_get_name (GTK_WIDGET (buildable)); +} + +static void +gtk_widget_buildable_set_property (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name, + const GValue *value) +{ + if (strcmp (name, "has-default") == 0 && g_value_get_boolean (value)) + g_object_set_qdata (G_OBJECT (buildable), quark_builder_has_default, + GINT_TO_POINTER (TRUE)); + else if (strcmp (name, "has-focus") == 0 && g_value_get_boolean (value)) + g_object_set_qdata (G_OBJECT (buildable), quark_builder_has_focus, + GINT_TO_POINTER (TRUE)); + else + g_object_set_property (G_OBJECT (buildable), name, value); +} + +static void +gtk_widget_buildable_parser_finshed (GtkBuildable *buildable, + GtkBuilder *builder) +{ + if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_default)) + gtk_widget_grab_default (GTK_WIDGET (buildable)); + if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_focus)) + gtk_widget_grab_focus (GTK_WIDGET (buildable)); +} + +typedef struct { + GObject *object; + guint key; + guint modifiers; + gchar *signal; +} AccelGroupParserData; + +static void +accel_group_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + gint i; + guint key = 0; + guint modifiers = 0; + gchar *signal = NULL; + AccelGroupParserData *parser_data = (AccelGroupParserData*)user_data; + + for (i = 0; names[i]; i++) + { + if (strcmp (names[i], "key") == 0) + key = gdk_keyval_from_name (values[i]); + else if (strcmp (names[i], "modifiers") == 0) + modifiers = _gtk_builder_flags_from_string (GDK_TYPE_MODIFIER_TYPE, values[i]); + else if (strcmp (names[i], "signal") == 0) + signal = g_strdup (values[i]); + } + + if (key == 0 || signal == NULL) + { + g_warning (" requires a key or signal attribute"); + return; + } + parser_data->key = key; + parser_data->modifiers = modifiers; + parser_data->signal = signal; +} + +static const GMarkupParser accel_group_parser = + { + accel_group_start_element, + }; + +static gboolean +gtk_widget_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + AccelGroupParserData *parser_data; + + g_assert (buildable); + + if (strcmp (tagname, "accelerator") == 0) + { + parser_data = g_new0 (AccelGroupParserData, 1); + parser_data->object = g_object_ref (buildable); + *parser = accel_group_parser; + *data = parser_data; + return TRUE; + } + return FALSE; +} + +static void +gtk_widget_buildable_custom_finshed (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data) +{ + AccelGroupParserData *data; + GtkWidget *toplevel; + GSList *accel_groups; + GtkAccelGroup *accel_group; + + if (strcmp (tagname, "accelerator") == 0) + { + data = (AccelGroupParserData*)user_data; + g_assert (data->object); + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (data->object)); + accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel)); + if (g_slist_length (accel_groups) == 0) + { + accel_group = gtk_accel_group_new (); + gtk_window_add_accel_group (GTK_WINDOW (toplevel), accel_group); + } + else + { + g_assert (g_slist_length (accel_groups) == 1); + accel_group = g_slist_nth_data (accel_groups, 0); + } + gtk_widget_add_accelerator (GTK_WIDGET(data->object), + data->signal, + accel_group, + data->key, + data->modifiers, + GTK_ACCEL_VISIBLE); + g_object_unref (data->object); + g_free (data->signal); + g_slice_free (AccelGroupParserData, data); + } +} + + /** * gtk_widget_get_clipboard: * @widget: a #GtkWidget @@ -8652,5 +8854,6 @@ gtk_widget_trigger_tooltip_query (GtkWidget *widget) gtk_tooltip_trigger_tooltip_query (gtk_widget_get_display (widget)); } + #define __GTK_WIDGET_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index a51c00a7cc..654ed152cb 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -46,6 +46,7 @@ #include "gtkicontheme.h" #include "gtkmarshalers.h" #include "gtkplug.h" +#include "gtkbuildable.h" #include "gtkalias.h" #ifdef GDK_WINDOWING_X11 @@ -182,6 +183,7 @@ struct _GtkWindowPrivate guint reset_type_hint : 1; guint opacity_set : 1; + guint builder_visible : 1; GdkWindowTypeHint type_hint; @@ -309,6 +311,8 @@ static GQuark quark_gtk_window_key_hash = 0; static GQuark quark_gtk_window_default_icon_pixmap = 0; static GQuark quark_gtk_window_icon_info = 0; +static GtkBuildableIface *parent_buildable_iface; + static void gtk_window_set_property (GObject *object, guint prop_id, const GValue *value, @@ -318,8 +322,19 @@ static void gtk_window_get_property (GObject *object, GValue *value, GParamSpec *pspec); +/* GtkBuildable */ +static void gtk_window_buildable_interface_init (GtkBuildableIface *iface); +static void gtk_window_buildable_set_property (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name, + const GValue *value); +static void gtk_window_buildable_parser_finished (GtkBuildable *buildable, + GtkBuilder *builder); -G_DEFINE_TYPE (GtkWindow, gtk_window, GTK_TYPE_BIN) + +G_DEFINE_TYPE_WITH_CODE (GtkWindow, gtk_window, GTK_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_window_buildable_interface_init)) static void add_tab_bindings (GtkBindingSet *binding_set, @@ -1114,6 +1129,39 @@ gtk_window_get_property (GObject *object, } } +static void +gtk_window_buildable_interface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->set_property = gtk_window_buildable_set_property; + iface->parser_finished = gtk_window_buildable_parser_finished; + +} + +static void +gtk_window_buildable_set_property (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *name, + const GValue *value) +{ + GtkWindowPrivate *priv = GTK_WINDOW_GET_PRIVATE (buildable); + + if (strcmp (name, "visible") == 0 && g_value_get_boolean (value)) + priv->builder_visible = TRUE; + else + parent_buildable_iface->set_property (buildable, builder, name, value); +} + +static void +gtk_window_buildable_parser_finished (GtkBuildable *buildable, + GtkBuilder *builder) +{ + GtkWindowPrivate *priv = GTK_WINDOW_GET_PRIVATE (buildable); + + if (priv->builder_visible) + gtk_widget_show (GTK_WIDGET (buildable)); +} + /** * gtk_window_new: * @type: type of window diff --git a/tests/Makefile.am b/tests/Makefile.am index 0f2a739eba..eed40262d8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,11 +24,12 @@ if USE_X11 testsocket_programs = testsocket testsocket_child endif -TESTS = floatingtest +TESTS = floatingtest buildertest noinst_PROGRAMS = \ autotestfilechooser \ floatingtest \ + buildertest \ simple \ print-editor \ testaccel \ @@ -91,6 +92,7 @@ noinst_PROGRAMS = \ autotestfilechooser_DEPENDENCIES = $(TEST_DEPS) simple_DEPENDENCIES = $(TEST_DEPS) floatingtest_DEPENDENCIES = $(TEST_DEPS) +buildertest_DEPENDENCIES = $(TEST_DEPS) print_editor_DEPENDENCIES = $(TEST_DEPS) testicontheme_DEPENDENCIES = $(TEST_DEPS) testiconview_DEPENDENCIES = $(TEST_DEPS) @@ -146,6 +148,7 @@ testvolumebutton_DEPENDENCIES = $(TEST_DEPS) autotestfilechooser_LDADD = $(LDADDS) simple_LDADD = $(LDADDS) floatingtest_LDADD = $(LDADDS) +buildertest_LDADD = $(LDADDS) print_editor_LDADD = $(LDADDS) testaccel_LDADD = $(LDADDS) testassistant_LDADD = $(LDADDS) @@ -205,6 +208,8 @@ testgrouping_LDADD = $(LDADDS) testtooltips_LDADD = $(LDADDS) testvolumebutton_LDADD = $(LDADDS) +buildertest_LDFLAGS = -export-dynamic + autotestfilechooser_SOURCES = \ autotestfilechooser.c diff --git a/tests/buildertest.c b/tests/buildertest.c new file mode 100644 index 0000000000..b5fbc6bf5b --- /dev/null +++ b/tests/buildertest.c @@ -0,0 +1,1462 @@ +/* buildertest.c + * Copyright (C) 2006-2007 Async Open Source + * Authors: Johan Dahlin + * Henrique Romano + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include +#include +#include + +static GtkBuilder * +builder_new_from_string (const gchar *buffer, + gsize length, + gchar *domain) +{ + GtkBuilder *builder; + builder = gtk_builder_new (); + if (domain) + gtk_builder_set_translation_domain (builder, domain); + gtk_builder_add_from_string (builder, buffer, length, NULL); + return builder; +} + +gboolean test_parser (void) +{ + GtkBuilder *builder; + GError *error; + + builder = gtk_builder_new (); + + error = NULL; + gtk_builder_add_from_string (builder, "", -1, &error); + g_assert (error != NULL); + g_return_val_if_fail (strcmp (error->message, "Invalid root element: 'xxx'") == 0, FALSE); + g_error_free (error); + + error = NULL; + gtk_builder_add_from_string (builder, "", -1, &error); + g_assert (error != NULL); + g_return_val_if_fail (strcmp (error->message, ":1:24 'X' is not a valid attribute of ") == 0, FALSE); + g_error_free (error); + + error = NULL; + gtk_builder_add_from_string (builder, "", -1, &error); + g_assert (error != NULL); + g_return_val_if_fail (strcmp (error->message, ":1:19 'child' is not a valid tag here, expected a 'object' tag") == 0, FALSE); + g_error_free (error); + + error = NULL; + gtk_builder_add_from_string (builder, "", -1, &error); + g_assert (error != NULL); + g_return_val_if_fail (strcmp (error->message, ":1:74 'object' is not a valid tag here") == 0, FALSE); + g_error_free (error); + + return TRUE; +} + + int normal; +int after; +int object; +int object_after; + +void +signal_normal (GtkWindow *window, GParamSpec spec) +{ + g_assert (GTK_IS_WINDOW (window)); + g_assert (normal == 0); + g_assert (after == 0); + + normal++; +} + +void +signal_after (GtkWindow *window, GParamSpec spec) +{ + g_assert (GTK_IS_WINDOW (window)); + g_assert (normal == 1); + g_assert (after == 0); + + after++; +} + +void +signal_object (GtkButton *button, GParamSpec spec) +{ + g_assert (GTK_IS_BUTTON (button)); + g_assert (object == 0); + g_assert (object_after == 0); + + object++; +} + +void +signal_object_after (GtkButton *button, GParamSpec spec) +{ + g_assert (GTK_IS_BUTTON (button)); + g_assert (object == 1); + g_assert (object_after == 0); + + object_after++; +} + +void +signal_first (GtkButton *button, GParamSpec spec) +{ + g_assert (normal == 0); + normal = 10; +} + +void +signal_second (GtkButton *button, GParamSpec spec) +{ + g_assert (normal == 10); + normal = 20; +} + +void +signal_extra (GtkButton *button, GParamSpec spec) +{ + g_assert (normal == 20); + normal = 30; +} + +void +signal_extra2 (GtkButton *button, GParamSpec spec) +{ + g_assert (normal == 30); + normal = 40; +} + +gboolean test_connect_signals (void) +{ + GtkBuilder *builder; + GObject *window; + const gchar buffer[] = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + const gchar buffer_order[] = + "" + " " + " " + " " + " " + ""; + const gchar buffer_extra[] = + "" + " " + " " + " " + ""; + const gchar buffer_extra2[] = + "" + " " + " " + " " + ""; + + builder = builder_new_from_string (buffer, -1, NULL); + gtk_builder_connect_signals (builder, NULL); + + window = gtk_builder_get_object (builder, "window1"); + gtk_window_set_title (GTK_WINDOW (window), "test"); + + g_return_val_if_fail (normal == 1, FALSE); + g_return_val_if_fail (after == 1, FALSE); + g_return_val_if_fail (object == 1, FALSE); + g_return_val_if_fail (object_after == 1, FALSE); + + g_object_unref (builder); + + builder = builder_new_from_string (buffer_order, -1, NULL); + gtk_builder_connect_signals (builder, NULL); + window = gtk_builder_get_object (builder, "window1"); + normal = 0; + gtk_window_set_title (GTK_WINDOW (window), "test"); + g_assert (normal == 20); + + gtk_builder_add_from_string (builder, buffer_extra, + strlen (buffer_extra), NULL); + gtk_builder_add_from_string (builder, buffer_extra2, + strlen (buffer_extra2), NULL); + gtk_builder_connect_signals (builder, NULL); + window = gtk_builder_get_object (builder, "window2"); + gtk_window_set_title (GTK_WINDOW (window), "test"); + g_assert (normal == 30); + window = gtk_builder_get_object (builder, "window3"); + gtk_window_set_title (GTK_WINDOW (window), "test"); + g_assert (normal == 40); + + g_object_unref (builder); + + return TRUE; +} + +gboolean test_uimanager_simple (void) +{ + GtkBuilder *builder; + GObject *uimgr, *menubar; + GObject *menu, *label; + GList *children; + const gchar buffer[] = + "" + " " + ""; + + const gchar buffer2[] = + "" + " " + " " + " " + " " + " " + " _File" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + + builder = builder_new_from_string (buffer, -1, NULL); + + uimgr = gtk_builder_get_object (builder, "uimgr1"); + g_return_val_if_fail (uimgr != NULL, FALSE); + g_return_val_if_fail (GTK_IS_UI_MANAGER (uimgr), FALSE); + + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + + menubar = gtk_builder_get_object (builder, "menubar1"); + g_return_val_if_fail (menubar != NULL, FALSE); + g_return_val_if_fail (GTK_IS_MENU_BAR (menubar), FALSE); + + children = gtk_container_get_children (GTK_CONTAINER (menubar)); + menu = children->data; + g_return_val_if_fail (menu != NULL, FALSE); + g_return_val_if_fail (GTK_IS_MENU_ITEM (menu), FALSE); + g_return_val_if_fail (strcmp (GTK_WIDGET (menu)->name, "file") == 0, FALSE); + g_list_free (children); + + label = G_OBJECT (GTK_BIN (menu)->child); + g_return_val_if_fail (label != NULL, FALSE); + g_return_val_if_fail (GTK_IS_LABEL (label), FALSE); + g_return_val_if_fail + (strcmp (gtk_label_get_text (GTK_LABEL (label)), "File") == 0, FALSE); + + g_object_unref (builder); + + return TRUE; +} + +gboolean test_domain (void) +{ + GtkBuilder *builder; + const gchar buffer1[] = ""; + const gchar buffer2[] = ""; + const gchar *domain; + + builder = builder_new_from_string (buffer1, -1, NULL); + domain = gtk_builder_get_translation_domain (builder); + g_assert (domain == NULL); + g_object_unref (builder); + + builder = builder_new_from_string (buffer1, -1, "domain-1"); + domain = gtk_builder_get_translation_domain (builder); + g_assert (domain); + g_assert (strcmp (domain, "domain-1") == 0); + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + domain = gtk_builder_get_translation_domain (builder); + g_assert (domain); + g_assert (strcmp (domain, "domain") == 0); + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, "domain-1"); + domain = gtk_builder_get_translation_domain (builder); + g_assert (domain); + g_assert (strcmp (domain, "domain-1") == 0); + g_object_unref (builder); + + return TRUE; +} + +#if 0 +gboolean test_translation (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + " " + " " + " " + " File" + " " + " " + " " + ""; + GtkLabel *label; + + setlocale (LC_ALL, "sv_SE"); + textdomain ("builder"); + bindtextdomain ("builder", "tests"); + + builder = builder_new_from_string (buffer, -1, NULL); + label = GTK_LABEL (gtk_builder_get_object (builder, "label")); + g_assert (strcmp (gtk_label_get_text (label), "Arkiv") == 0); + g_object_unref (builder); + + return TRUE; +} +#endif + +gboolean test_sizegroup (void) +{ + GtkBuilder * builder; + const gchar buffer1[] = + "" + " " + " GTK_SIZE_GROUP_HORIZONTAL" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + const gchar buffer2[] = + "" + " " + " GTK_SIZE_GROUP_HORIZONTAL" + " " + " " + " " + ""; + const gchar buffer3[] = + "" + " " + " GTK_SIZE_GROUP_HORIZONTAL" + " " + " " + " " + " " + " " + " " + " GTK_SIZE_GROUP_HORIZONTAL" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + GObject *sizegroup; + GSList *widgets; + + builder = builder_new_from_string (buffer1, -1, NULL); + sizegroup = gtk_builder_get_object (builder, "sizegroup1"); + widgets = gtk_size_group_get_widgets (GTK_SIZE_GROUP (sizegroup)); + g_return_val_if_fail (g_slist_length (widgets) == 2, FALSE); + g_slist_free (widgets); + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + sizegroup = gtk_builder_get_object (builder, "sizegroup1"); + widgets = gtk_size_group_get_widgets (GTK_SIZE_GROUP (sizegroup)); + g_return_val_if_fail (g_slist_length (widgets) == 0, FALSE); + g_slist_free (widgets); + g_object_unref (builder); + + builder = builder_new_from_string (buffer3, -1, NULL); + sizegroup = gtk_builder_get_object (builder, "sizegroup1"); + widgets = gtk_size_group_get_widgets (GTK_SIZE_GROUP (sizegroup)); + g_return_val_if_fail (g_slist_length (widgets) == 2, FALSE); + g_slist_free (widgets); + sizegroup = gtk_builder_get_object (builder, "sizegroup2"); + widgets = gtk_size_group_get_widgets (GTK_SIZE_GROUP (sizegroup)); + g_return_val_if_fail (g_slist_length (widgets) == 2, FALSE); + g_slist_free (widgets); + g_object_unref (builder); + + return TRUE; +} + +gboolean test_list_store (void) +{ + const gchar buffer1[] = + "" + " " + " " + " " + " " + " " + " " + ""; + const char buffer2[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " John" + " Doe" + " 25" + " " + " " + " Johan" + " Dole" + " 50" + " " + " " + " " + ""; + GtkBuilder *builder; + GObject *store; + GtkTreeIter iter; + gchar *surname, *lastname; + int age; + + builder = builder_new_from_string (buffer1, -1, NULL); + store = gtk_builder_get_object (builder, "liststore1"); + g_return_val_if_fail (gtk_tree_model_get_n_columns (GTK_TREE_MODEL (store)) == 2, FALSE); + g_return_val_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), 0) == G_TYPE_STRING, FALSE); + g_return_val_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), 1) == G_TYPE_UINT, FALSE); + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + store = gtk_builder_get_object (builder, "liststore1"); + g_return_val_if_fail (gtk_tree_model_get_n_columns (GTK_TREE_MODEL (store)) == 3, FALSE); + g_return_val_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), 0) == G_TYPE_STRING, FALSE); + g_return_val_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), 1) == G_TYPE_STRING, FALSE); + g_return_val_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), 2) == G_TYPE_INT, FALSE); + + g_return_val_if_fail (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter) == TRUE, FALSE); + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + 0, &surname, + 1, &lastname, + 2, &age, + -1); + g_assert (surname != NULL); + g_return_val_if_fail (strcmp (surname, "John") == 0, FALSE); + g_free (surname); + g_assert (lastname != NULL); + g_return_val_if_fail (strcmp (lastname, "Doe") == 0, FALSE); + g_free (lastname); + g_return_val_if_fail (age == 25, FALSE); + g_return_val_if_fail (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter) == TRUE, FALSE); + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + 0, &surname, + 1, &lastname, + 2, &age, + -1); + g_assert (surname != NULL); + g_return_val_if_fail (strcmp (surname, "Johan") == 0, FALSE); + g_free (surname); + g_assert (lastname != NULL); + g_return_val_if_fail (strcmp (lastname, "Dole") == 0, FALSE); + g_free (lastname); + g_return_val_if_fail (age == 50, FALSE); + g_return_val_if_fail (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter) == FALSE, FALSE); + + g_object_unref (builder); + + return TRUE; +} + +gboolean test_tree_store (void) +{ + const gchar buffer[] = + "" + " " + " " + " " + " " + " " + " " + ""; + GtkBuilder *builder; + GObject *store; + + builder = builder_new_from_string (buffer, -1, NULL); + store = gtk_builder_get_object (builder, "treestore1"); + g_return_val_if_fail (gtk_tree_model_get_n_columns (GTK_TREE_MODEL (store)) == 2, FALSE); + g_return_val_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), 0) == G_TYPE_STRING, FALSE); + g_return_val_if_fail (gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), 1) == G_TYPE_UINT, FALSE); + + g_object_unref (builder); + + return TRUE; +} + +gboolean test_types (void) +{ + const gchar buffer[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + GtkBuilder *builder; + + builder = builder_new_from_string (buffer, -1, NULL); + g_object_unref (builder); + + return TRUE; +} + +gboolean test_spin_button (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + "" + "0" + "10" + "2" + "3" + "5" + "1" + "" + "" + "True" + "adjustment1" + "" + ""; + GObject *object; + GtkAdjustment *adjustment; + gdouble value; + + builder = builder_new_from_string (buffer, -1, NULL); + object = gtk_builder_get_object (builder, "spinbutton1"); + g_assert (GTK_IS_SPIN_BUTTON (object)); + adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (object)); + g_assert (GTK_IS_ADJUSTMENT (adjustment)); + g_object_get (adjustment, "value", &value, NULL); + g_return_val_if_fail (value == 1, FALSE); + g_object_get (adjustment, "lower", &value, NULL); + g_return_val_if_fail (value == 0, FALSE); + g_object_get (adjustment, "upper", &value, NULL); + g_return_val_if_fail (value == 10, FALSE); + g_object_get (adjustment, "step-increment", &value, NULL); + g_return_val_if_fail (value == 2, FALSE); + g_object_get (adjustment, "page-increment", &value, NULL); + g_return_val_if_fail (value == 3, FALSE); + g_object_get (adjustment, "page-size", &value, NULL); + g_return_val_if_fail (value == 5, FALSE); + + g_object_unref (builder); + return TRUE; +} + +gboolean test_notebook (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + " " + " " + " " + " label1" + " " + " " + " " + " " + " tab_label1" + " " + " " + " " + " " + " label2" + " " + " " + " " + " " + " tab_label2" + " " + " " + " " + ""; + GObject *notebook; + GtkWidget *label; + + builder = builder_new_from_string (buffer, -1, NULL); + notebook = gtk_builder_get_object (builder, "notebook1"); + g_assert (notebook != NULL); + g_return_val_if_fail (gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) == 2, + FALSE); + + label = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 0); + g_return_val_if_fail (GTK_IS_LABEL (label), FALSE); + g_return_val_if_fail (strcmp (gtk_label_get_label (GTK_LABEL (label)), + "label1") == 0, FALSE); + label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), label); + g_return_val_if_fail (GTK_IS_LABEL (label), FALSE); + g_return_val_if_fail (strcmp (gtk_label_get_label (GTK_LABEL (label)), + "tab_label1") == 0, FALSE); + + label = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 1); + g_return_val_if_fail (GTK_IS_LABEL (label), FALSE); + g_return_val_if_fail (strcmp (gtk_label_get_label (GTK_LABEL (label)), + "label2") == 0, FALSE); + label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), label); + g_return_val_if_fail (GTK_IS_LABEL (label), FALSE); + g_return_val_if_fail (strcmp (gtk_label_get_label (GTK_LABEL (label)), + "tab_label2") == 0, FALSE); + + g_object_unref (builder); + return TRUE; +} + +gboolean test_construct_only_property (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + " " + " GTK_WINDOW_POPUP" + " " + ""; + const gchar buffer2[] = + "" + " " + " " + " tagtable1" + " " + ""; + GObject *widget, *tagtable, *textbuffer; + GtkWindowType type; + + builder = builder_new_from_string (buffer, -1, NULL); + widget = gtk_builder_get_object (builder, "window1"); + g_object_get (widget, "type", &type, NULL); + g_return_val_if_fail (type == GTK_WINDOW_POPUP, FALSE); + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + textbuffer = gtk_builder_get_object (builder, "textbuffer1"); + g_return_val_if_fail (textbuffer != NULL, FALSE); + g_object_get (textbuffer, "tag-table", &tagtable, NULL); + g_return_val_if_fail (tagtable == gtk_builder_get_object (builder, "tagtable1"), FALSE); + g_object_unref (builder); + + return TRUE; +} + +gboolean test_children (void) +{ + GtkBuilder * builder; + GList *children; + const gchar buffer1[] = + "" + " " + " " + " " + " Hello" + " " + " " + " " + ""; + const gchar buffer2[] = + "" + " " + " " + " " + " 10" + " " + " " + " 20" + " " + " " + " " + " " + " " + ""; + + GObject *window, *button; + GObject *dialog, *vbox, *action_area; + + builder = builder_new_from_string (buffer1, -1, NULL); + window = gtk_builder_get_object (builder, "window1"); + g_return_val_if_fail (window != NULL, FALSE); + g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); + + button = gtk_builder_get_object (builder, "button1"); + g_return_val_if_fail (button != NULL, FALSE); + g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE); + g_return_val_if_fail (strcmp (GTK_WIDGET (GTK_WIDGET (button)->parent)->name, "window1") == 0, FALSE); + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + dialog = gtk_builder_get_object (builder, "dialog1"); + g_return_val_if_fail (dialog != NULL, FALSE); + g_return_val_if_fail (GTK_IS_DIALOG (dialog), FALSE); + children = gtk_container_get_children (GTK_CONTAINER (dialog)); + g_return_val_if_fail (g_list_length (children) == 1, FALSE); + g_list_free (children); + + vbox = gtk_builder_get_object (builder, "dialog1-vbox"); + g_return_val_if_fail (vbox != NULL, FALSE); + g_return_val_if_fail (GTK_IS_VBOX (vbox), FALSE); + g_return_val_if_fail (GTK_WIDGET (vbox)->parent != NULL, FALSE); + g_return_val_if_fail (strcmp (GTK_WIDGET (GTK_WIDGET (vbox)->parent)->name, "dialog1") == 0, FALSE); + g_return_val_if_fail (GTK_CONTAINER (vbox)->border_width == 10, FALSE); + g_return_val_if_fail (strcmp (GTK_WIDGET (GTK_DIALOG (dialog)->vbox)->name, + "dialog1-vbox") == 0, FALSE); + + action_area = gtk_builder_get_object (builder, "dialog1-action_area"); + g_return_val_if_fail (action_area != NULL, FALSE); + g_return_val_if_fail (GTK_IS_HBUTTON_BOX (action_area), FALSE); + g_return_val_if_fail (GTK_WIDGET (action_area)->parent != NULL, FALSE); + g_return_val_if_fail (GTK_CONTAINER (action_area)->border_width == 20, FALSE); + g_return_val_if_fail (GTK_DIALOG (dialog)->action_area != NULL, FALSE); + g_return_val_if_fail (GTK_WIDGET (GTK_DIALOG (dialog)->action_area)->name != NULL, FALSE); + g_return_val_if_fail (strcmp (GTK_WIDGET (GTK_DIALOG (dialog)->action_area)->name, + "dialog1-action_area") == 0, FALSE); + g_object_unref (builder); + + return TRUE; +} + +gboolean test_child_properties (void) +{ + GtkBuilder * builder; + const gchar buffer1[] = + "" + " " + " " + " " + " " + " start" + " " + " " + " " + " " + " " + " end" + " " + " " + " " + ""; + + GObject *label, *vbox; + GtkPackType pack_type; + + builder = builder_new_from_string (buffer1, -1, NULL); + vbox = gtk_builder_get_object (builder, "vbox1"); + g_return_val_if_fail (GTK_IS_VBOX (vbox), FALSE); + + label = gtk_builder_get_object (builder, "label1"); + g_return_val_if_fail (GTK_IS_LABEL (label), FALSE); + gtk_container_child_get (GTK_CONTAINER (vbox), + GTK_WIDGET (label), + "pack-type", + &pack_type, + NULL); + g_return_val_if_fail (pack_type == GTK_PACK_START, FALSE); + + label = gtk_builder_get_object (builder, "label2"); + g_return_val_if_fail (GTK_IS_LABEL (label), FALSE); + gtk_container_child_get (GTK_CONTAINER (vbox), + GTK_WIDGET (label), + "pack-type", + &pack_type, + NULL); + g_return_val_if_fail (pack_type == GTK_PACK_END, FALSE); + + g_object_unref (builder); + + return TRUE; +} + +gboolean test_treeview_column (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + "" + " " + " " + " " + " " + " " + " " + " John" + " 25" + " " + " " + "" + "" + " " + " " + " True" + " liststore1" + " " + " " + " Test" + " " + " " + " " + " 1" + " " + " " + " " + " " + " " + " " + " Number" + " " + " " + " " + " 0" + " " + " " + " " + " " + " " + " " + "" + ""; + GObject *treeview; + GtkTreeViewColumn *column; + GList *renderers; + GObject *renderer; + gchar *text; + + builder = builder_new_from_string (buffer, -1, NULL); + treeview = gtk_builder_get_object (builder, "treeview1"); + g_return_val_if_fail (treeview, FALSE); + g_return_val_if_fail (GTK_IS_TREE_VIEW (treeview), FALSE); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 0); + g_return_val_if_fail (strcmp (gtk_tree_view_column_get_title (column), + "Test") == 0, FALSE); + + renderers = gtk_tree_view_column_get_cell_renderers (column); + g_return_val_if_fail (g_list_length (renderers) == 1, FALSE); + renderer = g_list_nth_data (renderers, 0); + g_return_val_if_fail (renderer, FALSE); + g_return_val_if_fail (GTK_IS_CELL_RENDERER_TEXT (renderer), FALSE); + g_list_free (renderers); + + gtk_widget_realize (GTK_WIDGET (treeview)); + + renderer = gtk_builder_get_object (builder, "renderer1"); + g_object_get (renderer, "text", &text, NULL); + g_assert (text); + g_return_val_if_fail (strcmp (text, "25") == 0, FALSE); + g_free (text); + + renderer = gtk_builder_get_object (builder, "renderer2"); + g_object_get (renderer, "text", &text, NULL); + g_assert (text); + g_return_val_if_fail (strcmp (text, "John") == 0, FALSE); + g_free (text); + + gtk_widget_unrealize (GTK_WIDGET (treeview)); + + g_object_unref (builder); + return TRUE; +} + +gboolean test_icon_view (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + " " + " " + " " + " " + " " + " " + " " + " test" + " " + " " + " " + " " + " " + " " + " liststore1" + " 0" + " 1" + " True" + " " + " " + " " + " 0" + " " + " " + " " + " " + " " + ""; + GObject *iconview; + GObject *renderer; + gchar *text; + + builder = builder_new_from_string (buffer, -1, NULL); + iconview = gtk_builder_get_object (builder, "iconview1"); + g_return_val_if_fail (iconview, FALSE); + g_return_val_if_fail (GTK_IS_ICON_VIEW (iconview), FALSE); + + gtk_widget_realize (GTK_WIDGET (iconview)); + + renderer = gtk_builder_get_object (builder, "renderer1"); + g_object_get (renderer, "text", &text, NULL); + g_assert (text); + g_return_val_if_fail (strcmp (text, "test") == 0, FALSE); + g_free (text); + + g_object_unref (builder); + return TRUE; +} + +gboolean test_combo_box (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + " " + " " + " " + " " + " " + " " + " " + " 1" + " Foo" + " " + " " + " 2" + " Bar" + " " + " " + " " + " " + " " + " " + " liststore1" + " True" + " " + " " + " " + " 0" + " " + " " + " " + " " + " " + " 1" + " " + " " + " " + " " + " " + ""; + GObject *combobox; + GObject *renderer; + gchar *text; + + builder = builder_new_from_string (buffer, -1, NULL); + combobox = gtk_builder_get_object (builder, "combobox1"); + g_return_val_if_fail (combobox, FALSE); + gtk_widget_realize (GTK_WIDGET (combobox)); + + renderer = gtk_builder_get_object (builder, "renderer2"); + g_assert (renderer); + g_object_get (renderer, "text", &text, NULL); + g_assert (text); + g_return_val_if_fail (strcmp (text, "Bar") == 0, FALSE); + g_free (text); + + renderer = gtk_builder_get_object (builder, "renderer1"); + g_assert (renderer); + g_object_get (renderer, "text", &text, NULL); + g_assert (text); + g_return_val_if_fail (strcmp (text, "2") == 0, FALSE); + g_free (text); + + g_object_unref (builder); + return TRUE; +} + +gboolean test_combo_box_entry (void) +{ + GtkBuilder *builder; + const gchar buffer[] = + "" + " " + " " + " " + " " + " " + " " + " " + " 1" + " Foo" + " " + " " + " 2" + " Bar" + " " + " " + " " + " " + " " + " " + " liststore1" + " True" + " " + " " + " " + " 0" + " " + " " + " " + " " + " " + " 1" + " " + " " + " " + " " + " " + ""; + GObject *combobox; + GObject *renderer; + gchar *text; + + builder = builder_new_from_string (buffer, -1, NULL); + combobox = gtk_builder_get_object (builder, "comboboxentry1"); + g_return_val_if_fail (combobox, FALSE); + + renderer = gtk_builder_get_object (builder, "renderer2"); + g_assert (renderer); + g_object_get (renderer, "text", &text, NULL); + g_assert (text); + g_return_val_if_fail (strcmp (text, "Bar") == 0, FALSE); + g_free (text); + + renderer = gtk_builder_get_object (builder, "renderer1"); + g_assert (renderer); + g_object_get (renderer, "text", &text, NULL); + g_assert (text); + g_return_val_if_fail (strcmp (text, "2") == 0, FALSE); + g_free (text); + + g_object_unref (builder); + return TRUE; +} + +gboolean test_cell_view (void) +{ + GtkBuilder *builder; + gchar *buffer = + "" + " " + " " + " " + " " + " " + " " + " test" + " " + " " + " " + " " + " " + " " + " True" + " liststore1" + " " + " " + " " + " 0" + " " + " " + " " + " " + " " + ""; + GObject *cellview; + GObject *model; + GtkTreePath *path; + GList *renderers; + GObject *renderer; + gchar *text; + + builder = builder_new_from_string (buffer, -1, NULL); + g_assert (builder); + cellview = gtk_builder_get_object (builder, "cellview1"); + g_assert (cellview); + g_return_val_if_fail (GTK_IS_CELL_VIEW (cellview), FALSE); + g_object_get (cellview, "model", &model, NULL); + g_assert (model); + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE); + path = gtk_tree_path_new_first (); + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cellview), path); + + renderers = gtk_cell_view_get_cell_renderers (GTK_CELL_VIEW (cellview)); + g_assert (renderers); + g_return_val_if_fail (g_list_length (renderers) == 1, FALSE); + + gtk_widget_realize (GTK_WIDGET (cellview)); + + renderer = g_list_nth_data (renderers, 0); + g_list_free (renderers); + g_assert (renderer); + g_object_get (renderer, "text", &text, NULL); + g_return_val_if_fail (strcmp (text, "test") == 0, FALSE); + g_free (text); + gtk_tree_path_free (path); + g_object_unref (builder); + return TRUE; +} + +gboolean test_dialog (void) +{ + GtkBuilder * builder; + const gchar buffer1[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " button_ok" + " button_cancel" + " " + " " + ""; + + GObject *dialog1; + GObject *button_ok; + GObject *button_cancel; + + builder = builder_new_from_string (buffer1, -1, NULL); + dialog1 = gtk_builder_get_object (builder, "dialog1"); + button_ok = gtk_builder_get_object (builder, "button_ok"); + g_return_val_if_fail (gtk_dialog_get_response_for_widget + (GTK_DIALOG (dialog1), + GTK_WIDGET (button_ok)) == 3, FALSE); + button_cancel = gtk_builder_get_object (builder, "button_cancel"); + g_return_val_if_fail (gtk_dialog_get_response_for_widget + (GTK_DIALOG (dialog1), + GTK_WIDGET (button_cancel)) == -5, FALSE); + + g_object_unref (builder); + + return TRUE; +} + +gboolean test_accelerators (void) +{ + GtkBuilder *builder; + gchar *buffer = + "" + " " + " " + " " + " " + " " + " " + " " + ""; + gchar *buffer2 = + "" + " " + " " + " " + " " + " " + " " + " " + " " + ""; + GObject *window1; + GSList *accel_groups; + GObject *accel_group; + + builder = builder_new_from_string (buffer, -1, NULL); + window1 = gtk_builder_get_object (builder, "window1"); + g_assert (window1); + g_assert (GTK_IS_WINDOW (window1)); + + accel_groups = gtk_accel_groups_from_object (window1); + g_return_val_if_fail (g_slist_length (accel_groups) == 1, FALSE); + accel_group = g_slist_nth_data (accel_groups, 0); + g_assert (accel_group); + + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + window1 = gtk_builder_get_object (builder, "window1"); + g_assert (window1); + g_assert (GTK_IS_WINDOW (window1)); + + accel_groups = gtk_accel_groups_from_object (window1); + g_return_val_if_fail (g_slist_length (accel_groups) == 1, FALSE); + accel_group = g_slist_nth_data (accel_groups, 0); + g_assert (accel_group); + + g_object_unref (builder); + return TRUE; +} + +gboolean test_widget (void) +{ + gchar *buffer = + "" + " " + " " + " " + " True" + " True" + " " + " " + " " + ""; + gchar *buffer2 = + "" + " " + " " + " " + " True" + " True" + " " + " " + " " + ""; + GtkBuilder *builder; + GObject *button1; + + builder = builder_new_from_string (buffer, -1, NULL); + button1 = gtk_builder_get_object (builder, "button1"); + +#if 0 + g_return_val_if_fail (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (button1)), FALSE); +#endif + g_object_unref (builder); + + builder = builder_new_from_string (buffer2, -1, NULL); + button1 = gtk_builder_get_object (builder, "button1"); + + g_return_val_if_fail (GTK_WIDGET_RECEIVES_DEFAULT (GTK_WIDGET (button1)), FALSE); + + g_object_unref (builder); + return TRUE; +} + +int +main (int argc, char **argv) +{ + gtk_init (&argc, &argv); + + g_print ("Testing parser\n"); + if (!test_parser ()) + g_error ("test_parser failed"); + + g_print ("Testing types\n"); + if (!test_types ()) + g_error ("test_types failed"); + + g_print ("Testing construct-only property\n"); + if (!test_construct_only_property ()) + g_error ("test_construct_only_property failed"); + + g_print ("Testing children\n"); + if (!test_children ()) + g_error ("test_children failed"); + + g_print ("Testing child properties\n"); + if (!test_child_properties ()) + g_error ("test_child_properties failed"); + + g_print ("Testing notebook\n"); + if (!test_notebook ()) + g_error ("test_notebook failed"); + + g_print ("Testing domain\n"); + if (!test_domain ()) + g_error ("test_domain failed"); + + g_print ("Testing signal autoconnect\n"); + if (!test_connect_signals ()) + g_error ("test_connect_signals failed"); + + g_print ("Testing uimanager simple\n"); + if (!test_uimanager_simple ()) + g_error ("test_uimanager_simple failed"); + + g_print ("Testing spin button\n"); + if (!test_spin_button ()) + g_error ("test_spin_button failed"); + + g_print ("Testing sizegroup\n"); + if (!test_sizegroup ()) + g_error ("test_sizegroup failed"); + + g_print ("Testing list store\n"); + if (!test_list_store ()) + g_error ("test_list_store failed"); + + g_print ("Testing tree store\n"); + if (!test_tree_store ()) + g_error ("test_tree_store failed"); + + g_print ("Testing treeview column\n"); + if (!test_treeview_column ()) + g_error ("test_treeview_column failed"); + + g_print ("Testing iconview\n"); + if (!test_icon_view ()) + g_error ("test_icon_view failed"); + + g_print ("Testing combobox\n"); + if (!test_combo_box ()) + g_error ("test_combo_box failed"); + + g_print ("Testing combobox entry\n"); + if (!test_combo_box_entry ()) + g_error ("test_combo_box_entry failed"); + + g_print ("Testing cell view\n"); + if (!test_cell_view ()) + g_error ("test_cell_view failed"); + + g_print ("Testing dialog\n"); + if (!test_dialog ()) + g_error ("test_dialog failed"); + + g_print ("Testing accelerators\n"); + if (!test_accelerators ()) + g_error ("test_accelerators failed"); + + g_print ("Testing widget\n"); + if (!test_widget ()) + g_error ("test_widget failed"); + + return 0; +}