/* * glade-gtk-stack.c - GladeWidgetAdaptor for GtkStack * * Copyright (C) 2014 Red Hat, Inc * * Authors: * Matthias Clasen * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "glade-stack-editor.h" static void glade_gtk_stack_selection_changed (GladeProject * project, GladeWidget * gwidget) { GList *list; GtkWidget *page, *sel_widget; GtkStack *stack = GTK_STACK (glade_widget_get_object (gwidget)); gint position; if ((list = glade_project_selection_get (project)) != NULL && g_list_length (list) == 1) { sel_widget = list->data; if (GTK_IS_WIDGET (sel_widget) && gtk_widget_is_ancestor (sel_widget, GTK_WIDGET (stack))) { GList *children, *l; children = gtk_container_get_children (GTK_CONTAINER (stack)); for (l = children; l; l = l->next) { page = l->data; if (sel_widget == page || gtk_widget_is_ancestor (sel_widget, page)) { gtk_stack_set_visible_child (stack, page); gtk_container_child_get (GTK_CONTAINER (stack), page, "position", &position, NULL); glade_widget_property_set (gwidget, "page", position); break; } } g_list_free (children); } } } static void glade_gtk_stack_project_changed (GladeWidget * gwidget, GParamSpec * pspec, gpointer userdata) { GladeProject * project = glade_widget_get_project (gwidget); GladeProject * old_project = g_object_get_data (G_OBJECT (gwidget), "stack-project-ptr"); if (old_project) g_signal_handlers_disconnect_by_func (G_OBJECT (old_project), G_CALLBACK (glade_gtk_stack_selection_changed), gwidget); if (project) g_signal_connect (G_OBJECT (project), "selection-changed", G_CALLBACK (glade_gtk_stack_selection_changed), gwidget); g_object_set_data (G_OBJECT (gwidget), "stack-project-ptr", project); } void glade_gtk_stack_post_create (GladeWidgetAdaptor *adaptor, GObject *container, GladeCreateReason reason) { GladeWidget *gwidget = glade_widget_get_from_gobject (container); if (reason == GLADE_CREATE_USER) gtk_stack_add_titled (GTK_STACK (container), glade_placeholder_new (), "page0", "page0"); g_signal_connect (G_OBJECT (gwidget), "notify::project", G_CALLBACK (glade_gtk_stack_project_changed), NULL); glade_gtk_stack_project_changed (gwidget, NULL, NULL); } static gchar * get_unused_name (GtkStack *stack) { gchar *name; gint i; i = 0; while (TRUE) { name = g_strdup_printf ("page%d", i); if (gtk_stack_get_child_by_name (stack, name) == NULL) return name; g_free (name); i++; } return NULL; } static void update_position_with_command (GtkWidget *widget, gpointer data) { GtkContainer *parent = data; GladeWidget *gwidget; GladeProperty *property; gint position; gwidget = glade_widget_get_from_gobject (widget); if (!gwidget) return; property = glade_widget_get_pack_property (gwidget, "position"); gtk_container_child_get (parent, widget, "position", &position, NULL); glade_command_set_property (property, position); } void glade_gtk_stack_child_action_activate (GladeWidgetAdaptor * adaptor, GObject * container, GObject * object, const gchar * action_path) { if (!strcmp (action_path, "insert_page_after") || !strcmp (action_path, "insert_page_before")) { GladeWidget *parent; GladeProperty *property; gint position; gchar *name; GtkWidget *new_widget; gint pages; parent = glade_widget_get_from_gobject (container); glade_widget_property_get (parent, "pages", &pages); glade_command_push_group (_("Insert placeholder to %s"), glade_widget_get_name (parent)); gtk_container_child_get (GTK_CONTAINER (container), GTK_WIDGET (object), "position", &position, NULL); if (!strcmp (action_path, "insert_page_after")) position++; name = get_unused_name (GTK_STACK (container)); new_widget = glade_placeholder_new (); gtk_stack_add_titled (GTK_STACK (container), new_widget, name, name); gtk_container_child_set (GTK_CONTAINER (container), new_widget, "position", position, NULL); gtk_stack_set_visible_child (GTK_STACK (container), new_widget); property = glade_widget_get_property (parent, "pages"); glade_command_set_property (property, pages + 1); gtk_container_forall (GTK_CONTAINER (container), update_position_with_command, container); property = glade_widget_get_property (parent, "page"); glade_command_set_property (property, position); glade_command_pop_group (); g_free (name); } else if (strcmp (action_path, "remove_page") == 0) { GladeWidget *parent; GladeProperty *property; gint pages; gint position; parent = glade_widget_get_from_gobject (container); glade_widget_property_get (parent, "pages", &pages); glade_command_push_group (_("Remove placeholder from %s"), glade_widget_get_name (parent)); g_assert (GLADE_IS_PLACEHOLDER (object)); gtk_container_remove (GTK_CONTAINER (container), GTK_WIDGET (object)); property = glade_widget_get_property (parent, "pages"); glade_command_set_property (property, pages - 1); gtk_container_forall (GTK_CONTAINER (container), update_position_with_command, container); glade_widget_property_get (parent, "page", &position); property = glade_widget_get_property (parent, "page"); glade_command_set_property (property, position); glade_command_pop_group (); } else GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_action_activate (adaptor, container, object, action_path); } GladeEditable * glade_gtk_stack_create_editable (GladeWidgetAdaptor * adaptor, GladeEditorPageType type) { if (type == GLADE_PAGE_GENERAL) return (GladeEditable *) glade_stack_editor_new (); return GWA_GET_CLASS (GTK_TYPE_CONTAINER)->create_editable (adaptor, type); } typedef struct { gint size; gboolean include_placeholders; } ChildData; static void count_child (GtkWidget *child, gpointer data) { ChildData *cdata = data; if (cdata->include_placeholders || !GLADE_IS_PLACEHOLDER (child)) cdata->size++; } static gint gtk_stack_get_n_pages (GtkStack *stack, gboolean include_placeholders) { ChildData data; data.size = 0; data.include_placeholders = include_placeholders; gtk_container_forall (GTK_CONTAINER (stack), count_child, &data); return data.size; } static GtkWidget * gtk_stack_get_nth_child (GtkStack *stack, gint n) { GList *children; GtkWidget *child; children = gtk_container_get_children (GTK_CONTAINER (stack)); child = g_list_nth_data (children, n); g_list_free (children); return child; } static void update_position (GtkWidget *widget, gpointer data) { GtkContainer *parent = data; GladeWidget *gwidget; gint position; gwidget = glade_widget_get_from_gobject (widget); if (gwidget) { gtk_container_child_get (parent, widget, "position", &position, NULL); glade_widget_pack_property_set (gwidget, "position", position); } } static void glade_gtk_stack_set_n_pages (GObject * object, const GValue * value) { GladeWidget *gbox; GtkStack *stack; GtkWidget *child; gint new_size, i; gint old_size; gchar *name; gint page; stack = GTK_STACK (object); new_size = g_value_get_int (value); old_size = gtk_stack_get_n_pages (stack, TRUE); if (old_size == new_size) return; for (i = old_size; i < new_size; i++) { name = get_unused_name (stack); child = glade_placeholder_new (); gtk_stack_add_titled (stack, child, name, name); g_free (name); } for (i = old_size; i > 0; i--) { if (old_size <= new_size) break; child = gtk_stack_get_nth_child (stack, i - 1); if (GLADE_IS_PLACEHOLDER (child)) { gtk_container_remove (GTK_CONTAINER (stack), child); old_size--; } } gtk_container_forall (GTK_CONTAINER (stack), update_position, stack); gbox = glade_widget_get_from_gobject (stack); glade_widget_property_get (gbox, "page", &page); glade_widget_property_set (gbox, "page", page); } static void glade_gtk_stack_set_page (GObject *object, const GValue *value) { gint new_page; GList *children; GtkWidget *child; new_page = g_value_get_int (value); children = gtk_container_get_children (GTK_CONTAINER (object)); child = g_list_nth_data (children, new_page); if (child) gtk_stack_set_visible_child (GTK_STACK (object), child); g_list_free (children); } static gint gtk_stack_get_page (GtkStack *stack) { GtkWidget *child; gint page; child = gtk_stack_get_visible_child (stack); gtk_container_child_get (GTK_CONTAINER (stack), child, "position", &page, NULL); return page; } void glade_gtk_stack_set_property (GladeWidgetAdaptor * adaptor, GObject * object, const gchar * id, const GValue * value) { if (!strcmp (id, "pages")) glade_gtk_stack_set_n_pages (object, value); else if (!strcmp (id, "page")) glade_gtk_stack_set_page (object, value); else GWA_GET_CLASS (GTK_TYPE_CONTAINER)->set_property (adaptor, object, id, value); } void glade_gtk_stack_get_property (GladeWidgetAdaptor * adaptor, GObject * object, const gchar * id, GValue * value) { if (!strcmp (id, "pages")) { g_value_reset (value); g_value_set_int (value, gtk_stack_get_n_pages (GTK_STACK (object), TRUE)); } else if (!strcmp (id, "page")) { g_value_reset (value); g_value_set_int (value, gtk_stack_get_page (GTK_STACK (object))); } else GWA_GET_CLASS (GTK_TYPE_CONTAINER)->get_property (adaptor, object, id, value); } static void glade_gtk_stack_set_child_position (GObject * container, GObject * child, const GValue * value) { static gboolean recursion = FALSE; gint new_position, old_position; GladeWidget *gbox; gint page; if (recursion) return; gtk_container_child_get (GTK_CONTAINER (container), GTK_WIDGET (child), "position", &old_position, NULL); new_position = g_value_get_int (value); if (old_position == new_position) return; recursion = TRUE; gtk_container_child_set (GTK_CONTAINER (container), GTK_WIDGET (child), "position", new_position, NULL); gtk_container_forall (GTK_CONTAINER (container), update_position, container); recursion = FALSE; gbox = glade_widget_get_from_gobject (container); glade_widget_property_get (gbox, "page", &page); glade_widget_property_set (gbox, "page", page); } void glade_gtk_stack_set_child_property (GladeWidgetAdaptor * adaptor, GObject * container, GObject * child, const gchar * id, GValue * value) { if (!strcmp (id, "position")) glade_gtk_stack_set_child_position (container, child, value); else GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, container, child, id, value); } static gboolean glade_gtk_stack_verify_n_pages (GObject * object, const GValue *value) { gint new_size, old_size; new_size = g_value_get_int (value); old_size = gtk_stack_get_n_pages (GTK_STACK (object), FALSE); return old_size <= new_size; } static gboolean glade_gtk_stack_verify_page (GObject *object, const GValue *value) { gint page; gint pages; page = g_value_get_int (value); pages = gtk_stack_get_n_pages (GTK_STACK (object), TRUE); return 0 <= page && page < pages; } gboolean glade_gtk_stack_verify_property (GladeWidgetAdaptor * adaptor, GObject * object, const gchar * id, const GValue * value) { if (!strcmp (id, "pages")) return glade_gtk_stack_verify_n_pages (object, value); else if (!strcmp (id, "page")) return glade_gtk_stack_verify_page (object, value); else if (GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property) return GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property (adaptor, object, id, value); return TRUE; } void glade_gtk_stack_add_child (GladeWidgetAdaptor * adaptor, GObject * object, GObject * child) { GladeWidget *gbox, *gchild; gint pages, page; if (!glade_widget_superuser () && !GLADE_IS_PLACEHOLDER (child)) { GList *l, *children; children = gtk_container_get_children (GTK_CONTAINER (object)); for (l = g_list_last (children); l; l = l->prev) { GtkWidget *widget = l->data; if (GLADE_IS_PLACEHOLDER (widget)) { gtk_container_remove (GTK_CONTAINER (object), widget); break; } } g_list_free (children); } gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); gchild = glade_widget_get_from_gobject (child); if (gchild != NULL) glade_widget_set_pack_action_visible (gchild, "remove_page", FALSE); gbox = glade_widget_get_from_gobject (object); glade_widget_property_get (gbox, "pages", &pages); glade_widget_property_set (gbox, "pages", pages); glade_widget_property_get (gbox, "page", &page); glade_widget_property_set (gbox, "page", page); } void glade_gtk_stack_remove_child (GladeWidgetAdaptor * adaptor, GObject * object, GObject * child) { GladeWidget *gbox; gint pages, page; gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); gbox = glade_widget_get_from_gobject (object); glade_widget_property_get (gbox, "pages", &pages); glade_widget_property_set (gbox, "pages", pages); glade_widget_property_get (gbox, "page", &page); glade_widget_property_set (gbox, "page", page); } void glade_gtk_stack_replace_child (GladeWidgetAdaptor * adaptor, GObject * container, GObject * current, GObject * new_widget) { GladeWidget *gchild; GladeWidget *gbox; gint pages, page; GWA_GET_CLASS (GTK_TYPE_CONTAINER)->replace_child (adaptor, container, current, new_widget); gbox = glade_widget_get_from_gobject (container); gchild = glade_widget_get_from_gobject (new_widget); if (gchild != NULL) glade_widget_set_pack_action_visible (gchild, "remove_page", FALSE); /* NOTE: make sure to sync this at the end because new_widget could be * a placeholder and syncing these properties could destroy it. */ glade_widget_property_get (gbox, "pages", &pages); glade_widget_property_set (gbox, "pages", pages); glade_widget_property_get (gbox, "page", &page); glade_widget_property_set (gbox, "page", page); }