/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * 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 #include #include #include #include #include #include "glade.h" #include "glade-widget.h" #include "glade-editor-property.h" #include "glade-property.h" #include "glade-command.h" #include "glade-project.h" #include "glade-builtins.h" #include "glade-marshallers.h" enum { PROP_0, PROP_PROPERTY_CLASS, PROP_USE_COMMAND, PROP_SHOW_INFO }; enum { GTK_DOC_SEARCH, LAST_SIGNAL }; static GtkTableClass *table_class; static GladeEditorPropertyClass *editor_property_class; static guint glade_editor_property_signals[LAST_SIGNAL] = { 0 }; #define GLADE_PROPERTY_TABLE_ROW_SPACING 2 #define FLAGS_COLUMN_SETTING 0 #define FLAGS_COLUMN_SYMBOL 1 /******************************************************************************* Boiler plate macros (inspired from glade-command.c) *******************************************************************************/ #define MAKE_TYPE(func, type, parent) \ GType \ func ## _get_type (void) \ { \ static GType cmd_type = 0; \ \ if (!cmd_type) \ { \ static const GTypeInfo info = \ { \ sizeof (type ## Class), \ (GBaseInitFunc) NULL, \ (GBaseFinalizeFunc) NULL, \ (GClassInitFunc) func ## _class_init, \ (GClassFinalizeFunc) NULL, \ NULL, \ sizeof (type), \ 0, \ (GInstanceInitFunc) NULL \ }; \ \ cmd_type = g_type_register_static (parent, #type, &info, 0); \ } \ \ return cmd_type; \ } \ #define GLADE_MAKE_EPROP(type, func) \ static void \ func ## _finalize (GObject *object); \ static void \ func ## _load (GladeEditorProperty *me, GladeProperty *property); \ static GtkWidget * \ func ## _create_input (GladeEditorProperty *me); \ static void \ func ## _class_init (gpointer parent_tmp, gpointer notused) \ { \ GladeEditorPropertyClass *parent = parent_tmp; \ GObjectClass* object_class; \ object_class = G_OBJECT_CLASS (parent); \ parent->load = func ## _load; \ parent->create_input = func ## _create_input; \ object_class->finalize = func ## _finalize; \ } \ typedef struct { \ GladeEditorPropertyClass cmd; \ } type ## Class; \ static MAKE_TYPE(func, type, GLADE_TYPE_EDITOR_PROPERTY) /******************************************************************************* GladeEditorPropertyClass *******************************************************************************/ /* declare this forwardly for the finalize routine */ static void glade_editor_property_load_common (GladeEditorProperty *eprop, GladeProperty *property); /* For use in editor implementations */ static void glade_editor_property_commit (GladeEditorProperty *eprop, GValue *value) { g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); if (eprop->use_command == FALSE) glade_property_set_value (eprop->property, value); else glade_command_set_property_value (eprop->property, value); /* If the value was denied by a verify function, we'll have to * reload the real value. */ if (g_param_values_cmp (eprop->property->klass->pspec, eprop->property->value, value) != 0) GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->load (eprop, eprop->property); } static void glade_editor_property_tooltip_cb (GladeProperty *property, const gchar *tooltip, GladeEditorProperty *eprop) { glade_util_widget_set_tooltip (eprop->input, tooltip); glade_util_widget_set_tooltip (eprop->eventbox, tooltip); } static void glade_eprop_label_style_update_cb (GtkWidget *label, GtkStyle *prev, GladeEditorProperty *eprop) { if (eprop->insensitive_colour) gdk_color_free (eprop->insensitive_colour); if (eprop->normal_colour) gdk_color_free (eprop->normal_colour); eprop->insensitive_colour = gdk_color_copy (&(label->style->fg[GTK_STATE_INSENSITIVE])); eprop->normal_colour = gdk_color_copy (&(label->style->fg[GTK_STATE_NORMAL])); } static void glade_editor_property_sensitivity_cb (GladeProperty *property, GParamSpec *pspec, GladeEditorProperty *eprop) { gboolean sensitive = glade_property_get_sensitive (eprop->property); g_signal_handlers_block_by_func (eprop->item_label, glade_eprop_label_style_update_cb, eprop); gtk_widget_modify_fg (GTK_WIDGET (eprop->item_label), GTK_STATE_NORMAL, sensitive ? eprop->normal_colour : eprop->insensitive_colour); g_signal_handlers_unblock_by_func (eprop->item_label, glade_eprop_label_style_update_cb, eprop); if (sensitive == FALSE) gtk_widget_set_sensitive (eprop->input, FALSE); else if (glade_property_get_enabled (property) != FALSE) gtk_widget_set_sensitive (eprop->input, TRUE); if (eprop->check) gtk_widget_set_sensitive (eprop->check, sensitive); } static void glade_editor_property_value_changed_cb (GladeProperty *property, GValue *old_value, GValue *value, GladeEditorProperty *eprop) { g_assert (eprop->property == property); glade_editor_property_load (eprop, eprop->property); } static void glade_editor_property_enabled_cb (GladeProperty *property, GParamSpec *pspec, GladeEditorProperty *eprop) { gboolean enabled; g_assert (eprop->property == property); if (eprop->klass->optional) { enabled = glade_property_get_enabled (property); /* sensitive = enabled && */ if (enabled == FALSE) gtk_widget_set_sensitive (eprop->input, FALSE); else if (glade_property_get_sensitive (property)) gtk_widget_set_sensitive (eprop->input, TRUE); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop->check), enabled); } } static void glade_editor_property_enabled_toggled_cb (GtkWidget *check, GladeEditorProperty *eprop) { glade_property_set_enabled (eprop->property, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check))); } static void glade_editor_property_info_clicked_cb (GtkWidget *info, GladeEditorProperty *eprop) { GladeWidgetAdaptor *adaptor; gchar *search, *book; adaptor = glade_widget_adaptor_from_pspec (eprop->klass->pspec); search = g_strdup_printf ("The %s property", eprop->klass->id); g_object_get (adaptor, "book", &book, NULL); g_signal_emit (G_OBJECT (eprop), glade_editor_property_signals[GTK_DOC_SEARCH], 0, book, g_type_name (eprop->klass->pspec->owner_type), search); g_free (book); g_free (search); } static GtkWidget * glade_editor_property_create_info_button (GladeEditorProperty *eprop) { GtkWidget *image; GtkWidget *button; button = gtk_button_new (); image = glade_util_get_devhelp_icon (GTK_ICON_SIZE_MENU); gtk_widget_show (image); gtk_container_add (GTK_CONTAINER (button), image); glade_util_widget_set_tooltip (button, _("View GTK+ documentation for this property")); return button; } static GObject * glade_editor_property_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *obj; GladeEditorProperty *eprop; gchar *text; /* Invoke parent constructor (eprop->klass should be resolved by this point) . */ obj = G_OBJECT_CLASS (table_class)->constructor (type, n_construct_properties, construct_properties); eprop = GLADE_EDITOR_PROPERTY (obj); /* Create label (make label visible but not the eventbox) */ text = g_strdup_printf ("%s:", eprop->klass->name); eprop->item_label = gtk_label_new (text); eprop->eventbox = gtk_event_box_new (); g_free (text); gtk_widget_show (eprop->item_label); /* Deal with label colours */ glade_eprop_label_style_update_cb (eprop->item_label, NULL, eprop); g_signal_connect (G_OBJECT (eprop->item_label), "style-set", G_CALLBACK (glade_eprop_label_style_update_cb), eprop); /* keep our own reference */ g_object_ref (G_OBJECT (eprop->eventbox)); gtk_misc_set_alignment (GTK_MISC (eprop->item_label), 1.0, 0.5); gtk_container_add (GTK_CONTAINER (eprop->eventbox), eprop->item_label); /* Create hbox and possibly check button */ if (eprop->klass->optional) { eprop->check = gtk_check_button_new (); gtk_widget_show (eprop->check); gtk_box_pack_start (GTK_BOX (eprop), eprop->check, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (eprop->check), "toggled", G_CALLBACK (glade_editor_property_enabled_toggled_cb), eprop); } /* Create the class specific input widget and add it */ eprop->input = GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->create_input (eprop); gtk_widget_show (eprop->input); gtk_box_pack_start (GTK_BOX (eprop), eprop->input, TRUE, TRUE, 0); /* Create the informational button and add it */ eprop->info = glade_editor_property_create_info_button (eprop); g_signal_connect (G_OBJECT (eprop->info), "clicked", G_CALLBACK (glade_editor_property_info_clicked_cb), eprop); gtk_box_pack_start (GTK_BOX (eprop), eprop->info, FALSE, FALSE, 2); return obj; } 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); if (eprop->insensitive_colour) gdk_color_free (eprop->insensitive_colour); if (eprop->normal_colour) gdk_color_free (eprop->normal_colour); G_OBJECT_CLASS (table_class)->finalize (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->klass = g_value_get_pointer (value); break; case PROP_USE_COMMAND: eprop->use_command = g_value_get_boolean (value); break; case PROP_SHOW_INFO: if (g_value_get_boolean (value)) glade_editor_property_show_info (eprop); else glade_editor_property_hide_info (eprop); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void glade_editor_property_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->klass); break; case PROP_USE_COMMAND: g_value_set_boolean (value, eprop->use_command); break; case PROP_SHOW_INFO: g_value_set_boolean (value, eprop->show_info); 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->tooltip_id = 0; eprop->sensitive_id = 0; eprop->changed_id = 0; eprop->enabled_id = 0; eprop->property = NULL; glade_editor_property_load (eprop, NULL); } static void glade_editor_property_load_common (GladeEditorProperty *eprop, GladeProperty *property) { /* Hide properties that are removed for some particular widgets. */ if (property) { gtk_widget_show (GTK_WIDGET (eprop)); gtk_widget_show (eprop->eventbox); } else { gtk_widget_hide (GTK_WIDGET (eprop)); gtk_widget_hide (eprop->eventbox); } /* disconnect anything from previously loaded property */ if (eprop->property != property && eprop->property != NULL) { if (eprop->tooltip_id > 0) g_signal_handler_disconnect (eprop->property, eprop->tooltip_id); if (eprop->sensitive_id > 0) g_signal_handler_disconnect (eprop->property, eprop->sensitive_id); if (eprop->changed_id > 0) g_signal_handler_disconnect (eprop->property, eprop->changed_id); if (eprop->enabled_id > 0) g_signal_handler_disconnect (eprop->property, eprop->enabled_id); eprop->tooltip_id = 0; eprop->sensitive_id = 0; eprop->changed_id = 0; eprop->enabled_id = 0; /* Unref it here */ g_object_weak_unref (G_OBJECT (eprop->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->property = NULL; } } /* Connect new stuff, deal with tooltip */ if (eprop->property != property && property != NULL) { eprop->property = property; eprop->tooltip_id = g_signal_connect (G_OBJECT (eprop->property), "tooltip-changed", G_CALLBACK (glade_editor_property_tooltip_cb), eprop); eprop->sensitive_id = g_signal_connect (G_OBJECT (eprop->property), "notify::sensitive", G_CALLBACK (glade_editor_property_sensitivity_cb), eprop); eprop->changed_id = g_signal_connect (G_OBJECT (eprop->property), "value-changed", G_CALLBACK (glade_editor_property_value_changed_cb), eprop); eprop->enabled_id = g_signal_connect (G_OBJECT (eprop->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->property), (GWeakNotify)glade_eprop_property_finalized, eprop); /* Load initial tooltips */ glade_editor_property_tooltip_cb (property, glade_property_get_tooltip (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) { } 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->constructor = glade_editor_property_constructor; object_class->finalize = glade_editor_property_finalize; object_class->get_property = glade_editor_property_get_property; object_class->set_property = glade_editor_property_set_property; /* Class methods */ eprop_class->load = glade_editor_property_load_common; eprop_class->create_input = NULL; /** * GladeEditorProperty::gtk-doc-search: * @gladeeditor: the #GladeEditorProperty which received the signal. * @arg1: the (#gchar *) book to search or %NULL * @arg2: the (#gchar *) page to search or %NULL * @arg3: the (#gchar *) search string or %NULL * * Emitted when the editor property requests that a doc-search be performed. */ glade_editor_property_signals[GTK_DOC_SEARCH] = g_signal_new ("gtk-doc-search", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeEditorPropertyClass, gtk_doc_search), NULL, NULL, glade_marshal_VOID__STRING_STRING_STRING, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); /* 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_SHOW_INFO, g_param_spec_boolean ("show-info", _("Show Info"), _("Whether we should show an informational button"), FALSE, G_PARAM_READWRITE)); } GType glade_editor_property_get_type (void) { static GType property_type = 0; if (!property_type) { static const GTypeInfo property_info = { sizeof (GladeEditorPropertyClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) glade_editor_property_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ sizeof (GladeEditorProperty), 0, /* n_preallocs */ (GInstanceInitFunc) glade_editor_property_init, }; property_type = g_type_register_static (GTK_TYPE_HBOX, "GladeEditorProperty", &property_info, 0); } return property_type; } /******************************************************************************* GladeEditorPropertyNumericClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *spin; } GladeEPropNumeric; GLADE_MAKE_EPROP (GladeEPropNumeric, glade_eprop_numeric) #define GLADE_TYPE_EPROP_NUMERIC (glade_eprop_numeric_get_type()) #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) { gfloat val = 0.0F; GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop); /* Chain up first */ editor_property_class->load (eprop, property); if (property) { if (G_IS_PARAM_SPEC_INT(eprop->klass->pspec)) val = (gfloat) g_value_get_int (property->value); else if (G_IS_PARAM_SPEC_UINT(eprop->klass->pspec)) val = (gfloat) g_value_get_uint (property->value); else if (G_IS_PARAM_SPEC_LONG(eprop->klass->pspec)) val = (gfloat) g_value_get_long (property->value); else if (G_IS_PARAM_SPEC_ULONG(eprop->klass->pspec)) val = (gfloat) g_value_get_ulong (property->value); else if (G_IS_PARAM_SPEC_INT64(eprop->klass->pspec)) val = (gfloat) g_value_get_int64 (property->value); else if (G_IS_PARAM_SPEC_UINT64(eprop->klass->pspec)) val = (gfloat) g_value_get_uint64 (property->value); else if (G_IS_PARAM_SPEC_DOUBLE(eprop->klass->pspec)) val = (gfloat) g_value_get_double (property->value); else if (G_IS_PARAM_SPEC_FLOAT(eprop->klass->pspec)) val = g_value_get_float (property->value); else g_warning ("Unsupported type %s\n", g_type_name(G_PARAM_SPEC_TYPE (eprop->klass->pspec))); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_numeric->spin), val); } } static void glade_eprop_numeric_changed (GtkWidget *spin, GladeEditorProperty *eprop) { GValue val = { 0, }; if (eprop->loading) return; g_value_init (&val, eprop->klass->pspec->value_type); if (G_IS_PARAM_SPEC_INT(eprop->klass->pspec)) g_value_set_int (&val, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin))); else if (G_IS_PARAM_SPEC_UINT(eprop->klass->pspec)) g_value_set_uint (&val, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin))); else if (G_IS_PARAM_SPEC_LONG(eprop->klass->pspec)) g_value_set_long (&val, (glong)gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin))); else if (G_IS_PARAM_SPEC_ULONG(eprop->klass->pspec)) g_value_set_ulong (&val, (gulong)gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin))); else if (G_IS_PARAM_SPEC_INT64(eprop->klass->pspec)) g_value_set_int64 (&val, (gint64)gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin))); else if (G_IS_PARAM_SPEC_UINT64(eprop->klass->pspec)) g_value_set_uint64 (&val, (guint64)gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin))); else if (G_IS_PARAM_SPEC_FLOAT(eprop->klass->pspec)) g_value_set_float (&val, (gfloat) gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin))); else if (G_IS_PARAM_SPEC_DOUBLE(eprop->klass->pspec)) g_value_set_double (&val, gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin))); else g_warning ("Unsupported type %s\n", g_type_name(G_PARAM_SPEC_TYPE (eprop->klass->pspec))); glade_editor_property_commit (eprop, &val); g_value_unset (&val); } static GtkWidget * glade_eprop_numeric_create_input (GladeEditorProperty *eprop) { GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop); GtkAdjustment *adjustment; adjustment = glade_property_class_make_adjustment (eprop->klass); eprop_numeric->spin = gtk_spin_button_new (adjustment, 4, G_IS_PARAM_SPEC_FLOAT (eprop->klass->pspec) || G_IS_PARAM_SPEC_DOUBLE (eprop->klass->pspec) ? 2 : 0); gtk_widget_show (eprop_numeric->spin); 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 *option_menu; } GladeEPropEnum; GLADE_MAKE_EPROP (GladeEPropEnum, glade_eprop_enum) #define GLADE_TYPE_EPROP_ENUM (glade_eprop_enum_get_type()) #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); GEnumClass *eclass; guint i; gint value; /* Chain up first */ editor_property_class->load (eprop, property); if (property) { eclass = g_type_class_ref (eprop->klass->pspec->value_type); value = g_value_get_enum (property->value); for (i = 0; i < eclass->n_values; i++) if (eclass->values[i].value == value) break; gtk_option_menu_set_history (GTK_OPTION_MENU (eprop_enum->option_menu), i < eclass->n_values ? i : 0); g_type_class_unref (eclass); } } static void glade_eprop_enum_changed (GtkWidget *menu_item, GladeEditorProperty *eprop) { gint ival; GValue val = { 0, }; GladeProperty *property; if (eprop->loading) return; ival = GPOINTER_TO_INT(g_object_get_data (G_OBJECT (menu_item), GLADE_ENUM_DATA_TAG)); property = eprop->property; g_value_init (&val, eprop->klass->pspec->value_type); g_value_set_enum (&val, ival); glade_editor_property_commit (eprop, &val); g_value_unset (&val); } static GtkWidget * glade_editor_create_input_enum_item (GladeEditorProperty *eprop, const gchar *name, gint value) { GtkWidget *menu_item; menu_item = gtk_menu_item_new_with_label (name); g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (glade_eprop_enum_changed), eprop); g_object_set_data (G_OBJECT (menu_item), GLADE_ENUM_DATA_TAG, GINT_TO_POINTER(value)); return menu_item; } static GtkWidget * glade_editor_create_input_stock_item (GladeEditorProperty *eprop, const gchar *id, gint value) { GtkWidget *menu_item = gtk_image_menu_item_new_from_stock (id, NULL); g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (glade_eprop_enum_changed), eprop); g_object_set_data (G_OBJECT (menu_item), GLADE_ENUM_DATA_TAG, GINT_TO_POINTER(value)); return menu_item; } static GtkWidget * glade_eprop_enum_create_input (GladeEditorProperty *eprop) { GladeEPropEnum *eprop_enum = GLADE_EPROP_ENUM (eprop); GtkWidget *menu_item, *menu; GladePropertyClass *klass; GEnumClass *eclass; gboolean stock; guint i; klass = eprop->klass; eclass = g_type_class_ref (klass->pspec->value_type); stock = (klass->pspec->value_type == GLADE_TYPE_STOCK) || (klass->pspec->value_type == GLADE_TYPE_STOCK_IMAGE); menu = gtk_menu_new (); for (i = 0; i < eclass->n_values; i++) { const gchar *value_name = glade_property_class_get_displayable_value (klass, eclass->values[i].value); if (value_name == NULL) value_name = eclass->values[i].value_name; if (stock && strcmp (eclass->values[i].value_nick, "glade-none")) menu_item = glade_editor_create_input_stock_item (eprop, eclass->values[i].value_nick, eclass->values[i].value); else menu_item = glade_editor_create_input_enum_item (eprop, value_name, eclass->values[i].value); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); } eprop_enum->option_menu = gtk_option_menu_new (); gtk_option_menu_set_menu (GTK_OPTION_MENU (eprop_enum->option_menu), menu); gtk_widget_show_all (eprop_enum->option_menu); g_type_class_unref (eclass); return eprop_enum->option_menu; } /******************************************************************************* GladeEditorPropertyFlagsClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkTreeModel *model; GtkWidget *entry; } GladeEPropFlags; GLADE_MAKE_EPROP (GladeEPropFlags, glade_eprop_flags) #define GLADE_TYPE_EPROP_FLAGS (glade_eprop_flags_get_type()) #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; 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 (property->value)); value = g_value_get_flags (property->value); /* 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; mask = klass->values[flag_num].value; setting = ((value & mask) == mask) ? TRUE : FALSE; value_name = glade_property_class_get_displayable_value (eprop->klass, klass->values[flag_num].value); 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, -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; guint value = 0; gint flag_num = 0; GFlagsClass *klass; GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS(eprop); if (!eprop->property) return ; klass = g_type_class_ref (G_VALUE_TYPE (eprop->property->value)); value = g_value_get_flags (eprop->property->value); 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); 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. */ for (flag_num = 0; flag_num < klass->n_values; flag_num++) { gboolean setting; gtk_tree_model_get (GTK_TREE_MODEL (eprop_flags->model), &iter, FLAGS_COLUMN_SETTING, &setting, -1); if (setting) new_value |= klass->values[flag_num].value; 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 != value) { GValue val = { 0, }; g_value_init (&val, G_VALUE_TYPE (eprop->property->value)); g_value_set_flags (&val, new_value); glade_editor_property_commit (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); if (!eprop_flags->model) eprop_flags->model = GTK_TREE_MODEL(gtk_list_store_new (2, G_TYPE_BOOLEAN, G_TYPE_STRING)); 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 (GtkWidget *button, GladeEditorProperty *eprop) { GtkWidget *dialog; GtkWidget *view; GtkWidget *label; GtkWidget *vbox; dialog = gtk_dialog_new_with_buttons (_("Select Fields"), GTK_WINDOW (gtk_widget_get_toplevel (button)), GTK_DIALOG_MODAL, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); /* HIG spacings */ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2); /* 2 * 5 + 2 = 12 */ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6); vbox = gtk_vbox_new (FALSE, 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_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); 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 (GTK_DIALOG (dialog)->vbox), 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) { GtkWidget *vbox, *hbox, *button, *widget; GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS(eprop); hbox = gtk_hbox_new (FALSE, 0); vbox = gtk_vbox_new (FALSE, 0); widget = glade_eprop_flags_create_treeview (eprop); eprop_flags->entry = gtk_entry_new (); gtk_entry_set_editable (GTK_ENTRY (eprop_flags->entry), FALSE); gtk_box_pack_start (GTK_BOX (vbox), eprop_flags->entry, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); button = gtk_button_new_with_label ("..."); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); gtk_widget_show_all (hbox); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_eprop_flags_show_dialog), eprop); return hbox; } /******************************************************************************* GladeEditorPropertyColorClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *cbutton; GtkWidget *entry; } GladeEPropColor; GLADE_MAKE_EPROP (GladeEPropColor, glade_eprop_color) #define GLADE_TYPE_EPROP_COLOR (glade_eprop_color_get_type()) #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); GdkColor *color; gchar *text; /* Chain up first */ editor_property_class->load (eprop, property); if (property) { if ((text = glade_property_class_make_string_from_gvalue (eprop->klass, property->value)) != 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 ((color = g_value_get_boxed (property->value)) != NULL) gtk_color_button_set_color (GTK_COLOR_BUTTON (eprop_color->cbutton), color); else { GdkColor black = { 0, }; /* Manually fill it with black for an NULL value. */ if (gdk_color_parse("Black", &black) && gdk_colormap_alloc_color(gtk_widget_get_default_colormap(), &black, FALSE, TRUE)) gtk_color_button_set_color (GTK_COLOR_BUTTON (eprop_color->cbutton), &black); } } } static void glade_eprop_color_changed (GtkWidget *button, GladeEditorProperty *eprop) { GdkColor color = { 0, }; GValue value = { 0, }; if (eprop->loading) return; gtk_color_button_get_color (GTK_COLOR_BUTTON (button), &color); g_value_init (&value, GDK_TYPE_COLOR); g_value_set_boxed (&value, &color); 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; hbox = gtk_hbox_new (FALSE, 0); eprop_color->entry = gtk_entry_new (); gtk_entry_set_editable (GTK_ENTRY (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); g_signal_connect (G_OBJECT (eprop_color->cbutton), "color-set", G_CALLBACK (glade_eprop_color_changed), eprop); return hbox; } /******************************************************************************* GladeEditorPropertyTextClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *text_entry; } GladeEPropText; GLADE_MAKE_EPROP (GladeEPropText, glade_eprop_text) #define GLADE_TYPE_EPROP_TEXT (glade_eprop_text_get_type()) #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 void glade_eprop_text_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop); /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; if (GTK_IS_ENTRY (eprop_text->text_entry)) { GtkEntry *entry = GTK_ENTRY (eprop_text->text_entry); const gchar *text = g_value_get_string (property->value); gtk_entry_set_text (entry, text ? text : ""); } else if (GTK_IS_TEXT_VIEW (eprop_text->text_entry)) { GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop_text->text_entry)); if (G_VALUE_HOLDS (property->value, G_TYPE_STRV) || G_VALUE_HOLDS (property->value, G_TYPE_VALUE_ARRAY)) { gchar *text = glade_property_class_make_string_from_gvalue ( property->klass, property->value); gtk_text_buffer_set_text (buffer, text ? text : "", -1); g_free (text); } else { const gchar *text = g_value_get_string (property->value); gtk_text_buffer_set_text (buffer, text ? text : "", -1); } } 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; gchar *prop_text; if (eprop->property->klass->pspec->value_type == G_TYPE_STRV || eprop->property->klass->pspec->value_type == G_TYPE_VALUE_ARRAY) { val = glade_property_class_make_gvalue_from_string (eprop->property->klass, text, NULL); } else { val = g_new0 (GValue, 1); g_value_init (val, G_TYPE_STRING); glade_property_get (eprop->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_text_changed (GtkWidget *entry, GladeEditorProperty *eprop) { gchar *text; if (eprop->loading) return; text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); glade_eprop_text_changed_common (eprop, text, eprop->use_command); g_free (text); } static gboolean glade_eprop_text_entry_focus_out (GtkWidget *entry, GdkEventFocus *event, GladeEditorProperty *eprop) { glade_eprop_text_changed (entry, eprop); return FALSE; } static gboolean glade_eprop_text_text_view_focus_out (GtkTextView *view, GdkEventFocus *event, GladeEditorProperty *eprop) { gchar *text; GtkTextBuffer *buffer; GtkTextIter start, end; if (eprop->loading) return FALSE; buffer = gtk_text_view_get_buffer (view); gtk_text_buffer_get_start_iter (buffer, &start); gtk_text_buffer_get_end_iter (buffer, &end); text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); glade_eprop_text_changed_common (eprop, text, eprop->use_command); g_free (text); return FALSE; } static void glade_eprop_text_show_i18n_dialog (GtkWidget *entry, GladeEditorProperty *eprop) { GtkWidget *dialog; GtkWidget *vbox, *hbox; GtkWidget *label; GtkWidget *sw; GtkWidget *alignment; GtkWidget *text_view, *comment_view; GtkTextBuffer *text_buffer, *comment_buffer; GtkWidget *translatable_button, *context_button; const gchar *text; gint res; gchar *str; GParamSpec *pspec; dialog = gtk_dialog_new_with_buttons (_("Edit Text"), GTK_WINDOW (gtk_widget_get_toplevel (entry)), GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); 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); /* HIG spacings */ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2); /* 2 * 5 + 2 = 12 */ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6); vbox = gtk_vbox_new (FALSE, 6); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); gtk_widget_show (vbox); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0); /* Text */ label = gtk_label_new_with_mnemonic (_("_Text:")); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); 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_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)); text = g_value_get_string (eprop->property->value); if (text) { gtk_text_buffer_set_text (text_buffer, text, -1); } /* Translatable and context prefix. */ hbox = gtk_hbox_new (FALSE, 12); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 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), glade_property_i18n_get_translatable (eprop->property)); /* Add a cute tooltip */ if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (eprop->property), "i18n-translatable")) != NULL) glade_util_widget_set_tooltip (translatable_button, g_param_spec_get_blurb (pspec)); context_button = gtk_check_button_new_with_mnemonic (_("_Has context prefix")); gtk_widget_show (context_button); gtk_box_pack_start (GTK_BOX (hbox), context_button, FALSE, FALSE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (context_button), glade_property_i18n_get_has_context (eprop->property)); /* Add a cute tooltip */ if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (eprop->property), "i18n-has-context")) != NULL) glade_util_widget_set_tooltip (context_button, g_param_spec_get_blurb (pspec)); 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); /* Comments. */ label = gtk_label_new_with_mnemonic (_("Co_mments for translators:")); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); 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_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)); text = glade_property_i18n_get_comment (eprop->property); if (text) { gtk_text_buffer_set_text (comment_buffer, text, -1); } res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_OK) { GtkTextIter start, end; gboolean translatable, has_context; /* get the new values for translatable, has_context, and comment */ translatable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (translatable_button)); has_context = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (context_button)); gtk_text_buffer_get_bounds (comment_buffer, &start, &end); str = gtk_text_buffer_get_text (comment_buffer, &start, &end, TRUE); if (str[0] == '\0') { g_free (str); str = NULL; } /* set the new i18n data via a glade command so it can be undone */ glade_command_set_i18n (eprop->property, translatable, has_context, str); g_free (str); /* Text */ gtk_text_buffer_get_bounds (text_buffer, &start, &end); str = gtk_text_buffer_get_text (text_buffer, &start, &end, TRUE); if (str[0] == '\0') { g_free (str); str = NULL; } /* set the new text */ glade_eprop_text_changed_common (eprop, str, eprop->use_command); g_free (str); } gtk_widget_destroy (dialog); } static GtkWidget * glade_eprop_text_create_input (GladeEditorProperty *eprop) { GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop); GladePropertyClass *klass; GtkWidget *hbox; klass = eprop->klass; hbox = gtk_hbox_new (FALSE, 0); if (klass->visible_lines > 1 || klass->pspec->value_type == G_TYPE_STRV || klass->pspec->value_type == G_TYPE_VALUE_ARRAY) { GtkWidget *swindow; swindow = gtk_scrolled_window_new (NULL, NULL); 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); eprop_text->text_entry = gtk_text_view_new (); 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); g_signal_connect (G_OBJECT (eprop_text->text_entry), "focus-out-event", G_CALLBACK (glade_eprop_text_text_view_focus_out), eprop); } else { eprop_text->text_entry = gtk_entry_new (); 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), "activate", G_CALLBACK (glade_eprop_text_changed), eprop); g_signal_connect (G_OBJECT (eprop_text->text_entry), "focus-out-event", G_CALLBACK (glade_eprop_text_entry_focus_out), eprop); } if (klass->translatable) { GtkWidget *button = gtk_button_new_with_label ("..."); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (button, "clicked", G_CALLBACK (glade_eprop_text_show_i18n_dialog), eprop); } return hbox; } /******************************************************************************* GladeEditorPropertyBoolClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *toggle; } GladeEPropBool; GLADE_MAKE_EPROP (GladeEPropBool, glade_eprop_bool) #define GLADE_TYPE_EPROP_BOOL (glade_eprop_bool_get_type()) #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) { GladeEPropBool *eprop_bool = GLADE_EPROP_BOOL (eprop); GtkWidget *label; gboolean state; /* Chain up first */ editor_property_class->load (eprop, property); if (property) { state = g_value_get_boolean (property->value); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop_bool->toggle), state); label = GTK_BIN (eprop_bool->toggle)->child; gtk_label_set_text (GTK_LABEL (label), state ? _("Yes") : _("No")); } } static void glade_eprop_bool_changed (GtkWidget *button, GladeEditorProperty *eprop) { GtkWidget *label; gboolean state; GValue val = { 0, }; if (eprop->loading) return; state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); label = GTK_BIN (button)->child; gtk_label_set_text (GTK_LABEL (label), state ? _("Yes") : _("No")); g_value_init (&val, G_TYPE_BOOLEAN); g_value_set_boolean (&val, state); glade_editor_property_commit (eprop, &val); g_value_unset (&val); } static GtkWidget * glade_eprop_bool_create_input (GladeEditorProperty *eprop) { GladeEPropBool *eprop_bool = GLADE_EPROP_BOOL (eprop); eprop_bool->toggle = gtk_toggle_button_new_with_label (_("No")); g_signal_connect (G_OBJECT (eprop_bool->toggle), "toggled", G_CALLBACK (glade_eprop_bool_changed), eprop); return eprop_bool->toggle; } /******************************************************************************* GladeEditorPropertyUnicharClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; } GladeEPropUnichar; GLADE_MAKE_EPROP (GladeEPropUnichar, glade_eprop_unichar) #define GLADE_TYPE_EPROP_UNICHAR (glade_eprop_unichar_get_type()) #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 (property->value), 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->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 (eprop, &val); g_value_unset (&val); } } static void glade_eprop_unichar_delete (GtkEditable *editable, gint start_pos, gint end_pos, GladeEditorProperty *eprop) { if (eprop->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->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 (); /* 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; } /******************************************************************************* GladeEditorPropertyResourceClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry, *button; } GladeEPropResource; GLADE_MAKE_EPROP (GladeEPropResource, glade_eprop_resource) #define GLADE_TYPE_EPROP_RESOURCE (glade_eprop_resource_get_type()) #define GLADE_EPROP_RESOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_RESOURCE, GladeEPropResource)) #define GLADE_EPROP_RESOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_RESOURCE, GladeEPropResourceClass)) #define GLADE_IS_EPROP_RESOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_RESOURCE)) #define GLADE_IS_EPROP_RESOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_RESOURCE)) #define GLADE_EPROP_RESOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_RESOURCE, GladeEPropResourceClass)) static void glade_eprop_resource_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_resource_entry_activate (GtkEntry *entry, GladeEditorProperty *eprop) { GladeProject *project = glade_widget_get_project (eprop->property->widget); GValue *value = glade_property_class_make_gvalue_from_string (eprop->klass, gtk_entry_get_text(entry), project); /* Set project resource here where we still have the fullpath. */ glade_project_set_resource (project, eprop->property, gtk_entry_get_text(entry)); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } static gboolean glade_eprop_resource_entry_focus_out (GtkWidget *entry, GdkEventFocus *event, GladeEditorProperty *eprop) { glade_eprop_resource_entry_activate (GTK_ENTRY (entry), eprop); return FALSE; } static void glade_eprop_resource_select_file (GtkButton *button, GladeEditorProperty *eprop) { GladeProject *project = glade_widget_get_project (eprop->property->widget); GtkWidget *dialog; GtkFileFilter *filter; GValue *value; gchar *file, *basename; if (eprop->loading) return; dialog = gtk_file_chooser_dialog_new ("Select a File", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); if (eprop->klass->pspec->value_type == GDK_TYPE_PIXBUF) { filter = gtk_file_filter_new (); gtk_file_filter_add_pixbuf_formats (filter); gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter); } if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); /* Set project resource here where we still have the fullpath. */ glade_project_set_resource (project, eprop->property, file); basename = g_path_get_basename (file); value = glade_property_class_make_gvalue_from_string (eprop->klass, basename, project); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); g_free (file); g_free (basename); } gtk_widget_destroy (dialog); } static void glade_eprop_resource_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropResource *eprop_resource = GLADE_EPROP_RESOURCE (eprop); gchar *file; /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; file = glade_property_class_make_string_from_gvalue (eprop->klass, property->value); if (file) { gtk_entry_set_text (GTK_ENTRY (eprop_resource->entry), file); g_free (file); } else { gtk_entry_set_text (GTK_ENTRY (eprop_resource->entry), ""); } } static GtkWidget * glade_eprop_resource_create_input (GladeEditorProperty *eprop) { GladeEPropResource *eprop_resource = GLADE_EPROP_RESOURCE (eprop); GtkWidget *hbox; hbox = gtk_hbox_new (FALSE, 0); eprop_resource->entry = gtk_entry_new (); gtk_widget_show (eprop_resource->entry); eprop_resource->button = gtk_button_new_with_label ("..."); gtk_widget_show (eprop_resource->button); gtk_box_pack_start (GTK_BOX (hbox), eprop_resource->entry, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), eprop_resource->button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (eprop_resource->entry), "activate", G_CALLBACK (glade_eprop_resource_entry_activate), eprop); g_signal_connect (G_OBJECT (eprop_resource->entry), "focus-out-event", G_CALLBACK (glade_eprop_resource_entry_focus_out), eprop); g_signal_connect (G_OBJECT (eprop_resource->button), "clicked", G_CALLBACK (glade_eprop_resource_select_file), eprop); return hbox; } /******************************************************************************* 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 typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; } GladeEPropObject; GLADE_MAKE_EPROP (GladeEPropObject, glade_eprop_object) #define GLADE_TYPE_EPROP_OBJECT (glade_eprop_object_get_type()) #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 glade_eprop_object_is_selected (GladeEditorProperty *eprop, GladeWidget *widget) { GList *list; if (GLADE_IS_PARAM_SPEC_OBJECTS (eprop->klass->pspec)) { glade_property_get (eprop->property, &list); return g_list_find (list, widget->object) != NULL; } return glade_property_equals (eprop->property, widget->object); } /* * 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 (GladeEditorProperty *eprop, GtkTreeStore *model, GList *widgets, GtkTreeIter *parent_iter) { GList *children, *list; GtkTreeIter iter; gboolean good_type, has_decendant; for (list = widgets; list; list = list->next) { GladeWidget *widget; if ((widget = glade_widget_get_from_gobject (list->data)) != NULL) { if (GLADE_IS_PARAM_SPEC_OBJECTS (eprop->klass->pspec)) { has_decendant = glade_widget_has_decendant (widget, glade_param_spec_objects_get_type (GLADE_PARAM_SPEC_OBJECTS(eprop->klass->pspec))); good_type = glade_util_class_implements_interface (widget->adaptor->type, glade_param_spec_objects_get_type (GLADE_PARAM_SPEC_OBJECTS(eprop->klass->pspec))); } else { has_decendant = glade_widget_has_decendant (widget, eprop->klass->pspec->value_type); good_type = g_type_is_a (widget->adaptor->type, eprop->klass->pspec->value_type); } 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, widget->adaptor->title, /* Selectable if its a compatible type and * its not itself. */ OBJ_COLUMN_SELECTABLE, good_type && (widget != eprop->property->widget), OBJ_COLUMN_SELECTED, good_type && glade_eprop_object_is_selected (eprop, widget), -1); } if (has_decendant && (children = glade_widget_adaptor_get_children (widget->adaptor, widget->object)) != NULL) { GtkTreeIter *copy = NULL; copy = gtk_tree_iter_copy (&iter); glade_eprop_object_populate_view_real (eprop, model, children, copy); gtk_tree_iter_free (copy); g_list_free (children); } } } } static void glade_eprop_object_populate_view (GladeEditorProperty *eprop, GtkTreeView *view) { GtkTreeStore *model = (GtkTreeStore *)gtk_tree_view_get_model (view); GladeProject *project = glade_app_get_project (); 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 (gwidget->parent == NULL) toplevels = g_list_append (toplevels, object); } /* add the widgets and recurse */ glade_eprop_object_populate_view_real (eprop, model, toplevels, NULL); 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 (GladeEditorProperty *eprop, 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); /* 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) { GladeWidgetAdaptor *adaptor; const gchar *format = GLADE_IS_PARAM_SPEC_OBJECTS (eprop->klass->pspec) ? _("Choose %s implementors") : _("Choose a %s in this project"); if (GLADE_IS_PARAM_SPEC_OBJECTS (eprop->klass->pspec)) return g_strdup_printf (format, g_type_name (glade_param_spec_objects_get_type (GLADE_PARAM_SPEC_OBJECTS (eprop->klass->pspec)))); else if ((adaptor = glade_widget_adaptor_get_by_type (eprop->klass->pspec->value_type)) != NULL) return g_strdup_printf (format, adaptor->title); /* Fallback on type name (which would look like "GtkButton" * instead of "Button" and maybe not translated). */ return g_strdup_printf (format, g_type_name (eprop->klass->pspec->value_type)); } static void glade_eprop_object_show_dialog (GtkWidget *dialog_button, GladeEditorProperty *eprop) { GtkWidget *dialog, *parent; GtkWidget *vbox, *label, *sw; GtkWidget *tree_view; GladeProject *project; gchar *title = glade_eprop_object_dialog_title (eprop); gint res; project = glade_widget_get_project (eprop->property->widget); parent = gtk_widget_get_toplevel (GTK_WIDGET (eprop)); dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), GTK_DIALOG_MODAL, GTK_STOCK_CLEAR, GLADE_RESPONSE_CLEAR, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_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_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); /* HIG settings */ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2); /* 2 * 5 + 2 = 12 */ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6); vbox = gtk_vbox_new (FALSE, 6); gtk_widget_show (vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0); /* Checklist */ label = gtk_label_new_with_mnemonic (_("O_bjects:")); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); 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 (eprop, TRUE); glade_eprop_object_populate_view (eprop, GTK_TREE_VIEW (tree_view)); 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 = glade_property_class_make_gvalue_from_string (eprop->klass, selected->name, project); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } } else if (res == GLADE_RESPONSE_CLEAR) { GValue *value = glade_property_class_make_gvalue_from_string (eprop->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_property_class_make_string_from_gvalue (eprop->klass, property->value)) != 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); GtkWidget *hbox; GtkWidget *button; hbox = gtk_hbox_new (FALSE, 0); eprop_object->entry = gtk_entry_new (); gtk_entry_set_editable (GTK_ENTRY (eprop_object->entry), FALSE); gtk_widget_show (eprop_object->entry); gtk_box_pack_start (GTK_BOX (hbox), eprop_object->entry, TRUE, TRUE, 0); button = gtk_button_new_with_label ("..."); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_eprop_object_show_dialog), eprop); return hbox; } /******************************************************************************* GladeEditorPropertyObjectsClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; } GladeEPropObjects; GLADE_MAKE_EPROP (GladeEPropObjects, glade_eprop_objects) #define GLADE_TYPE_EPROP_OBJECTS (glade_eprop_objects_get_type()) #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_property_class_make_string_from_gvalue (eprop->klass, property->value)) != 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, widget->object); return FALSE; } static void glade_eprop_objects_show_dialog (GtkWidget *dialog_button, GladeEditorProperty *eprop) { GtkWidget *dialog, *parent; GtkWidget *vbox, *label, *sw; GtkWidget *tree_view; GladeProject *project; gchar *title = glade_eprop_object_dialog_title (eprop); gint res; project = glade_widget_get_project (eprop->property->widget); parent = gtk_widget_get_toplevel (GTK_WIDGET (eprop)); dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLEAR, GLADE_RESPONSE_CLEAR, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); g_free (title); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); vbox = gtk_vbox_new (FALSE, 6); gtk_widget_show (vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0); /* Checklist */ label = gtk_label_new (_("Objects:")); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); 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 (eprop, FALSE); glade_eprop_object_populate_view (eprop, GTK_TREE_VIEW (tree_view)); 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; gtk_tree_model_foreach (gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)), (GtkTreeModelForeachFunc) glade_eprop_objects_selected_widget, &selected); value = glade_property_class_make_gvalue (eprop->klass, selected); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } else if (res == GLADE_RESPONSE_CLEAR) { GValue *value = glade_property_class_make_gvalue (eprop->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); GtkWidget *hbox; GtkWidget *button; hbox = gtk_hbox_new (FALSE, 0); eprop_objects->entry = gtk_entry_new (); gtk_entry_set_editable (GTK_ENTRY (eprop_objects->entry), FALSE); gtk_widget_show (eprop_objects->entry); gtk_box_pack_start (GTK_BOX (hbox), eprop_objects->entry, TRUE, TRUE, 0); button = gtk_button_new_with_label ("..."); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_eprop_objects_show_dialog), eprop); return hbox; } /******************************************************************************* GladeEditorPropertyAdjustmentClass *******************************************************************************/ typedef struct { GladeEditorProperty parent_instance; GtkWidget *value, *lower, *upper, *step_increment, *page_increment, *page_size; GtkAdjustment *value_adj; struct { gulong value, lower, upper, step_increment, page_increment, page_size; }ids; } GladeEPropAdjustment; GLADE_MAKE_EPROP (GladeEPropAdjustment, glade_eprop_adjustment) #define GLADE_TYPE_EPROP_ADJUSTMENT (glade_eprop_adjustment_get_type()) #define GLADE_EPROP_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_ADJUSTMENT, GladeEPropAdjustment)) #define GLADE_EPROP_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_ADJUSTMENT, GladeEPropAdjustmentClass)) #define GLADE_IS_EPROP_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_ADJUSTMENT)) #define GLADE_IS_EPROP_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_ADJUSTMENT)) #define GLADE_EPROP_ADJUSTMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_ADJUSTMENT, GladeEPropAdjustmentClass)) static void glade_eprop_adjustment_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } typedef struct _EPropAdjIdle EPropAdjIdleData; struct _EPropAdjIdle { GladeEditorProperty *eprop; gdouble value; }; static gboolean glade_eprop_adj_set_value_idle (gpointer p) { EPropAdjIdleData *data = (EPropAdjIdleData *) p; GladeEPropAdjustment *eprop_adj = GLADE_EPROP_ADJUSTMENT (data->eprop); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_adj->value), data->value); g_free (p); return FALSE; } static void glade_eprop_adj_value_changed (GtkAdjustment *adj, GladeEditorProperty *eprop) { EPropAdjIdleData *data; g_signal_handlers_disconnect_by_func (adj, glade_eprop_adj_value_changed, eprop); /* Don`t do anything if the loaded property is not the same */ if (adj != g_value_get_object (eprop->property->value)) return; data = g_new (EPropAdjIdleData, 1); data->eprop = eprop; data->value = adj->value; /* Update GladeEPropAdjustment value spinbutton in an idle funtion */ g_idle_add (glade_eprop_adj_set_value_idle, data); /* Set adjustment to the old value */ adj->value = gtk_spin_button_get_value (GTK_SPIN_BUTTON ( GLADE_EPROP_ADJUSTMENT (eprop)->value)); } static void glade_eprop_adjustment_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropAdjustment *eprop_adj = GLADE_EPROP_ADJUSTMENT (eprop); GObject *object; GtkAdjustment *adj; /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; object = g_value_get_object (property->value); if (object == NULL) return; adj = GTK_ADJUSTMENT (object); /* Keep track of external adjustment changes */ g_signal_connect (object, "value-changed", G_CALLBACK (glade_eprop_adj_value_changed), eprop); /* Update adjustment's values */ eprop_adj->value_adj->lower = adj->lower; eprop_adj->value_adj->upper = adj->upper; eprop_adj->value_adj->step_increment = adj->step_increment; eprop_adj->value_adj->page_increment = adj->page_increment; eprop_adj->value_adj->page_size = adj->page_size; /* Block Handlers */ g_signal_handler_block (eprop_adj->value, eprop_adj->ids.value); g_signal_handler_block (eprop_adj->lower, eprop_adj->ids.lower); g_signal_handler_block (eprop_adj->upper, eprop_adj->ids.upper); g_signal_handler_block (eprop_adj->step_increment, eprop_adj->ids.step_increment); g_signal_handler_block (eprop_adj->page_increment, eprop_adj->ids.page_increment); g_signal_handler_block (eprop_adj->page_size, eprop_adj->ids.page_size); /* Update spinbuttons values */ gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_adj->value), adj->value); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_adj->lower), adj->lower); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_adj->upper), adj->upper); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_adj->step_increment), adj->step_increment); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_adj->page_increment), adj->page_increment); gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_adj->page_size), adj->page_size); /* Unblock Handlers */ g_signal_handler_unblock (eprop_adj->value, eprop_adj->ids.value); g_signal_handler_unblock (eprop_adj->lower, eprop_adj->ids.lower); g_signal_handler_unblock (eprop_adj->upper, eprop_adj->ids.upper); g_signal_handler_unblock (eprop_adj->step_increment, eprop_adj->ids.step_increment); g_signal_handler_unblock (eprop_adj->page_increment, eprop_adj->ids.page_increment); g_signal_handler_unblock (eprop_adj->page_size, eprop_adj->ids.page_size); } static GtkAdjustment * glade_eprop_adjustment_dup_adj (GladeEditorProperty *eprop) { GtkAdjustment *adj; GObject *object; object = g_value_get_object (eprop->property->value); if (object == NULL) return NULL; adj = GTK_ADJUSTMENT (object); return GTK_ADJUSTMENT (gtk_adjustment_new (adj->value, adj->lower, adj->upper, adj->step_increment, adj->page_increment, adj->page_size)); } static void glade_eprop_adjustment_prop_changed_common (GladeEditorProperty *eprop, GtkAdjustment *adjustment) { GValue value = {0, }; g_value_init (&value, GTK_TYPE_ADJUSTMENT); g_value_set_object (&value, G_OBJECT (adjustment)); glade_editor_property_commit (eprop, &value); g_value_unset (&value); } #define GLADE_EPROP_ADJUSTMENT_DEFINE_VALUE_CHANGED_FUNC(p) \ static void \ glade_eprop_adjustment_ ## p ## _changed (GtkSpinButton *spin, \ GladeEditorProperty *eprop) \ { \ GtkAdjustment *adj = glade_eprop_adjustment_dup_adj (eprop); \ if (adj == NULL) return; \ adj->p = gtk_spin_button_get_value (spin); \ glade_eprop_adjustment_prop_changed_common (eprop, adj); \ } GLADE_EPROP_ADJUSTMENT_DEFINE_VALUE_CHANGED_FUNC (value) GLADE_EPROP_ADJUSTMENT_DEFINE_VALUE_CHANGED_FUNC (lower) GLADE_EPROP_ADJUSTMENT_DEFINE_VALUE_CHANGED_FUNC (upper) GLADE_EPROP_ADJUSTMENT_DEFINE_VALUE_CHANGED_FUNC (step_increment) GLADE_EPROP_ADJUSTMENT_DEFINE_VALUE_CHANGED_FUNC (page_increment) GLADE_EPROP_ADJUSTMENT_DEFINE_VALUE_CHANGED_FUNC (page_size) #define GLADE_EPROP_ADJUSTMENT_CONNECT(object, prop) \ g_signal_connect (object, "value_changed", \ G_CALLBACK (glade_eprop_adjustment_ ## prop ## _changed), eprop); static void glade_eprop_adjustment_table_add_label (GtkTable *table, gint pos, gchar *label, gchar *tip) { GtkWidget *widget, *eventbox = gtk_event_box_new (); widget = gtk_label_new (label); gtk_misc_set_alignment (GTK_MISC (widget), 1, 0); gtk_container_add (GTK_CONTAINER (eventbox), widget); glade_util_widget_set_tooltip (eventbox, tip); gtk_table_attach_defaults (table, eventbox, 0, 1, pos, pos + 1); } static GtkWidget * glade_eprop_adjustment_create_input (GladeEditorProperty *eprop) { GladeEPropAdjustment *eprop_adj = GLADE_EPROP_ADJUSTMENT (eprop); GtkWidget *widget; GtkTable *table; eprop_adj->value = gtk_spin_button_new_with_range (-G_MAXDOUBLE, G_MAXDOUBLE, 1); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (eprop_adj->value), 2); eprop_adj->ids.value = GLADE_EPROP_ADJUSTMENT_CONNECT (eprop_adj->value, value); eprop_adj->value_adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (eprop_adj->value)); eprop_adj->lower = gtk_spin_button_new_with_range (-G_MAXDOUBLE, G_MAXDOUBLE, 1); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (eprop_adj->lower), 2); eprop_adj->ids.lower = GLADE_EPROP_ADJUSTMENT_CONNECT (eprop_adj->lower, lower); eprop_adj->upper = gtk_spin_button_new_with_range (-G_MAXDOUBLE, G_MAXDOUBLE, 1); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (eprop_adj->upper), 2); eprop_adj->ids.upper = GLADE_EPROP_ADJUSTMENT_CONNECT (eprop_adj->upper, upper); eprop_adj->step_increment = gtk_spin_button_new_with_range (0, G_MAXDOUBLE, 1); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (eprop_adj->step_increment), 2); eprop_adj->ids.step_increment = GLADE_EPROP_ADJUSTMENT_CONNECT (eprop_adj->step_increment, step_increment); eprop_adj->page_increment = gtk_spin_button_new_with_range (0, G_MAXDOUBLE, 1); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (eprop_adj->page_increment), 2); eprop_adj->ids.page_increment = GLADE_EPROP_ADJUSTMENT_CONNECT (eprop_adj->page_increment, page_increment); eprop_adj->page_size = gtk_spin_button_new_with_range (0, G_MAXDOUBLE, 1); gtk_spin_button_set_digits (GTK_SPIN_BUTTON (eprop_adj->page_size), 2); eprop_adj->ids.page_size = GLADE_EPROP_ADJUSTMENT_CONNECT (eprop_adj->page_size, page_size); /* Eprop */ widget = gtk_table_new (6, 2, FALSE); table = GTK_TABLE (widget); gtk_table_set_col_spacings (table, 4); glade_eprop_adjustment_table_add_label (table, 0, _("Value:"), _("The current value")); glade_eprop_adjustment_table_add_label (table, 1, _("Lower:"), _("The minimum value")); glade_eprop_adjustment_table_add_label (table, 2, _("Upper:"), _("The maximum value")); glade_eprop_adjustment_table_add_label (table, 3, _("Step inc:"), _("The increment to use to make minor changes to the value")); glade_eprop_adjustment_table_add_label (table, 4, _("Page inc:"), _("The increment to use to make major changes to the value")); glade_eprop_adjustment_table_add_label (table, 5, _("Page size:"), _("The page size (in a GtkScrollbar this is the size of the area which is currently visible)")); gtk_table_attach_defaults (table, eprop_adj->value, 1, 2, 0, 1); gtk_table_attach_defaults (table, eprop_adj->lower, 1, 2, 1, 2); gtk_table_attach_defaults (table, eprop_adj->upper, 1, 2, 2, 3); gtk_table_attach_defaults (table, eprop_adj->step_increment, 1, 2, 3, 4); gtk_table_attach_defaults (table, eprop_adj->page_increment, 1, 2, 4, 5); gtk_table_attach_defaults (table, eprop_adj->page_size, 1, 2, 5, 6); gtk_widget_show_all (widget); return widget; } /******************************************************************************* GladeEditorPropertyAccelClass *******************************************************************************/ enum { ACCEL_COLUMN_SIGNAL = 0, ACCEL_COLUMN_REAL_SIGNAL, ACCEL_COLUMN_KEY, ACCEL_COLUMN_MOD_SHIFT, ACCEL_COLUMN_MOD_CNTL, ACCEL_COLUMN_MOD_ALT, ACCEL_COLUMN_IS_CLASS, ACCEL_COLUMN_IS_SIGNAL, ACCEL_COLUMN_KEY_ENTERED, ACCEL_COLUMN_KEY_SLOT, ACCEL_NUM_COLUMNS }; enum { ACCEL_COMBO_COLUMN_TEXT = 0, ACCEL_COMBO_NUM_COLUMNS, }; typedef struct { GladeEditorProperty parent_instance; GtkWidget *entry; GList *parent_iters; GtkTreeModel *model; } GladeEPropAccel; typedef struct { GtkTreeIter *iter; gchar *name; /* <-- dont free */ } GladeEpropIterTab; static GtkTreeModel *keysyms_model = NULL; GLADE_MAKE_EPROP (GladeEPropAccel, glade_eprop_accel) #define GLADE_TYPE_EPROP_ACCEL (glade_eprop_accel_get_type()) #define GLADE_EPROP_ACCEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_ACCEL, GladeEPropAccel)) #define GLADE_EPROP_ACCEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_ACCEL, GladeEPropAccelClass)) #define GLADE_IS_EPROP_ACCEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_ACCEL)) #define GLADE_IS_EPROP_ACCEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_ACCEL)) #define GLADE_EPROP_ACCEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_ACCEL, GladeEPropAccelClass)) static GtkTreeModel * create_keysyms_model (void) { GtkTreeModel *model; GtkTreeIter iter, alphanum, fkey, keypad, other, extra; GtkTreeIter *parent; gint i; model = (GtkTreeModel *)gtk_tree_store_new (ACCEL_COMBO_NUM_COLUMNS, G_TYPE_STRING); /* The Key charachter name */ gtk_tree_store_append (GTK_TREE_STORE (model), &alphanum, NULL); gtk_tree_store_set (GTK_TREE_STORE (model), &alphanum, ACCEL_COMBO_COLUMN_TEXT, _("Alphanumerical"), -1); gtk_tree_store_append (GTK_TREE_STORE (model), &extra, NULL); gtk_tree_store_set (GTK_TREE_STORE (model), &extra, ACCEL_COMBO_COLUMN_TEXT, _("Extra"), -1); gtk_tree_store_append (GTK_TREE_STORE (model), &keypad, NULL); gtk_tree_store_set (GTK_TREE_STORE (model), &keypad, ACCEL_COMBO_COLUMN_TEXT, _("Keypad"), -1); gtk_tree_store_append (GTK_TREE_STORE (model), &fkey, NULL); gtk_tree_store_set (GTK_TREE_STORE (model), &fkey, ACCEL_COMBO_COLUMN_TEXT, _("Functions"), -1); gtk_tree_store_append (GTK_TREE_STORE (model), &other, NULL); gtk_tree_store_set (GTK_TREE_STORE (model), &other, ACCEL_COMBO_COLUMN_TEXT, _("Other"), -1); parent = &alphanum; for (i = 0; GladeKeys[i].name != NULL; i++) { gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent); gtk_tree_store_set (GTK_TREE_STORE (model), &iter, ACCEL_COMBO_COLUMN_TEXT, GladeKeys[i].name, -1); if (!strcmp (GladeKeys[i].name, GLADE_KEYS_LAST_ALPHANUM)) parent = &extra; else if (!strcmp (GladeKeys[i].name, GLADE_KEYS_LAST_EXTRA)) parent = &keypad; else if (!strcmp (GladeKeys[i].name, GLADE_KEYS_LAST_KP)) parent = &fkey; else if (!strcmp (GladeKeys[i].name, GLADE_KEYS_LAST_FKEY)) parent = &other; } return model; } static void glade_eprop_accel_finalize (GObject *object) { /* Chain up */ G_OBJECT_CLASS (editor_property_class)->finalize (object); } static void glade_eprop_accel_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropAccel *eprop_accel = GLADE_EPROP_ACCEL (eprop); gchar *accels; /* Chain up first */ editor_property_class->load (eprop, property); if (property == NULL) return; if ((accels = glade_property_class_make_string_from_gvalue (eprop->klass, property->value)) != NULL) { gtk_entry_set_text (GTK_ENTRY (eprop_accel->entry), accels); g_free (accels); } else gtk_entry_set_text (GTK_ENTRY (eprop_accel->entry), ""); } static gint eprop_find_iter (GladeEpropIterTab *iter_tab, gchar *name) { return strcmp (iter_tab->name, name); } static void iter_tab_free (GladeEpropIterTab *iter_tab) { gtk_tree_iter_free (iter_tab->iter); g_free (iter_tab); } static void glade_eprop_accel_populate_view (GladeEditorProperty *eprop, GtkTreeView *view) { GladeEPropAccel *eprop_accel = GLADE_EPROP_ACCEL (eprop); GladeSignalClass *sclass; GladeWidgetAdaptor *adaptor = glade_widget_adaptor_from_pclass (eprop->klass); GtkTreeStore *model = (GtkTreeStore *)gtk_tree_view_get_model (view); GtkTreeIter iter; GladeEpropIterTab *parent_tab; GladeAccelInfo *info; GList *list, *l, *found, *accelerators; gchar *name; accelerators = g_value_get_boxed (eprop->property->value); /* First make parent iters... */ for (list = adaptor->signals; list; list = list->next) { sclass = list->data; /* Only action signals have accelerators. */ if ((sclass->query.signal_flags & G_SIGNAL_ACTION) == 0) continue; if (g_list_find_custom (eprop_accel->parent_iters, sclass->type, (GCompareFunc)eprop_find_iter) == NULL) { gtk_tree_store_append (model, &iter, NULL); gtk_tree_store_set (model, &iter, ACCEL_COLUMN_SIGNAL, sclass->type, ACCEL_COLUMN_IS_CLASS, TRUE, ACCEL_COLUMN_IS_SIGNAL, FALSE, -1); parent_tab = g_new0 (GladeEpropIterTab, 1); parent_tab->name = sclass->type; parent_tab->iter = gtk_tree_iter_copy (&iter); eprop_accel->parent_iters = g_list_prepend (eprop_accel->parent_iters, parent_tab); } } /* Now we populate... */ for (list = adaptor->signals; list; list = list->next) { sclass = list->data; /* Only action signals have accelerators. */ if ((sclass->query.signal_flags & G_SIGNAL_ACTION) == 0) continue; if ((found = g_list_find_custom (eprop_accel->parent_iters, sclass->type, (GCompareFunc)eprop_find_iter)) != NULL) { parent_tab = found->data; name = g_strdup_printf (" %s", sclass->name); /* Populate from accelerator list */ for (l = accelerators; l; l = l->next) { info = l->data; if (strcmp (info->signal, sclass->name)) continue; gtk_tree_store_append (model, &iter, parent_tab->iter); gtk_tree_store_set (model, &iter, ACCEL_COLUMN_SIGNAL, name, ACCEL_COLUMN_REAL_SIGNAL, sclass->name, ACCEL_COLUMN_IS_CLASS, FALSE, ACCEL_COLUMN_IS_SIGNAL, TRUE, ACCEL_COLUMN_MOD_SHIFT, (info->modifiers & GDK_SHIFT_MASK) != 0, ACCEL_COLUMN_MOD_CNTL, (info->modifiers & GDK_CONTROL_MASK) != 0, ACCEL_COLUMN_MOD_ALT, (info->modifiers & GDK_MOD1_MASK) != 0, ACCEL_COLUMN_KEY, glade_builtin_string_from_key (info->key), ACCEL_COLUMN_KEY_ENTERED, TRUE, ACCEL_COLUMN_KEY_SLOT, FALSE, -1); } /* Append a new empty slot at the end */ gtk_tree_store_append (model, &iter, parent_tab->iter); gtk_tree_store_set (model, &iter, ACCEL_COLUMN_SIGNAL, name, ACCEL_COLUMN_REAL_SIGNAL, sclass->name, ACCEL_COLUMN_IS_CLASS, FALSE, ACCEL_COLUMN_IS_SIGNAL, TRUE, ACCEL_COLUMN_MOD_SHIFT, FALSE, ACCEL_COLUMN_MOD_CNTL, FALSE, ACCEL_COLUMN_MOD_ALT, FALSE, ACCEL_COLUMN_KEY, _(""), ACCEL_COLUMN_KEY_ENTERED, FALSE, ACCEL_COLUMN_KEY_SLOT, TRUE, -1); g_free (name); } } } static void key_edited (GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text, GladeEditorProperty *eprop) { GladeEPropAccel *eprop_accel = GLADE_EPROP_ACCEL (eprop); gboolean key_was_set; const gchar *text; GtkTreeIter iter, parent_iter, new_iter; if (!gtk_tree_model_get_iter_from_string (eprop_accel->model, &iter, path_string)) return; gtk_tree_model_get (eprop_accel->model, &iter, ACCEL_COLUMN_KEY_ENTERED, &key_was_set, -1); /* If user selects "none"; remove old entry or ignore new one. */ if (!new_text || new_text[0] == '\0' || glade_builtin_string_from_key ((guint)new_text[0]) == NULL || g_utf8_collate (new_text, _("None")) == 0 || g_utf8_collate (new_text, _("")) == 0) { if (key_was_set) gtk_tree_store_remove (GTK_TREE_STORE (eprop_accel->model), &iter); return; } if (glade_builtin_key_from_string (new_text) != 0) text = new_text; else text = glade_builtin_string_from_key ((guint)new_text[0]); gtk_tree_store_set (GTK_TREE_STORE (eprop_accel->model), &iter, ACCEL_COLUMN_KEY, text, ACCEL_COLUMN_KEY_ENTERED, TRUE, ACCEL_COLUMN_KEY_SLOT, FALSE, -1); /* Append a new one if needed */ if (key_was_set == FALSE && gtk_tree_model_iter_parent (eprop_accel->model, &parent_iter, &iter)) { gchar *signal, *real_signal; gtk_tree_model_get (eprop_accel->model, &iter, ACCEL_COLUMN_SIGNAL, &signal, ACCEL_COLUMN_REAL_SIGNAL, &real_signal, -1); /* Append a new empty slot at the end */ gtk_tree_store_insert_after (GTK_TREE_STORE (eprop_accel->model), &new_iter, &parent_iter, &iter); gtk_tree_store_set (GTK_TREE_STORE (eprop_accel->model), &new_iter, ACCEL_COLUMN_SIGNAL, signal, ACCEL_COLUMN_REAL_SIGNAL, real_signal, ACCEL_COLUMN_IS_CLASS, FALSE, ACCEL_COLUMN_IS_SIGNAL, TRUE, ACCEL_COLUMN_MOD_SHIFT, FALSE, ACCEL_COLUMN_MOD_CNTL, FALSE, ACCEL_COLUMN_MOD_ALT, FALSE, ACCEL_COLUMN_KEY, _(""), ACCEL_COLUMN_KEY_ENTERED, FALSE, ACCEL_COLUMN_KEY_SLOT, TRUE, -1); g_free (signal); g_free (real_signal); } } static void modifier_toggled (GtkCellRendererToggle *cell, gchar *path_string, GladeEditorProperty *eprop) { GladeEPropAccel *eprop_accel = GLADE_EPROP_ACCEL (eprop); GtkTreeIter iter; gint column; gboolean active, key_entered; if (!gtk_tree_model_get_iter_from_string (eprop_accel->model, &iter, path_string)) return; column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "model-column")); gtk_tree_model_get (eprop_accel->model, &iter, ACCEL_COLUMN_KEY_ENTERED, &key_entered, column, &active, -1); if (key_entered) gtk_tree_store_set (GTK_TREE_STORE (eprop_accel->model), &iter, column, !active, -1); } static GtkWidget * glade_eprop_accel_view (GladeEditorProperty *eprop) { GladeEPropAccel *eprop_accel = GLADE_EPROP_ACCEL (eprop); GtkWidget *view_widget; GtkCellRenderer *renderer; GtkTreeViewColumn *column; eprop_accel->model = (GtkTreeModel *)gtk_tree_store_new (ACCEL_NUM_COLUMNS, G_TYPE_STRING, /* The GSignal name formatted for display */ G_TYPE_STRING, /* The GSignal name */ G_TYPE_STRING, /* The Gdk keycode */ G_TYPE_BOOLEAN, /* The shift modifier */ G_TYPE_BOOLEAN, /* The cntl modifier */ G_TYPE_BOOLEAN, /* The alt modifier */ G_TYPE_BOOLEAN, /* Whether this is a class entry */ G_TYPE_BOOLEAN, /* Whether this is a signal entry (oposite of above) */ G_TYPE_BOOLEAN, /* Whether the key has been entered for this row */ G_TYPE_BOOLEAN); /* Oposite of above */ view_widget = gtk_tree_view_new_with_model (eprop_accel->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); /********************* signal name column *********************/ renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", FALSE, "weight", PANGO_WEIGHT_BOLD, NULL); column = gtk_tree_view_column_new_with_attributes (_("Signal"), renderer, "text", ACCEL_COLUMN_SIGNAL, "weight-set", ACCEL_COLUMN_IS_CLASS, NULL); g_object_set (G_OBJECT (column), "expand", TRUE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column); /********************* key name column *********************/ if (keysyms_model == NULL) keysyms_model = create_keysyms_model (); renderer = gtk_cell_renderer_combo_new (); g_object_set (G_OBJECT (renderer), "editable", TRUE, "model", keysyms_model, "text-column", ACCEL_COMBO_COLUMN_TEXT, "has-entry", TRUE, "style", PANGO_STYLE_ITALIC, "foreground", "Gray", NULL); g_signal_connect (renderer, "edited", G_CALLBACK (key_edited), eprop); column = gtk_tree_view_column_new_with_attributes (_("Key"), renderer, "text", ACCEL_COLUMN_KEY, "style-set", ACCEL_COLUMN_KEY_SLOT, "foreground-set", ACCEL_COLUMN_KEY_SLOT, "visible", ACCEL_COLUMN_IS_SIGNAL, NULL); g_object_set (G_OBJECT (column), "expand", TRUE, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column); /********************* shift modifier column *********************/ renderer = gtk_cell_renderer_toggle_new (); column = gtk_tree_view_column_new_with_attributes (_("Shift"), renderer, "visible", ACCEL_COLUMN_IS_SIGNAL, "sensitive", ACCEL_COLUMN_KEY_ENTERED, "active", ACCEL_COLUMN_MOD_SHIFT, NULL); g_object_set_data (G_OBJECT (renderer), "model-column", GINT_TO_POINTER (ACCEL_COLUMN_MOD_SHIFT)); g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (modifier_toggled), eprop); gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column); /********************* control modifier column *********************/ renderer = gtk_cell_renderer_toggle_new (); column = gtk_tree_view_column_new_with_attributes (_("Control"), renderer, "visible", ACCEL_COLUMN_IS_SIGNAL, "sensitive", ACCEL_COLUMN_KEY_ENTERED, "active", ACCEL_COLUMN_MOD_CNTL, NULL); g_object_set_data (G_OBJECT (renderer), "model-column", GINT_TO_POINTER (ACCEL_COLUMN_MOD_CNTL)); g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (modifier_toggled), eprop); gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column); /********************* alt modifier column *********************/ renderer = gtk_cell_renderer_toggle_new (); column = gtk_tree_view_column_new_with_attributes (_("Alt"), renderer, "visible", ACCEL_COLUMN_IS_SIGNAL, "sensitive", ACCEL_COLUMN_KEY_ENTERED, "active", ACCEL_COLUMN_MOD_ALT, NULL); g_object_set_data (G_OBJECT (renderer), "model-column", GINT_TO_POINTER (ACCEL_COLUMN_MOD_ALT)); g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (modifier_toggled), eprop); gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column); return view_widget; } static gboolean glade_eprop_accel_accum_accelerators (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **ret) { GladeAccelInfo *info; gchar *signal, *key_str; gboolean shift, cntl, alt, entered; gtk_tree_model_get (model, iter, ACCEL_COLUMN_KEY_ENTERED, &entered, -1); if (entered == FALSE) return FALSE; gtk_tree_model_get (model, iter, ACCEL_COLUMN_REAL_SIGNAL, &signal, ACCEL_COLUMN_KEY, &key_str, ACCEL_COLUMN_MOD_SHIFT, &shift, ACCEL_COLUMN_MOD_CNTL, &cntl, ACCEL_COLUMN_MOD_ALT, &alt, -1); info = g_new0 (GladeAccelInfo, 1); info->signal = signal; info->key = glade_builtin_key_from_string (key_str); info->modifiers = (shift ? GDK_SHIFT_MASK : 0) | (cntl ? GDK_CONTROL_MASK : 0) | (alt ? GDK_MOD1_MASK : 0); *ret = g_list_prepend (*ret, info); g_free (key_str); return FALSE; } static void glade_eprop_accel_show_dialog (GtkWidget *dialog_button, GladeEditorProperty *eprop) { GladeEPropAccel *eprop_accel = GLADE_EPROP_ACCEL (eprop); GtkWidget *dialog, *parent, *vbox, *sw, *tree_view; GladeProject *project; GValue *value; GList *accelerators = NULL; gint res; project = glade_widget_get_project (eprop->property->widget); parent = gtk_widget_get_toplevel (GTK_WIDGET (eprop)); dialog = gtk_dialog_new_with_buttons (_("Choose accelerator keys..."), GTK_WINDOW (parent), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLEAR, GLADE_RESPONSE_CLEAR, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); vbox = gtk_vbox_new (FALSE, 6); gtk_widget_show (vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 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_accel_view (eprop); glade_eprop_accel_populate_view (eprop, GTK_TREE_VIEW (tree_view)); 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) { gtk_tree_model_foreach (gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)), (GtkTreeModelForeachFunc) glade_eprop_accel_accum_accelerators, &accelerators); value = g_new0 (GValue, 1); g_value_init (value, GLADE_TYPE_ACCEL_GLIST); g_value_take_boxed (value, accelerators); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } else if (res == GLADE_RESPONSE_CLEAR) { value = g_new0 (GValue, 1); g_value_init (value, GLADE_TYPE_ACCEL_GLIST); g_value_set_boxed (value, NULL); glade_editor_property_commit (eprop, value); g_value_unset (value); g_free (value); } /* Clean up ... */ gtk_widget_destroy (dialog); g_object_unref (G_OBJECT (eprop_accel->model)); eprop_accel->model = NULL; if (eprop_accel->parent_iters) { g_list_foreach (eprop_accel->parent_iters, (GFunc)iter_tab_free, NULL); g_list_free (eprop_accel->parent_iters); eprop_accel->parent_iters = NULL; } } static GtkWidget * glade_eprop_accel_create_input (GladeEditorProperty *eprop) { GladeEPropAccel *eprop_accel = GLADE_EPROP_ACCEL (eprop); GtkWidget *hbox; GtkWidget *button; hbox = gtk_hbox_new (FALSE, 0); eprop_accel->entry = gtk_entry_new (); gtk_entry_set_editable (GTK_ENTRY (eprop_accel->entry), FALSE); gtk_widget_show (eprop_accel->entry); gtk_box_pack_start (GTK_BOX (hbox), eprop_accel->entry, TRUE, TRUE, 0); button = gtk_button_new_with_label ("..."); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_eprop_accel_show_dialog), eprop); return hbox; } /******************************************************************************* Misc static stuff *******************************************************************************/ static GType glade_editor_property_type (GParamSpec *pspec) { GType type = 0; if (pspec->value_type == GLADE_TYPE_STOCK || G_IS_PARAM_SPEC_ENUM(pspec)) type = GLADE_TYPE_EPROP_ENUM; else if (G_IS_PARAM_SPEC_FLAGS(pspec)) type = GLADE_TYPE_EPROP_FLAGS; else if (G_IS_PARAM_SPEC_VALUE_ARRAY (pspec)) { if (pspec->value_type == G_TYPE_VALUE_ARRAY) type = GLADE_TYPE_EPROP_TEXT; } else if (G_IS_PARAM_SPEC_BOXED(pspec)) { if (pspec->value_type == GDK_TYPE_COLOR) type = GLADE_TYPE_EPROP_COLOR; else if (pspec->value_type == G_TYPE_STRV) type = GLADE_TYPE_EPROP_TEXT; } else if (G_IS_PARAM_SPEC_STRING(pspec)) type = GLADE_TYPE_EPROP_TEXT; else if (G_IS_PARAM_SPEC_BOOLEAN(pspec)) type = GLADE_TYPE_EPROP_BOOL; else if (G_IS_PARAM_SPEC_FLOAT(pspec) || G_IS_PARAM_SPEC_DOUBLE(pspec) || G_IS_PARAM_SPEC_INT(pspec) || G_IS_PARAM_SPEC_UINT(pspec) || G_IS_PARAM_SPEC_LONG(pspec) || G_IS_PARAM_SPEC_ULONG(pspec) || G_IS_PARAM_SPEC_INT64(pspec) || G_IS_PARAM_SPEC_UINT64(pspec)) type = GLADE_TYPE_EPROP_NUMERIC; else if (G_IS_PARAM_SPEC_UNICHAR(pspec)) type = GLADE_TYPE_EPROP_UNICHAR; else if (G_IS_PARAM_SPEC_OBJECT(pspec)) { if (pspec->value_type == GDK_TYPE_PIXBUF) type = GLADE_TYPE_EPROP_RESOURCE; else if (pspec->value_type == GTK_TYPE_ADJUSTMENT) type = GLADE_TYPE_EPROP_ADJUSTMENT; else type = GLADE_TYPE_EPROP_OBJECT; } else if (GLADE_IS_PARAM_SPEC_OBJECTS(pspec)) type = GLADE_TYPE_EPROP_OBJECTS; else if (GLADE_IS_PARAM_SPEC_ACCEL(pspec)) type = GLADE_TYPE_EPROP_ACCEL; return type; } /******************************************************************************* API *******************************************************************************/ /** * glade_editor_property_new: * @klass: A #GladePropertyClass * @use_command: Whether the undo/redo stack applies here. * * This is a factory function to create the correct type of * editor property that can edit the said type of #GladePropertyClass * * Returns: A newly created GladeEditorProperty of the correct type */ GladeEditorProperty * glade_editor_property_new (GladePropertyClass *klass, gboolean use_command) { GladeEditorProperty *eprop; GType type = 0; /* Find the right type of GladeEditorProperty for this * GladePropertyClass. */ if ((type = glade_editor_property_type (klass->pspec)) == 0) g_error ("%s : pspec '%s' type '%s' not implemented (%s)\n", G_GNUC_PRETTY_FUNCTION, klass->name, g_type_name (G_PARAM_SPEC_TYPE (klass->pspec)), g_type_name (klass->pspec->value_type)); /* special case for resource specs which are hand specified in the catalog. */ if (klass->resource) type = GLADE_TYPE_EPROP_RESOURCE; /* Create and return the correct type of GladeEditorProperty */ eprop = g_object_new (type, "property-class", klass, "use-command", use_command, NULL); return eprop; } /** * glade_editor_property_new_from_widget: * @widget: A #GladeWidget * @property: The widget's property * @use_command: Whether the undo/redo stack applies here. * * This is a convenience function to create a GladeEditorProperty corresponding * to @property * * Returns: A newly created and connected GladeEditorProperty */ GladeEditorProperty * glade_editor_property_new_from_widget (GladeWidget *widget, const gchar *property, gboolean use_command) { GladeEditorProperty *eprop; GladeProperty *p; p = glade_widget_get_property (widget, property); g_return_val_if_fail (GLADE_IS_PROPERTY (p), NULL); eprop = glade_editor_property_new (p->klass, use_command); glade_editor_property_load (eprop, p); return eprop; } /** * glade_editor_property_supported: * @pspec: A #GParamSpec * * Returns: whether this pspec is supported by GladeEditorProperties. */ gboolean glade_editor_property_supported (GParamSpec *pspec) { return glade_editor_property_type (pspec) != 0; } /** * glade_editor_property_load: * @eprop: A #GladeEditorProperty * @property: A #GladeProperty * * Loads @property values into @eprop and connects. */ 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->loading = TRUE; GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->load (eprop, property); eprop->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 */ property = glade_widget_get_property (widget, eprop->klass->id); glade_editor_property_load (eprop, property); } /** * glade_editor_property_show_info: * @eprop: A #GladeEditorProperty * * Show the control widget to access help for @eprop */ void glade_editor_property_show_info (GladeEditorProperty *eprop) { GladeWidgetAdaptor *adaptor; gchar *book; g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); adaptor = glade_widget_adaptor_from_pspec (eprop->klass->pspec); g_object_get (adaptor, "book", &book, NULL); if (eprop->klass->virt == FALSE && book != NULL) gtk_widget_show (eprop->info); else { /* Put insensitive controls to balance the UI with * other eprops. */ gtk_widget_show (eprop->info); gtk_widget_set_sensitive (eprop->info, FALSE); } g_free (book); eprop->show_info = TRUE; g_object_notify (G_OBJECT (eprop), "show-info"); } /** * glade_editor_property_hide_info: * @eprop: A #GladeEditorProperty * * Hide the control widget to access help for @eprop */ void glade_editor_property_hide_info (GladeEditorProperty *eprop) { g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); gtk_widget_hide (eprop->info); eprop->show_info = FALSE; g_object_notify (G_OBJECT (eprop), "show-info"); }