/* * Copyright (C) 2001 Ximian, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 /** * SECTION:glade-editor-property * @Short_Description: A generic widget to edit a #GladeProperty. * * The #GladeEditorProperty is a factory that will create the correct * control for the #GladePropertyClass it was created for and provides * a simple unified api to them. */ #include #include #include #include #include #include #include "glade.h" #include "glade-widget.h" #include "glade-editable.h" #include "glade-editor-property.h" #include "glade-property-label.h" #include "glade-property.h" #include "glade-command.h" #include "glade-project.h" #include "glade-popup.h" #include "glade-builtins.h" #include "glade-marshallers.h" #include "glade-displayable-values.h" #include "glade-named-icon-chooser-dialog.h" #include "glade-private.h" enum { PROP_0, PROP_PROPERTY_CLASS, PROP_USE_COMMAND, PROP_DISABLE_CHECK, PROP_CUSTOM_TEXT }; enum { CHANGED, COMMIT, LAST_SIGNAL }; static GtkTableClass *table_class; static GladeEditorPropertyClass *editor_property_class; static guint glade_eprop_signals[LAST_SIGNAL] = { 0, }; #define GLADE_PROPERTY_TABLE_ROW_SPACING 2 #define FLAGS_COLUMN_SETTING 0 #define FLAGS_COLUMN_SYMBOL 1 #define FLAGS_COLUMN_VALUE 2 struct _GladeEditorPropertyPrivate { GladePropertyClass *klass; /* The property class this GladeEditorProperty was created for */ GladeProperty *property; /* The currently loaded property */ GtkWidget *item_label; /* A GladePropertyLabel, if one was constructed */ GtkWidget *input; /* Input part of property (need to set sensitivity seperately) */ GtkWidget *check; /* Check button for optional properties. */ gulong tooltip_id; /* signal connection id for tooltip changes */ gulong sensitive_id; /* signal connection id for sensitivity changes */ gulong changed_id; /* signal connection id for value changes */ gulong enabled_id; /* signal connection id for enable/disable changes */ gchar *custom_text; /* Custom text to display in the property label */ guint loading : 1; /* True during glade_editor_property_load calls, this * is used to avoid feedback from input widgets. */ guint committing : 1; /* True while the editor property itself is applying * the property with glade_editor_property_commit_no_callback (). */ guint use_command : 1; /* Whether we should use the glade command interface * or skip directly to GladeProperty interface. * (used for query dialogs). */ guint changed_blocked : 1; /* Whether the GladeProperty changed signal is currently blocked */ guint disable_check : 1; /* Whether to explicitly disable the optional check button */ }; static void glade_editor_property_editable_init (GladeEditableIface *iface); static GladeEditableIface *parent_editable_iface; G_DEFINE_TYPE_WITH_CODE (GladeEditorProperty, glade_editor_property, GTK_TYPE_BOX, G_ADD_PRIVATE (GladeEditorProperty) G_IMPLEMENT_INTERFACE (GLADE_TYPE_EDITABLE, glade_editor_property_editable_init)); /******************************************************************************* * GladeEditableIface * *******************************************************************************/ static void glade_editor_property_editable_load (GladeEditable *editable, GladeWidget *widget) { /* Chain up to default implementation */ parent_editable_iface->load (editable, widget); glade_editor_property_load_by_widget (GLADE_EDITOR_PROPERTY (editable), widget); } static void glade_editor_property_set_show_name (GladeEditable *editable, gboolean show_name) { } static void glade_editor_property_editable_init (GladeEditableIface *iface) { parent_editable_iface = g_type_default_interface_peek (GLADE_TYPE_EDITABLE); iface->load = glade_editor_property_editable_load; iface->set_show_name = glade_editor_property_set_show_name; } /******************************************************************************* GladeEditorPropertyClass *******************************************************************************/ static void deepest_child_grab_focus (GtkWidget *widget, gpointer data) { gboolean *focus_set = data; if (*focus_set) return; if (GTK_IS_CONTAINER (widget)) gtk_container_foreach (GTK_CONTAINER (widget), deepest_child_grab_focus, data); if (gtk_widget_get_can_focus (widget)) { gtk_widget_grab_focus (widget); *focus_set = TRUE; } } /* declare this forwardly for the finalize routine */ static void glade_editor_property_load_common (GladeEditorProperty *eprop, GladeProperty *property); static void glade_editor_property_commit_common (GladeEditorProperty *eprop, GValue *value) { if (eprop->priv->use_command == FALSE) glade_property_set_value (eprop->priv->property, value); else glade_command_set_property_value (eprop->priv->property, value); /* If the value was denied by a verify function, we'll have to * reload the real value. */ if (!glade_property_equals_value (eprop->priv->property, value)) glade_editor_property_load (eprop, eprop->priv->property); /* Restore input focus. If the property is construct-only, then * glade_widget_rebuild() will be called which means the object will be * removed from the project/selection and a new one will be added, which makes * the eprop loose its focus. * * FIXME: find a better way to set focus? * make gtk_widget_grab_focus(eprop->priv->input) work? */ if (glade_property_class_get_construct_only (eprop->priv->klass)) { gboolean focus_set = FALSE; gtk_container_foreach (GTK_CONTAINER (eprop->priv->input), deepest_child_grab_focus, &focus_set); } } void glade_editor_property_commit_no_callback (GladeEditorProperty *eprop, GValue *value) { g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); if (eprop->priv->committing) return; g_signal_handler_block (G_OBJECT (eprop->priv->property), eprop->priv->changed_id); eprop->priv->changed_blocked = TRUE; eprop->priv->committing = TRUE; glade_editor_property_commit (eprop, value); eprop->priv->committing = FALSE; /* When construct-only properties are set, we are disconnected and re-connected * to the GladeWidget while it's rebuilding it's instance, in this case the * signal handler is no longer blocked at this point. */ if (eprop->priv->changed_blocked) g_signal_handler_unblock (G_OBJECT (eprop->priv->property), eprop->priv->changed_id); } void glade_editor_property_set_custom_text (GladeEditorProperty *eprop, const gchar *custom_text) { GladeEditorPropertyPrivate *priv; g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); priv = eprop->priv; if (g_strcmp0 (priv->custom_text, custom_text) != 0) { g_free (priv->custom_text); priv->custom_text = g_strdup (custom_text); if (priv->item_label) glade_property_label_set_custom_text (GLADE_PROPERTY_LABEL (priv->item_label), custom_text); g_object_notify (G_OBJECT (eprop), "custom-text"); } } const gchar * glade_editor_property_get_custom_text (GladeEditorProperty *eprop) { g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL); return eprop->priv->custom_text; } void glade_editor_property_set_disable_check (GladeEditorProperty *eprop, gboolean disable_check) { GladeEditorPropertyPrivate *priv; g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); priv = eprop->priv; if (priv->disable_check != disable_check) { priv->disable_check = disable_check; gtk_widget_set_visible (priv->check, !disable_check); g_object_notify (G_OBJECT (eprop), "disable-check"); } } gboolean glade_editor_property_get_disable_check (GladeEditorProperty *eprop) { g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), FALSE); return eprop->priv->disable_check; } GtkWidget * glade_editor_property_get_item_label (GladeEditorProperty *eprop) { g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL); if (!eprop->priv->item_label) { eprop->priv->item_label = glade_property_label_new(); g_object_ref_sink (eprop->priv->item_label); if (eprop->priv->property) glade_property_label_set_property (GLADE_PROPERTY_LABEL (eprop->priv->item_label), eprop->priv->property); } return eprop->priv->item_label; } GladePropertyClass * glade_editor_property_get_pclass (GladeEditorProperty *eprop) { g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL); return eprop->priv->klass; } GladeProperty * glade_editor_property_get_property (GladeEditorProperty *eprop) { g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL); return eprop->priv->property; } gboolean glade_editor_property_loading (GladeEditorProperty *eprop) { g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), FALSE); return eprop->priv->loading; } static void glade_editor_property_tooltip_cb (GladeProperty *property, const gchar *tooltip, const gchar *insensitive, const gchar *support, GladeEditorProperty *eprop) { const gchar *choice_tooltip; if (glade_property_get_sensitive (property)) choice_tooltip = tooltip; else choice_tooltip = insensitive; gtk_widget_set_tooltip_text (eprop->priv->input, choice_tooltip); } static void glade_editor_property_sensitivity_cb (GladeProperty *property, GParamSpec *pspec, GladeEditorProperty *eprop) { GladeEditorPropertyPrivate *priv = eprop->priv; gboolean property_enabled = glade_property_get_enabled (property); gboolean sensitive = glade_property_get_sensitive (priv->property); gboolean support_sensitive = (glade_property_get_state (priv->property) & GLADE_STATE_SUPPORT_DISABLED) == 0; gtk_widget_set_sensitive (priv->input, sensitive && support_sensitive && property_enabled); if (priv->check) gtk_widget_set_sensitive (priv->check, sensitive && support_sensitive); } static void glade_editor_property_value_changed_cb (GladeProperty *property, GValue *old_value, GValue *value, GladeEditorProperty *eprop) { g_assert (eprop->priv->property == property); glade_editor_property_load (eprop, eprop->priv->property); } static void glade_editor_property_enabled_toggled_cb (GtkWidget *check, GladeEditorProperty *eprop) { glade_command_set_property_enabled (eprop->priv->property, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check))); } static void glade_editor_property_enabled_cb (GladeProperty *property, GParamSpec *pspec, GladeEditorProperty *eprop) { gboolean enabled; g_assert (eprop->priv->property == property); if (glade_property_class_optional (eprop->priv->klass)) { enabled = glade_property_get_enabled (property); /* sensitive = enabled && support enabled && sensitive */ if (enabled == FALSE) gtk_widget_set_sensitive (eprop->priv->input, FALSE); else if (glade_property_get_sensitive (property) || (glade_property_get_state (property) & GLADE_STATE_SUPPORT_DISABLED) != 0) gtk_widget_set_sensitive (eprop->priv->input, TRUE); g_signal_handlers_block_by_func (eprop->priv->check, glade_editor_property_enabled_toggled_cb, eprop); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop->priv->check), enabled); g_signal_handlers_unblock_by_func (eprop->priv->check, glade_editor_property_enabled_toggled_cb, eprop); } } static gboolean glade_editor_property_button_pressed (GtkWidget *widget, GdkEventButton *event, GladeEditorProperty *eprop) { if (glade_popup_is_popup_event (event)) { glade_popup_property_pop (eprop->priv->property, event); return TRUE; } return FALSE; } static void glade_editor_property_constructed (GObject *object) { GladeEditorProperty *eprop; eprop = GLADE_EDITOR_PROPERTY (object); G_OBJECT_CLASS (glade_editor_property_parent_class)->constructed (object); /* Create hbox and possibly check button */ if (glade_property_class_optional (eprop->priv->klass)) { eprop->priv->check = gtk_check_button_new (); gtk_button_set_focus_on_click (GTK_BUTTON (eprop->priv->check), FALSE); if (!eprop->priv->disable_check) gtk_widget_show (eprop->priv->check); gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->check, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (eprop->priv->check), "toggled", G_CALLBACK (glade_editor_property_enabled_toggled_cb), eprop); } /* Create the class specific input widget and add it */ eprop->priv->input = GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->create_input (eprop); gtk_widget_show (eprop->priv->input); g_signal_connect (G_OBJECT (eprop->priv->input), "button-press-event", G_CALLBACK (glade_editor_property_button_pressed), eprop); if (gtk_widget_get_halign (eprop->priv->input) != GTK_ALIGN_FILL) gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->input, FALSE, TRUE, 0); else gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->input, TRUE, TRUE, 0); } static void glade_editor_property_finalize (GObject *object) { GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object); /* detatch from loaded property */ glade_editor_property_load_common (eprop, NULL); g_free (eprop->priv->custom_text); G_OBJECT_CLASS (table_class)->finalize (object); } static void glade_editor_property_dispose (GObject *object) { GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object); if (eprop->priv->item_label) { g_object_unref (eprop->priv->item_label); eprop->priv->item_label = NULL; } G_OBJECT_CLASS (table_class)->dispose (object); } static void glade_editor_property_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object); switch (prop_id) { case PROP_PROPERTY_CLASS: eprop->priv->klass = g_value_get_pointer (value); break; case PROP_USE_COMMAND: eprop->priv->use_command = g_value_get_boolean (value); break; case PROP_DISABLE_CHECK: glade_editor_property_set_disable_check (eprop, g_value_get_boolean (value)); break; case PROP_CUSTOM_TEXT: glade_editor_property_set_custom_text (eprop, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void glade_editor_property_real_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object); switch (prop_id) { case PROP_PROPERTY_CLASS: g_value_set_pointer (value, eprop->priv->klass); break; case PROP_USE_COMMAND: g_value_set_boolean (value, eprop->priv->use_command); break; case PROP_DISABLE_CHECK: g_value_set_boolean (value, eprop->priv->disable_check); break; case PROP_CUSTOM_TEXT: g_value_set_string (value, eprop->priv->custom_text); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void glade_eprop_property_finalized (GladeEditorProperty *eprop, GladeProperty *where_property_was) { eprop->priv->tooltip_id = 0; eprop->priv->sensitive_id = 0; eprop->priv->changed_id = 0; eprop->priv->enabled_id = 0; eprop->priv->property = NULL; glade_editor_property_load (eprop, NULL); } static void glade_editor_property_load_common (GladeEditorProperty *eprop, GladeProperty *property) { /* NOTE THIS CODE IS FINALIZE SAFE */ /* disconnect anything from previously loaded property */ if (eprop->priv->property != property && eprop->priv->property != NULL) { if (eprop->priv->tooltip_id > 0) g_signal_handler_disconnect (eprop->priv->property, eprop->priv->tooltip_id); if (eprop->priv->sensitive_id > 0) g_signal_handler_disconnect (eprop->priv->property, eprop->priv->sensitive_id); if (eprop->priv->changed_id > 0) g_signal_handler_disconnect (eprop->priv->property, eprop->priv->changed_id); if (eprop->priv->enabled_id > 0) g_signal_handler_disconnect (eprop->priv->property, eprop->priv->enabled_id); eprop->priv->tooltip_id = 0; eprop->priv->sensitive_id = 0; eprop->priv->changed_id = 0; eprop->priv->enabled_id = 0; eprop->priv->changed_blocked = FALSE; /* Unref it here */ g_object_weak_unref (G_OBJECT (eprop->priv->property), (GWeakNotify) glade_eprop_property_finalized, eprop); /* For a reason I cant quite tell yet, this is the only * safe way to nullify the property member of the eprop * without leeking signal connections to properties :-/ */ if (property == NULL) { eprop->priv->property = NULL; } } /* Connect new stuff, deal with tooltip */ if (eprop->priv->property != property && property != NULL) { GladePropertyClass *pclass = glade_property_get_class (property); eprop->priv->property = property; eprop->priv->tooltip_id = g_signal_connect (G_OBJECT (eprop->priv->property), "tooltip-changed", G_CALLBACK (glade_editor_property_tooltip_cb), eprop); eprop->priv->sensitive_id = g_signal_connect (G_OBJECT (eprop->priv->property), "notify::sensitive", G_CALLBACK (glade_editor_property_sensitivity_cb), eprop); eprop->priv->changed_id = g_signal_connect (G_OBJECT (eprop->priv->property), "value-changed", G_CALLBACK (glade_editor_property_value_changed_cb), eprop); eprop->priv->enabled_id = g_signal_connect (G_OBJECT (eprop->priv->property), "notify::enabled", G_CALLBACK (glade_editor_property_enabled_cb), eprop); /* In query dialogs when the user hits cancel, * these babies go away (so better stay protected). */ g_object_weak_ref (G_OBJECT (eprop->priv->property), (GWeakNotify) glade_eprop_property_finalized, eprop); /* Load initial tooltips */ glade_editor_property_tooltip_cb (property, glade_property_class_get_tooltip (pclass), glade_propert_get_insensitive_tooltip (property), glade_property_get_support_warning (property), eprop); /* Load initial enabled state */ glade_editor_property_enabled_cb (property, NULL, eprop); /* Load initial sensitive state. */ glade_editor_property_sensitivity_cb (property, NULL, eprop); } } static void glade_editor_property_init (GladeEditorProperty *eprop) { eprop->priv = glade_editor_property_get_instance_private (eprop); gtk_box_set_spacing (GTK_BOX (eprop), 4); gtk_orientable_set_orientation (GTK_ORIENTABLE (eprop), GTK_ORIENTATION_HORIZONTAL); } static void glade_editor_property_class_init (GladeEditorPropertyClass *eprop_class) { GObjectClass *object_class; g_return_if_fail (eprop_class != NULL); /* Both parent classes assigned here. */ editor_property_class = eprop_class; table_class = g_type_class_peek_parent (eprop_class); object_class = G_OBJECT_CLASS (eprop_class); /* GObjectClass */ object_class->constructed = glade_editor_property_constructed; object_class->finalize = glade_editor_property_finalize; object_class->dispose = glade_editor_property_dispose; object_class->get_property = glade_editor_property_real_get_property; object_class->set_property = glade_editor_property_set_property; /* Class methods */ eprop_class->load = glade_editor_property_load_common; eprop_class->commit = glade_editor_property_commit_common; eprop_class->create_input = NULL; /** * GladeEditorProperty::value-changed: * @gladeeditorproperty: the #GladeEditorProperty which changed value * @arg1: the #GladeProperty that's value changed. * * Emitted when a contained property changes value */ glade_eprop_signals[CHANGED] = g_signal_new ("value-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeEditorPropertyClass, changed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GLADE_TYPE_PROPERTY); /** * GladeEditorProperty::commit: * @gladeeditorproperty: the #GladeEditorProperty which changed value * @arg1: the new #GValue to commit. * * Emitted when a property's value is committed, can be useful to serialize * commands before and after the property's commit command from custom editors. */ glade_eprop_signals[COMMIT] = g_signal_new ("commit", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeEditorPropertyClass, commit), NULL, NULL, _glade_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); /* Properties */ g_object_class_install_property (object_class, PROP_PROPERTY_CLASS, g_param_spec_pointer ("property-class", _("Property Class"), _("The GladePropertyClass this GladeEditorProperty was created for"), G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_USE_COMMAND, g_param_spec_boolean ("use-command", _("Use Command"), _("Whether we should use the command API for the undo/redo stack"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_DISABLE_CHECK, g_param_spec_boolean ("disable-check", _("Disable Check"), _("Whether to explicitly disable the check button"), FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_CUSTOM_TEXT, g_param_spec_string ("custom-text", _("Custom Text"), _("Custom Text to display in the property label"), NULL, G_PARAM_READWRITE)); } /******************************************************************************* GladeEditorPropertyNumericClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *spin; gboolean refreshing; } GladeEPropNumeric; GLADE_MAKE_EPROP (GladeEPropNumeric, glade_eprop_numeric) #define GLADE_EPROP_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_NUMERIC, GladeEPropNumeric)) #define GLADE_EPROP_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_NUMERIC, GladeEPropNumericClass)) #define GLADE_IS_EPROP_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_NUMERIC)) #define GLADE_IS_EPROP_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_NUMERIC)) #define GLADE_EPROP_NUMERIC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_NUMERIC, GladeEPropNumericClass)) static void glade_eprop_numeric_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_numeric_load (GladeEditorProperty *eprop, GladeProperty *property) { gdouble val = 0.0F; GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop); GParamSpec *pspec; GValue *value; if (eprop_numeric->refreshing) return; /* Chain up first */ editor_property_class->load (eprop, property); if (property) { value = glade_property_inline_value (property); pspec = glade_property_class_get_pspec (eprop->priv->klass); if (G_IS_PARAM_SPEC_INT (pspec)) val = g_value_get_int (value); else if (G_IS_PARAM_SPEC_UINT (pspec)) val = g_value_get_uint (value); else if (G_IS_PARAM_SPEC_LONG (pspec)) val = g_value_get_long (value); else if (G_IS_PARAM_SPEC_ULONG (pspec)) val = g_value_get_ulong (value); else if (G_IS_PARAM_SPEC_INT64 (pspec)) val = g_value_get_int64 (value); else if (G_IS_PARAM_SPEC_UINT64 (pspec)) val = g_value_get_uint64 (value); else if (G_IS_PARAM_SPEC_DOUBLE (pspec)) val = g_value_get_double (value); else if (G_IS_PARAM_SPEC_FLOAT (pspec)) val = g_value_get_float (value); else g_warning ("Unsupported type %s\n", g_type_name (G_PARAM_SPEC_TYPE (pspec))); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_numeric->spin), val); } } #define NEAREST_INT_CAST(x) (((x - floor (x) < ceil (x) - x)) ? floor (x) : ceil (x)) static void glade_eprop_numeric_value_set (GValue *val, gdouble value) { if (G_VALUE_HOLDS_INT (val)) g_value_set_int (val, NEAREST_INT_CAST (value)); else if (G_VALUE_HOLDS_UINT (val)) g_value_set_uint (val, NEAREST_INT_CAST (value)); else if (G_VALUE_HOLDS_LONG (val)) g_value_set_long (val, NEAREST_INT_CAST (value)); else if (G_VALUE_HOLDS_ULONG (val)) g_value_set_ulong (val, NEAREST_INT_CAST (value)); else if (G_VALUE_HOLDS_INT64 (val)) g_value_set_int64 (val, NEAREST_INT_CAST (value)); else if (G_VALUE_HOLDS_UINT64 (val)) g_value_set_uint64 (val, NEAREST_INT_CAST (value)); else if (G_VALUE_HOLDS_FLOAT (val)) g_value_set_float (val, value); else if (G_VALUE_HOLDS_DOUBLE (val)) g_value_set_double (val, value); else g_warning ("Unsupported type %s\n", G_VALUE_TYPE_NAME (val)); } static void glade_eprop_numeric_changed (GtkWidget *spin, GladeEditorProperty *eprop) { GValue val = { 0, }; GParamSpec *pspec; if (eprop->priv->loading) return; pspec = glade_property_class_get_pspec (eprop->priv->klass); g_value_init (&val, pspec->value_type); glade_eprop_numeric_value_set (&val, gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin))); glade_editor_property_commit_no_callback (eprop, &val); g_value_unset (&val); } static void glade_eprop_numeric_force_update (GtkSpinButton *spin, GladeEditorProperty *eprop) { GladeProperty *prop = glade_editor_property_get_property (eprop); GladePropertyClass *klass = glade_property_get_class (prop); GValue *val, newval = G_VALUE_INIT; gdouble value; gchar *text; text = gtk_editable_get_chars (GTK_EDITABLE (spin), 0, -1); /* * Skip empty strings, otherwise if 0 is out of range the spin will get a * bogus update. */ if (text && *text == '\0') return; val = glade_property_inline_value (prop); g_value_init (&newval, G_VALUE_TYPE (val)); value = g_strtod (text, NULL); glade_eprop_numeric_value_set (&newval, value); /* * If we unconditionally update the spin button whenever * the entry changes we get bogus results (notably, the * updating the spin button will insert 0 whenever text * is removed, so selecting and inserting text will have * an appended 0). */ if (glade_property_class_compare (klass, val, &newval)) { gdouble min, max; gtk_spin_button_get_range (spin, &min, &max); if (value < min || value > max) { /* Special case, if the value is out of range, we force an update to * change the value in the spin so the user knows about the range issue. */ gtk_spin_button_update (spin); } else { /* Here we commit the new property value but we make sure * glade_eprop_numeric_load() is not called to prevent * gtk_spin_button_set_value() changing the the value the * user is trying to input. */ GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop); eprop_numeric->refreshing = TRUE; glade_editor_property_commit_no_callback (eprop, &newval); eprop_numeric->refreshing = FALSE; } } g_value_unset (&newval); g_free (text); } static GtkWidget * glade_eprop_numeric_create_input (GladeEditorProperty *eprop) { GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop); GtkAdjustment *adjustment; GParamSpec *pspec; pspec = glade_property_class_get_pspec (eprop->priv->klass); adjustment = glade_property_class_make_adjustment (eprop->priv->klass); eprop_numeric->spin = gtk_spin_button_new (adjustment, 4, G_IS_PARAM_SPEC_FLOAT (pspec) || G_IS_PARAM_SPEC_DOUBLE (pspec) ? 2 : 0); gtk_widget_set_hexpand (eprop_numeric->spin, TRUE); gtk_widget_set_halign (eprop_numeric->spin, GTK_ALIGN_FILL); gtk_widget_set_valign (eprop_numeric->spin, GTK_ALIGN_CENTER); gtk_entry_set_activates_default (GTK_ENTRY (eprop_numeric->spin), TRUE); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (eprop_numeric->spin), TRUE); glade_util_remove_scroll_events (eprop_numeric->spin); gtk_widget_show (eprop_numeric->spin); /* Limit the size of the spin if max allowed value is too big */ if (gtk_adjustment_get_upper (adjustment) > 9999999999999999.0) gtk_entry_set_width_chars (GTK_ENTRY (eprop_numeric->spin), 16); /* The force update callback is here to ensure that whenever the value * is modified, it's committed immediately without requiring entry activation * (this avoids lost modifications when modifying a value and navigating away) */ g_signal_connect (G_OBJECT (eprop_numeric->spin), "changed", G_CALLBACK (glade_eprop_numeric_force_update), eprop); g_signal_connect (G_OBJECT (eprop_numeric->spin), "value-changed", G_CALLBACK (glade_eprop_numeric_changed), eprop); return eprop_numeric->spin; } /******************************************************************************* GladeEditorPropertyEnumClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *combo_box; } GladeEPropEnum; GLADE_MAKE_EPROP (GladeEPropEnum, glade_eprop_enum) #define GLADE_EPROP_ENUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_ENUM, GladeEPropEnum)) #define GLADE_EPROP_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_ENUM, GladeEPropEnumClass)) #define GLADE_IS_EPROP_ENUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_ENUM)) #define GLADE_IS_EPROP_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_ENUM)) #define GLADE_EPROP_ENUM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_ENUM, GladeEPropEnumClass)) static void glade_eprop_enum_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_enum_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropEnum *eprop_enum = GLADE_EPROP_ENUM (eprop); GParamSpec *pspec; GEnumClass *eclass; guint i; gint value; /* Chain up first */ editor_property_class->load (eprop, property); if (property) { pspec = glade_property_class_get_pspec (eprop->priv->klass); eclass = g_type_class_ref (pspec->value_type); value = g_value_get_enum (glade_property_inline_value (property)); for (i = 0; i < eclass->n_values; i++) if (eclass->values[i].value == value) break; gtk_combo_box_set_active (GTK_COMBO_BOX (eprop_enum->combo_box), i < eclass->n_values ? i : 0); g_type_class_unref (eclass); } } static void glade_eprop_enum_changed (GtkWidget *combo_box, GladeEditorProperty *eprop) { gint ival; GValue val = { 0, }; GParamSpec *pspec; GtkTreeModel *tree_model; GtkTreeIter iter; if (eprop->priv->loading) return; tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter); gtk_tree_model_get (tree_model, &iter, 1, &ival, -1); pspec = glade_property_class_get_pspec (eprop->priv->klass); g_value_init (&val, pspec->value_type); g_value_set_enum (&val, ival); glade_editor_property_commit_no_callback (eprop, &val); g_value_unset (&val); } static GtkWidget * glade_eprop_enum_create_input (GladeEditorProperty *eprop) { GladeEPropEnum *eprop_enum = GLADE_EPROP_ENUM (eprop); GladePropertyClass *klass; GParamSpec *pspec; GEnumClass *eclass; GtkListStore *list_store; GtkTreeIter iter; GtkCellRenderer *cell_renderer; guint i; klass = eprop->priv->klass; pspec = glade_property_class_get_pspec (klass); eclass = g_type_class_ref (pspec->value_type); list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter); for (i = 0; i < eclass->n_values; i++) { const gchar *value_name; if (glade_displayable_value_is_disabled (pspec->value_type, eclass->values[i].value_nick)) continue; value_name = glade_get_displayable_value (pspec->value_type, eclass->values[i].value_nick); if (value_name == NULL) value_name = eclass->values[i].value_nick; gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, 0, value_name, 1, eclass->values[i].value, -1); } eprop_enum->combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store)); gtk_widget_set_halign (eprop_enum->combo_box, GTK_ALIGN_FILL); gtk_widget_set_valign (eprop_enum->combo_box, GTK_ALIGN_CENTER); gtk_widget_set_hexpand (eprop_enum->combo_box, TRUE); cell_renderer = gtk_cell_renderer_text_new (); g_object_set (cell_renderer, "wrap-mode", PANGO_WRAP_WORD, "wrap-width", 1, "width-chars", 8, NULL); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (eprop_enum->combo_box), cell_renderer, TRUE); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (eprop_enum->combo_box), cell_renderer, "text", 0); g_signal_connect (G_OBJECT (eprop_enum->combo_box), "changed", G_CALLBACK (glade_eprop_enum_changed), eprop); glade_util_remove_scroll_events (eprop_enum->combo_box); gtk_widget_show_all (eprop_enum->combo_box); g_type_class_unref (eclass); return eprop_enum->combo_box; } /******************************************************************************* GladeEditorPropertyFlagsClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkTreeModel *model; GtkWidget *entry; } GladeEPropFlags; GLADE_MAKE_EPROP (GladeEPropFlags, glade_eprop_flags) #define GLADE_EPROP_FLAGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_FLAGS, GladeEPropFlags)) #define GLADE_EPROP_FLAGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_FLAGS, GladeEPropFlagsClass)) #define GLADE_IS_EPROP_FLAGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_FLAGS)) #define GLADE_IS_EPROP_FLAGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_FLAGS)) #define GLADE_EPROP_FLAGS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_FLAGS, GladeEPropFlagsClass)) static void glade_eprop_flags_finalize (GObject *object) { GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (object); g_object_unref (G_OBJECT (eprop_flags->model)); /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_flags_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop); GFlagsClass *klass; GParamSpec *pspec; guint flag_num, value; GString *string = g_string_new (NULL); /* Chain up first */ editor_property_class->load (eprop, property); gtk_list_store_clear (GTK_LIST_STORE (eprop_flags->model)); if (property) { /* Populate the model with the flags. */ klass = g_type_class_ref (G_VALUE_TYPE (glade_property_inline_value (property))); value = g_value_get_flags (glade_property_inline_value (property)); pspec = glade_property_class_get_pspec (eprop->priv->klass); /* Step through each of the flags in the class. */ for (flag_num = 0; flag_num < klass->n_values; flag_num++) { GtkTreeIter iter; guint mask; gboolean setting; const gchar *value_name; if (glade_displayable_value_is_disabled (pspec->value_type, klass->values[flag_num].value_nick)) continue; mask = klass->values[flag_num].value; setting = ((value & mask) == mask) ? TRUE : FALSE; value_name = glade_get_displayable_value (pspec->value_type, klass->values[flag_num].value_nick); if (value_name == NULL) value_name = klass->values[flag_num].value_name; /* Setup string for property label */ if (setting) { if (string->len > 0) g_string_append (string, " | "); g_string_append (string, value_name); } /* Add a row to represent the flag. */ gtk_list_store_append (GTK_LIST_STORE (eprop_flags->model), &iter); gtk_list_store_set (GTK_LIST_STORE (eprop_flags->model), &iter, FLAGS_COLUMN_SETTING, setting, FLAGS_COLUMN_SYMBOL, value_name, FLAGS_COLUMN_VALUE, mask, -1); } g_type_class_unref (klass); } gtk_entry_set_text (GTK_ENTRY (eprop_flags->entry), string->str); g_string_free (string, TRUE); } static void flag_toggled_direct (GtkCellRendererToggle *cell, gchar *path_string, GladeEditorProperty *eprop) { GtkTreeIter iter; guint new_value = 0; gboolean selected; GValue *gvalue; gboolean valid; GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop); if (!eprop->priv->property) return; gvalue = glade_property_inline_value (eprop->priv->property); gtk_tree_model_get_iter_from_string (eprop_flags->model, &iter, path_string); gtk_tree_model_get (eprop_flags->model, &iter, FLAGS_COLUMN_SETTING, &selected, -1); selected = selected ? FALSE : TRUE; gtk_list_store_set (GTK_LIST_STORE (eprop_flags->model), &iter, FLAGS_COLUMN_SETTING, selected, -1); valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (eprop_flags->model), &iter); /* Step through each of the flags in the class, checking if the corresponding toggle in the dialog is selected, If it is, OR the flags' mask with the new value. */ while (valid) { gboolean setting; guint value; gtk_tree_model_get (GTK_TREE_MODEL (eprop_flags->model), &iter, FLAGS_COLUMN_SETTING, &setting, FLAGS_COLUMN_VALUE, &value, -1); if (setting) new_value |= value; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (eprop_flags->model), &iter); } /* If the new_value is different from the old value, we need to update the property. */ if (new_value != g_value_get_flags (gvalue)) { GValue val = { 0, }; g_value_init (&val, G_VALUE_TYPE (gvalue)); g_value_set_flags (&val, new_value); glade_editor_property_commit_no_callback (eprop, &val); g_value_unset (&val); } } static GtkWidget * glade_eprop_flags_create_treeview (GladeEditorProperty *eprop) { GtkWidget *scrolled_window; GtkWidget *tree_view; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop); scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN); gtk_widget_show (scrolled_window); tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (eprop_flags->model)); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE); gtk_widget_show (tree_view); gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view); column = gtk_tree_view_column_new (); renderer = gtk_cell_renderer_toggle_new (); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_set_attributes (column, renderer, "active", FLAGS_COLUMN_SETTING, NULL); g_signal_connect (renderer, "toggled", G_CALLBACK (flag_toggled_direct), eprop); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_attributes (column, renderer, "text", FLAGS_COLUMN_SYMBOL, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column); return scrolled_window; } static void glade_eprop_flags_show_dialog (GladeEditorProperty *eprop) { GtkWidget *window = gtk_widget_get_toplevel (GTK_WIDGET (eprop)); GtkWidget *dialog; GtkWidget *view; GtkWidget *label; GtkWidget *vbox; GtkWidget *content_area; dialog = gtk_dialog_new_with_buttons (_("Select Fields"), GTK_WINDOW (window), GTK_DIALOG_MODAL, _("_Close"), GTK_RESPONSE_CLOSE, NULL); gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); _glade_util_dialog_set_hig (GTK_DIALOG (dialog)); content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); view = glade_eprop_flags_create_treeview (eprop); label = gtk_label_new_with_mnemonic (_("_Select individual fields:")); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_label_set_mnemonic_widget (GTK_LABEL (label), gtk_bin_get_child (GTK_BIN (view))); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), view, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0); gtk_widget_show (label); gtk_widget_show (view); gtk_widget_show (vbox); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static GtkWidget * glade_eprop_flags_create_input (GladeEditorProperty *eprop) { GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop); if (!eprop_flags->model) eprop_flags->model = GTK_TREE_MODEL (gtk_list_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_UINT)); eprop_flags->entry = gtk_entry_new (); gtk_widget_set_hexpand (eprop_flags->entry, TRUE); gtk_editable_set_editable (GTK_EDITABLE (eprop_flags->entry), FALSE); gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_flags->entry), GTK_ENTRY_ICON_SECONDARY, "gtk-edit"); g_signal_connect_swapped (eprop_flags->entry, "icon-release", G_CALLBACK (glade_eprop_flags_show_dialog), eprop); return eprop_flags->entry; } /******************************************************************************* GladeEditorPropertyColorClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *cbutton; GtkWidget *entry; } GladeEPropColor; GLADE_MAKE_EPROP (GladeEPropColor, glade_eprop_color) #define GLADE_EPROP_COLOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_COLOR, GladeEPropColor)) #define GLADE_EPROP_COLOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_COLOR, GladeEPropColorClass)) #define GLADE_IS_EPROP_COLOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_COLOR)) #define GLADE_IS_EPROP_COLOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_COLOR)) #define GLADE_EPROP_COLOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_COLOR, GladeEPropColorClass)) static void glade_eprop_color_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_color_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropColor *eprop_color = GLADE_EPROP_COLOR (eprop); GParamSpec *pspec; GdkColor *color; GdkRGBA *rgba; gchar *text; /* Chain up first */ editor_property_class->load (eprop, property); pspec = glade_property_class_get_pspec (eprop->priv->klass); if (property) { if ((text = glade_property_make_string (property)) != NULL) { gtk_entry_set_text (GTK_ENTRY (eprop_color->entry), text); g_free (text); } else gtk_entry_set_text (GTK_ENTRY (eprop_color->entry), ""); if (pspec->value_type == GDK_TYPE_COLOR) { if ((color = g_value_get_boxed (glade_property_inline_value (property))) != NULL) { GdkRGBA copy; copy.red = color->red / 65535.0; copy.green = color->green / 65535.0; copy.blue = color->blue / 65535.0; copy.alpha = 1.0; gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), ©); } else { GdkRGBA black = { 0, }; /* Manually fill it with black for an NULL value. */ if (gdk_rgba_parse (&black, "Black")) gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), &black); } } else if (pspec->value_type == GDK_TYPE_RGBA) { if ((rgba = g_value_get_boxed (glade_property_inline_value (property))) != NULL) gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), rgba); else { GdkRGBA black = { 0, }; /* Manually fill it with black for an NULL value. */ if (gdk_rgba_parse (&black, "Black")) gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), &black); } } } } static void glade_eprop_color_changed (GtkWidget *button, GladeEditorProperty *eprop) { GdkRGBA rgba = { 0, }; GValue value = { 0, }; GParamSpec *pspec; if (eprop->priv->loading) return; pspec = glade_property_class_get_pspec (eprop->priv->klass); g_value_init (&value, pspec->value_type); gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (button), &rgba); if (pspec->value_type == GDK_TYPE_COLOR) { GdkColor color = { 0, }; color.red = (gint16) (rgba.red * 65535); color.green = (gint16) (rgba.green * 65535); color.blue = (gint16) (rgba.blue * 65535); g_value_set_boxed (&value, &color); } else if (pspec->value_type == GDK_TYPE_RGBA) g_value_set_boxed (&value, &rgba); glade_editor_property_commit (eprop, &value); g_value_unset (&value); } static GtkWidget * glade_eprop_color_create_input (GladeEditorProperty *eprop) { GladeEPropColor *eprop_color = GLADE_EPROP_COLOR (eprop); GtkWidget *hbox; GParamSpec *pspec; pspec = glade_property_class_get_pspec (eprop->priv->klass); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_set_halign (hbox, GTK_ALIGN_START); gtk_widget_set_valign (hbox, GTK_ALIGN_CENTER); eprop_color->entry = gtk_entry_new (); gtk_widget_set_hexpand (eprop_color->entry, TRUE); gtk_editable_set_editable (GTK_EDITABLE (eprop_color->entry), FALSE); gtk_widget_show (eprop_color->entry); gtk_box_pack_start (GTK_BOX (hbox), eprop_color->entry, TRUE, TRUE, 0); eprop_color->cbutton = gtk_color_button_new (); gtk_widget_show (eprop_color->cbutton); gtk_box_pack_start (GTK_BOX (hbox), eprop_color->cbutton, FALSE, FALSE, 0); if (pspec->value_type == GDK_TYPE_RGBA) gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (eprop_color->cbutton), TRUE); else gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (eprop_color->cbutton), FALSE); g_signal_connect (G_OBJECT (eprop_color->cbutton), "color-set", G_CALLBACK (glade_eprop_color_changed), eprop); return hbox; } /******************************************************************************* GladeEditorPropertyNamedIconClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; gchar *current_context; } GladeEPropNamedIcon; GLADE_MAKE_EPROP (GladeEPropNamedIcon, glade_eprop_named_icon) #define GLADE_EPROP_NAMED_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_NAMED_ICON, GladeEPropNamedIcon)) #define GLADE_EPROP_NAMED_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_NAMED_ICON, GladeEPropNamedIconClass)) #define GLADE_IS_EPROP_NAMED_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_NAMED_ICON)) #define GLADE_IS_EPROP_NAMED_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_NAMED_ICON)) #define GLADE_EPROP_NAMED_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_NAMED_ICON, GladeEPropNamedIconClass)) static void glade_eprop_named_icon_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_named_icon_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropNamedIcon *eprop_named_icon = GLADE_EPROP_NAMED_ICON (eprop); GtkEntry *entry; gchar *text; /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; entry = GTK_ENTRY (eprop_named_icon->entry); text = glade_property_make_string (property); gtk_entry_set_text (entry, text ? text : ""); g_free (text); } static void glade_eprop_named_icon_changed_common (GladeEditorProperty *eprop, const gchar *text, gboolean use_command) { GValue *val; gchar *prop_text; val = g_new0 (GValue, 1); g_value_init (val, G_TYPE_STRING); glade_property_get (eprop->priv->property, &prop_text); /* Here we try not to modify the project state by not * modifying a null value for an unchanged property. */ if (prop_text == NULL && text && text[0] == '\0') g_value_set_string (val, NULL); else if (text == NULL && prop_text && prop_text == '\0') g_value_set_string (val, ""); else g_value_set_string (val, text); glade_editor_property_commit (eprop, val); g_value_unset (val); g_free (val); } static void glade_eprop_named_icon_changed (GtkWidget *entry, GladeEditorProperty *eprop) { gchar *text; if (eprop->priv->loading) return; text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); glade_eprop_named_icon_changed_common (eprop, text, eprop->priv->use_command); g_free (text); } static gboolean glade_eprop_named_icon_focus_out (GtkWidget *entry, GdkEventFocus *event, GladeEditorProperty *eprop) { glade_eprop_named_icon_changed (entry, eprop); return FALSE; } static void glade_eprop_named_icon_activate (GtkEntry *entry, GladeEPropNamedIcon *eprop) { glade_eprop_named_icon_changed (GTK_WIDGET (entry), GLADE_EDITOR_PROPERTY (eprop)); } static void chooser_response (GladeNamedIconChooserDialog *dialog, gint response_id, GladeEPropNamedIcon *eprop) { gchar *icon_name; switch (response_id) { case GTK_RESPONSE_OK: g_free (eprop->current_context); eprop->current_context = glade_named_icon_chooser_dialog_get_context (dialog); icon_name = glade_named_icon_chooser_dialog_get_icon_name (dialog); gtk_entry_set_text (GTK_ENTRY (eprop->entry), icon_name); gtk_widget_destroy (GTK_WIDGET (dialog)); g_free (icon_name); glade_eprop_named_icon_changed (eprop->entry, GLADE_EDITOR_PROPERTY (eprop)); break; case GTK_RESPONSE_CANCEL: gtk_widget_destroy (GTK_WIDGET (dialog)); break; case GTK_RESPONSE_HELP: break; case GTK_RESPONSE_DELETE_EVENT: gtk_widget_destroy (GTK_WIDGET (dialog)); } } static void glade_eprop_named_icon_show_chooser_dialog (GladeEditorProperty *eprop) { GtkWidget *dialog; dialog = glade_named_icon_chooser_dialog_new (_("Select Named Icon"), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (eprop))), _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); glade_named_icon_chooser_dialog_set_context (GLADE_NAMED_ICON_CHOOSER_DIALOG (dialog), GLADE_EPROP_NAMED_ICON (eprop)-> current_context); glade_named_icon_chooser_dialog_set_icon_name (GLADE_NAMED_ICON_CHOOSER_DIALOG (dialog), gtk_entry_get_text (GTK_ENTRY (GLADE_EPROP_NAMED_ICON (eprop)-> entry))); g_signal_connect (dialog, "response", G_CALLBACK (chooser_response), eprop); gtk_widget_show (dialog); } static GtkWidget * glade_eprop_named_icon_create_input (GladeEditorProperty *eprop) { GladeEPropNamedIcon *eprop_named_icon = GLADE_EPROP_NAMED_ICON (eprop); eprop_named_icon->entry = gtk_entry_new (); gtk_widget_set_hexpand (eprop_named_icon->entry, TRUE); gtk_widget_set_valign (eprop_named_icon->entry, GTK_ALIGN_CENTER); gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_named_icon->entry), GTK_ENTRY_ICON_SECONDARY, "gtk-edit"); eprop_named_icon->current_context = NULL; g_signal_connect (G_OBJECT (eprop_named_icon->entry), "activate", G_CALLBACK (glade_eprop_named_icon_activate), eprop); g_signal_connect (G_OBJECT (eprop_named_icon->entry), "focus-out-event", G_CALLBACK (glade_eprop_named_icon_focus_out), eprop); g_signal_connect_swapped (eprop_named_icon->entry, "icon-release", G_CALLBACK (glade_eprop_named_icon_show_chooser_dialog), eprop); return eprop_named_icon->entry; } /******************************************************************************* GladeEditorPropertyTextClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *text_entry; GtkTreeModel *store; } GladeEPropText; GLADE_MAKE_EPROP (GladeEPropText, glade_eprop_text) #define GLADE_EPROP_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_TEXT, GladeEPropText)) #define GLADE_EPROP_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_TEXT, GladeEPropTextClass)) #define GLADE_IS_EPROP_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_TEXT)) #define GLADE_IS_EPROP_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_TEXT)) #define GLADE_EPROP_TEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_TEXT, GladeEPropTextClass)) static void glade_eprop_text_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static gchar * text_buffer_get_text (GtkTextBuffer *buffer) { GtkTextIter start, end; gchar *retval; gtk_text_buffer_get_bounds (buffer, &start, &end); retval = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); if (retval && retval[0] == '\0') { g_free (retval); return NULL; } return retval; } static void glade_eprop_text_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop); GParamSpec *pspec; /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; pspec = glade_property_class_get_pspec (eprop->priv->klass); if (GTK_IS_COMBO_BOX (eprop_text->text_entry)) { if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (eprop_text->text_entry))) { GtkWidget *entry = gtk_bin_get_child (GTK_BIN (eprop_text->text_entry)); gchar *text = glade_property_make_string (property); gtk_entry_set_text (GTK_ENTRY (entry), text ? text : ""); g_free (text); } else { gchar *text = glade_property_make_string (property); gint value = text ? glade_utils_enum_value_from_string (GLADE_TYPE_STOCK, text) : 0; /* Set active iter... */ gtk_combo_box_set_active (GTK_COMBO_BOX (eprop_text->text_entry), value); g_free (text); } } else if (GTK_IS_ENTRY (eprop_text->text_entry)) { GtkEntry *entry = GTK_ENTRY (eprop_text->text_entry); gchar *text = glade_property_make_string (property); gtk_entry_set_text (entry, text ? text : ""); g_free (text); } else if (GTK_IS_TEXT_VIEW (eprop_text->text_entry)) { GtkTextBuffer *buffer; GType value_array_type; /* Deprecated GValueArray */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS; value_array_type = G_TYPE_VALUE_ARRAY; G_GNUC_END_IGNORE_DEPRECATIONS; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop_text->text_entry)); if (pspec->value_type == G_TYPE_STRV || pspec->value_type == value_array_type) { GladePropertyClass *pclass = glade_property_get_class (property); gchar *text = glade_widget_adaptor_string_from_value (glade_property_class_get_adaptor (pclass), pclass, glade_property_inline_value (property)); gchar *old_text = text_buffer_get_text (buffer); /* Only update it if necessary, see notes bellow */ if (g_strcmp0 (text, old_text)) gtk_text_buffer_set_text (buffer, text ? text : "", -1); g_free (text); } else { gchar *text = glade_property_make_string (property); gchar *old_text = text_buffer_get_text (buffer); /* NOTE: GtkTextBuffer does not like to be updated from a "changed" * signal callback. It prints a iterator warning and moves the cursor * to the end. */ if (g_strcmp0 (text, old_text)) gtk_text_buffer_set_text (buffer, text ? text : "", -1); g_free (old_text); g_free (text); } } else { g_warning ("BUG! Invalid Text Widget type."); } } static void glade_eprop_text_changed_common (GladeEditorProperty *eprop, const gchar *text, gboolean use_command) { GValue *val; GParamSpec *pspec; gchar *prop_text; GType value_array_type; /* Deprecated GValueArray */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS; value_array_type = G_TYPE_VALUE_ARRAY; G_GNUC_END_IGNORE_DEPRECATIONS; pspec = glade_property_class_get_pspec (eprop->priv->klass); if (pspec->value_type == G_TYPE_STRV || pspec->value_type == value_array_type || pspec->value_type == GDK_TYPE_PIXBUF) { GladeWidget *gwidget = glade_property_get_widget (eprop->priv->property); val = glade_property_class_make_gvalue_from_string (eprop->priv->klass, text, glade_widget_get_project (gwidget)); } else { val = g_new0 (GValue, 1); g_value_init (val, G_TYPE_STRING); glade_property_get (eprop->priv->property, &prop_text); /* Here we try not to modify the project state by not * modifying a null value for an unchanged property. */ if (prop_text == NULL && text && text[0] == '\0') g_value_set_string (val, NULL); else if (text == NULL && prop_text && prop_text == '\0') g_value_set_string (val, ""); else g_value_set_string (val, text); } glade_editor_property_commit_no_callback (eprop, val); g_value_unset (val); g_free (val); } static void glade_eprop_text_changed (GtkWidget *entry, GladeEditorProperty *eprop) { gchar *text; if (eprop->priv->loading) return; text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command); g_free (text); } static void glade_eprop_text_buffer_changed (GtkTextBuffer *buffer, GladeEditorProperty *eprop) { gchar *text; if (eprop->priv->loading) return; text = text_buffer_get_text (buffer); glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command); g_free (text); } /** * glade_editor_property_show_i18n_dialog: * @parent: The parent widget for the dialog. * @text: A read/write pointer to the text property * @context: A read/write pointer to the translation context * @comment: A read/write pointer to the translator comment * @translatable: A read/write pointer to the translatable setting] * * Runs a dialog and updates the provided values. * * Returns: %TRUE if OK was selected. */ gboolean glade_editor_property_show_i18n_dialog (GtkWidget *parent, gchar **text, gchar **context, gchar **comment, gboolean *translatable) { GtkWidget *dialog; GtkWidget *vbox, *hbox; GtkWidget *label; GtkWidget *sw; GtkWidget *alignment; GtkWidget *text_view, *comment_view, *context_view; GtkTextBuffer *text_buffer, *comment_buffer, *context_buffer = NULL; GtkWidget *translatable_button; GtkWidget *content_area; gint res; g_return_val_if_fail (text && context && comment && translatable, FALSE); dialog = gtk_dialog_new_with_buttons (_("Edit Text"), parent ? GTK_WINDOW (gtk_widget_get_toplevel (parent)) : NULL, GTK_DIALOG_MODAL, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); _glade_util_dialog_set_hig (GTK_DIALOG (dialog)); content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); gtk_widget_show (vbox); gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0); /* Text */ label = gtk_label_new_with_mnemonic (_("_Text:")); gtk_widget_show (label); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_widget_set_size_request (sw, 400, 200); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); text_view = gtk_text_view_new (); gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (text_view), GTK_SCROLL_MINIMUM); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD); gtk_widget_show (text_view); gtk_label_set_mnemonic_widget (GTK_LABEL (label), text_view); gtk_container_add (GTK_CONTAINER (sw), text_view); text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)); if (*text) { gtk_text_buffer_set_text (text_buffer, *text, -1); } /* Translatable and context prefix. */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); /* Translatable */ translatable_button = gtk_check_button_new_with_mnemonic (_("T_ranslatable")); gtk_widget_show (translatable_button); gtk_box_pack_start (GTK_BOX (hbox), translatable_button, FALSE, FALSE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (translatable_button), *translatable); gtk_widget_set_tooltip_text (translatable_button, _("Whether this property is translatable")); /* Context. */ alignment = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0); gtk_widget_show (alignment); label = gtk_label_new_with_mnemonic (_("Conte_xt for translation:")); gtk_widget_show (label); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_container_add (GTK_CONTAINER (alignment), label); gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0); gtk_widget_set_tooltip_text (alignment, _("For short and ambiguous strings: type a word here to differentiate " "the meaning of this string from the meaning of other occurrences of " "the same string")); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); context_view = gtk_text_view_new (); gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (context_view), GTK_SCROLL_MINIMUM); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (context_view), GTK_WRAP_WORD); gtk_widget_show (context_view); gtk_label_set_mnemonic_widget (GTK_LABEL (label), context_view); gtk_container_add (GTK_CONTAINER (sw), context_view); context_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (context_view)); if (*context) { gtk_text_buffer_set_text (context_buffer, *context, -1); } /* Comments. */ alignment = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0); gtk_widget_show (alignment); label = gtk_label_new_with_mnemonic (_("Co_mments for translators:")); gtk_widget_show (label); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_container_add (GTK_CONTAINER (alignment), label); gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); comment_view = gtk_text_view_new (); gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (comment_view), GTK_SCROLL_MINIMUM); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (comment_view), GTK_WRAP_WORD); gtk_widget_show (comment_view); gtk_label_set_mnemonic_widget (GTK_LABEL (label), comment_view); gtk_container_add (GTK_CONTAINER (sw), comment_view); comment_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (comment_view)); if (*comment) { gtk_text_buffer_set_text (comment_buffer, *comment, -1); } res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_OK) { g_free ((gpointer) * text); g_free ((gpointer) * context); g_free ((gpointer) * comment); /* Get the new values for translatable */ *translatable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (translatable_button)); /* Comment, text and context */ *comment = text_buffer_get_text (comment_buffer); *text = text_buffer_get_text (text_buffer); *context = text_buffer_get_text (context_buffer); gtk_widget_destroy (dialog); return TRUE; } gtk_widget_destroy (dialog); return FALSE; } static void glade_eprop_text_show_i18n_dialog (GladeEditorProperty *eprop) { GladeEditorPropertyPrivate *priv = eprop->priv; gchar *text = glade_property_make_string (priv->property); gchar *context = g_strdup (glade_property_i18n_get_context (priv->property)); gchar *comment = g_strdup (glade_property_i18n_get_comment (priv->property)); gboolean translatable = glade_property_i18n_get_translatable (priv->property); if (glade_editor_property_show_i18n_dialog (GTK_WIDGET (eprop), &text, &context, &comment, &translatable)) { glade_command_set_i18n (priv->property, translatable, context, comment); glade_eprop_text_changed_common (eprop, text, priv->use_command); glade_editor_property_load (eprop, priv->property); } g_free (text); g_free (context); g_free (comment); } gboolean glade_editor_property_show_resource_dialog (GladeProject *project, GtkWidget *parent, gchar **filename) { GFile *resource_folder; GtkWidget *dialog; gchar *folder; g_return_val_if_fail (filename != NULL, FALSE); *filename = NULL; dialog = gtk_file_chooser_dialog_new (_ ("Select a file from the project resource directory"), parent ? GTK_WINDOW (gtk_widget_get_toplevel (parent)) : NULL, GTK_FILE_CHOOSER_ACTION_OPEN, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Open"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); _glade_util_dialog_set_hig (GTK_DIALOG (dialog)); folder = glade_project_resource_fullpath (project, ""); gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), folder); resource_folder = g_file_new_for_path (folder); g_free (folder); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); *filename = _glade_util_file_get_relative_path (resource_folder, file); g_object_unref (file); } gtk_widget_destroy (dialog); g_object_unref (resource_folder); return *filename != NULL; } static void glade_eprop_text_show_resource_dialog (GladeEditorProperty *eprop) { GladeWidget *widget = glade_property_get_widget (eprop->priv->property); GladeProject *project = glade_widget_get_project (widget); gchar *text = NULL; if (glade_editor_property_show_resource_dialog (project, GTK_WIDGET (eprop), &text)) { glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command); glade_editor_property_load (eprop, eprop->priv->property); g_free (text); } } enum { COMBO_COLUMN_TEXT = 0, COMBO_COLUMN_PIXBUF, COMBO_LAST_COLUMN }; static GtkListStore * glade_eprop_text_create_store (GType enum_type) { GtkListStore *store; GtkTreeIter iter; GEnumClass *eclass; guint i; eclass = g_type_class_ref (enum_type); store = gtk_list_store_new (COMBO_LAST_COLUMN, G_TYPE_STRING, G_TYPE_STRING); for (i = 0; i < eclass->n_values; i++) { const gchar *displayable = glade_get_displayable_value (enum_type, eclass->values[i].value_nick); if (!displayable) displayable = eclass->values[i].value_nick; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COMBO_COLUMN_TEXT, displayable, COMBO_COLUMN_PIXBUF, eclass->values[i].value_nick, -1); } g_type_class_unref (eclass); return store; } static void eprop_text_stock_changed (GtkComboBox *combo, GladeEditorProperty *eprop) { GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop); GtkTreeIter iter; gchar *text = NULL; const gchar *str; if (eprop->priv->loading) return; if (gtk_combo_box_get_active_iter (combo, &iter)) { gtk_tree_model_get (GTK_TREE_MODEL (eprop_text->store), &iter, COMBO_COLUMN_PIXBUF, &text, -1); glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command); g_free (text); } else if (gtk_combo_box_get_has_entry (combo)) { str = gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo)))); glade_eprop_text_changed_common (eprop, str, eprop->priv->use_command); } } static gint get_text_view_height (void) { static gint height = -1; if (height < 0) { GtkWidget *label = gtk_label_new (NULL); PangoLayout *layout = gtk_widget_create_pango_layout (label, "The quick\n" "brown fox\n" "jumped over\n" "the lazy dog"); pango_layout_get_pixel_size (layout, NULL, &height); g_object_unref (layout); g_object_ref_sink (label); g_object_unref (label); } return height; } static GtkWidget * glade_eprop_text_create_input (GladeEditorProperty *eprop) { GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop); GladePropertyClass *klass; GParamSpec *pspec; GtkWidget *hbox; GType value_array_type; /* Deprecated GValueArray */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS; value_array_type = G_TYPE_VALUE_ARRAY; G_GNUC_END_IGNORE_DEPRECATIONS; klass = eprop->priv->klass; pspec = glade_property_class_get_pspec (klass); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); if (glade_property_class_stock (klass) || glade_property_class_stock_icon (klass)) { GtkCellRenderer *renderer; GtkWidget *child; GtkWidget *combo = gtk_combo_box_new_with_entry (); gtk_widget_set_halign (hbox, GTK_ALIGN_START); gtk_widget_set_valign (hbox, GTK_ALIGN_CENTER); gtk_widget_set_hexpand (combo, TRUE); glade_util_remove_scroll_events (combo); eprop_text->store = (GtkTreeModel *) glade_eprop_text_create_store (glade_property_class_stock (klass) ? GLADE_TYPE_STOCK : GLADE_TYPE_STOCK_IMAGE); gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (eprop_text->store)); /* let the comboboxentry prepend its intrusive cell first... */ gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo), COMBO_COLUMN_TEXT); renderer = gtk_cell_renderer_pixbuf_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo), renderer, 0); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "stock-id", COMBO_COLUMN_PIXBUF, NULL); /* Dont allow custom items where an actual GTK+ stock item is expected * (i.e. real items come with labels) */ child = gtk_bin_get_child (GTK_BIN (combo)); if (glade_property_class_stock (klass)) gtk_editable_set_editable (GTK_EDITABLE (child), FALSE); else gtk_editable_set_editable (GTK_EDITABLE (child), TRUE); gtk_widget_show (combo); gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (combo), "changed", G_CALLBACK (eprop_text_stock_changed), eprop); eprop_text->text_entry = combo; } else if (glade_property_class_multiline (klass) || pspec->value_type == G_TYPE_STRV || pspec->value_type == value_array_type) { GtkWidget *swindow; GtkTextBuffer *buffer; gint min_height = get_text_view_height (); swindow = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (swindow), min_height); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow), GTK_SHADOW_IN); glade_util_remove_scroll_events (swindow); eprop_text->text_entry = gtk_text_view_new (); gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (eprop_text->text_entry), GTK_SCROLL_MINIMUM); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (eprop_text->text_entry), GTK_WRAP_WORD); buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop_text->text_entry)); gtk_container_add (GTK_CONTAINER (swindow), eprop_text->text_entry); gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (swindow), TRUE, TRUE, 0); gtk_widget_show_all (swindow); gtk_widget_set_hexpand (swindow, TRUE); g_signal_connect (G_OBJECT (buffer), "changed", G_CALLBACK (glade_eprop_text_buffer_changed), eprop); } else { eprop_text->text_entry = gtk_entry_new (); gtk_widget_set_hexpand (eprop_text->text_entry, TRUE); gtk_widget_show (eprop_text->text_entry); gtk_box_pack_start (GTK_BOX (hbox), eprop_text->text_entry, TRUE, TRUE, 0); g_signal_connect (G_OBJECT (eprop_text->text_entry), "changed", G_CALLBACK (glade_eprop_text_changed), eprop); if (pspec->value_type == GDK_TYPE_PIXBUF) { gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_text->text_entry), GTK_ENTRY_ICON_SECONDARY, "document-open"); g_signal_connect_swapped (eprop_text->text_entry, "icon-release", G_CALLBACK (glade_eprop_text_show_resource_dialog), eprop); } } if (glade_property_class_translatable (klass)) { if (GTK_IS_ENTRY (eprop_text->text_entry)) { gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_text->text_entry), GTK_ENTRY_ICON_SECONDARY, "gtk-edit"); g_signal_connect_swapped (eprop_text->text_entry, "icon-release", G_CALLBACK (glade_eprop_text_show_i18n_dialog), eprop); } else { GtkWidget *button = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_icon_name ("gtk-edit", GTK_ICON_SIZE_MENU)); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect_swapped (button, "clicked", G_CALLBACK (glade_eprop_text_show_i18n_dialog), eprop); } } return hbox; } /******************************************************************************* GladeEditorPropertyBoolClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *button; } GladeEPropBool; GLADE_MAKE_EPROP (GladeEPropBool, glade_eprop_bool) #define GLADE_EPROP_BOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_BOOL, GladeEPropBool)) #define GLADE_EPROP_BOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_BOOL, GladeEPropBoolClass)) #define GLADE_IS_EPROP_BOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_BOOL)) #define GLADE_IS_EPROP_BOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_BOOL)) #define GLADE_EPROP_BOOL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_BOOL, GladeEPropBoolClass)) static void glade_eprop_bool_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_bool_load (GladeEditorProperty *eprop, GladeProperty *property) { /* Chain up first */ editor_property_class->load (eprop, property); if (property) { GladeEPropBool *eprop_bool = GLADE_EPROP_BOOL (eprop); gboolean state = g_value_get_boolean (glade_property_inline_value (property)); gtk_switch_set_active (GTK_SWITCH (eprop_bool->button), state); } } static void glade_eprop_bool_active_notify (GObject *gobject, GParamSpec *pspec, GladeEditorProperty *eprop) { GValue val = { 0, }; if (eprop->priv->loading) return; g_value_init (&val, G_TYPE_BOOLEAN); g_value_set_boolean (&val, gtk_switch_get_active (GTK_SWITCH (gobject))); glade_editor_property_commit_no_callback (eprop, &val); g_value_unset (&val); } static GtkWidget * glade_eprop_bool_create_input (GladeEditorProperty *eprop) { GladeEPropBool *eprop_bool = GLADE_EPROP_BOOL (eprop); eprop_bool->button = gtk_switch_new (); gtk_widget_set_halign (eprop_bool->button, GTK_ALIGN_START); gtk_widget_set_valign (eprop_bool->button, GTK_ALIGN_CENTER); g_signal_connect (eprop_bool->button, "notify::active", G_CALLBACK (glade_eprop_bool_active_notify), eprop); return eprop_bool->button; } /******************************************************************************* GladeEditorPropertyCheckClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *button; } GladeEPropCheck; GLADE_MAKE_EPROP (GladeEPropCheck, glade_eprop_check) #define GLADE_EPROP_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_CHECK, GladeEPropCheck)) #define GLADE_EPROP_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_CHECK, GladeEPropCheckClass)) #define GLADE_IS_EPROP_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_CHECK)) #define GLADE_IS_EPROP_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_CHECK)) #define GLADE_EPROP_CHECK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_CHECK, GladeEPropCheckClass)) static void glade_eprop_check_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_check_load (GladeEditorProperty *eprop, GladeProperty *property) { /* Chain up first */ editor_property_class->load (eprop, property); if (property) { GladeEPropCheck *eprop_check = GLADE_EPROP_CHECK (eprop); gboolean state = g_value_get_boolean (glade_property_inline_value (property)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop_check->button), state); } } static void glade_eprop_check_active_notify (GObject *gobject, GParamSpec *pspec, GladeEditorProperty *eprop) { GValue val = { 0, }; if (eprop->priv->loading) return; g_value_init (&val, G_TYPE_BOOLEAN); g_value_set_boolean (&val, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gobject))); glade_editor_property_commit_no_callback (eprop, &val); g_value_unset (&val); } static GtkWidget * glade_eprop_check_create_input (GladeEditorProperty *eprop) { GladeEPropCheck *eprop_check = GLADE_EPROP_CHECK (eprop); GladePropertyClass *pclass; GtkWidget *label; pclass = eprop->priv->klass; /* Add the property label as the check button's child */ label = glade_editor_property_get_item_label (eprop); glade_property_label_set_property_name (GLADE_PROPERTY_LABEL (label), glade_property_class_id (pclass)); glade_property_label_set_packing (GLADE_PROPERTY_LABEL (label), glade_property_class_get_is_packing (pclass)); glade_property_label_set_append_colon (GLADE_PROPERTY_LABEL (label), FALSE); glade_property_label_set_custom_text (GLADE_PROPERTY_LABEL (label), eprop->priv->custom_text); gtk_widget_show (label); eprop_check->button = gtk_check_button_new (); gtk_button_set_focus_on_click (GTK_BUTTON (eprop_check->button), FALSE); gtk_container_add (GTK_CONTAINER (eprop_check->button), label); gtk_widget_set_halign (eprop_check->button, GTK_ALIGN_START); gtk_widget_set_valign (eprop_check->button, GTK_ALIGN_CENTER); g_signal_connect (eprop_check->button, "notify::active", G_CALLBACK (glade_eprop_check_active_notify), eprop); return eprop_check->button; } /******************************************************************************* GladeEditorPropertyUnicharClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; } GladeEPropUnichar; GLADE_MAKE_EPROP (GladeEPropUnichar, glade_eprop_unichar) #define GLADE_EPROP_UNICHAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_UNICHAR, GladeEPropUnichar)) #define GLADE_EPROP_UNICHAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_UNICHAR, GladeEPropUnicharClass)) #define GLADE_IS_EPROP_UNICHAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_UNICHAR)) #define GLADE_IS_EPROP_UNICHAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_UNICHAR)) #define GLADE_EPROP_UNICHAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_UNICHAR, GladeEPropUnicharClass)) static void glade_eprop_unichar_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_unichar_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropUnichar *eprop_unichar = GLADE_EPROP_UNICHAR (eprop); /* Chain up first */ editor_property_class->load (eprop, property); if (property && GTK_IS_ENTRY (eprop_unichar->entry)) { GtkEntry *entry = GTK_ENTRY (eprop_unichar->entry); gchar utf8st[8]; gint n; if ((n = g_unichar_to_utf8 (g_value_get_uint (glade_property_inline_value (property)), utf8st))) { utf8st[n] = '\0'; gtk_entry_set_text (entry, utf8st); } } } static void glade_eprop_unichar_changed (GtkWidget *entry, GladeEditorProperty *eprop) { const gchar *text; if (eprop->priv->loading) return; if ((text = gtk_entry_get_text (GTK_ENTRY (entry))) != NULL) { gunichar unich = g_utf8_get_char (text); GValue val = { 0, }; g_value_init (&val, G_TYPE_UINT); g_value_set_uint (&val, unich); glade_editor_property_commit_no_callback (eprop, &val); g_value_unset (&val); } } static void glade_eprop_unichar_delete (GtkEditable *editable, gint start_pos, gint end_pos, GladeEditorProperty *eprop) { if (eprop->priv->loading) return; gtk_editable_select_region (editable, 0, -1); g_signal_stop_emission_by_name (G_OBJECT (editable), "delete_text"); } static void glade_eprop_unichar_insert (GtkWidget *entry, const gchar *text, gint length, gint *position, GladeEditorProperty *eprop) { if (eprop->priv->loading) return; g_signal_handlers_block_by_func (G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_changed), eprop); g_signal_handlers_block_by_func (G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_insert), eprop); g_signal_handlers_block_by_func (G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_delete), eprop); gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1); *position = 0; gtk_editable_insert_text (GTK_EDITABLE (entry), text, 1, position); g_signal_handlers_unblock_by_func (G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_changed), eprop); g_signal_handlers_unblock_by_func (G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_insert), eprop); g_signal_handlers_unblock_by_func (G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_delete), eprop); g_signal_stop_emission_by_name (G_OBJECT (entry), "insert_text"); glade_eprop_unichar_changed (entry, eprop); } static GtkWidget * glade_eprop_unichar_create_input (GladeEditorProperty *eprop) { GladeEPropUnichar *eprop_unichar = GLADE_EPROP_UNICHAR (eprop); eprop_unichar->entry = gtk_entry_new (); gtk_widget_set_halign (eprop_unichar->entry, GTK_ALIGN_START); gtk_widget_set_valign (eprop_unichar->entry, GTK_ALIGN_CENTER); /* it's 2 to prevent spirious beeps... */ gtk_entry_set_max_length (GTK_ENTRY (eprop_unichar->entry), 2); g_signal_connect (G_OBJECT (eprop_unichar->entry), "changed", G_CALLBACK (glade_eprop_unichar_changed), eprop); g_signal_connect (G_OBJECT (eprop_unichar->entry), "insert_text", G_CALLBACK (glade_eprop_unichar_insert), eprop); g_signal_connect (G_OBJECT (eprop_unichar->entry), "delete_text", G_CALLBACK (glade_eprop_unichar_delete), eprop); return eprop_unichar->entry; } /******************************************************************************* GladeEditorPropertyObjectClass *******************************************************************************/ enum { OBJ_COLUMN_WIDGET = 0, OBJ_COLUMN_WIDGET_NAME, OBJ_COLUMN_WIDGET_CLASS, OBJ_COLUMN_SELECTED, OBJ_COLUMN_SELECTABLE, OBJ_NUM_COLUMNS }; #define GLADE_RESPONSE_CLEAR 42 #define GLADE_RESPONSE_CREATE 43 typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; } GladeEPropObject; GLADE_MAKE_EPROP (GladeEPropObject, glade_eprop_object) #define GLADE_EPROP_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_OBJECT, GladeEPropObject)) #define GLADE_EPROP_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_OBJECT, GladeEPropObjectClass)) #define GLADE_IS_EPROP_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_OBJECT)) #define GLADE_IS_EPROP_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_OBJECT)) #define GLADE_EPROP_OBJECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_OBJECT, GladeEPropObjectClass)) static void glade_eprop_object_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static gchar * glade_eprop_object_name (const gchar *name, GtkTreeStore *model, GtkTreeIter *parent_iter) { GtkTreePath *path; GString *string; gint i; string = g_string_new (name); if (parent_iter) { path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), parent_iter); for (i = 0; i < gtk_tree_path_get_depth (path); i++) g_string_prepend (string, " "); } return g_string_free (string, FALSE); } static gboolean search_list (GList * list, gpointer data) { return g_list_find (list, data) != NULL; } /* * Note that widgets is a list of GtkWidgets, while what we store * in the model are the associated GladeWidgets. */ static void glade_eprop_object_populate_view_real (GtkTreeStore *model, GtkTreeIter *parent_iter, GList *widgets, GList *selected_widgets, GList *exception_widgets, GType object_type, gboolean parentless) { GList *children, *list; GtkTreeIter iter; gboolean good_type, has_decendant; for (list = widgets; list; list = list->next) { GladeWidget *widget; GladeWidgetAdaptor *adaptor; const gchar *widget_name; if ((widget = glade_widget_get_from_gobject (list->data)) != NULL) { adaptor = glade_widget_get_adaptor (widget); has_decendant = !parentless && glade_widget_has_decendant (widget, object_type); good_type = (glade_widget_adaptor_get_object_type (adaptor) == object_type || g_type_is_a (glade_widget_adaptor_get_object_type (adaptor), object_type)); widget_name = glade_widget_get_display_name (widget); if (parentless) good_type = good_type && !GWA_IS_TOPLEVEL (adaptor); if (good_type || has_decendant) { gtk_tree_store_append (model, &iter, parent_iter); gtk_tree_store_set (model, &iter, OBJ_COLUMN_WIDGET, widget, OBJ_COLUMN_WIDGET_NAME, glade_eprop_object_name (widget_name, model, parent_iter), OBJ_COLUMN_WIDGET_CLASS, glade_widget_adaptor_get_title (adaptor), /* Selectable if its a compatible type and * its not itself. */ OBJ_COLUMN_SELECTABLE, good_type && !search_list (exception_widgets, widget), OBJ_COLUMN_SELECTED, good_type && search_list (selected_widgets, widget), -1); } if (has_decendant && (children = glade_widget_adaptor_get_children (adaptor, glade_widget_get_object (widget))) != NULL) { GtkTreeIter *copy = NULL; copy = gtk_tree_iter_copy (&iter); glade_eprop_object_populate_view_real (model, copy, children, selected_widgets, exception_widgets, object_type, parentless); gtk_tree_iter_free (copy); g_list_free (children); } } } } static void glade_eprop_object_populate_view (GladeProject *project, GtkTreeView *view, GList *selected, GList *exceptions, GType object_type, gboolean parentless) { GtkTreeStore *model = (GtkTreeStore *) gtk_tree_view_get_model (view); GList *list, *toplevels = NULL; /* Make a list of only the toplevel widgets */ for (list = (GList *) glade_project_get_objects (project); list; list = list->next) { GObject *object = G_OBJECT (list->data); GladeWidget *gwidget = glade_widget_get_from_gobject (object); g_assert (gwidget); if (glade_widget_get_parent (gwidget) == NULL) toplevels = g_list_append (toplevels, object); } /* add the widgets and recurse */ glade_eprop_object_populate_view_real (model, NULL, toplevels, selected, exceptions, object_type, parentless); g_list_free (toplevels); } static gboolean glade_eprop_object_clear_iter (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { gtk_tree_store_set (GTK_TREE_STORE (model), iter, OBJ_COLUMN_SELECTED, FALSE, -1); return FALSE; } static gboolean glade_eprop_object_selected_widget (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GladeWidget **ret) { gboolean selected; GladeWidget *widget; gtk_tree_model_get (model, iter, OBJ_COLUMN_SELECTED, &selected, OBJ_COLUMN_WIDGET, &widget, -1); if (selected) { *ret = widget; return TRUE; } return FALSE; } static void glade_eprop_object_selected (GtkCellRendererToggle *cell, gchar *path_str, GtkTreeModel *model) { GtkTreePath *path = gtk_tree_path_new_from_string (path_str); GtkTreeIter iter; gboolean enabled, radio; radio = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model), "radio-list")); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, OBJ_COLUMN_SELECTED, &enabled, -1); /* Clear the rest of the view first */ if (radio) gtk_tree_model_foreach (model, glade_eprop_object_clear_iter, NULL); gtk_tree_store_set (GTK_TREE_STORE (model), &iter, OBJ_COLUMN_SELECTED, radio ? TRUE : !enabled, -1); gtk_tree_path_free (path); } static GtkWidget * glade_eprop_object_view (gboolean radio) { GtkWidget *view_widget; GtkTreeModel *model; GtkCellRenderer *renderer; GtkTreeViewColumn *column; model = (GtkTreeModel *) gtk_tree_store_new (OBJ_NUM_COLUMNS, G_TYPE_OBJECT, /* The GladeWidget */ G_TYPE_STRING, /* The GladeWidget's name */ G_TYPE_STRING, /* The GladeWidgetClass title */ G_TYPE_BOOLEAN, /* Whether this row is selected or not */ G_TYPE_BOOLEAN); /* Whether this GladeWidget is * of an acceptable type and * therefore can be selected. */ g_object_set_data (G_OBJECT (model), "radio-list", GINT_TO_POINTER (radio)); view_widget = gtk_tree_view_new_with_model (model); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view_widget), FALSE); /* Pass ownership to the view */ g_object_unref (G_OBJECT (model)); g_object_set (G_OBJECT (view_widget), "enable-search", FALSE, NULL); /********************* fake invisible column *********************/ renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", FALSE, "visible", FALSE, NULL); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column); gtk_tree_view_column_set_visible (column, FALSE); gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view_widget), column); /************************ selected column ************************/ renderer = gtk_cell_renderer_toggle_new (); g_object_set (G_OBJECT (renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, "activatable", TRUE, "radio", radio, NULL); g_signal_connect (renderer, "toggled", G_CALLBACK (glade_eprop_object_selected), model); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view_widget), 0, NULL, renderer, "visible", OBJ_COLUMN_SELECTABLE, "sensitive", OBJ_COLUMN_SELECTABLE, "active", OBJ_COLUMN_SELECTED, NULL); /********************* widget name column *********************/ renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view_widget), 1, _("Name"), renderer, "text", OBJ_COLUMN_WIDGET_NAME, NULL); /***************** widget class title column ******************/ renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", FALSE, "style", PANGO_STYLE_ITALIC, "foreground", "Gray", NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view_widget), 2, _("Class"), renderer, "text", OBJ_COLUMN_WIDGET_CLASS, NULL); return view_widget; } static gchar * glade_eprop_object_dialog_title (GladeEditorProperty *eprop) { gboolean parentless; GParamSpec *pspec; parentless = glade_property_class_parentless_widget (eprop->priv->klass); pspec = glade_property_class_get_pspec (eprop->priv->klass); if (GLADE_IS_PARAM_SPEC_OBJECTS (pspec)) { const gchar *typename = g_type_name (glade_param_spec_objects_get_type (GLADE_PARAM_SPEC_OBJECTS (pspec))); if (parentless) return g_strdup_printf (_("Choose parentless %s type objects in this project"), typename); else return g_strdup_printf (_("Choose %s type objects in this project"), typename); } else { GladeWidgetAdaptor *adaptor; const gchar *title; adaptor = glade_widget_adaptor_get_by_type (pspec->value_type); if (adaptor != NULL) title = glade_widget_adaptor_get_title (adaptor); else { /* Fallback on type name (which would look like "GtkButton" * instead of "Button" and maybe not translated). */ title = g_type_name (pspec->value_type); } if (parentless) return g_strdup_printf (_("Choose a parentless %s in this project"), title); else return g_strdup_printf (_("Choose a %s in this project"), title); } } gboolean glade_editor_property_show_object_dialog (GladeProject *project, const gchar *title, GtkWidget *parent, GType object_type, GladeWidget *exception, GladeWidget **object) { GtkWidget *dialog; GtkWidget *vbox, *label, *sw; GtkWidget *tree_view; GtkWidget *content_area; GList *selected_list = NULL, *exception_list = NULL; gint res; g_return_val_if_fail (object != NULL, -1); if (!parent) parent = glade_app_get_window (); dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), GTK_DIALOG_MODAL, _("_Cancel"), GTK_RESPONSE_CANCEL, _("C_lear"), GLADE_RESPONSE_CLEAR, _("_OK"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, GLADE_RESPONSE_CLEAR, -1); gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); _glade_util_dialog_set_hig (GTK_DIALOG (dialog)); content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_widget_show (vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0); /* Checklist */ label = gtk_label_new_with_mnemonic (_("O_bjects:")); gtk_widget_show (label); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_widget_set_size_request (sw, 400, 200); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); if (*object) selected_list = g_list_prepend (selected_list, *object); if (exception) exception_list = g_list_prepend (exception_list, exception); tree_view = glade_eprop_object_view (TRUE); glade_eprop_object_populate_view (project, GTK_TREE_VIEW (tree_view), selected_list, exception_list, object_type, FALSE); g_list_free (selected_list); g_list_free (exception_list); gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); gtk_widget_show (tree_view); gtk_container_add (GTK_CONTAINER (sw), tree_view); gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view); /* Run the dialog */ res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_OK) { GladeWidget *selected = NULL; gtk_tree_model_foreach (gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)), (GtkTreeModelForeachFunc) glade_eprop_object_selected_widget, &selected); *object = selected; } else if (res == GLADE_RESPONSE_CLEAR) *object = NULL; gtk_widget_destroy (dialog); return (res == GTK_RESPONSE_OK || res == GLADE_RESPONSE_CLEAR); } static void glade_eprop_object_show_dialog (GladeEditorProperty *eprop) { GtkWidget *dialog, *parent; GtkWidget *vbox, *label, *sw; GtkWidget *tree_view; GtkWidget *content_area; GladeProject *project; GladeWidget *widget; GParamSpec *pspec; gchar *title = glade_eprop_object_dialog_title (eprop); gint res; GladeWidgetAdaptor *create_adaptor = NULL; GList *selected_list = NULL, *exception_list = NULL; widget = glade_property_get_widget (eprop->priv->property); project = glade_widget_get_project (widget); parent = gtk_widget_get_toplevel (GTK_WIDGET (eprop)); pspec = glade_property_class_get_pspec (eprop->priv->klass); if (glade_property_class_create_type (eprop->priv->klass)) create_adaptor = glade_widget_adaptor_get_by_name (glade_property_class_create_type (eprop->priv->klass)); if (!create_adaptor && G_TYPE_IS_INSTANTIATABLE (pspec->value_type) && !G_TYPE_IS_ABSTRACT (pspec->value_type)) create_adaptor = glade_widget_adaptor_get_by_type (pspec->value_type); if (create_adaptor) { dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), GTK_DIALOG_MODAL, _("_Cancel"), GTK_RESPONSE_CANCEL, _("C_lear"), GLADE_RESPONSE_CLEAR, _("_New"), GLADE_RESPONSE_CREATE, _("_OK"), GTK_RESPONSE_OK, NULL); g_free (title); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GLADE_RESPONSE_CREATE, GTK_RESPONSE_CANCEL, GLADE_RESPONSE_CLEAR, -1); } else { dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), GTK_DIALOG_MODAL, _("_Cancel"), GTK_RESPONSE_CANCEL, _("C_lear"), GLADE_RESPONSE_CLEAR, _("_OK"), GTK_RESPONSE_OK, NULL); g_free (title); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, GLADE_RESPONSE_CLEAR, -1); } gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); _glade_util_dialog_set_hig (GTK_DIALOG (dialog)); content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_widget_show (vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0); /* Checklist */ label = gtk_label_new_with_mnemonic (_("O_bjects:")); gtk_widget_show (label); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_widget_set_size_request (sw, 400, 200); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); exception_list = g_list_prepend (exception_list, widget); if (g_value_get_object (glade_property_inline_value (eprop->priv->property))) selected_list = g_list_prepend (selected_list, glade_widget_get_from_gobject (g_value_get_object (glade_property_inline_value (eprop->priv->property)))); tree_view = glade_eprop_object_view (TRUE); glade_eprop_object_populate_view (project, GTK_TREE_VIEW (tree_view), selected_list, exception_list, pspec->value_type, glade_property_class_parentless_widget (eprop->priv->klass)); g_list_free (selected_list); g_list_free (exception_list); gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); gtk_widget_show (tree_view); gtk_container_add (GTK_CONTAINER (sw), tree_view); gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view); /* Run the dialog */ res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_OK) { GladeWidget *selected = NULL; gtk_tree_model_foreach (gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)), (GtkTreeModelForeachFunc) glade_eprop_object_selected_widget, &selected); if (selected) { GValue *value; GObject *new_object, *old_object = NULL; GladeWidget *new_widget; const gchar *current_name; glade_project_selection_set (project, glade_widget_get_object (widget), TRUE); value = glade_property_class_make_gvalue_from_string (eprop->priv->klass, glade_widget_get_name (selected), project); glade_property_get (eprop->priv->property, &old_object); new_object = g_value_get_object (value); new_widget = glade_widget_get_from_gobject (new_object); glade_command_push_group (_("Setting %s of %s to %s"), glade_property_class_get_name (eprop->priv->klass), glade_widget_get_name (widget), glade_widget_get_name (new_widget)); /* Unparent the widget so we can reuse it for this property */ if (glade_property_class_parentless_widget (eprop->priv->klass)) { GladeProperty *old_ref; if (!G_IS_PARAM_SPEC_OBJECT (pspec)) g_warning ("Parentless widget property should be of object type"); else if (new_object && old_object != new_object) { /* Steal parentless reference widget references, basically some references * can only be referenced by one property, here we clear it if such a reference * exists for the target object */ if ((old_ref = glade_widget_get_parentless_widget_ref (new_widget))) glade_command_set_property (old_ref, NULL); } } /* Ensure that the object we will now refer to has an ID, the NULL * check is just paranoia, it *always* has a name. * * To refer to a widget, it needs to have a name. */ glade_widget_ensure_name (new_widget, project, TRUE); glade_editor_property_commit (eprop, value); glade_command_pop_group (); g_value_unset (value); g_free (value); } } else if (res == GLADE_RESPONSE_CREATE) { GValue *value; GladeWidget *new_widget; /* translators: Creating 'a widget' for 'a property' of 'a widget' */ glade_command_push_group (_("Creating %s for %s of %s"), glade_widget_adaptor_get_name (create_adaptor), glade_property_class_get_name (eprop->priv->klass), glade_widget_get_name (widget)); /* Dont bother if the user canceled the widget */ if ((new_widget = glade_command_create (create_adaptor, NULL, NULL, project)) != NULL) { GValue *value; glade_project_selection_set (project, glade_widget_get_object (widget), TRUE); /* Give the newly created object a name */ glade_widget_ensure_name (new_widget, project, TRUE); value = g_new0 (GValue, 1); g_value_init (value, pspec->value_type); g_value_set_object (value, glade_widget_get_object (new_widget)); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } glade_command_pop_group (); } else if (res == GLADE_RESPONSE_CLEAR) { GValue *value = glade_property_class_make_gvalue_from_string (eprop->priv->klass, NULL, project); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } gtk_widget_destroy (dialog); } static void glade_eprop_object_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropObject *eprop_object = GLADE_EPROP_OBJECT (eprop); gchar *obj_name; /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; if ((obj_name = glade_widget_adaptor_string_from_value (glade_property_class_get_adaptor (eprop->priv->klass), eprop->priv->klass, glade_property_inline_value (property))) != NULL) { gtk_entry_set_text (GTK_ENTRY (eprop_object->entry), obj_name); g_free (obj_name); } else gtk_entry_set_text (GTK_ENTRY (eprop_object->entry), ""); } static GtkWidget * glade_eprop_object_create_input (GladeEditorProperty *eprop) { GladeEPropObject *eprop_object = GLADE_EPROP_OBJECT (eprop); eprop_object->entry = gtk_entry_new (); gtk_widget_set_hexpand (eprop_object->entry, TRUE); gtk_widget_set_valign (eprop_object->entry, GTK_ALIGN_CENTER); gtk_editable_set_editable (GTK_EDITABLE (eprop_object->entry), FALSE); gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_object->entry), GTK_ENTRY_ICON_SECONDARY, "gtk-edit"); g_signal_connect_swapped (eprop_object->entry, "icon-release", G_CALLBACK (glade_eprop_object_show_dialog), eprop); return eprop_object->entry; } /******************************************************************************* GladeEditorPropertyObjectsClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; } GladeEPropObjects; GLADE_MAKE_EPROP (GladeEPropObjects, glade_eprop_objects) #define GLADE_EPROP_OBJECTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_OBJECTS, GladeEPropObjects)) #define GLADE_EPROP_OBJECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_OBJECTS, GladeEPropObjectsClass)) #define GLADE_IS_EPROP_OBJECTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_OBJECTS)) #define GLADE_IS_EPROP_OBJECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_OBJECTS)) #define GLADE_EPROP_OBJECTS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_OBJECTS, GladeEPropObjectsClass)) static void glade_eprop_objects_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_objects_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropObjects *eprop_objects = GLADE_EPROP_OBJECTS (eprop); gchar *obj_name; /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; if ((obj_name = glade_widget_adaptor_string_from_value (glade_property_class_get_adaptor (eprop->priv->klass), eprop->priv->klass, glade_property_inline_value (property))) != NULL) { gtk_entry_set_text (GTK_ENTRY (eprop_objects->entry), obj_name); g_free (obj_name); } else gtk_entry_set_text (GTK_ENTRY (eprop_objects->entry), ""); } static gboolean glade_eprop_objects_selected_widget (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **ret) { gboolean selected; GladeWidget *widget; gtk_tree_model_get (model, iter, OBJ_COLUMN_SELECTED, &selected, OBJ_COLUMN_WIDGET, &widget, -1); if (selected) { *ret = g_list_append (*ret, glade_widget_get_object (widget)); g_object_unref (widget); } return FALSE; } static void glade_eprop_objects_show_dialog (GladeEditorProperty *eprop) { GtkWidget *dialog, *parent; GtkWidget *vbox, *label, *sw; GtkWidget *tree_view; GladeWidget *widget; GladeProject *project; GParamSpec *pspec; gchar *title = glade_eprop_object_dialog_title (eprop); gint res; GList *selected_list = NULL, *exception_list = NULL, *selected_objects = NULL, *l; /* It's improbable but possible the editor is visible with no * property selected, in this case avoid crashes */ if (!eprop->priv->property) return; widget = glade_property_get_widget (eprop->priv->property); project = glade_widget_get_project (widget); parent = gtk_widget_get_toplevel (GTK_WIDGET (eprop)); pspec = glade_property_class_get_pspec (eprop->priv->klass); dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, _("C_lear"), GLADE_RESPONSE_CLEAR, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL); g_free (title); gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500); _glade_util_dialog_set_hig (GTK_DIALOG (dialog)); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_widget_show (vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), vbox, TRUE, TRUE, 0); /* Checklist */ label = gtk_label_new (_("Objects:")); gtk_widget_show (label); gtk_widget_set_halign (label, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_widget_set_size_request (sw, 400, 200); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); tree_view = glade_eprop_object_view (FALSE); /* Dont allow selecting the widget owning this property (perhaps this is wrong) */ exception_list = g_list_prepend (exception_list, widget); /* Build the list of already selected objects */ glade_property_get (eprop->priv->property, &selected_objects); for (l = selected_objects; l; l = l->next) selected_list = g_list_prepend (selected_list, glade_widget_get_from_gobject (l->data)); glade_eprop_object_populate_view (project, GTK_TREE_VIEW (tree_view), selected_list, exception_list, glade_param_spec_objects_get_type (GLADE_PARAM_SPEC_OBJECTS (pspec)), glade_property_class_parentless_widget (eprop->priv->klass)); g_list_free (selected_list); g_list_free (exception_list); gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); gtk_widget_show (tree_view); gtk_container_add (GTK_CONTAINER (sw), tree_view); /* Run the dialog */ res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_OK) { GValue *value; GList *selected = NULL, *l; gtk_tree_model_foreach (gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)), (GtkTreeModelForeachFunc) glade_eprop_objects_selected_widget, &selected); if (selected) { glade_command_push_group (_("Setting %s of %s"), glade_property_class_get_name (eprop->priv->klass), glade_widget_get_name (widget)); /* Make sure the selected widgets have names now */ for (l = selected; l; l = l->next) { GObject *object = l->data; GladeWidget *selected_widget = glade_widget_get_from_gobject (object); glade_widget_ensure_name (selected_widget, project, TRUE); } } value = glade_property_class_make_gvalue (eprop->priv->klass, selected); glade_editor_property_commit (eprop, value); if (selected) glade_command_pop_group (); g_value_unset (value); g_free (value); } else if (res == GLADE_RESPONSE_CLEAR) { GValue *value = glade_property_class_make_gvalue (eprop->priv->klass, NULL); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } gtk_widget_destroy (dialog); } static GtkWidget * glade_eprop_objects_create_input (GladeEditorProperty *eprop) { GladeEPropObjects *eprop_objects = GLADE_EPROP_OBJECTS (eprop); eprop_objects->entry = gtk_entry_new (); gtk_widget_set_hexpand (eprop_objects->entry, TRUE); gtk_widget_set_valign (eprop_objects->entry, GTK_ALIGN_CENTER); gtk_editable_set_editable (GTK_EDITABLE (eprop_objects->entry), FALSE); gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_objects->entry), GTK_ENTRY_ICON_SECONDARY, "gtk-edit"); g_signal_connect_swapped (eprop_objects->entry, "icon-release", G_CALLBACK (glade_eprop_objects_show_dialog), eprop); return eprop_objects->entry; } /******************************************************************************* API *******************************************************************************/ /** * glade_editor_property_commit: * @eprop: A #GladeEditorProperty * @value: The #GValue to commit * * Commits @value to the property currently being edited by @eprop. * */ void glade_editor_property_commit (GladeEditorProperty *eprop, GValue *value) { g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); g_return_if_fail (G_IS_VALUE (value)); g_signal_emit (G_OBJECT (eprop), glade_eprop_signals[COMMIT], 0, value); } /** * glade_editor_property_load: * @eprop: A #GladeEditorProperty * @property: A #GladeProperty * * Loads @property values into @eprop and connects. * (the editor property will watch the property's value * until its loaded with another property or %NULL) */ void glade_editor_property_load (GladeEditorProperty *eprop, GladeProperty *property) { g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); g_return_if_fail (property == NULL || GLADE_IS_PROPERTY (property)); eprop->priv->loading = TRUE; GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->load (eprop, property); eprop->priv->loading = FALSE; } /** * glade_editor_property_load_by_widget: * @eprop: A #GladeEditorProperty * @widget: A #GladeWidget * * Convenience function to load the appropriate #GladeProperty into * @eprop from @widget */ void glade_editor_property_load_by_widget (GladeEditorProperty *eprop, GladeWidget *widget) { GladeProperty *property = NULL; g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); g_return_if_fail (widget == NULL || GLADE_IS_WIDGET (widget)); if (widget) { /* properties are allowed to be missing on some internal widgets */ if (glade_property_class_get_is_packing (eprop->priv->klass)) property = glade_widget_get_pack_property (widget, glade_property_class_id (eprop->priv->klass)); else property = glade_widget_get_property (widget, glade_property_class_id (eprop->priv->klass)); glade_editor_property_load (eprop, property); if (eprop->priv->item_label) glade_property_label_set_property (GLADE_PROPERTY_LABEL (eprop->priv->item_label), property); if (property) { g_assert (eprop->priv->klass == glade_property_get_class (property)); gtk_widget_show (GTK_WIDGET (eprop)); if (eprop->priv->item_label) gtk_widget_show (eprop->priv->item_label); } else { gtk_widget_hide (GTK_WIDGET (eprop)); if (eprop->priv->item_label) gtk_widget_hide (eprop->priv->item_label); } } else glade_editor_property_load (eprop, NULL); }