diff options
Diffstat (limited to 'subprojects/libhandy/glade')
27 files changed, 4060 insertions, 0 deletions
diff --git a/subprojects/libhandy/glade/glade-catalog.dtd b/subprojects/libhandy/glade/glade-catalog.dtd new file mode 100644 index 0000000..c0d073e --- /dev/null +++ b/subprojects/libhandy/glade/glade-catalog.dtd @@ -0,0 +1,196 @@ +<!ELEMENT glade-catalog (init-function?, + glade-widget-classes?, + glade-widget-group*)> + +<!ATTLIST glade-catalog name CDATA #REQUIRED + version CDATA #IMPLIED + targetable CDATA #IMPLIED + library CDATA #IMPLIED + depends CDATA #IMPLIED + domain CDATA #IMPLIED + book CDATA #IMPLIED + icon-prefix CDATA #IMPLIED + requires CDATA #IMPLIED> + +<!ELEMENT glade-widget-classes (glade-widget-class+)> + +<!ELEMENT glade-widget-class (post-create-function?, + add-child-verify-function?, + add-child-function?, + remove-child-function?, + replace-child-function?, + get-children-function?, + get-internal-child-function?, + child-property-applies-function?, + child-action-activate-function?, + read-widget-function?, + write-widget-function?, + get-property-function?, + set-property-function?, + child-set-property-function?, + child-get-property-function?, + action-activate-function?, + verify-function?, + special-child-type?, + packing-properties?, + packing-actions?, + properties?, + children?, + packing-defaults?, + actions?)> + +<!ATTLIST glade-widget-class toplevel CDATA #IMPLIED + since CDATA #IMPLIED + deprecated CDATA #IMPLIED + use-placeholders CDATA #IMPLIED + default-width CDATA #IMPLIED + default-height CDATA #IMPLIED + name CDATA #REQUIRED + generic-name CDATA #IMPLIED + icon-name CDATA #IMPLIED + title CDATA #REQUIRED + parent CDATA #IMPLIED + get-type-function CDATA #IMPLIED + adaptor CDATA #IMPLIED> + +<!ELEMENT properties (property+)> + +<!ELEMENT property (spec?, + type?, + parameter-spec?, + tooltip?, + parameters?, + set-function?, + get-function?, + verify-function?, + displayable-values?)> + +<!ATTLIST property id CDATA #REQUIRED + since CDATA #IMPLIED + deprecated CDATA #IMPLIED + create-type CDATA #IMPLIED + name CDATA #IMPLIED + tooltip CDATA #IMPLIED + themed-icon CDATA #IMPLIED + stock CDATA #IMPLIED + stock-icon CDATA #IMPLIED + weight CDATA #IMPLIED + transfer-on-paste CDATA #IMPLIED + save-always CDATA #IMPLIED + parentless-widget CDATA #IMPLIED + atk-property CDATA #IMPLIED + default CDATA #IMPLIED + query CDATA #IMPLIED + save CDATA #IMPLIED + common CDATA #IMPLIED + disabled CDATA #IMPLIED + visible CDATA #IMPLIED + custom-layout CDATA #IMPLIED + multiline CDATA #IMPLIED + optional CDATA #IMPLIED + optional-default CDATA #IMPLIED + ignore CDATA #IMPLIED + needs-sync CDATA #IMPLIED + construct-only CDATA #IMPLIED + translatable CDATA #IMPLIED> + +<!ELEMENT parameter-spec (type?, + value-type?, + min?)> +<!ELEMENT value-type (#PCDATA)> +<!ELEMENT min (#PCDATA)> +<!ELEMENT set-function (#PCDATA)> +<!ELEMENT get-function (#PCDATA)> +<!ELEMENT spec (#PCDATA)> +<!ELEMENT tooltip (#PCDATA)> +<!ELEMENT verify-function (#PCDATA)> + +<!ELEMENT displayable-values (value+)> + +<!ELEMENT value EMPTY> + +<!ATTLIST value id CDATA #REQUIRED + name CDATA #REQUIRED> + +<!ELEMENT parameters (parameter+)> + +<!ELEMENT parameter EMPTY> + +<!ATTLIST parameter key CDATA #REQUIRED + value CDATA #REQUIRED> + +<!ELEMENT children (child+)> + +<!ELEMENT child (type, + add-child-function?, + remove-child-function?, + get-children-function?, + get-all-children-function?, + set-property-function?, + get-property-function?, + replace-child-function?, + fill-empty-function?, + properties?)> + +<!ELEMENT type (#PCDATA)> +<!ELEMENT add-child-verify-function (#PCDATA)> +<!ELEMENT add-child-function (#PCDATA)> +<!ELEMENT remove-child-function (#PCDATA)> +<!ELEMENT get-children-function (#PCDATA)> +<!ELEMENT get-all-children-function (#PCDATA)> +<!ELEMENT set-prop-function (#PCDATA)> +<!ELEMENT get-prop-function (#PCDATA)> +<!ELEMENT fill-empty-function (#PCDATA)> +<!ELEMENT replace-child-function (#PCDATA)> +<!ELEMENT child-set-property-function (#PCDATA)> +<!ELEMENT child-get-property-function (#PCDATA)> +<!ELEMENT action-activate-function (#PCDATA)> + +<!ELEMENT post-create-function (#PCDATA)> +<!ELEMENT get-internal-child-function (#PCDATA)> +<!ELEMENT child-property-applies-function (#PCDATA)> + +<!ELEMENT child-action-activate-function (#PCDATA)> + +<!ELEMENT read-widget-function (#PCDATA)> +<!ELEMENT write-widget-function (#PCDATA)> +<!ELEMENT get-property-function (#PCDATA)> +<!ELEMENT set-property-function (#PCDATA)> + +<!ELEMENT glade-widget-group (default-palette-state?, + glade-widget-class-ref+)> + +<!ATTLIST glade-widget-group name CDATA #REQUIRED + title CDATA #REQUIRED> + +<!ELEMENT default-palette-state EMPTY> +<!ATTLIST default-palette-state expanded CDATA #IMPLIED> + +<!ELEMENT glade-widget-class-ref EMPTY> +<!ATTLIST glade-widget-class-ref name CDATA #REQUIRED> + +<!ELEMENT packing-defaults (parent-class+)> + +<!ELEMENT parent-class (child-property+)> +<!ATTLIST parent-class name CDATA #REQUIRED> + +<!ELEMENT child-property EMPTY> +<!ATTLIST child-property id CDATA #REQUIRED + default CDATA #REQUIRED> + +<!ELEMENT special-child-type (#PCDATA)> + +<!ELEMENT packing-properties (property+)> + +<!ELEMENT packing-actions (action+)> + +<!ELEMENT actions (action+)> + +<!ELEMENT action EMPTY> + +<!ATTLIST action id CDATA #REQUIRED + name CDATA #REQUIRED + stock CDATA #IMPLIED + important CDATA #IMPLIED> + +<!ELEMENT init-function (#PCDATA)> diff --git a/subprojects/libhandy/glade/glade-hdy-carousel.c b/subprojects/libhandy/glade/glade-hdy-carousel.c new file mode 100644 index 0000000..c06a4e4 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-carousel.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-stack.c - GladeWidgetAdaptor for GtkStack + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-carousel.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +#include <math.h> + +static gint +hdy_carousel_get_page (HdyCarousel *carousel) +{ + return round (hdy_carousel_get_position (carousel)); +} + +static gboolean +hdy_carousel_is_transient (HdyCarousel *carousel) +{ + return fmod (hdy_carousel_get_position (carousel), 1.0) > 0.00001; +} + +static gint +get_n_pages_excluding_placeholders (GtkContainer *container) +{ + GList *children, *l; + gint n_pages; + + children = gtk_container_get_children (container); + + n_pages = 0; + for (l = children; l; l = l->next) + if (!GLADE_IS_PLACEHOLDER (l->data)) + n_pages++; + + g_list_free (children); + + + return n_pages; +} + +static void +selection_changed_cb (GladeProject *project, + GladeWidget *gwidget) +{ + GList *list; + GtkWidget *page, *sel_widget; + GtkContainer *container; + GList *children, *l; + gint index; + + list = glade_project_selection_get (project); + if (!list || g_list_length (list) != 1) + return; + + sel_widget = list->data; + + container = GTK_CONTAINER (glade_widget_get_object (gwidget)); + + if (!GTK_IS_WIDGET (sel_widget) || + !gtk_widget_is_ancestor (sel_widget, GTK_WIDGET (container))) + return; + + children = gtk_container_get_children (container); + for (l = children; l; l = l->next) { + page = l->data; + if (sel_widget == page || gtk_widget_is_ancestor (sel_widget, page)) { + hdy_carousel_scroll_to (HDY_CAROUSEL (container), page); + index = glade_hdy_get_child_index (container, page); + glade_widget_property_set (gwidget, "page", index); + break; + } + } + g_list_free (children); +} + +static void +project_changed_cb (GladeWidget *gwidget, + GParamSpec *pspec, + gpointer user_data) +{ + GladeProject *project, *old_project; + + project = glade_widget_get_project (gwidget); + old_project = g_object_get_data (G_OBJECT (gwidget), "carousel-project-ptr"); + + if (old_project) + g_signal_handlers_disconnect_by_func (G_OBJECT (old_project), + G_CALLBACK (selection_changed_cb), + gwidget); + + if (project) + g_signal_connect (G_OBJECT (project), "selection-changed", + G_CALLBACK (selection_changed_cb), gwidget); + + g_object_set_data (G_OBJECT (gwidget), "carousel-project-ptr", project); +} + +static void +position_changed_cb (HdyCarousel *carousel, + GParamSpec *pspec, + GladeWidget *gwidget) +{ + gint old_page, new_page; + + glade_widget_property_get (gwidget, "page", &old_page); + new_page = hdy_carousel_get_page (carousel); + + if (old_page == new_page || hdy_carousel_is_transient (carousel)) + return; + + glade_widget_property_set (gwidget, "page", new_page); +} + +void +glade_hdy_carousel_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason) +{ + GladeWidget *gwidget = glade_widget_get_from_gobject (container); + + if (reason == GLADE_CREATE_USER) + gtk_container_add (GTK_CONTAINER (container), glade_placeholder_new ()); + + g_signal_connect (G_OBJECT (gwidget), "notify::project", + G_CALLBACK (project_changed_cb), NULL); + + project_changed_cb (gwidget, NULL, NULL); + + g_signal_connect (G_OBJECT (container), "notify::position", + G_CALLBACK (position_changed_cb), gwidget); +} + +void +glade_hdy_carousel_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; + GtkWidget *placeholder; + gint pages, index; + + 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)); + + index = glade_hdy_get_child_index (GTK_CONTAINER (container), GTK_WIDGET (object)); + if (!strcmp (action_path, "insert_page_after")) + index++; + + placeholder = glade_placeholder_new (); + + hdy_carousel_insert (HDY_CAROUSEL (container), placeholder, index); + hdy_carousel_scroll_to (HDY_CAROUSEL (container), placeholder); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + property = glade_widget_get_property (parent, "pages"); + glade_command_set_property (property, pages + 1); + + property = glade_widget_get_property (parent, "page"); + glade_command_set_property (property, index); + + glade_command_pop_group (); + } else if (strcmp (action_path, "remove_page") == 0) { + GladeWidget *parent; + GladeProperty *property; + gint pages, 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)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + property = glade_widget_get_property (parent, "pages"); + glade_command_set_property (property, pages - 1); + + 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); +} + +static void +set_n_pages (GObject *container, + const GValue *value) +{ + GladeWidget *gbox; + GtkWidget *child; + gint old_size, new_size, i, page; + + new_size = g_value_get_int (value); + old_size = hdy_carousel_get_n_pages (HDY_CAROUSEL (container)); + + if (old_size == new_size) + return; + + for (i = old_size; i < new_size; i++) + gtk_container_add (GTK_CONTAINER (container), glade_placeholder_new ()); + + for (i = old_size; i > 0; i--) { + if (old_size <= new_size) + break; + child = glade_hdy_get_nth_child (GTK_CONTAINER (container), i - 1); + if (GLADE_IS_PLACEHOLDER (child)) { + gtk_container_remove (GTK_CONTAINER (container), child); + old_size--; + } + } + + gbox = glade_widget_get_from_gobject (container); + glade_widget_property_get (gbox, "page", &page); + glade_widget_property_set (gbox, "page", page); +} + +static void +set_page (GObject *object, + const GValue *value) +{ + gint new_page; + GtkWidget *child; + + new_page = g_value_get_int (value); + child = glade_hdy_get_nth_child (GTK_CONTAINER (object), new_page); + + if (child) + hdy_carousel_scroll_to (HDY_CAROUSEL (object), child); +} + +void +glade_hdy_carousel_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value) +{ + if (!strcmp (id, "pages")) + set_n_pages (object, value); + else if (!strcmp (id, "page")) + set_page (object, value); + else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->set_property (adaptor, object, id, value); + } +} + +void +glade_hdy_carousel_get_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + GValue *value) +{ + if (!strcmp (id, "pages")) { + g_value_reset (value); + g_value_set_int (value, hdy_carousel_get_n_pages (HDY_CAROUSEL (object))); + } else if (!strcmp (id, "page")) { + g_value_reset (value); + g_value_set_int (value, hdy_carousel_get_page (HDY_CAROUSEL (object))); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->get_property (adaptor, object, id, value); + } +} + +static gboolean +glade_hdy_carousel_verify_n_pages (GObject *object, + const GValue *value) +{ + gint new_size, old_size; + + new_size = g_value_get_int (value); + old_size = get_n_pages_excluding_placeholders (GTK_CONTAINER (object)); + + return old_size <= new_size; +} + +static gboolean +glade_hdy_carousel_verify_page (GObject *object, + const GValue *value) +{ + gint page, pages; + + page = g_value_get_int (value); + pages = hdy_carousel_get_n_pages (HDY_CAROUSEL (object)); + + return 0 <= page && page < pages; +} + +gboolean +glade_hdy_carousel_verify_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value) +{ + if (!strcmp (id, "pages")) + return glade_hdy_carousel_verify_n_pages (object, value); + else if (!strcmp (id, "page")) + return glade_hdy_carousel_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_hdy_carousel_add_child (GladeWidgetAdaptor *adaptor, + GObject *container, + 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 (container)); + + for (l = g_list_last (children); l; l = l->prev) { + GtkWidget *widget = l->data; + + if (GLADE_IS_PLACEHOLDER (widget)) { + gtk_container_remove (GTK_CONTAINER (container), widget); + break; + } + } + + g_list_free (children); + } + + gtk_container_add (GTK_CONTAINER (container), GTK_WIDGET (child)); + + gchild = glade_widget_get_from_gobject (child); + if (gchild) + glade_widget_set_pack_action_visible (gchild, "remove_page", FALSE); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + gbox = glade_widget_get_from_gobject (container); + 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_hdy_carousel_remove_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child) +{ + GladeWidget *gbox; + gint pages, page; + + gtk_container_remove (GTK_CONTAINER (container), GTK_WIDGET (child)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + gbox = glade_widget_get_from_gobject (container); + 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_hdy_carousel_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget) +{ + GladeWidget *gbox, *gchild; + gint pages, page, index; + + index = glade_hdy_get_child_index (GTK_CONTAINER (container), GTK_WIDGET (current)); + gtk_container_remove (GTK_CONTAINER (container), GTK_WIDGET (current)); + hdy_carousel_insert (HDY_CAROUSEL (container), GTK_WIDGET (new_widget), + index); + hdy_carousel_scroll_to_full (HDY_CAROUSEL (container), + GTK_WIDGET (new_widget), 0); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + gchild = glade_widget_get_from_gobject (new_widget); + if (gchild) + 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. + */ + gbox = glade_widget_get_from_gobject (container); + 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_hdy_carousel_get_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (strcmp (property_name, "position") == 0) + g_value_set_int (value, glade_hdy_get_child_index (GTK_CONTAINER (container), + GTK_WIDGET (child))); + else + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_get_property (adaptor, + container, + child, + property_name, + value); +} + +void +glade_hdy_carousel_set_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (strcmp (property_name, "position") == 0) { + glade_hdy_reorder_child (GTK_CONTAINER (container), + GTK_WIDGET (child), + g_value_get_int (value)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, + container, + child, + property_name, + value); + } +} diff --git a/subprojects/libhandy/glade/glade-hdy-carousel.h b/subprojects/libhandy/glade/glade-hdy-carousel.h new file mode 100644 index 0000000..211c0a6 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-carousel.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + + +void glade_hdy_carousel_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason); + +void glade_hdy_carousel_child_action_activate (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *object, + const gchar *action_path); + +void glade_hdy_carousel_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value); +void glade_hdy_carousel_get_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + GValue *value); +gboolean glade_hdy_carousel_verify_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value); + +void glade_hdy_carousel_add_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_carousel_remove_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_carousel_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget); + +void glade_hdy_carousel_get_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); +void glade_hdy_carousel_set_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); diff --git a/subprojects/libhandy/glade/glade-hdy-expander-row.c b/subprojects/libhandy/glade/glade-hdy-expander-row.c new file mode 100644 index 0000000..d1a2ccc --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-expander-row.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-list-box.c - GladeWidgetAdaptor for GtkListBox + * Copyright (C) 2013 Kalev Lember + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-expander-row.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +void +glade_hdy_expander_row_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason) +{ + g_object_set (container, "expanded", TRUE, NULL); +} + +void +glade_hdy_expander_row_get_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (strcmp (property_name, "position") == 0) + g_value_set_int (value, glade_hdy_get_child_index (GTK_CONTAINER (container), + GTK_WIDGET (child))); + else + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_get_property (adaptor, + container, + child, + property_name, + value); +} + +void +glade_hdy_expander_row_set_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (strcmp (property_name, "position") == 0) + glade_hdy_reorder_child (GTK_CONTAINER (container), + GTK_WIDGET (child), + g_value_get_int (value)); + else + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, + container, + child, + property_name, + value); +} + +void +glade_hdy_expander_row_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (object)); +} + +void +glade_hdy_expander_row_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (object)); +} + +gboolean +glade_hdy_expander_row_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback) +{ + if (GTK_IS_LIST_BOX_ROW (child)) + return TRUE; + + if (user_feedback) { + GladeWidgetAdaptor *row_adaptor = + glade_widget_adaptor_get_by_type (GTK_TYPE_LIST_BOX_ROW); + + glade_util_ui_message (glade_app_get_window (), + GLADE_UI_INFO, NULL, + ONLY_THIS_GOES_IN_THAT_MSG, + glade_widget_adaptor_get_title (row_adaptor), + glade_widget_adaptor_get_title (adaptor)); + } + + return FALSE; +} diff --git a/subprojects/libhandy/glade/glade-hdy-expander-row.h b/subprojects/libhandy/glade/glade-hdy-expander-row.h new file mode 100644 index 0000000..221f65e --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-expander-row.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + +void glade_hdy_expander_row_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason); + +void glade_hdy_expander_row_get_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); +void glade_hdy_expander_row_set_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); + +void glade_hdy_expander_row_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child); + +void glade_hdy_expander_row_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child); + +gboolean glade_hdy_expander_row_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback); diff --git a/subprojects/libhandy/glade/glade-hdy-header-bar.c b/subprojects/libhandy/glade/glade-hdy-header-bar.c new file mode 100644 index 0000000..12b7afe --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-header-bar.c @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-header-bar.c - GladeWidgetAdaptor for GtkHeaderBar + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-header-bar.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +#define TITLE_DISABLED_MESSAGE _("This property does not apply when a custom title is set") + +typedef struct { + GtkContainer *parent; + GtkWidget *custom_title; + gboolean include_placeholders; + gint count; +} ChildrenData; + +static void +count_children (GtkWidget *widget, gpointer data) +{ + ChildrenData *cdata = data; + + if (widget == cdata->custom_title) + return; + + if ((GLADE_IS_PLACEHOLDER (widget) && cdata->include_placeholders) || + glade_widget_get_from_gobject (widget) != NULL) + cdata->count++; +} + +static gboolean +verify_size (GObject *object, + const GValue *value) +{ + gint new_size; + ChildrenData data; + + new_size = g_value_get_int (value); + + data.parent = GTK_CONTAINER (object); + data.custom_title = hdy_header_bar_get_custom_title (HDY_HEADER_BAR (object)); + data.include_placeholders = FALSE; + data.count = 0; + + gtk_container_foreach (data.parent, count_children, &data); + + return data.count <= new_size; +} + +static gint +get_n_children (GObject *object) +{ + ChildrenData data; + + data.parent = GTK_CONTAINER (object); + data.custom_title = hdy_header_bar_get_custom_title (HDY_HEADER_BAR (object)); + data.include_placeholders = TRUE; + data.count = 0; + + gtk_container_foreach (data.parent, count_children, &data); + + return data.count; +} + +static void +parse_finished_cb (GladeProject *project, + GObject *object) +{ + GladeWidget *gbox; + + gbox = glade_widget_get_from_gobject (object); + glade_widget_property_set (gbox, "size", get_n_children (object)); + glade_widget_property_set (gbox, "use-custom-title", hdy_header_bar_get_custom_title (HDY_HEADER_BAR (object)) != NULL); +} + +void +glade_hdy_header_bar_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason) +{ + GladeWidget *parent = glade_widget_get_from_gobject (container); + GladeProject *project = glade_widget_get_project (parent); + + if (reason == GLADE_CREATE_LOAD) { + g_signal_connect (project, "parse-finished", + G_CALLBACK (parse_finished_cb), + container); + + return; + } + + if (reason == GLADE_CREATE_USER) + hdy_header_bar_pack_start (HDY_HEADER_BAR (container), + glade_placeholder_new ()); +} + +void +glade_hdy_header_bar_action_activate (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *action_path) +{ + if (!strcmp (action_path, "add_slot")) { + GladeWidget *parent; + GladeProperty *property; + gint size; + + parent = glade_widget_get_from_gobject (object); + + glade_command_push_group (_("Insert placeholder to %s"), + glade_widget_get_name (parent)); + + property = glade_widget_get_property (parent, "size"); + glade_property_get (property, &size); + glade_command_set_property (property, size + 1); + + glade_command_pop_group (); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->action_activate (adaptor, + object, + action_path); + } +} + +void +glade_hdy_header_bar_child_action_activate (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *object, + const gchar *action_path) +{ + if (strcmp (action_path, "remove_slot") == 0) { + GladeWidget *parent; + GladeProperty *property; + + parent = glade_widget_get_from_gobject (container); + glade_command_push_group (_("Remove placeholder from %s"), + glade_widget_get_name (parent)); + + if (g_object_get_data (object, "special-child-type")) { + property = glade_widget_get_property (parent, "use-custom-title"); + glade_command_set_property (property, FALSE); + } else { + gint size; + + gtk_container_remove (GTK_CONTAINER (container), GTK_WIDGET (object)); + + property = glade_widget_get_property (parent, "size"); + glade_property_get (property, &size); + glade_command_set_property (property, size - 1); + } + + glade_command_pop_group (); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_action_activate (adaptor, + container, + object, + action_path); + } +} + +void +glade_hdy_header_bar_get_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + GValue *value) +{ + if (!strcmp (id, "use-custom-title")) { + g_value_reset (value); + g_value_set_boolean (value, hdy_header_bar_get_custom_title (HDY_HEADER_BAR (object)) != NULL); + } else if (!strcmp (id, "size")) { + g_value_reset (value); + g_value_set_int (value, get_n_children (object)); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->get_property (adaptor, object, id, value); + } +} + +static void +set_size (GObject *object, + const GValue *value) +{ + GList *l, *next; + g_autoptr (GList) children = NULL; + GtkWidget *child; + guint new_size, old_size, i; + + if (glade_util_object_is_loading (object)) + return; + + children = gtk_container_get_children (GTK_CONTAINER (object)); + l = children; + + while (l) { + next = l->next; + if (l->data == hdy_header_bar_get_custom_title (HDY_HEADER_BAR (object)) || + (!glade_widget_get_from_gobject (l->data) && !GLADE_IS_PLACEHOLDER (l->data))) + children = g_list_delete_link (children, l); + l = next; + } + + old_size = g_list_length (children); + new_size = g_value_get_int (value); + + if (old_size == new_size) + return; + + for (i = old_size; i < new_size; i++) { + GtkWidget *placeholder = glade_placeholder_new (); + hdy_header_bar_pack_start (HDY_HEADER_BAR (object), placeholder); + } + + for (l = g_list_last (children); l && old_size > new_size; l = l->prev) { + child = l->data; + if (glade_widget_get_from_gobject (child) || !GLADE_IS_PLACEHOLDER (child)) + continue; + + gtk_container_remove (GTK_CONTAINER (object), child); + old_size--; + } +} + +static void +set_use_custom_title (GObject *object, + gboolean use_custom_title) +{ + GladeWidget *gwidget = glade_widget_get_from_gobject (object); + GtkWidget *child; + + if (use_custom_title) { + child = hdy_header_bar_get_custom_title (HDY_HEADER_BAR (object)); + if (!child) { + child = glade_placeholder_new (); + g_object_set_data (G_OBJECT (child), "special-child-type", "title"); + } + } else { + child = NULL; + } + + hdy_header_bar_set_custom_title (HDY_HEADER_BAR (object), child); + + if (GLADE_IS_PLACEHOLDER (child)) { + GList *list, *l; + + list = glade_placeholder_packing_actions (GLADE_PLACEHOLDER (child)); + for (l = list; l; l = l->next) { + GladeWidgetAction *gwa = l->data; + if (!strcmp (glade_widget_action_get_def (gwa)->id, "remove_slot")) + glade_widget_action_set_visible (gwa, FALSE); + } + } + + if (use_custom_title) { + glade_widget_property_set_sensitive (gwidget, "title", FALSE, TITLE_DISABLED_MESSAGE); + glade_widget_property_set_sensitive (gwidget, "subtitle", FALSE, TITLE_DISABLED_MESSAGE); + glade_widget_property_set_sensitive (gwidget, "has-subtitle", FALSE, TITLE_DISABLED_MESSAGE); + } else { + glade_widget_property_set_sensitive (gwidget, "title", TRUE, NULL); + glade_widget_property_set_sensitive (gwidget, "subtitle", TRUE, NULL); + glade_widget_property_set_sensitive (gwidget, "has-subtitle", TRUE, NULL); + } +} + +void +glade_hdy_header_bar_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value) +{ + if (!strcmp (id, "use-custom-title")) { + set_use_custom_title (object, g_value_get_boolean (value)); + } else if (!strcmp (id, "show-close-button")) { + GladeWidget *gwidget = glade_widget_get_from_gobject (object); + + /* We don't set the property to 'ignore' so that we catch this in the adaptor, + * but we also do not apply the property to the runtime object here, thus + * avoiding showing the close button which would in turn close glade itself + * when clicked. + */ + glade_widget_property_set_sensitive (gwidget, "decoration-layout", + g_value_get_boolean (value), + _("The decoration layout does not apply to header bars " + "which do no show window controls")); + } else if (!strcmp (id, "size")) { + set_size (object, value); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->set_property (adaptor, object, id, value); + } +} + +void +glade_hdy_header_bar_add_child (GladeWidgetAdaptor *adaptor, + GObject *parent, + GObject *child) +{ + GladeWidget *gbox, *gchild; + gint size; + gchar *special_child_type; + + gchild = glade_widget_get_from_gobject (child); + if (gchild) + glade_widget_set_pack_action_visible (gchild, "remove_slot", FALSE); + + special_child_type = g_object_get_data (child, "special-child-type"); + + if (special_child_type && !strcmp (special_child_type, "title")) { + hdy_header_bar_set_custom_title (HDY_HEADER_BAR (parent), GTK_WIDGET (child)); + + return; + } + + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->add (adaptor, parent, child); + + gbox = glade_widget_get_from_gobject (parent); + if (!glade_widget_superuser ()) { + glade_widget_property_get (gbox, "size", &size); + glade_widget_property_set (gbox, "size", size); + } +} + +void +glade_hdy_header_bar_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + GladeWidget *gbox; + gint size; + gchar *special_child_type; + + special_child_type = g_object_get_data (child, "special-child-type"); + + if (special_child_type && !strcmp (special_child_type, "title")) { + GtkWidget *replacement = glade_placeholder_new (); + + g_object_set_data (G_OBJECT (replacement), "special-child-type", "title"); + hdy_header_bar_set_custom_title (HDY_HEADER_BAR (object), replacement); + + return; + } + + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); + + /* Synchronize number of placeholders, this should trigger the set_property method with the + * correct value (not the arbitrary number of children currently in the headerbar) + */ + gbox = glade_widget_get_from_gobject (object); + if (!glade_widget_superuser ()) { + glade_widget_property_get (gbox, "size", &size); + glade_widget_property_set (gbox, "size", size); + } +} + +void +glade_hdy_header_bar_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget) +{ + GladeWidget *gbox; + gchar *special_child_type; + gint size; + + special_child_type = + g_object_get_data (G_OBJECT (current), "special-child-type"); + + if (special_child_type && !strcmp (special_child_type, "title")) { + g_object_set_data (G_OBJECT (new_widget), "special-child-type", "title"); + hdy_header_bar_set_custom_title (HDY_HEADER_BAR (container), + GTK_WIDGET (new_widget)); + + return; + } + + g_object_set_data (G_OBJECT (new_widget), "special-child-type", NULL); + + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->replace_child (adaptor, + container, + current, + new_widget); + + gbox = glade_widget_get_from_gobject (container); + if (!glade_widget_superuser ()) { + glade_widget_property_get (gbox, "size", &size); + glade_widget_property_set (gbox, "size", size); + } +} + +gboolean +glade_hdy_header_bar_verify_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value) +{ + if (!strcmp (id, "size")) + return verify_size (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; +} + +static gint +sort_children (GtkWidget *widget_a, GtkWidget *widget_b, GtkWidget *bar) +{ + GladeWidget *gwidget_a, *gwidget_b; + gint position_a, position_b; + GtkWidget *title; + + /* title goes first */ + title = hdy_header_bar_get_custom_title (HDY_HEADER_BAR (bar)); + if (title == widget_a) + return -1; + if (title == widget_b) + return 1; + + if ((gwidget_a = glade_widget_get_from_gobject (widget_a)) && + (gwidget_b = glade_widget_get_from_gobject (widget_b))) { + glade_widget_pack_property_get (gwidget_a, "position", &position_a); + glade_widget_pack_property_get (gwidget_b, "position", &position_b); + + /* If position is the same, try to give an stable order */ + if (position_a == position_b) + return g_strcmp0 (glade_widget_get_name (gwidget_a), + glade_widget_get_name (gwidget_b)); + } else { + gtk_container_child_get (GTK_CONTAINER (bar), widget_a, + "position", &position_a, NULL); + gtk_container_child_get (GTK_CONTAINER (bar), widget_b, + "position", &position_b, NULL); + } + + return position_a - position_b; +} + +GList * +glade_hdy_header_bar_get_children (GladeWidgetAdaptor *adaptor, + GObject *container) +{ + GList *children = GWA_GET_CLASS (GTK_TYPE_CONTAINER)->get_children (adaptor, container); + + return g_list_sort_with_data (children, (GCompareDataFunc) sort_children, container); +} + + +void +glade_hdy_header_bar_child_set_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + const GValue *value) +{ + GladeWidget *gbox, *gchild, *gchild_iter; + GList *children, *list; + gboolean is_position; + gint old_position, iter_position, new_position; + static gboolean recursion = FALSE; + + g_return_if_fail (HDY_IS_HEADER_BAR (container)); + g_return_if_fail (GTK_IS_WIDGET (child)); + g_return_if_fail (property_name != NULL || value != NULL); + + gbox = glade_widget_get_from_gobject (container); + gchild = glade_widget_get_from_gobject (child); + + g_return_if_fail (GLADE_IS_WIDGET (gbox)); + + /* Get old position */ + if ((is_position = (strcmp (property_name, "position") == 0)) != FALSE) { + gtk_container_child_get (GTK_CONTAINER (container), + GTK_WIDGET (child), + "position", &old_position, + NULL); + + + /* Get the real value */ + new_position = g_value_get_int (value); + } + + if (is_position && recursion == FALSE) { + children = glade_widget_get_children (gbox); + + for (list = children; list; list = list->next) { + gchild_iter = glade_widget_get_from_gobject (list->data); + + if (gchild_iter == gchild) { + gtk_container_child_set (GTK_CONTAINER (container), + GTK_WIDGET (child), + "position", new_position, + NULL); + + continue; + } + + /* Get the old value from glade */ + glade_widget_pack_property_get (gchild_iter, "position", &iter_position); + + /* Search for the child at the old position and update it */ + if (iter_position == new_position && + glade_property_superuser () == FALSE) { + /* Update glade with the real value */ + recursion = TRUE; + glade_widget_pack_property_set (gchild_iter, "position", old_position); + recursion = FALSE; + + continue; + } else { + gtk_container_child_set (GTK_CONTAINER (container), + GTK_WIDGET (list->data), + "position", iter_position, + NULL); + } + } + + for (list = children; list; list = list->next) { + gchild_iter = glade_widget_get_from_gobject (list->data); + + /* Refresh values yet again */ + glade_widget_pack_property_get (gchild_iter, "position", &iter_position); + + gtk_container_child_set (GTK_CONTAINER (container), + GTK_WIDGET (list->data), + "position", iter_position, + NULL); + } + + if (children) + g_list_free (children); + } + + /* Chain Up */ + if (!is_position) + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, + container, + child, + property_name, + value); +} diff --git a/subprojects/libhandy/glade/glade-hdy-header-bar.h b/subprojects/libhandy/glade/glade-hdy-header-bar.h new file mode 100644 index 0000000..8f6c84a --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-header-bar.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + + +void glade_hdy_header_bar_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason); + +void glade_hdy_header_bar_action_activate (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *action_path); + +void glade_hdy_header_bar_child_action_activate (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *object, + const gchar *action_path); + +void glade_hdy_header_bar_get_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + GValue *value); +void glade_hdy_header_bar_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value); + +void glade_hdy_header_bar_add_child (GladeWidgetAdaptor *adaptor, + GObject *parent, + GObject *child); +void glade_hdy_header_bar_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child); +void glade_hdy_header_bar_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget); + +gboolean glade_hdy_header_bar_verify_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value); + +GList *glade_hdy_header_bar_get_children (GladeWidgetAdaptor *adaptor, + GObject *container); + +void glade_hdy_header_bar_child_set_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + const GValue *value); diff --git a/subprojects/libhandy/glade/glade-hdy-header-group.c b/subprojects/libhandy/glade/glade-hdy-header-group.c new file mode 100644 index 0000000..3ef633e --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-header-group.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-size-group.c - GladeWidgetAdaptor for GtkSizeGroup + * Copyright (C) 2013 Tristan Van Berkom + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-header-group.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +#define GLADE_TAG_HEADERGROUP_WIDGETS "headerbars" +#define GLADE_TAG_HEADERGROUP_WIDGET "headerbar" + + +static void +glade_hdy_header_group_read_widgets (GladeWidget *widget, GladeXmlNode *node) +{ + GladeXmlNode *widgets_node; + GladeProperty *property; + gchar *string = NULL; + + if ((widgets_node = + glade_xml_search_child (node, GLADE_TAG_HEADERGROUP_WIDGETS)) != NULL) { + GladeXmlNode *n; + + for (n = glade_xml_node_get_children (widgets_node); + n; n = glade_xml_node_next (n)) { + gchar *widget_name, *tmp; + + if (!glade_xml_node_verify (n, GLADE_TAG_HEADERGROUP_WIDGET)) + continue; + + widget_name = glade_xml_get_property_string_required + (n, GLADE_TAG_NAME, NULL); + + if (string == NULL) { + string = widget_name; + } else if (widget_name != NULL) { + tmp = + g_strdup_printf ("%s%s%s", string, GLADE_PROPERTY_DEF_OBJECT_DELIMITER, + widget_name); + string = (g_free (string), tmp); + g_free (widget_name); + } + } + } + + if (string) { + property = glade_widget_get_property (widget, "headerbars"); + g_assert (property); + + /* we must synchronize this directly after loading this project + * (i.e. lookup the actual objects after they've been parsed and + * are present). + */ + g_object_set_data_full (G_OBJECT (property), + "glade-loaded-object", string, g_free); + } +} + +void +glade_hdy_header_group_read_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlNode *node) +{ + if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) || + glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE))) + return; + + /* First chain up and read in all the normal properties.. */ + GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node); + + glade_hdy_header_group_read_widgets (widget, node); +} + + +static void +glade_hdy_header_group_write_widgets (GladeWidget *widget, + GladeXmlContext *context, + GladeXmlNode *node) +{ + GladeXmlNode *widgets_node, *widget_node; + GList *widgets = NULL, *list; + GladeWidget *awidget; + + widgets_node = glade_xml_node_new (context, GLADE_TAG_HEADERGROUP_WIDGETS); + + if (glade_widget_property_get (widget, "headerbars", &widgets)) { + for (list = widgets; list; list = list->next) { + awidget = glade_widget_get_from_gobject (list->data); + widget_node = + glade_xml_node_new (context, GLADE_TAG_HEADERGROUP_WIDGET); + glade_xml_node_append_child (widgets_node, widget_node); + glade_xml_node_set_property_string (widget_node, GLADE_TAG_NAME, + glade_widget_get_name (awidget)); + } + } + + if (!glade_xml_node_get_children (widgets_node)) + glade_xml_node_delete (widgets_node); + else + glade_xml_node_append_child (node, widgets_node); +} + + +void +glade_hdy_header_group_write_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlContext *context, + GladeXmlNode *node) +{ + if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) || + glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE))) + return; + + /* First chain up and read in all the normal properties.. */ + GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node); + + glade_hdy_header_group_write_widgets (widget, context, node); +} + + +void +glade_hdy_header_group_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *property_name, + const GValue *value) +{ + if (!strcmp (property_name, "headerbars")) { + GSList *sg_widgets, *slist; + GList *widgets, *list; + + /* remove old widgets */ + if ((sg_widgets = + hdy_header_group_get_children (HDY_HEADER_GROUP (object))) != NULL) { + /* copy since we are modifying an internal list */ + sg_widgets = g_slist_copy (sg_widgets); + for (slist = sg_widgets; slist; slist = slist->next) + hdy_header_group_remove_child (HDY_HEADER_GROUP (object), + HDY_HEADER_GROUP_CHILD (slist->data)); + g_slist_free (sg_widgets); + } + + /* add new widgets */ + if ((widgets = g_value_get_boxed (value)) != NULL) { + for (list = widgets; list; list = list->next) + hdy_header_group_add_header_bar (HDY_HEADER_GROUP (object), + HDY_HEADER_BAR (list->data)); + } + } else { + GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor, object, + property_name, value); + } +} diff --git a/subprojects/libhandy/glade/glade-hdy-header-group.h b/subprojects/libhandy/glade/glade-hdy-header-group.h new file mode 100644 index 0000000..5333c0f --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-header-group.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + + +void glade_hdy_header_group_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *property_name, + const GValue *value); +void glade_hdy_header_group_write_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlContext *context, + GladeXmlNode *node); +void glade_hdy_header_group_read_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlNode *node); diff --git a/subprojects/libhandy/glade/glade-hdy-leaflet.c b/subprojects/libhandy/glade/glade-hdy-leaflet.c new file mode 100644 index 0000000..4032f9f --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-leaflet.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-stack.c - GladeWidgetAdaptor for GtkStack + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-leaflet.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +#define PAGE_DISABLED_MESSAGE _("This property only applies when the leaflet is folded") + +static void +selection_changed_cb (GladeProject *project, + GladeWidget *gwidget) +{ + GList *list; + GtkWidget *page, *sel_widget; + GtkContainer *container = GTK_CONTAINER (glade_widget_get_object (gwidget)); + gint index; + + 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 (container))) { + g_autoptr (GList) children = gtk_container_get_children (container); + GList *l; + + index = 0; + for (l = children; l; l = l->next) { + page = l->data; + if (sel_widget == page || + gtk_widget_is_ancestor (sel_widget, page)) { + glade_widget_property_set (gwidget, "page", index); + + break; + } + + index++; + } + } + } +} + +static void +project_changed_cb (GladeWidget *gwidget, + GParamSpec *pspec, + gpointer userdata) +{ + GladeProject *project = glade_widget_get_project (gwidget); + GladeProject *old_project = g_object_get_data (G_OBJECT (gwidget), + "project-ptr"); + + if (old_project) + g_signal_handlers_disconnect_by_func (G_OBJECT (old_project), + G_CALLBACK (selection_changed_cb), + gwidget); + + if (project) + g_signal_connect (G_OBJECT (project), + "selection-changed", + G_CALLBACK (selection_changed_cb), + gwidget); + + g_object_set_data (G_OBJECT (gwidget), "project-ptr", project); +} + +static void +add_named (GtkContainer *container, + GtkWidget *child, + const gchar *name) +{ + gtk_container_add_with_properties (container, + child, + "name", name, + NULL); +} + +static void +folded_changed_cb (HdyLeaflet *leaflet, + GParamSpec *pspec, + gpointer userdata) +{ + GladeWidget *gwidget = glade_widget_get_from_gobject (leaflet); + gboolean folded = hdy_leaflet_get_folded (leaflet); + + glade_widget_property_set_sensitive (gwidget, + "page", + folded, + folded ? NULL : PAGE_DISABLED_MESSAGE); +} + +void +glade_hdy_leaflet_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason) +{ + GladeWidget *gwidget = glade_widget_get_from_gobject (container); + + if (reason == GLADE_CREATE_USER) + add_named (GTK_CONTAINER (container), + glade_placeholder_new (), + "page0"); + + g_signal_connect (G_OBJECT (gwidget), + "notify::project", + G_CALLBACK (project_changed_cb), + NULL); + + project_changed_cb (gwidget, NULL, NULL); + + if (HDY_IS_LEAFLET (container)) { + g_signal_connect (container, + "notify::folded", + G_CALLBACK (folded_changed_cb), + NULL); + + folded_changed_cb (HDY_LEAFLET (container), NULL, NULL); + } +} + +static GtkWidget * +get_child_by_name (GtkContainer *container, + const gchar *name) +{ + g_autoptr (GList) children = gtk_container_get_children (container); + GList *l; + + for (l = children; l; l = l->next) { + const gchar *child_name; + + gtk_container_child_get (container, l->data, "name", &child_name, NULL); + + if (child_name && !strcmp (child_name, name)) + return l->data; + } + + return NULL; +} + +static gchar * +get_unused_name (GtkContainer *container) +{ + gint i = 0; + + while (TRUE) { + g_autofree gchar *name = g_strdup_printf ("page%d", i); + + if (get_child_by_name (container, name) == NULL) + return g_steal_pointer (&name); + + i++; + } + + return NULL; +} + +void +glade_hdy_leaflet_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 = glade_widget_get_from_gobject (container); + GladeProperty *property; + g_autofree gchar *name = NULL; + GtkWidget *new_widget; + gint pages, index; + + glade_widget_property_get (parent, "pages", &pages); + + glade_command_push_group (_("Insert placeholder to %s"), + glade_widget_get_name (parent)); + + index = glade_hdy_get_child_index (GTK_CONTAINER (container), GTK_WIDGET (object)); + + if (!strcmp (action_path, "insert_page_after")) + index++; + + name = get_unused_name (GTK_CONTAINER (container)); + new_widget = glade_placeholder_new (); + add_named (GTK_CONTAINER (container), new_widget, name); + glade_hdy_reorder_child (GTK_CONTAINER (container), new_widget, index); + g_object_set (container, "visible-child", new_widget, NULL); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + property = glade_widget_get_property (parent, "pages"); + glade_command_set_property (property, pages + 1); + + property = glade_widget_get_property (parent, "page"); + glade_command_set_property (property, index); + + glade_command_pop_group (); + } else if (strcmp (action_path, "remove_page") == 0) { + GladeWidget *parent = glade_widget_get_from_gobject (container); + GladeProperty *property; + gint pages, index; + + 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)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + property = glade_widget_get_property (parent, "pages"); + glade_command_set_property (property, pages - 1); + + glade_widget_property_get (parent, "page", &index); + property = glade_widget_get_property (parent, "page"); + glade_command_set_property (property, index); + + glade_command_pop_group (); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_action_activate (adaptor, + container, + object, + action_path); + } +} + +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 +get_n_pages (GtkContainer *container, + gboolean include_placeholders) +{ + ChildData data; + + data.size = 0; + data.include_placeholders = include_placeholders; + gtk_container_foreach (container, count_child, &data); + + return data.size; +} + +static void +set_n_pages (GObject *object, + const GValue *value) +{ + GladeWidget *gbox; + GtkContainer *container = GTK_CONTAINER (object); + GtkWidget *child; + gint new_size = g_value_get_int (value); + gint old_size = get_n_pages (container, TRUE); + gint i, page; + + if (old_size == new_size) + return; + + for (i = old_size; i < new_size; i++) { + g_autofree gchar *name = get_unused_name (container); + child = glade_placeholder_new (); + add_named (container, child, name); + } + + for (i = old_size; i > 0; i--) { + if (old_size <= new_size) + break; + + child = glade_hdy_get_nth_child (container, i - 1); + if (GLADE_IS_PLACEHOLDER (child)) { + gtk_container_remove (container, child); + old_size--; + } + } + + gbox = glade_widget_get_from_gobject (container); + glade_widget_property_get (gbox, "page", &page); + glade_widget_property_set (gbox, "page", page); +} + +static void +set_page (GObject *object, + const GValue *value) +{ + gint new_page = g_value_get_int (value); + GtkWidget *child = glade_hdy_get_nth_child (GTK_CONTAINER (object), new_page); + + if (!child) + return; + + g_object_set (object, "visible-child", child, NULL); +} + +static gint +get_page (GtkContainer *container) +{ + GtkWidget *child; + + g_object_get (container, "visible-child", &child, NULL); + + return glade_hdy_get_child_index (container, child); +} + +void +glade_hdy_leaflet_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value) +{ + if (!strcmp (id, "pages")) + set_n_pages (object, value); + else if (!strcmp (id, "page")) + set_page (object, value); + else + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->set_property (adaptor, object, id, value); +} + +void +glade_hdy_leaflet_get_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + GValue *value) +{ + if (!strcmp (id, "pages")) { + g_value_reset (value); + g_value_set_int (value, get_n_pages (GTK_CONTAINER (object), TRUE)); + } else if (!strcmp (id, "page")) { + g_value_reset (value); + g_value_set_int (value, get_page (GTK_CONTAINER (object))); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->get_property (adaptor, object, id, value); + } +} + +static gboolean +verify_n_pages (GObject *object, + const GValue *value) +{ + gint new_size = g_value_get_int (value); + gint old_size = get_n_pages (GTK_CONTAINER (object), FALSE); + + return old_size <= new_size; +} + +static gboolean +verify_page (GObject *object, + const GValue *value) +{ + gint page = g_value_get_int (value); + gint pages = get_n_pages (GTK_CONTAINER (object), TRUE); + + if (page < 0 && page >= pages) + return FALSE; + + if (HDY_IS_LEAFLET (object)) { + GtkWidget *child = glade_hdy_get_nth_child (GTK_CONTAINER (object), page); + gboolean navigatable; + + gtk_container_child_get (GTK_CONTAINER (object), child, + "navigatable", &navigatable, + NULL); + + if (!navigatable) + return FALSE; + } + + return TRUE; +} + +gboolean +glade_hdy_leaflet_verify_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value) +{ + if (!strcmp (id, "pages")) + return verify_n_pages (object, value); + else if (!strcmp (id, "page")) + return 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_hdy_leaflet_get_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (strcmp (property_name, "position") == 0) + g_value_set_int (value, glade_hdy_get_child_index (GTK_CONTAINER (container), + GTK_WIDGET (child))); + else + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_get_property (adaptor, + container, + child, + property_name, + value); +} + +void +glade_hdy_leaflet_set_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (strcmp (property_name, "position") == 0) { + glade_hdy_reorder_child (GTK_CONTAINER (container), + GTK_WIDGET (child), + g_value_get_int (value)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, + container, + child, + property_name, + value); + } +} + +void +glade_hdy_leaflet_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + GladeWidget *gbox, *gchild; + gint pages, page; + + if (!glade_widget_superuser () && !GLADE_IS_PLACEHOLDER (child)) { + g_autoptr (GList) children = gtk_container_get_children (GTK_CONTAINER (object)); + GList *l; + + 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; + } + } + } + + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (object)); + + 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_hdy_leaflet_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + GladeWidget *gbox; + gint pages, page; + + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); + + glade_hdy_sync_child_positions (GTK_CONTAINER (object)); + + 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_hdy_leaflet_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget) +{ + GladeWidget *gchild; + GladeWidget *gbox; + gint pages, page, index; + + index = glade_hdy_get_child_index (GTK_CONTAINER (container), GTK_WIDGET (current)); + gtk_container_remove (GTK_CONTAINER (container), GTK_WIDGET (current)); + gtk_container_add (GTK_CONTAINER (container), GTK_WIDGET (new_widget)); + glade_hdy_reorder_child (GTK_CONTAINER (container), GTK_WIDGET (new_widget), index); + + glade_hdy_sync_child_positions (GTK_CONTAINER (container)); + + 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); +} diff --git a/subprojects/libhandy/glade/glade-hdy-leaflet.h b/subprojects/libhandy/glade/glade-hdy-leaflet.h new file mode 100644 index 0000000..9f274b0 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-leaflet.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + + +void glade_hdy_leaflet_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason); + +void glade_hdy_leaflet_child_action_activate (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *object, + const gchar *action_path); + +void glade_hdy_leaflet_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value); +void glade_hdy_leaflet_get_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + GValue *value); +gboolean glade_hdy_leaflet_verify_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *id, + const GValue *value); + +void glade_hdy_leaflet_get_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); +void glade_hdy_leaflet_set_child_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); + +void glade_hdy_leaflet_add_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_leaflet_remove_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_leaflet_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget); diff --git a/subprojects/libhandy/glade/glade-hdy-preferences-page.c b/subprojects/libhandy/glade/glade-hdy-preferences-page.c new file mode 100644 index 0000000..f1a62c7 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-preferences-page.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-stack.c - GladeWidgetAdaptor for GtkStack + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-preferences-page.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +static GtkWidget * +get_child_by_title (GtkContainer *container, + const gchar *title) +{ + g_autoptr (GList) children = gtk_container_get_children (container); + GList *l; + + for (l = children; l; l = l->next) { + const gchar *child_title; + + g_assert (HDY_IS_PREFERENCES_GROUP (l->data)); + + child_title = hdy_preferences_group_get_title (HDY_PREFERENCES_GROUP (l->data)); + + if (child_title && !strcmp (child_title, title)) + return l->data; + } + + return NULL; +} + +static gchar * +get_unused_title (GtkContainer *container) +{ + gint i = 1; + + while (TRUE) { + g_autofree gchar *title = g_strdup_printf ("Group %d", i); + + if (get_child_by_title (container, title) == NULL) + return g_steal_pointer (&title); + + i++; + } + + return NULL; +} + +static void +add_group (GladeWidgetAdaptor *adaptor, + GObject *container) +{ + GladeWidget *gwidget = glade_widget_get_from_gobject (container); + GladeWidget *gpage; + GladeWidgetAdaptor *page_adaptor; + g_autofree gchar *title = get_unused_title (GTK_CONTAINER (container)); + + page_adaptor = glade_widget_adaptor_get_by_type (HDY_TYPE_PREFERENCES_GROUP); + + gpage = glade_widget_adaptor_create_widget (page_adaptor, FALSE, + "parent", gwidget, + "project", glade_widget_get_project (gwidget), + NULL); + + glade_widget_property_set (gpage, "title", title); + + glade_widget_add_child (gwidget, gpage, FALSE); +} + +void +glade_hdy_preferences_page_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason) +{ + if (reason == GLADE_CREATE_USER) { + add_group (adaptor, container); + add_group (adaptor, container); + add_group (adaptor, container); + } +} + +gboolean +glade_hdy_preferences_page_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback) +{ + if (!HDY_IS_PREFERENCES_GROUP (child)) { + if (user_feedback) { + GladeWidgetAdaptor *page_adaptor = + glade_widget_adaptor_get_by_type (HDY_TYPE_PREFERENCES_GROUP); + + glade_util_ui_message (glade_app_get_window (), + GLADE_UI_INFO, NULL, + ONLY_THIS_GOES_IN_THAT_MSG, + glade_widget_adaptor_get_title (page_adaptor), + glade_widget_adaptor_get_title (adaptor)); + } + + return FALSE; + } + + return TRUE; +} + +void +glade_hdy_preferences_page_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); +} + +void +glade_hdy_preferences_page_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); +} + +void +glade_hdy_preferences_page_replace_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *current, + GObject *new_widget) +{ + gint index = glade_hdy_get_child_index (GTK_CONTAINER (object), GTK_WIDGET (current)); + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (current)); + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (new_widget)); + glade_hdy_reorder_child (GTK_CONTAINER (object), GTK_WIDGET (new_widget), index); +} + +GList * +glade_hdy_preferences_page_get_children (GladeWidgetAdaptor *adaptor, + GObject *object) +{ + return gtk_container_get_children (GTK_CONTAINER (object)); +} + +void +glade_hdy_preferences_page_action_activate (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *action_path) +{ + GladeWidget *parent = glade_widget_get_from_gobject (object); + + if (!g_strcmp0 (action_path, "add_group")) { + g_autofree gchar *title = get_unused_title (GTK_CONTAINER (object)); + GladeWidget *gchild; + + glade_command_push_group (_("Add group to %s"), + glade_widget_get_name (parent)); + + gchild = glade_command_create (glade_widget_adaptor_get_by_type (HDY_TYPE_PREFERENCES_GROUP), + parent, + NULL, + glade_widget_get_project (parent)); + + glade_widget_property_set (gchild, "title", title); + + glade_command_pop_group (); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->action_activate (adaptor, + object, + action_path); + } +} + +void +glade_hdy_preferences_page_child_set_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + const GValue *value) +{ + if (!g_strcmp0 (property_name, "position")) { + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (child)); + + gtk_container_child_set_property (GTK_CONTAINER (parent), + GTK_WIDGET (child), property_name, value); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, + container, + child, + property_name, + value); + } +} + +void +glade_hdy_preferences_page_child_get_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (!g_strcmp0 (property_name, "position")) { + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (child)); + + gtk_container_child_get_property (GTK_CONTAINER (parent), + GTK_WIDGET (child), property_name, value); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_get_property (adaptor, + container, + child, + property_name, + value); + } +} diff --git a/subprojects/libhandy/glade/glade-hdy-preferences-page.h b/subprojects/libhandy/glade/glade-hdy-preferences-page.h new file mode 100644 index 0000000..2fd3f88 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-preferences-page.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + +void glade_hdy_preferences_page_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason); + +gboolean glade_hdy_preferences_page_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback); + +void glade_hdy_preferences_page_add_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_preferences_page_remove_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_preferences_page_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget); + +GList *glade_hdy_preferences_page_get_children (GladeWidgetAdaptor *adaptor, + GObject *object); + +void glade_hdy_preferences_page_action_activate (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *action_path); + +void glade_hdy_preferences_page_child_set_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + const GValue *value); + +void glade_hdy_preferences_page_child_get_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); diff --git a/subprojects/libhandy/glade/glade-hdy-preferences-window.c b/subprojects/libhandy/glade/glade-hdy-preferences-window.c new file mode 100644 index 0000000..1add369 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-preferences-window.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-stack.c - GladeWidgetAdaptor for GtkStack + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-preferences-window.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +static void +selection_changed_cb (GladeProject *project, + GladeWidget *gwidget) +{ + GList *list; + GtkWidget *page, *sel_widget; + GtkContainer *container = GTK_CONTAINER (glade_widget_get_object (gwidget)); + gint index; + + 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 (container))) { + g_autoptr (GList) children = gtk_container_get_children (container); + GList *l; + + index = 0; + for (l = children; l; l = l->next) { + page = l->data; + if (sel_widget == page || + gtk_widget_is_ancestor (sel_widget, page)) { + GtkWidget *parent = gtk_widget_get_parent (page); + + g_object_set (parent, "visible-child", page, NULL); + + break; + } + + index++; + } + } + } +} + +static void +project_changed_cb (GladeWidget *gwidget, + GParamSpec *pspec, + gpointer userdata) +{ + GladeProject *project = glade_widget_get_project (gwidget); + GladeProject *old_project = g_object_get_data (G_OBJECT (gwidget), + "project-ptr"); + + if (old_project) + g_signal_handlers_disconnect_by_func (G_OBJECT (old_project), + G_CALLBACK (selection_changed_cb), + gwidget); + + if (project) + g_signal_connect (G_OBJECT (project), + "selection-changed", + G_CALLBACK (selection_changed_cb), + gwidget); + + g_object_set_data (G_OBJECT (gwidget), "project-ptr", project); +} + +static GtkWidget * +get_child_by_title (GtkContainer *container, + const gchar *title) +{ + g_autoptr (GList) children = gtk_container_get_children (container); + GList *l; + + for (l = children; l; l = l->next) { + const gchar *child_title; + + g_assert (HDY_IS_PREFERENCES_PAGE (l->data)); + + child_title = hdy_preferences_page_get_title (HDY_PREFERENCES_PAGE (l->data)); + + if (child_title && !strcmp (child_title, title)) + return l->data; + } + + return NULL; +} + +static gchar * +get_unused_title (GtkContainer *container) +{ + gint i = 1; + + while (TRUE) { + g_autofree gchar *title = g_strdup_printf ("Page %d", i); + + if (get_child_by_title (container, title) == NULL) + return g_steal_pointer (&title); + + i++; + } + + return NULL; +} + +static void +add_page (GladeWidgetAdaptor *adaptor, + GObject *container) +{ + GladeWidget *gwidget = glade_widget_get_from_gobject (container); + GladeWidget *gpage; + GladeWidgetAdaptor *page_adaptor; + g_autofree gchar *title = get_unused_title (GTK_CONTAINER (container)); + + page_adaptor = glade_widget_adaptor_get_by_type (HDY_TYPE_PREFERENCES_PAGE); + + gpage = glade_widget_adaptor_create_widget (page_adaptor, FALSE, + "parent", gwidget, + "project", glade_widget_get_project (gwidget), + NULL); + + glade_widget_property_set (gpage, "title", title); + + glade_widget_add_child (gwidget, gpage, FALSE); +} + +void +glade_hdy_preferences_window_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason) +{ + GladeWidget *gwidget = glade_widget_get_from_gobject (container); + + if (reason == GLADE_CREATE_USER) { + add_page (adaptor, container); + add_page (adaptor, container); + add_page (adaptor, container); + } + + g_signal_connect (G_OBJECT (gwidget), + "notify::project", + G_CALLBACK (project_changed_cb), + NULL); + + project_changed_cb (gwidget, NULL, NULL); +} + +gboolean +glade_hdy_preferences_window_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback) +{ + if (!HDY_IS_PREFERENCES_PAGE (child)) { + if (user_feedback) { + GladeWidgetAdaptor *page_adaptor = + glade_widget_adaptor_get_by_type (HDY_TYPE_PREFERENCES_PAGE); + + glade_util_ui_message (glade_app_get_window (), + GLADE_UI_INFO, NULL, + ONLY_THIS_GOES_IN_THAT_MSG, + glade_widget_adaptor_get_title (page_adaptor), + glade_widget_adaptor_get_title (adaptor)); + } + + return FALSE; + } + + return TRUE; +} + +void +glade_hdy_preferences_window_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); +} + +void +glade_hdy_preferences_window_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); +} + +void +glade_hdy_preferences_window_replace_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *current, + GObject *new_widget) +{ + gint index = glade_hdy_get_child_index (GTK_CONTAINER (object), GTK_WIDGET (current)); + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (current)); + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (new_widget)); + glade_hdy_reorder_child (GTK_CONTAINER (object), GTK_WIDGET (new_widget), index); +} + +GList * +glade_hdy_preferences_window_get_children (GladeWidgetAdaptor *adaptor, + GObject *object) +{ + return gtk_container_get_children (GTK_CONTAINER (object)); +} + +void +glade_hdy_preferences_window_action_activate (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *action_path) +{ + GladeWidget *parent = glade_widget_get_from_gobject (object); + + if (!g_strcmp0 (action_path, "add_page")) { + g_autofree gchar *title = get_unused_title (GTK_CONTAINER (object)); + GladeWidget *gchild; + + glade_command_push_group (_("Add page to %s"), + glade_widget_get_name (parent)); + + gchild = glade_command_create (glade_widget_adaptor_get_by_type (HDY_TYPE_PREFERENCES_PAGE), + parent, + NULL, + glade_widget_get_project (parent)); + + glade_widget_property_set (gchild, "title", title); + + glade_command_pop_group (); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->action_activate (adaptor, + object, + action_path); + } +} + +void +glade_hdy_preferences_window_child_set_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + const GValue *value) +{ + if (!g_strcmp0 (property_name, "position")) { + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (child)); + + gtk_container_child_set_property (GTK_CONTAINER (parent), + GTK_WIDGET (child), property_name, value); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_set_property (adaptor, + container, + child, + property_name, + value); + } +} + +void +glade_hdy_preferences_window_child_get_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value) +{ + if (!g_strcmp0 (property_name, "position")) { + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (child)); + + gtk_container_child_get_property (GTK_CONTAINER (parent), + GTK_WIDGET (child), property_name, value); + } else { + GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_get_property (adaptor, + container, + child, + property_name, + value); + } +} diff --git a/subprojects/libhandy/glade/glade-hdy-preferences-window.h b/subprojects/libhandy/glade/glade-hdy-preferences-window.h new file mode 100644 index 0000000..e4f1f37 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-preferences-window.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + +void glade_hdy_preferences_window_post_create (GladeWidgetAdaptor *adaptor, + GObject *container, + GladeCreateReason reason); + +gboolean glade_hdy_preferences_window_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback); + +void glade_hdy_preferences_window_add_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_preferences_window_remove_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child); +void glade_hdy_preferences_window_replace_child (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *current, + GObject *new_widget); + +GList *glade_hdy_preferences_window_get_children (GladeWidgetAdaptor *adaptor, + GObject *object); + +void glade_hdy_preferences_window_action_activate (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *action_path); + +void glade_hdy_preferences_window_child_set_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + const GValue *value); + +void glade_hdy_preferences_window_child_get_property (GladeWidgetAdaptor *adaptor, + GObject *container, + GObject *child, + const gchar *property_name, + GValue *value); diff --git a/subprojects/libhandy/glade/glade-hdy-search-bar.c b/subprojects/libhandy/glade/glade-hdy-search-bar.c new file mode 100644 index 0000000..c13f1eb --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-search-bar.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-searchbar.c - GladeWidgetAdaptor for GtkSearchBar + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-search-bar.h" + +#include <gladeui/glade.h> + +void +glade_hdy_search_bar_post_create (GladeWidgetAdaptor *adaptor, + GObject *widget, + GladeCreateReason reason) +{ + if (reason == GLADE_CREATE_USER) { + GtkWidget *child = glade_placeholder_new (); + gtk_container_add (GTK_CONTAINER (widget), child); + g_object_set_data (G_OBJECT (widget), "child", child); + } + + hdy_search_bar_set_search_mode (HDY_SEARCH_BAR (widget), TRUE); + hdy_search_bar_set_show_close_button (HDY_SEARCH_BAR (widget), FALSE); +} + +void +glade_hdy_search_bar_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + GObject *current = g_object_get_data (G_OBJECT (object), "child"); + + if (current) + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (current))), + GTK_WIDGET (current)); + + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); + g_object_set_data (G_OBJECT (object), "child", child); +} + +void +glade_hdy_search_bar_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + GObject *current = g_object_get_data (G_OBJECT (object), "child"); + GtkWidget *new_child; + + if (current != child) + return; + + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (child))), GTK_WIDGET (child)); + new_child = glade_placeholder_new (); + gtk_container_add (GTK_CONTAINER (object), new_child); + g_object_set_data (G_OBJECT (object), "child", new_child); +} + +void +glade_hdy_search_bar_replace_child (GladeWidgetAdaptor *adaptor, + GtkWidget *container, + GtkWidget *current, + GtkWidget *new_widget) +{ + if (current != (GtkWidget *) g_object_get_data (G_OBJECT (container), "child")) + return; + + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (current))), + GTK_WIDGET (current)); + gtk_container_add (GTK_CONTAINER (container), new_widget); + g_object_set_data (G_OBJECT (container), "child", new_widget); +} + +GList * +glade_hdy_search_bar_get_children (GladeWidgetAdaptor *adaptor, + GObject *widget) +{ + GObject *current = g_object_get_data (G_OBJECT (widget), "child"); + + return g_list_append (NULL, current); +} + +gboolean +glade_hdy_search_bar_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *container, + GtkWidget *child, + gboolean user_feedback) +{ + GObject *current = g_object_get_data (G_OBJECT (container), "child"); + + if (!GLADE_IS_PLACEHOLDER (current)) { + if (user_feedback) + glade_util_ui_message (glade_app_get_window (), + GLADE_UI_INFO, NULL, + _("Search bar is already full")); + + return FALSE; + } + + return TRUE; +} diff --git a/subprojects/libhandy/glade/glade-hdy-search-bar.h b/subprojects/libhandy/glade/glade-hdy-search-bar.h new file mode 100644 index 0000000..05306b8 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-search-bar.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + +void glade_hdy_search_bar_post_create (GladeWidgetAdaptor *adaptor, + GObject *widget, + GladeCreateReason reason); + +void glade_hdy_search_bar_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child); +void glade_hdy_search_bar_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child); +void glade_hdy_search_bar_replace_child (GladeWidgetAdaptor *adaptor, + GtkWidget *container, + GtkWidget *current, + GtkWidget *new_widget); + +GList *glade_hdy_search_bar_get_children (GladeWidgetAdaptor *adaptor, + GObject *widget); + +gboolean glade_hdy_search_bar_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *container, + GtkWidget *child, + gboolean user_feedback); diff --git a/subprojects/libhandy/glade/glade-hdy-swipe-group.c b/subprojects/libhandy/glade/glade-hdy-swipe-group.c new file mode 100644 index 0000000..059138d --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-swipe-group.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * hdy-header-group.c - GladeWidgetAdaptor for HdyHeaderGroup + * Copyright (C) 2018 Purism SPC + * Copyright (C) 2013 Tristan Van Berkom + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-swipe-group.h" + +#include <gladeui/glade.h> +#include "glade-hdy-utils.h" + +#define PROP_SWIPEABLES "swipeables" +#define GLADE_TAG_SWIPEGROUP_SWIPEABLES "swipeables" +#define GLADE_TAG_SWIPEGROUP_SWIPEABLE "swipeable" + +static void +glade_hdy_swipe_group_read_widgets (GladeWidget *widget, + GladeXmlNode *node) +{ + GladeXmlNode *widgets_node; + GladeProperty *property; + gchar *string = NULL; + + if ((widgets_node = + glade_xml_search_child (node, GLADE_TAG_SWIPEGROUP_SWIPEABLES)) != NULL) { + GladeXmlNode *n; + + for (n = glade_xml_node_get_children (widgets_node); + n; n = glade_xml_node_next (n)) { + gchar *widget_name, *tmp; + + if (!glade_xml_node_verify (n, GLADE_TAG_SWIPEGROUP_SWIPEABLE)) + continue; + + widget_name = glade_xml_get_property_string_required + (n, GLADE_TAG_NAME, NULL); + + if (string == NULL) { + string = widget_name; + } else if (widget_name != NULL) { + tmp = + g_strdup_printf ("%s%s%s", string, GLADE_PROPERTY_DEF_OBJECT_DELIMITER, + widget_name); + string = (g_free (string), tmp); + g_free (widget_name); + } + } + } + + if (string) { + property = glade_widget_get_property (widget, PROP_SWIPEABLES); + g_assert (property); + + /* we must synchronize this directly after loading this project + * (i.e. lookup the actual objects after they've been parsed and + * are present). + */ + g_object_set_data_full (G_OBJECT (property), + "glade-loaded-object", string, g_free); + } +} + +void +glade_hdy_swipe_group_read_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlNode *node) +{ + if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) || + glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE))) + return; + + /* First chain up and read in all the normal properties.. */ + GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node); + + glade_hdy_swipe_group_read_widgets (widget, node); +} + +static void +glade_hdy_swipe_group_write_widgets (GladeWidget *widget, + GladeXmlContext *context, + GladeXmlNode *node) +{ + GladeXmlNode *widgets_node, *widget_node; + GList *widgets = NULL, *list; + GladeWidget *awidget; + + widgets_node = glade_xml_node_new (context, GLADE_TAG_SWIPEGROUP_SWIPEABLES); + + if (glade_widget_property_get (widget, PROP_SWIPEABLES, &widgets)) { + for (list = widgets; list; list = list->next) { + awidget = glade_widget_get_from_gobject (list->data); + widget_node = + glade_xml_node_new (context, GLADE_TAG_SWIPEGROUP_SWIPEABLE); + glade_xml_node_append_child (widgets_node, widget_node); + glade_xml_node_set_property_string (widget_node, GLADE_TAG_NAME, + glade_widget_get_name (awidget)); + } + } + + if (!glade_xml_node_get_children (widgets_node)) + glade_xml_node_delete (widgets_node); + else + glade_xml_node_append_child (node, widgets_node); +} + +void +glade_hdy_swipe_group_write_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlContext *context, + GladeXmlNode *node) +{ + if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) || + glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE))) + return; + + /* First chain up and read in all the normal properties.. */ + GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node); + + glade_hdy_swipe_group_write_widgets (widget, context, node); +} + +void +glade_hdy_swipe_group_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *property_name, + const GValue *value) +{ + if (!strcmp (property_name, PROP_SWIPEABLES)) { + GSList *sg_widgets, *slist; + GList *widgets, *list; + + /* remove old widgets */ + if ((sg_widgets = + hdy_swipe_group_get_swipeables (HDY_SWIPE_GROUP (object))) != NULL) { + /* copy since we are modifying an internal list */ + sg_widgets = g_slist_copy (sg_widgets); + for (slist = sg_widgets; slist; slist = slist->next) + hdy_swipe_group_remove_swipeable (HDY_SWIPE_GROUP (object), + HDY_SWIPEABLE (slist->data)); + g_slist_free (sg_widgets); + } + + /* add new widgets */ + if ((widgets = g_value_get_boxed (value)) != NULL) { + for (list = widgets; list; list = list->next) + hdy_swipe_group_add_swipeable (HDY_SWIPE_GROUP (object), + HDY_SWIPEABLE (list->data)); + } + } else { + GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor, object, + property_name, value); + } +} diff --git a/subprojects/libhandy/glade/glade-hdy-swipe-group.h b/subprojects/libhandy/glade/glade-hdy-swipe-group.h new file mode 100644 index 0000000..5f593e5 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-swipe-group.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * hdy-header-group.h - GladeWidgetAdaptor for HdyHeaderGroup + * Copyright (C) 2018 Purism SPC + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + + +void glade_hdy_swipe_group_set_property (GladeWidgetAdaptor *adaptor, + GObject *object, + const gchar *property_name, + const GValue *value); +void glade_hdy_swipe_group_write_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlContext *context, + GladeXmlNode *node); +void glade_hdy_swipe_group_read_widget (GladeWidgetAdaptor *adaptor, + GladeWidget *widget, + GladeXmlNode *node); diff --git a/subprojects/libhandy/glade/glade-hdy-utils.c b/subprojects/libhandy/glade/glade-hdy-utils.c new file mode 100644 index 0000000..81b8ae5 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-utils.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-utils.h" + +#include <gladeui/glade.h> + +void +glade_hdy_init (const gchar *name) +{ + g_assert (strcmp (name, "libhandy") == 0); + + gtk_init (NULL, NULL); + hdy_init (); +} + +/* This function has been copied and modified from: + * glade-gtk-list-box.c - GladeWidgetAdaptor for GtkListBox widget + * + * Copyright (C) 2013 Kalev Lember + * + * Authors: + * Kalev Lember <kalevlember@gmail.com> + */ +void +glade_hdy_sync_child_positions (GtkContainer *container) +{ + g_autoptr (GList) children = NULL; + GList *l; + gint position; + static gboolean recursion = FALSE; + + /* Avoid feedback loop */ + if (recursion) + return; + + children = gtk_container_get_children (container); + + position = 0; + for (l = children; l; l = l->next) { + gint old_position; + + glade_widget_pack_property_get (glade_widget_get_from_gobject (l->data), + "position", &old_position); + if (position != old_position) { + /* Update glade with the new value */ + recursion = TRUE; + glade_widget_pack_property_set (glade_widget_get_from_gobject (l->data), + "position", position); + recursion = FALSE; + } + + position++; + } +} + +gint +glade_hdy_get_child_index (GtkContainer *container, + GtkWidget *child) +{ + g_autoptr (GList) children = gtk_container_get_children (container); + + return g_list_index (children, child); +} + +void +glade_hdy_reorder_child (GtkContainer *container, + GtkWidget *child, + gint index) +{ + gint old_index = glade_hdy_get_child_index (container, child); + gint i = 0, n; + g_autoptr (GList) children = NULL; + g_autoptr (GList) removed_children = NULL; + GList *l; + + if (old_index == index) + return; + + gtk_container_remove (container, g_object_ref (child)); + + children = gtk_container_get_children (container); + n = g_list_length (children); + + children = g_list_reverse (children); + l = children; + + if (index > old_index) + n--; + + for (i = n - 1; i >= index; i--) { + GtkWidget *last_child = l->data; + + gtk_container_remove (container, g_object_ref (last_child)); + l = l->next; + + removed_children = g_list_prepend (removed_children, last_child); + } + + removed_children = g_list_prepend (removed_children, child); + + for (l = removed_children; l; l = l->next) { + gtk_container_add (container, l->data); + g_object_unref (l->data); + } +} + +GtkWidget * +glade_hdy_get_nth_child (GtkContainer *container, + gint n) +{ + g_autoptr (GList) children = gtk_container_get_children (container); + + return g_list_nth_data (children, n); +} diff --git a/subprojects/libhandy/glade/glade-hdy-utils.h b/subprojects/libhandy/glade/glade-hdy-utils.h new file mode 100644 index 0000000..11870d1 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-utils.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + +#define ONLY_THIS_GOES_IN_THAT_MSG _("Only objects of type %s can be added to objects of type %s.") + +/* Guess whether we are using a Glade version older than 3.36. + * + * If yes, redefine some symbols which got renamed. + */ +#ifndef GLADE_PROPERTY_DEF_OBJECT_DELIMITER +#define GLADE_PROPERTY_DEF_OBJECT_DELIMITER GPC_OBJECT_DELIMITER +#define glade_widget_action_get_def glade_widget_action_get_class +#endif + +void glade_hdy_init (const gchar *name); + +void glade_hdy_sync_child_positions (GtkContainer *container); + +gint glade_hdy_get_child_index (GtkContainer *container, + GtkWidget *child); + +void glade_hdy_reorder_child (GtkContainer *container, + GtkWidget *child, + gint index); + +GtkWidget *glade_hdy_get_nth_child (GtkContainer *container, + gint n); diff --git a/subprojects/libhandy/glade/glade-hdy-window.c b/subprojects/libhandy/glade/glade-hdy-window.c new file mode 100644 index 0000000..7832f91 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-window.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + * + * Based on + * glade-gtk-searchbar.c - GladeWidgetAdaptor for GtkSearchBar + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include <config.h> +#include <glib/gi18n-lib.h> + +#include "glade-hdy-window.h" + +#include <gladeui/glade.h> + +#define ALREADY_HAS_A_CHILD_MSG _("%s cannot have more than one child.") + +static GtkWidget * +get_child (GtkContainer *window) +{ + g_autoptr (GList) children = gtk_container_get_children (window); + + if (!children) + return NULL; + + return children->data; +} + +void +glade_hdy_window_post_create (GladeWidgetAdaptor *adaptor, + GObject *object, + GladeCreateReason reason) +{ + if (reason != GLADE_CREATE_USER) + return; + + gtk_container_add (GTK_CONTAINER (object), glade_placeholder_new ()); +} + +void +glade_hdy_window_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + GtkWidget *window_child = get_child (GTK_CONTAINER (object)); + + if (window_child) { + if (GLADE_IS_PLACEHOLDER (window_child)) { + gtk_container_remove (GTK_CONTAINER (object), window_child); + } else { + g_critical ("Can't add more than one widget to a HdyWindow"); + + return; + } + } + + gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child)); +} + +void +glade_hdy_window_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child) +{ + gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child)); + gtk_container_add (GTK_CONTAINER (object), glade_placeholder_new ()); +} + +void +glade_hdy_window_replace_child (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *current, + GtkWidget *new_widget) +{ + gtk_container_remove (GTK_CONTAINER (object), current); + gtk_container_add (GTK_CONTAINER (object), new_widget); +} + +GList * +glade_hdy_window_get_children (GladeWidgetAdaptor *adaptor, + GObject *object) +{ + return gtk_container_get_children (GTK_CONTAINER (object)); +} + +gboolean +glade_hdy_window_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback) +{ + GtkWidget *window_child = get_child (GTK_CONTAINER (object)); + + if (window_child && !GLADE_IS_PLACEHOLDER (window_child)) { + if (user_feedback) + glade_util_ui_message (glade_app_get_window (), + GLADE_UI_INFO, NULL, + ALREADY_HAS_A_CHILD_MSG, + glade_widget_adaptor_get_title (adaptor)); + + return FALSE; + } + + return TRUE; +} + diff --git a/subprojects/libhandy/glade/glade-hdy-window.h b/subprojects/libhandy/glade/glade-hdy-window.h new file mode 100644 index 0000000..75f15e4 --- /dev/null +++ b/subprojects/libhandy/glade/glade-hdy-window.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 Alexander Mikhaylenko <alexm@gnome.org> + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include <gladeui/glade.h> + +#include <handy.h> + +void glade_hdy_window_post_create (GladeWidgetAdaptor *adaptor, + GObject *object, + GladeCreateReason reason); + +void glade_hdy_window_add_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child); +void glade_hdy_window_remove_child (GladeWidgetAdaptor *adaptor, + GObject *object, + GObject *child); +void glade_hdy_window_replace_child (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *current, + GtkWidget *new_widget); + +GList *glade_hdy_window_get_children (GladeWidgetAdaptor *adaptor, + GObject *object); + +gboolean glade_hdy_window_add_verify (GladeWidgetAdaptor *adaptor, + GtkWidget *object, + GtkWidget *child, + gboolean user_feedback); diff --git a/subprojects/libhandy/glade/libhandy.xml b/subprojects/libhandy/glade/libhandy.xml new file mode 100644 index 0000000..7f60118 --- /dev/null +++ b/subprojects/libhandy/glade/libhandy.xml @@ -0,0 +1,420 @@ +<?xml version="1.0" encoding="UTF-8"?> +<glade-catalog name="libhandy" library="glade-handy-1" depends="gtk+" book="libhandy"> + <init-function>glade_hdy_init</init-function> + <glade-widget-classes> + <glade-widget-class name="HdyActionRow" generic-name="actionrow" title="Action Row" since="0.0.6"> + <properties> + <property id="icon-name" themed-icon="True" /> + <property id="title" translatable="True" /> + <property id="subtitle" translatable="True" /> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyAvatar" generic-name="avatar" title="Avatar" since="1.0"/> + <glade-widget-class name="HdyApplicationWindow" generic-name="applicationwindow" title="Application Window" since="1.0" use-placeholders="False"> + <post-create-function>glade_hdy_window_post_create</post-create-function> + <add-child-verify-function>glade_hdy_window_add_verify</add-child-verify-function> + <add-child-function>glade_hdy_window_add_child</add-child-function> + <remove-child-function>glade_hdy_window_remove_child</remove-child-function> + <replace-child-function>glade_hdy_window_replace_child</replace-child-function> + <get-children-function>glade_hdy_window_get_children</get-children-function> + <properties> + <property id="show-menubar" disabled="True" /> + <property id="use-csd" disabled="True" /> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyCarousel" generic-name="carousel" title="Carousel" since="1.0"> + <post-create-function>glade_hdy_carousel_post_create</post-create-function> + <add-child-function>glade_hdy_carousel_add_child</add-child-function> + <remove-child-function>glade_hdy_carousel_remove_child</remove-child-function> + <replace-child-function>glade_hdy_carousel_replace_child</replace-child-function> + <child-action-activate-function>glade_hdy_carousel_child_action_activate</child-action-activate-function> + <get-property-function>glade_hdy_carousel_get_property</get-property-function> + <set-property-function>glade_hdy_carousel_set_property</set-property-function> + <child-set-property-function>glade_hdy_carousel_set_child_property</child-set-property-function> + <child-get-property-function>glade_hdy_carousel_get_child_property</child-get-property-function> + <verify-function>glade_hdy_carousel_verify_property</verify-function> + <packing-properties> + <property id="position" name="Position" default="0" save="False"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>The position of the page in the carousel</tooltip> + </property> + </packing-properties> + <packing-actions> + <action id="insert_page_before" name="Insert Page Before" stock="list-add"/> + <action id="insert_page_after" name="Insert Page After" stock="list-add"/> + <action id="remove_page" name="Remove Page" stock="list-remove"/> + </packing-actions> + <properties> + <property id="pages" name="Number of pages" save="False" default="1"> + <parameter-spec> + <type>GParamInt</type> + <min>1</min> + </parameter-spec> + <tooltip>The number of pages in the stack</tooltip> + </property> + <property id="page" name="Edit page" save="False" default="0"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>Set the currently active page to edit, this property will not be saved</tooltip> + </property> + <property id="above-child" disabled="True" /> + <property id="visible-window" disabled="True" /> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyCarouselIndicatorDots" generic-name="carouselindicatordots" title="Carousel Indicator Dots" since="1.0"/> + <glade-widget-class name="HdyCarouselIndicatorLines" generic-name="carouselindicatorlines" title="Carousel Indicator Lines" since="1.0"/> + <glade-widget-class name="HdyClamp" generic-name="clamp" title="Clamp" since="1.0"/> + <glade-widget-class name="HdyComboRow" generic-name="comborow" title="Combo Row" since="0.0.6"/> + <glade-widget-class name="HdyDeck" generic-name="deck" title="Deck" since="1.0"> + <post-create-function>glade_hdy_leaflet_post_create</post-create-function> + <add-child-function>glade_hdy_leaflet_add_child</add-child-function> + <remove-child-function>glade_hdy_leaflet_remove_child</remove-child-function> + <replace-child-function>glade_hdy_leaflet_replace_child</replace-child-function> + <child-action-activate-function>glade_hdy_leaflet_child_action_activate</child-action-activate-function> + <get-property-function>glade_hdy_leaflet_get_property</get-property-function> + <set-property-function>glade_hdy_leaflet_set_property</set-property-function> + <child-set-property-function>glade_hdy_leaflet_set_child_property</child-set-property-function> + <child-get-property-function>glade_hdy_leaflet_get_child_property</child-get-property-function> + <verify-function>glade_hdy_leaflet_verify_property</verify-function> + <packing-properties> + <property id="position" name="Position" default="0" save="False"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>The position of the page in the deck</tooltip> + </property> + </packing-properties> + <packing-actions> + <action id="insert_page_before" name="Insert Page Before" stock="list-add"/> + <action id="insert_page_after" name="Insert Page After" stock="list-add"/> + <action id="remove_page" name="Remove Page" stock="list-remove"/> + </packing-actions> + <properties> + <property id="pages" name="Number of pages" save="False" default="1"> + <parameter-spec> + <type>GParamInt</type> + <min>1</min> + </parameter-spec> + <tooltip>The number of pages in the deck</tooltip> + </property> + <property id="page" name="Edit page" save="False" default="0"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>Set the currently active page to edit, this property will not be saved</tooltip> + </property> + <property id="transition-type"> + <displayable-values> + <!-- HdyDeckTransitionType enumeration value --> + <value id="HDY_DECK_TRANSITION_TYPE_OVER" name="Over"/> + <!-- HdyDeckTransitionType enumeration value --> + <value id="HDY_DECK_TRANSITION_TYPE_UNDER" name="Under"/> + <!-- HdyDeckTransitionType enumeration value --> + <value id="HDY_DECK_TRANSITION_TYPE_SLIDE" name="Slide"/> + </displayable-values> + </property> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyExpanderRow" generic-name="expanderrow" title="Expander Row" since="0.0.6" use-placeholders="False"> + <post-create-function>glade_hdy_expander_row_post_create</post-create-function> + <add-child-verify-function>glade_hdy_expander_row_add_verify</add-child-verify-function> + <add-child-function>glade_hdy_expander_row_add_child</add-child-function> + <remove-child-function>glade_hdy_expander_row_remove_child</remove-child-function> + <child-set-property-function>glade_hdy_expander_row_set_child_property</child-set-property-function> + <child-get-property-function>glade_hdy_expander_row_get_child_property</child-get-property-function> + <packing-properties> + <property id="position" name="Position" default="0" save="False"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>The position of the list row in the expander row</tooltip> + </property> + </packing-properties> + <properties> + <property id="expanded" save="True" ignore="True"/> + <property id="enable-expansion" save="True" ignore="True"/> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyHeaderBar" generic-name="headerbar" title="Header Bar" since="0.0.10"> + <post-create-function>glade_hdy_header_bar_post_create</post-create-function> + <add-child-function>glade_hdy_header_bar_add_child</add-child-function> + <remove-child-function>glade_hdy_header_bar_remove_child</remove-child-function> + <replace-child-function>glade_hdy_header_bar_replace_child</replace-child-function> + <get-children-function>glade_hdy_header_bar_get_children</get-children-function> + <child-action-activate-function>glade_hdy_header_bar_child_action_activate</child-action-activate-function> + <get-property-function>glade_hdy_header_bar_get_property</get-property-function> + <set-property-function>glade_hdy_header_bar_set_property</set-property-function> + <child-set-property-function>glade_hdy_header_bar_child_set_property</child-set-property-function> + <action-activate-function>glade_hdy_header_bar_action_activate</action-activate-function> + <verify-function>glade_hdy_header_bar_verify_property</verify-function> + <special-child-type>type</special-child-type> + <packing-properties> + <property id="pack-type" transfer-on-paste="True" /> + </packing-properties> + <packing-actions> + <action id="remove_slot" name="Remove Slot" stock="gtk-remove"/> + </packing-actions> + <properties> + <property id="title" translatable="True"/> + <property id="subtitle" translatable="True"/> + <property id="has-subtitle" name="Reserve space for subtitle"> + <tooltip>Keep the headerbar height the same as the subtitle changes dynamically.</tooltip> + </property> + <property id="show-close-button" name="Show window controls" needs-sync="True"/> + <property id="spacing"/> + <property id="decoration-layout"/> + <property id="decoration-layout-set" disabled="True"/> + <property id="custom-title" disabled="True"/> + <property id="use-custom-title" name="Custom Title" default="FALSE" visible="True" save="False"> + <parameter-spec> + <type>GParamBoolean</type> + </parameter-spec> + </property> + <property visible="True" save="False" id="size" default="1" name="Number of items"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>The number of items in the header bar</tooltip> + </property> + <property id="centering-policy"> + <displayable-values> + <!-- HdyCenteringPolicy enumeration value --> + <value id="HDY_CENTERING_POLICY_LOOSE" name="Loose"/> + <!-- HdyCenteringPolicy enumeration value --> + <value id="HDY_CENTERING_POLICY_STRICT" name="Strict"/> + </displayable-values> + </property> + </properties> + <actions> + <action id="add_slot" name="Add Slot" stock="list-add"/> + </actions> + </glade-widget-class> + <glade-widget-class name="HdyHeaderGroup" generic-name="headergroup" title="Header Group" toplevel="True"> + <read-widget-function>glade_hdy_header_group_read_widget</read-widget-function> + <write-widget-function>glade_hdy_header_group_write_widget</write-widget-function> + <set-property-function>glade_hdy_header_group_set_property</set-property-function> + <properties> + <property id="headerbars" name="Headerbars" save="False"> + <parameter-spec> + <type>GladeParamObjects</type> + <value-type>HdyHeaderBar</value-type> + </parameter-spec> + <tooltip>List of headerbars in this group</tooltip> + </property> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyKeypad" generic-name="keypad" title="Keypad"/> + <glade-widget-class name="HdyLeaflet" generic-name="leaflet" title="Leaflet"> + <post-create-function>glade_hdy_leaflet_post_create</post-create-function> + <add-child-function>glade_hdy_leaflet_add_child</add-child-function> + <remove-child-function>glade_hdy_leaflet_remove_child</remove-child-function> + <replace-child-function>glade_hdy_leaflet_replace_child</replace-child-function> + <child-action-activate-function>glade_hdy_leaflet_child_action_activate</child-action-activate-function> + <get-property-function>glade_hdy_leaflet_get_property</get-property-function> + <set-property-function>glade_hdy_leaflet_set_property</set-property-function> + <child-set-property-function>glade_hdy_leaflet_set_child_property</child-set-property-function> + <child-get-property-function>glade_hdy_leaflet_get_child_property</child-get-property-function> + <verify-function>glade_hdy_leaflet_verify_property</verify-function> + <packing-properties> + <property id="position" name="Position" default="0" save="False"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>The position of the page in the leaflet</tooltip> + </property> + </packing-properties> + <packing-actions> + <action id="insert_page_before" name="Insert Page Before" stock="list-add"/> + <action id="insert_page_after" name="Insert Page After" stock="list-add"/> + <action id="remove_page" name="Remove Page" stock="list-remove"/> + </packing-actions> + <properties> + <property id="pages" name="Number of pages" save="False" default="1"> + <parameter-spec> + <type>GParamInt</type> + <min>1</min> + </parameter-spec> + <tooltip>The number of pages in the leaflet</tooltip> + </property> + <property id="page" name="Edit page" save="False" default="0"> + <parameter-spec> + <type>GParamInt</type> + <min>0</min> + </parameter-spec> + <tooltip>Set the currently active page to edit, this property will not be saved</tooltip> + </property> + <property id="transition-type"> + <displayable-values> + <!-- HdyLeafletTransitionType enumeration value --> + <value id="HDY_LEAFLET_TRANSITION_TYPE_OVER" name="Over"/> + <!-- HdyLeafletTransitionType enumeration value --> + <value id="HDY_LEAFLET_TRANSITION_TYPE_UNDER" name="Under"/> + <!-- HdyLeafletTransitionType enumeration value --> + <value id="HDY_LEAFLET_TRANSITION_TYPE_SLIDE" name="Slide"/> + </displayable-values> + </property> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyPreferencesGroup" generic-name="preferencesgroup" title="Preferences Group" since="0.0.10" use-placeholders="False"> + <properties> + <property id="title" translatable="True" /> + <property id="description" translatable="True" /> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyPreferencesPage" generic-name="preferencespage" title="Preferences Page" since="0.0.10" use-placeholders="False"> + <post-create-function>glade_hdy_preferences_page_post_create</post-create-function> + <add-child-verify-function>glade_hdy_preferences_page_add_verify</add-child-verify-function> + <add-child-function>glade_hdy_preferences_page_add_child</add-child-function> + <remove-child-function>glade_hdy_preferences_page_remove_child</remove-child-function> + <replace-child-function>glade_hdy_preferences_page_replace_child</replace-child-function> + <get-children-function>glade_hdy_preferences_page_get_children</get-children-function> + <child-set-property-function>glade_hdy_preferences_page_child_set_property</child-set-property-function> + <child-get-property-function>glade_hdy_preferences_page_child_get_property</child-get-property-function> + <action-activate-function>glade_hdy_preferences_page_action_activate</action-activate-function> + <packing-properties> + <property id="position" name="Position" default="-1" save="False"> + <parameter-spec> + <type>GParamInt</type> + <min>-1</min> + </parameter-spec> + <tooltip>The position of the group in the preferences page</tooltip> + </property> + </packing-properties> + <properties> + <property id="icon-name" themed-icon="True" /> + <property id="title" translatable="True" /> + </properties> + <actions> + <action id="add_group" name="Add Group" stock="list-add" important="True"/> + </actions> + </glade-widget-class> + <glade-widget-class name="HdyPreferencesRow" generic-name="preferencesrow" title="Preferences Row" since="0.0.10"/> + <glade-widget-class name="HdyPreferencesWindow" generic-name="preferenceswindow" title="Preferences Window" since="0.0.10" use-placeholders="False"> + <post-create-function>glade_hdy_preferences_window_post_create</post-create-function> + <add-child-verify-function>glade_hdy_preferences_window_add_verify</add-child-verify-function> + <add-child-function>glade_hdy_preferences_window_add_child</add-child-function> + <remove-child-function>glade_hdy_preferences_window_remove_child</remove-child-function> + <replace-child-function>glade_hdy_preferences_window_replace_child</replace-child-function> + <get-children-function>glade_hdy_preferences_window_get_children</get-children-function> + <child-set-property-function>glade_hdy_preferences_window_child_set_property</child-set-property-function> + <child-get-property-function>glade_hdy_preferences_window_child_get_property</child-get-property-function> + <action-activate-function>glade_hdy_preferences_window_action_activate</action-activate-function> + <packing-properties> + <property id="position" name="Position" default="-1" save="False"> + <parameter-spec> + <type>GParamInt</type> + <min>-1</min> + </parameter-spec> + <tooltip>The position of the page in the preferences window</tooltip> + </property> + </packing-properties> + <properties> + <property id="use-csd" disabled="True" /> + </properties> + <actions> + <action id="add_page" name="Add Page" stock="list-add" important="True"/> + </actions> + </glade-widget-class> + <glade-widget-class name="HdySearchBar" generic-name="searchbar" title="Search Bar" since="0.0.6"> + <post-create-function>glade_hdy_search_bar_post_create</post-create-function> + <add-child-verify-function>glade_hdy_search_bar_add_verify</add-child-verify-function> + <add-child-function>glade_hdy_search_bar_add_child</add-child-function> + <remove-child-function>glade_hdy_search_bar_remove_child</remove-child-function> + <replace-child-function>glade_hdy_search_bar_replace_child</replace-child-function> + <get-children-function>glade_hdy_search_bar_get_children</get-children-function> + <properties> + <property id="search-mode-enabled" save="True" ignore="True"/> + <property id="show-close-button" save="True" ignore="True"/> + </properties> + </glade-widget-class> + <glade-widget-class name="HdySqueezer" generic-name="squeezer" title="Squeezer" since="0.0.10"/> + <glade-widget-class name="HdySwipeGroup" generic-name="swipegroup" title="Swipe Group" toplevel="True"> + <read-widget-function>glade_hdy_swipe_group_read_widget</read-widget-function> + <write-widget-function>glade_hdy_swipe_group_write_widget</write-widget-function> + <set-property-function>glade_hdy_swipe_group_set_property</set-property-function> + <properties> + <property id="swipeables" name="Widgets" save="False"> + <parameter-spec> + <type>GladeParamObjects</type> + <value-type>HdySwipeable</value-type> + </parameter-spec> + <tooltip>List of widgets in this group</tooltip> + </property> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyTitleBar" generic-name="titlebar" title="Title Bar"/> + <glade-widget-class name="HdyViewSwitcher" generic-name="viewswitcher" title="View Switcher" since="0.0.10"/> + <glade-widget-class name="HdyViewSwitcherBar" generic-name="viewswitcherbar" title="View Switcher Bar" since="0.0.10"/> + <glade-widget-class name="HdyViewSwitcherTitle" generic-name="viewswitchertitle" title="View Switcher Title" since="1.0"> + <properties> + <property id="policy"> + <displayable-values> + <!-- HdyViewSwitcherPolicy enumeration value --> + <value id="HDY_VIEW_SWITCHER_POLICY_AUTO" name="Auto"/> + <!-- HdyViewSwitcherPolicy enumeration value --> + <value id="HDY_VIEW_SWITCHER_POLICY_NARROW" name="Narrow"/> + <!-- HdyViewSwitcherPolicy enumeration value --> + <value id="HDY_VIEW_SWITCHER_POLICY_WIDE" name="Wide"/> + </displayable-values> + </property> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyWindow" generic-name="window" title="Window" since="1.0" use-placeholders="False"> + <post-create-function>glade_hdy_window_post_create</post-create-function> + <add-child-verify-function>glade_hdy_window_add_verify</add-child-verify-function> + <add-child-function>glade_hdy_window_add_child</add-child-function> + <remove-child-function>glade_hdy_window_remove_child</remove-child-function> + <replace-child-function>glade_hdy_window_replace_child</replace-child-function> + <get-children-function>glade_hdy_window_get_children</get-children-function> + <properties> + <property id="use-csd" disabled="True" /> + </properties> + </glade-widget-class> + <glade-widget-class name="HdyWindowHandle" generic-name="windowhandle" title="Window Handle" since="1.0"> + <properties> + <property id="above-child" disabled="True" /> + <property id="visible-window" disabled="True" /> + </properties> + </glade-widget-class> + </glade-widget-classes> + + <glade-widget-group name="handy" title="Libhandy Widgets"> + <glade-widget-class-ref name="HdyActionRow"/> + <glade-widget-class-ref name="HdyApplicationWindow"/> + <glade-widget-class-ref name="HdyAvatar"/> + <glade-widget-class-ref name="HdyCarousel"/> + <glade-widget-class-ref name="HdyCarouselIndicatorDots"/> + <glade-widget-class-ref name="HdyCarouselIndicatorLines"/> + <glade-widget-class-ref name="HdyClamp"/> + <glade-widget-class-ref name="HdyComboRow"/> + <glade-widget-class-ref name="HdyDeck"/> + <glade-widget-class-ref name="HdyExpanderRow"/> + <glade-widget-class-ref name="HdyHeaderBar"/> + <glade-widget-class-ref name="HdyHeaderGroup"/> + <glade-widget-class-ref name="HdyKeypad"/> + <glade-widget-class-ref name="HdyLeaflet"/> + <glade-widget-class-ref name="HdyPreferencesGroup"/> + <glade-widget-class-ref name="HdyPreferencesPage"/> + <glade-widget-class-ref name="HdyPreferencesRow"/> + <glade-widget-class-ref name="HdyPreferencesWindow"/> + <glade-widget-class-ref name="HdySearchBar"/> + <glade-widget-class-ref name="HdySqueezer"/> + <glade-widget-class-ref name="HdySwipeGroup"/> + <glade-widget-class-ref name="HdyTitleBar"/> + <glade-widget-class-ref name="HdyViewSwitcher"/> + <glade-widget-class-ref name="HdyViewSwitcherBar"/> + <glade-widget-class-ref name="HdyViewSwitcherTitle"/> + <glade-widget-class-ref name="HdyWindow"/> + <glade-widget-class-ref name="HdyWindowHandle"/> + </glade-widget-group> +</glade-catalog> diff --git a/subprojects/libhandy/glade/meson.build b/subprojects/libhandy/glade/meson.build new file mode 100644 index 0000000..cba9a36 --- /dev/null +++ b/subprojects/libhandy/glade/meson.build @@ -0,0 +1,59 @@ +if glade_catalog + +glade_xml = 'libhandy.xml' +module_dir = gladeui_dep.get_pkgconfig_variable('moduledir', + define_variable: ['libdir', get_option('libdir')]) +dtd = meson.current_source_dir() / 'glade-catalog.dtd' +glade_catalogdir = gladeui_dep.get_pkgconfig_variable('catalogdir', + define_variable: ['datadir', get_option('datadir')]) + +libglade_hdy_sources = [ + 'glade-hdy-carousel.c', + 'glade-hdy-expander-row.c', + 'glade-hdy-header-bar.c', + 'glade-hdy-header-group.c', + 'glade-hdy-leaflet.c', + 'glade-hdy-preferences-page.c', + 'glade-hdy-preferences-window.c', + 'glade-hdy-search-bar.c', + 'glade-hdy-swipe-group.c', + 'glade-hdy-window.c', + 'glade-hdy-utils.c', +] + +libglade_hdy_deps = [ + libhandy_dep, + gladeui_dep, +] + +libglade_hdy_args = [] +# Our custom glade module +libglade_hdy = shared_library( + 'glade-handy-' + apiversion, + libglade_hdy_sources, + c_args: libglade_hdy_args, + dependencies: libglade_hdy_deps, + include_directories: [ root_inc, src_inc ], + install: true, + install_dir: module_dir, +) + +# Validate glade catalog +xmllint = find_program('xmllint', required: true) +if xmllint.found() + custom_target( + 'xmllint', + build_by_default: true, + input: glade_xml, + output: 'doesnotexist', + command: [xmllint, '--dtdvalid', dtd, '--noout', '@INPUT@'], + ) +endif + +# Install glade catalog +install_data( + glade_xml, + rename: 'libhandy-@0@.xml'.format(apiversion), + install_dir: glade_catalogdir) + +endif diff --git a/subprojects/libhandy/glade/rename-id.patch b/subprojects/libhandy/glade/rename-id.patch new file mode 100644 index 0000000..0dc3ab1 --- /dev/null +++ b/subprojects/libhandy/glade/rename-id.patch @@ -0,0 +1,28 @@ +From f4711b392e26d36266a88df4371230418e24cfc9 Mon Sep 17 00:00:00 2001 +From: Adrien Plazas <kekun.plazas@laposte.net> +Date: Mon, 11 May 2020 10:45:17 +0200 +Subject: [PATCH] Rename the ID to sm.puri.Handy.Glade + +Make the GtkApplication ID match the Flatpak ID, which is required for +the app to start. + +--- + src/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/main.c b/src/main.c +index 8a81771f..7a505255 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -161,7 +161,7 @@ main (int argc, char *argv[]) + return -1; + } + +- app = gtk_application_new ("org.gnome.Glade", G_APPLICATION_HANDLES_OPEN); ++ app = gtk_application_new ("sm.puri.Handy.Glade", G_APPLICATION_HANDLES_OPEN); + + g_application_set_option_context_summary (G_APPLICATION (app), + N_("Create or edit user interface designs for GTK+ or GNOME applications.")); +-- +2.26.0 + diff --git a/subprojects/libhandy/glade/sm.puri.Handy.Glade.json b/subprojects/libhandy/glade/sm.puri.Handy.Glade.json new file mode 100644 index 0000000..ef73458 --- /dev/null +++ b/subprojects/libhandy/glade/sm.puri.Handy.Glade.json @@ -0,0 +1,83 @@ +{ + "app-id" : "sm.puri.Handy.Glade", + "runtime" : "org.gnome.Platform", + "runtime-version" : "master", + "sdk" : "org.gnome.Sdk", + "command" : "glade", + "rename-desktop-file" : "org.gnome.Glade.desktop", + "rename-appdata-file" : "org.gnome.Glade.appdata.xml", + "rename-icon" : "org.gnome.Glade", + "copy-icon" : true, + "desktop-file-name-suffix" : " (Handy Nightly)", + "finish-args" : [ + /* X11 + XShm access */ + "--share=ipc", "--socket=fallback-x11", + /* Wayland access */ + "--socket=wayland", + /* We want full fs access so we can read the files */ + "--filesystem=host", + /* Support GL widgets */ + "--device=dri" + ], + "cleanup" : ["/include", "/lib/pkgconfig", + "/share/pkgconfig", "/share/aclocal", + "/man", "/share/man", "/share/gtk-doc", + "/share/vala", + "*.la", "*.a"], + "modules" : [ + { + "name" : "gnome-common", + "sources" : [ + { + "type" : "git", + "url" : "https://gitlab.gnome.org/GNOME/gnome-common.git" + } + ] + }, + { + "name" : "intltool", + "sources" : [ + { + "type" : "git", + "url" : "https://gitlab.gnome.org/World/intltool.git" + } + ] + }, + { + "name" : "glade", + "config-opts": [ + "--disable-man-pages", + "--disable-introspection" + ], + "sources" : [ + { + "type" : "git", + "url" : "https://gitlab.gnome.org/GNOME/glade.git", + "tag" : "GLADE_3_36_0" + }, + { + "type" : "patch", + "path" : "rename-id.patch" + } + ] + }, + { + "name" : "libhandy", + "buildsystem" : "meson", + "builddir" : true, + "config-opts" : [ + "-Dexamples=false", + "-Dglade_catalog=enabled", + "-Dintrospection=disabled", + "-Dtests=false", + "-Dvapi=false" + ], + "sources" : [ + { + "type" : "git", + "url" : "https://gitlab.gnome.org/GNOME/libhandy.git" + } + ] + } + ] +} |