From 714a937b7b520454e4ac90b28abca36cd511f30a Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 8 Oct 2018 11:13:32 +1300 Subject: [PATCH] GtkFileChooser: Support setting filters in GtkBuilder files Set using: filter_id1 filter_id2 --- gtk/gtkfilechooser.c | 148 +++++++++++++++++++++++++++++++++++++ gtk/gtkfilechooser.h | 13 ++++ gtk/gtkfilechooserbutton.c | 58 ++++++++++++++- gtk/gtkfilechooserdialog.c | 57 +++++++++++++- gtk/gtkfilechooserwidget.c | 55 +++++++++++++- testsuite/gtk/builder.c | 84 +++++++++++++++++++++ 6 files changed, 412 insertions(+), 3 deletions(-) diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c index 26240c95a6..9f22e28522 100644 --- a/gtk/gtkfilechooser.c +++ b/gtk/gtkfilechooser.c @@ -19,6 +19,8 @@ #include "config.h" #include "gtkfilechooser.h" #include "gtkfilechooserprivate.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" #include "gtkintl.h" #include "gtktypebuiltins.h" #include "gtkprivate.h" @@ -2359,3 +2361,149 @@ gtk_file_chooser_get_choice (GtkFileChooser *chooser, return NULL; } +typedef struct { + gchar *widget_name; + gint line; + gint col; +} FilterInfo; + +typedef struct { + GtkFileChooser *chooser; + GtkBuilder *builder; + gboolean in_tag; + GList *items; + gint line; + gint col; +} SubParserData; + +static void +free_filter_info (gpointer data) +{ + FilterInfo *item = data; + + g_free (item->widget_name); + g_free (item); +} + +static void +parser_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + SubParserData *parser_data = user_data; + + if (strcmp (element_name, "filter") == 0) + { + if (!_gtk_builder_check_parent (parser_data->builder, context, "filters", error)) + return; + + parser_data->in_tag = TRUE; + g_markup_parse_context_get_position (context, &parser_data->line, &parser_data->col); + } + else if (strcmp (element_name, "filters") == 0) + { + if (!_gtk_builder_check_parent (parser_data->builder, context, "object", error)) + return; + } + else + { + _gtk_builder_error_unhandled_tag (parser_data->builder, context, + "GtkFileChooser", element_name, + error); + } +} + +static void +parser_text_element (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + SubParserData *parser_data = user_data; + + if (parser_data->in_tag) + { + FilterInfo *item; + + item = g_new (FilterInfo, 1); + item->widget_name = g_strdup (text); + item->line = parser_data->line; + item->col = parser_data->col; + parser_data->items = g_list_append (parser_data->items, item); + } +} + +static void +parser_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + SubParserData *parser_data = user_data; + + parser_data->in_tag = FALSE; +} + +static const GMarkupParser sub_parser = + { + parser_start_element, + parser_end_element, + parser_text_element, + }; + +gboolean +_gtk_file_chooser_buildable_custom_tag_start (GtkFileChooser *chooser, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + if (strcmp (tagname, "filters") == 0) + { + SubParserData *parser_data = g_slice_new0 (SubParserData); + parser_data->chooser = chooser; + parser_data->builder = builder; + *parser = sub_parser; + *data = parser_data; + return TRUE; + } + + return FALSE; +} + +gboolean +_gtk_file_chooser_buildable_custom_finished (GtkFileChooser *chooser, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + if (strcmp (tagname, "filters") == 0) + { + SubParserData *parser_data = (SubParserData*)data; + GList *l; + + for (l = parser_data->items; l; l = l->next) + { + FilterInfo *info = l->data; + GObject *object; + + object = _gtk_builder_lookup_object (builder, info->widget_name, info->line, info->col); + if (!object) + continue; + + gtk_file_chooser_add_filter (chooser, GTK_FILE_FILTER (object)); + } + + g_list_free_full (parser_data->items, free_filter_info); + g_slice_free (SubParserData, parser_data); + return TRUE; + } + + return FALSE; +} diff --git a/gtk/gtkfilechooser.h b/gtk/gtkfilechooser.h index 92cdbb3658..1f725d22e9 100644 --- a/gtk/gtkfilechooser.h +++ b/gtk/gtkfilechooser.h @@ -321,6 +321,19 @@ GDK_AVAILABLE_IN_3_22 const char *gtk_file_chooser_get_choice (GtkFileChooser *chooser, const char *id); +gboolean _gtk_file_chooser_buildable_custom_tag_start (GtkFileChooser *chooser, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); + +gboolean _gtk_file_chooser_buildable_custom_finished (GtkFileChooser *chooser, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data); + G_END_DECLS #endif /* __GTK_FILE_CHOOSER_H__ */ diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c index 42a8abb84e..b639655459 100644 --- a/gtk/gtkfilechooserbutton.c +++ b/gtk/gtkfilechooserbutton.c @@ -229,6 +229,21 @@ enum * Function Prototypes * * ********************* */ +/* GtkBuildableIface Functions */ +static GtkBuildableIface *parent_buildable_iface; +static void gtk_file_chooser_buildable_iface_init (GtkBuildableIface *iface); +static gboolean gtk_file_chooser_button_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_file_chooser_button_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data); + /* GtkFileChooserIface Functions */ static void gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface); static gboolean gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser, @@ -359,7 +374,9 @@ static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_BOX, G_ADD_PRIVATE (GtkFileChooserButton) G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, - gtk_file_chooser_button_file_chooser_iface_init)) + gtk_file_chooser_button_file_chooser_iface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_file_chooser_buildable_iface_init)) /* ***************** * @@ -507,6 +524,45 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button) } +/* ******************************* * + * GtkBuildableIface Functions * + * ******************************* */ +static void +gtk_file_chooser_buildable_iface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->custom_tag_start = gtk_file_chooser_button_buildable_custom_tag_start; + iface->custom_finished = gtk_file_chooser_button_buildable_custom_finished; +} + +static gboolean +gtk_file_chooser_button_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_file_chooser_buildable_custom_tag_start (GTK_FILE_CHOOSER (buildable), builder, child, + tagname, parser, data); +} + +static void +gtk_file_chooser_button_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data) +{ + if (!_gtk_file_chooser_buildable_custom_finished (GTK_FILE_CHOOSER (buildable), builder, child, tagname, data)) + parent_buildable_iface->custom_finished (buildable, builder, child, tagname, data); +} + + /* ******************************* * * GtkFileChooserIface Functions * * ******************************* */ diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c index db1d8f6de7..590f37e9d5 100644 --- a/gtk/gtkfilechooserdialog.c +++ b/gtk/gtkfilechooserdialog.c @@ -37,6 +37,7 @@ #include "gtkdialogprivate.h" #include "gtklabel.h" #include "gtkfilechooserentry.h" +#include "gtkbuildable.h" #include @@ -217,6 +218,22 @@ struct _GtkFileChooserDialogPrivate gboolean has_entry; }; +/* GtkBuildable method implementation */ +static GtkBuildableIface *parent_buildable_iface; + +static void gtk_file_chooser_buildable_iface_init (GtkBuildableIface *iface); +static gboolean gtk_file_chooser_dialog_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_file_chooser_dialog_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data); + static void gtk_file_chooser_dialog_set_property (GObject *object, guint prop_id, const GValue *value, @@ -249,7 +266,45 @@ static void setup_save_entry (GtkFileChooserDialog *dialog); G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDialog, gtk_file_chooser_dialog, GTK_TYPE_DIALOG, G_ADD_PRIVATE (GtkFileChooserDialog) G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, - _gtk_file_chooser_delegate_iface_init)) + _gtk_file_chooser_delegate_iface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_file_chooser_buildable_iface_init)) + + +static void +gtk_file_chooser_buildable_iface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->custom_tag_start = gtk_file_chooser_dialog_buildable_custom_tag_start; + iface->custom_finished = gtk_file_chooser_dialog_buildable_custom_finished; +} + +static gboolean +gtk_file_chooser_dialog_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_file_chooser_buildable_custom_tag_start (GTK_FILE_CHOOSER (buildable), builder, child, + tagname, parser, data); +} + +static void +gtk_file_chooser_dialog_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data) +{ + if (!_gtk_file_chooser_buildable_custom_finished (GTK_FILE_CHOOSER (buildable), builder, child, tagname, data)) + parent_buildable_iface->custom_finished (buildable, builder, child, tagname, data); +} static void gtk_file_chooser_dialog_class_init (GtkFileChooserDialogClass *class) diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 5cdb208ff7..d5c8c0c319 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -23,6 +23,7 @@ #include "gtkfilechooserwidgetprivate.h" #include "gtkbindings.h" +#include "gtkbuildable.h" #include "gtkbutton.h" #include "gtkcelllayout.h" #include "gtkcellrendererpixbuf.h" @@ -437,8 +438,23 @@ enum { #define NUM_LINES 45 #define NUM_CHARS 60 +/* GtkBuildable method implementation */ +static GtkBuildableIface *parent_buildable_iface; + static void gtk_file_chooser_widget_iface_init (GtkFileChooserIface *iface); static void gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface); +static void gtk_file_chooser_buildable_iface_init (GtkBuildableIface *iface); +static gboolean gtk_file_chooser_widget_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_file_chooser_widget_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data); static void gtk_file_chooser_widget_constructed (GObject *object); static void gtk_file_chooser_widget_finalize (GObject *object); @@ -614,7 +630,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkFileChooserWidget, gtk_file_chooser_widget, GTK_TYPE G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_widget_iface_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED, - gtk_file_chooser_embed_default_iface_init)); + gtk_file_chooser_embed_default_iface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_file_chooser_buildable_iface_init)) static void gtk_file_chooser_widget_iface_init (GtkFileChooserIface *iface) @@ -650,6 +668,41 @@ gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface) iface->initial_focus = gtk_file_chooser_widget_initial_focus; } +static void +gtk_file_chooser_buildable_iface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->custom_tag_start = gtk_file_chooser_widget_buildable_custom_tag_start; + iface->custom_finished = gtk_file_chooser_widget_buildable_custom_finished; +} + +static gboolean +gtk_file_chooser_widget_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_file_chooser_buildable_custom_tag_start (GTK_FILE_CHOOSER (buildable), builder, child, + tagname, parser, data); +} + +static void +gtk_file_chooser_widget_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer data) +{ + if (!_gtk_file_chooser_buildable_custom_finished (GTK_FILE_CHOOSER (buildable), builder, child, tagname, data)) + parent_buildable_iface->custom_finished (buildable, builder, child, tagname, data); +} + static void pending_select_files_free (GtkFileChooserWidget *impl) { diff --git a/testsuite/gtk/builder.c b/testsuite/gtk/builder.c index 339ad110b2..06067e11da 100644 --- a/testsuite/gtk/builder.c +++ b/testsuite/gtk/builder.c @@ -2940,6 +2940,87 @@ test_file_filter (void) g_object_unref (builder); } +static void +test_file_chooser_widget (void) +{ + GtkBuilder *builder; + GObject *obj; + GSList *filters; + + const gchar buffer[] = + "" + " " + " " + " filter1" + " " + " " + " " + ""; + + builder = builder_new_from_string (buffer, -1, NULL); + obj = gtk_builder_get_object (builder, "widget1"); + g_assert (GTK_IS_FILE_CHOOSER_WIDGET (obj)); + filters = gtk_file_chooser_list_filters (GTK_FILE_CHOOSER (obj)); + g_assert (g_slist_length (filters) == 1); + g_slist_free (filters); + + g_object_unref (builder); +} + +static void +test_file_chooser_button (void) +{ + GtkBuilder *builder; + GObject *obj; + GSList *filters; + + const gchar buffer[] = + "" + " " + " " + " filter1" + " " + " " + " " + ""; + + builder = builder_new_from_string (buffer, -1, NULL); + obj = gtk_builder_get_object (builder, "button1"); + g_assert (GTK_IS_FILE_CHOOSER_BUTTON (obj)); + filters = gtk_file_chooser_list_filters (GTK_FILE_CHOOSER (obj)); + g_assert (g_slist_length (filters) == 1); + g_slist_free (filters); + + g_object_unref (builder); +} + +static void +test_file_chooser_dialog (void) +{ + GtkBuilder *builder; + GObject *obj; + GSList *filters; + + const gchar buffer[] = + "" + " " + " " + " filter1" + " " + " " + " " + ""; + + builder = builder_new_from_string (buffer, -1, NULL); + obj = gtk_builder_get_object (builder, "dialog1"); + g_assert (GTK_IS_FILE_CHOOSER_DIALOG (obj)); + filters = gtk_file_chooser_list_filters (GTK_FILE_CHOOSER (obj)); + g_assert (g_slist_length (filters) == 1); + g_slist_free (filters); + + g_object_unref (builder); +} + int main (int argc, char **argv) { @@ -2994,6 +3075,9 @@ main (int argc, char **argv) g_test_add_func ("/Builder/Property Bindings", test_property_bindings); g_test_add_func ("/Builder/anaconda-signal", test_anaconda_signal); g_test_add_func ("/Builder/FileFilter", test_file_filter); + g_test_add_func ("/Builder/FileChooserWidget", test_file_chooser_widget); + g_test_add_func ("/Builder/FileChooserButton", test_file_chooser_button); + g_test_add_func ("/Builder/FileChooserDialog", test_file_chooser_dialog); return g_test_run(); } -- GitLab