/* * Copyright (C) 2013 Tristan Van Berkom. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Authors: * Tristan Van Berkom */ #ifdef HAVE_CONFIG_H #include #endif #include #include "glade.h" #include "glade-widget.h" #include "glade-popup.h" #include "glade-editable.h" #include "glade-property-shell.h" #include "glade-marshallers.h" /* GObjectClass */ static void glade_property_shell_finalize (GObject *object); static void glade_property_shell_set_real_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void glade_property_shell_get_real_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); /* GladeEditableIface */ static void glade_property_shell_editable_init (GladeEditableIface *iface); struct _GladePropertyShellPrivate { /* Current State */ GladeWidgetAdaptor *adaptor; GladeEditorProperty *property_editor; gulong pre_commit_id; gulong post_commit_id; /* Properties, used to load the internal editor */ GType editor_type; gchar *property_name; gchar *custom_text; guint packing : 1; guint use_command : 1; guint disable_check : 1; }; enum { PROP_0, PROP_PROPERTY_NAME, PROP_PACKING, PROP_USE_COMMAND, PROP_EDITOR_TYPE, PROP_CUSTOM_TEXT, PROP_DISABLE_CHECK }; enum { PRE_COMMIT, POST_COMMIT, LAST_SIGNAL }; static guint glade_property_shell_signals[LAST_SIGNAL] = { 0, }; static GladeEditableIface *parent_editable_iface; G_DEFINE_TYPE_WITH_CODE (GladePropertyShell, glade_property_shell, GTK_TYPE_BOX, G_ADD_PRIVATE (GladePropertyShell) G_IMPLEMENT_INTERFACE (GLADE_TYPE_EDITABLE, glade_property_shell_editable_init)); static void glade_property_shell_init (GladePropertyShell *shell) { shell->priv = glade_property_shell_get_instance_private (shell); shell->priv->packing = FALSE; shell->priv->use_command = TRUE; shell->priv->disable_check = FALSE; } static void glade_property_shell_class_init (GladePropertyShellClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = glade_property_shell_finalize; gobject_class->set_property = glade_property_shell_set_real_property; gobject_class->get_property = glade_property_shell_get_real_property; g_object_class_install_property (gobject_class, PROP_PROPERTY_NAME, g_param_spec_string ("property-name", _("Property Name"), _("The property name to use when loading by widget"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_PACKING, g_param_spec_boolean ("packing", _("Packing"), _("Whether the property to load is a packing property or not"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_USE_COMMAND, g_param_spec_boolean ("use-command", _("Use Command"), _("Whether to use the GladeCommand API when modifying properties"), TRUE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_EDITOR_TYPE, g_param_spec_string ("editor-type", _("Editor Property Type Name"), _("Specify the actual editor property type name to use for this shell"), NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_CUSTOM_TEXT, g_param_spec_string ("custom-text", _("Custom Text"), _("Custom Text to display in the property label"), NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_DISABLE_CHECK, g_param_spec_boolean ("disable-check", _("Disable Check"), _("Whether to explicitly disable the check button"), FALSE, G_PARAM_READWRITE)); /** * GladePropertyShell::pre-commit: * @gladeeditorproperty: the #GladeEditorProperty which changed value * @arg1: the new #GValue to commit. * * Emitted before a property's value is committed, can be useful to serialize * commands before a property's commit command from custom editors. */ glade_property_shell_signals[PRE_COMMIT] = g_signal_new ("pre-commit", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _glade_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); /** * GladePropertyShell::post-commit: * @gladeeditorproperty: the #GladeEditorProperty which changed value * @arg1: the new #GValue to commit. * * Emitted after a property's value is committed, can be useful to serialize * commands after a property's commit command from custom editors. */ glade_property_shell_signals[POST_COMMIT] = g_signal_new ("post-commit", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _glade_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); } /*********************************************************** * GObjectClass * ***********************************************************/ static void glade_property_shell_finalize (GObject *object) { GladePropertyShell *shell = GLADE_PROPERTY_SHELL (object); g_free (shell->priv->property_name); g_free (shell->priv->custom_text); G_OBJECT_CLASS (glade_property_shell_parent_class)->finalize (object); } static void glade_property_shell_set_real_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GladePropertyShell *shell = GLADE_PROPERTY_SHELL (object); GladePropertyShellPrivate *priv = shell->priv; const gchar *type_name = NULL; GType type = 0; switch (prop_id) { case PROP_PROPERTY_NAME: glade_property_shell_set_property_name (shell, g_value_get_string (value)); break; case PROP_PACKING: glade_property_shell_set_packing (shell, g_value_get_boolean (value)); break; case PROP_USE_COMMAND: glade_property_shell_set_use_command (shell, g_value_get_boolean (value)); break; case PROP_EDITOR_TYPE: type_name = g_value_get_string (value); if (type_name) type = glade_util_get_type_from_name (type_name, FALSE); if (type > 0 && !g_type_is_a (type, GLADE_TYPE_EDITOR_PROPERTY)) g_warning ("Editor type '%s' is not a GladeEditorProperty", type_name); else priv->editor_type = type; break; case PROP_CUSTOM_TEXT: glade_property_shell_set_custom_text (shell, g_value_get_string (value)); break; case PROP_DISABLE_CHECK: glade_property_shell_set_disable_check (shell, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void glade_property_shell_get_real_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GladePropertyShell *shell = GLADE_PROPERTY_SHELL (object); switch (prop_id) { case PROP_PROPERTY_NAME: g_value_set_string (value, glade_property_shell_get_property_name (shell)); break; case PROP_PACKING: g_value_set_boolean (value, glade_property_shell_get_packing (shell)); break; case PROP_USE_COMMAND: g_value_set_boolean (value, glade_property_shell_get_use_command (shell)); break; case PROP_CUSTOM_TEXT: g_value_set_string (value, glade_property_shell_get_custom_text (shell)); break; case PROP_DISABLE_CHECK: g_value_set_boolean (value, glade_property_shell_get_disable_check (shell)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /******************************************************************************* * GladeEditableIface * *******************************************************************************/ static void propagate_pre_commit (GladeEditorProperty *property, GValue *value, GladePropertyShell *shell) { g_signal_emit (G_OBJECT (shell), glade_property_shell_signals[PRE_COMMIT], 0, value); } static void propagate_post_commit (GladeEditorProperty *property, GValue *value, GladePropertyShell *shell) { g_signal_emit (G_OBJECT (shell), glade_property_shell_signals[POST_COMMIT], 0, value); } static void glade_property_shell_set_eprop (GladePropertyShell *shell, GladeEditorProperty *eprop) { GladePropertyShellPrivate *priv = shell->priv; if (priv->property_editor != eprop) { if (priv->property_editor) { g_signal_handler_disconnect (priv->property_editor, priv->pre_commit_id); g_signal_handler_disconnect (priv->property_editor, priv->post_commit_id); priv->pre_commit_id = 0; priv->post_commit_id = 0; gtk_widget_destroy (GTK_WIDGET (priv->property_editor)); } priv->property_editor = eprop; if (priv->property_editor) { glade_editor_property_set_custom_text (priv->property_editor, priv->custom_text); glade_editor_property_set_disable_check (priv->property_editor, priv->disable_check); priv->pre_commit_id = g_signal_connect (priv->property_editor, "commit", G_CALLBACK (propagate_pre_commit), shell); priv->post_commit_id = g_signal_connect_after (priv->property_editor, "commit", G_CALLBACK (propagate_post_commit), shell); gtk_container_add (GTK_CONTAINER (shell), GTK_WIDGET (priv->property_editor)); } } } static void glade_property_shell_load (GladeEditable *editable, GladeWidget *widget) { GladePropertyShell *shell = GLADE_PROPERTY_SHELL (editable); GladePropertyShellPrivate *priv; /* Chain up to default implementation */ parent_editable_iface->load (editable, widget); g_return_if_fail (shell->priv->property_name != NULL); priv = shell->priv; if (widget) { GladeWidgetAdaptor *adaptor = NULL; /* Use the parent adaptor if we're a packing property */ if (priv->packing) { GladeWidget *parent = glade_widget_get_parent (widget); if (parent) adaptor = glade_widget_get_adaptor (parent); } else adaptor = glade_widget_get_adaptor (widget); /* Need to rebuild the internal editor */ if (priv->adaptor != adaptor) { GladePropertyClass *pclass = NULL; GladeEditorProperty *eprop = NULL; priv->adaptor = adaptor; if (adaptor) { if (priv->packing) pclass = glade_widget_adaptor_get_pack_property_class (priv->adaptor, priv->property_name); else pclass = glade_widget_adaptor_get_property_class (priv->adaptor, priv->property_name); } /* Be forgiving, allow usage of properties that wont work, so that * some editors can include properties for subclasses, and hide * those properties if they're not applicable */ if (pclass == NULL) { priv->property_editor = NULL; } /* Construct custom editor property if specified */ else if (g_type_is_a (priv->editor_type, GLADE_TYPE_EDITOR_PROPERTY)) { eprop = g_object_new (priv->editor_type, "property-class", pclass, "use-command", priv->use_command, NULL); } else { /* Let the adaptor create one */ eprop = glade_widget_adaptor_create_eprop_by_name (priv->adaptor, priv->property_name, priv->packing, priv->use_command); } glade_property_shell_set_eprop (shell, eprop); } /* If we have an editor for the right adaptor, load it */ if (priv->property_editor) glade_editable_load (GLADE_EDITABLE (priv->property_editor), widget); } else if (priv->property_editor) glade_editable_load (GLADE_EDITABLE (priv->property_editor), NULL); } static void glade_property_shell_set_show_name (GladeEditable *editable, gboolean show_name) { } static void glade_property_shell_editable_init (GladeEditableIface *iface) { parent_editable_iface = g_type_default_interface_peek (GLADE_TYPE_EDITABLE); iface->load = glade_property_shell_load; iface->set_show_name = glade_property_shell_set_show_name; } /*********************************************************** * API * ***********************************************************/ GtkWidget * glade_property_shell_new (void) { return g_object_new (GLADE_TYPE_PROPERTY_SHELL, NULL); } void glade_property_shell_set_property_name (GladePropertyShell *shell, const gchar *property_name) { GladePropertyShellPrivate *priv; g_return_if_fail (GLADE_IS_PROPERTY_SHELL (shell)); priv = shell->priv; if (g_strcmp0 (priv->property_name, property_name) != 0) { g_free (priv->property_name); priv->property_name = g_strdup (property_name); g_object_notify (G_OBJECT (shell), "property-name"); } } const gchar * glade_property_shell_get_property_name (GladePropertyShell *shell) { g_return_val_if_fail (GLADE_IS_PROPERTY_SHELL (shell), NULL); return shell->priv->property_name; } void glade_property_shell_set_custom_text (GladePropertyShell *shell, const gchar *custom_text) { GladePropertyShellPrivate *priv; g_return_if_fail (GLADE_IS_PROPERTY_SHELL (shell)); priv = shell->priv; if (g_strcmp0 (priv->custom_text, custom_text) != 0) { g_free (priv->custom_text); priv->custom_text = g_strdup (custom_text); if (priv->property_editor) glade_editor_property_set_custom_text (priv->property_editor, custom_text); g_object_notify (G_OBJECT (shell), "custom-text"); } } const gchar * glade_property_shell_get_custom_text (GladePropertyShell *shell) { g_return_val_if_fail (GLADE_IS_PROPERTY_SHELL (shell), NULL); return shell->priv->custom_text; } void glade_property_shell_set_packing (GladePropertyShell *shell, gboolean packing) { GladePropertyShellPrivate *priv; g_return_if_fail (GLADE_IS_PROPERTY_SHELL (shell)); priv = shell->priv; if (priv->packing != packing) { priv->packing = packing; g_object_notify (G_OBJECT (shell), "packing"); } } gboolean glade_property_shell_get_packing (GladePropertyShell *shell) { g_return_val_if_fail (GLADE_IS_PROPERTY_SHELL (shell), FALSE); return shell->priv->packing; } void glade_property_shell_set_use_command (GladePropertyShell *shell, gboolean use_command) { GladePropertyShellPrivate *priv; g_return_if_fail (GLADE_IS_PROPERTY_SHELL (shell)); priv = shell->priv; if (priv->use_command != use_command) { priv->use_command = use_command; g_object_notify (G_OBJECT (shell), "use-command"); } } gboolean glade_property_shell_get_use_command (GladePropertyShell *shell) { g_return_val_if_fail (GLADE_IS_PROPERTY_SHELL (shell), FALSE); return shell->priv->use_command; } void glade_property_shell_set_disable_check (GladePropertyShell *shell, gboolean disable_check) { GladePropertyShellPrivate *priv; g_return_if_fail (GLADE_IS_PROPERTY_SHELL (shell)); priv = shell->priv; if (priv->disable_check != disable_check) { priv->disable_check = disable_check; if (priv->property_editor) g_object_set (priv->property_editor, "disable-check", disable_check, NULL); g_object_notify (G_OBJECT (shell), "disable-check"); } } gboolean glade_property_shell_get_disable_check (GladePropertyShell *shell) { g_return_val_if_fail (GLADE_IS_PROPERTY_SHELL (shell), FALSE); return shell->priv->disable_check; }