#include #include #include #include #include #include #include #include "glade-eprop-enum-int.h" /* GObjectClass */ static void glade_eprop_enum_int_init (GladeEPropEnumInt *eprop); static void glade_eprop_enum_int_class_init (GladeEPropEnumIntClass *klass); static void glade_eprop_enum_int_finalize (GObject *object); static void glade_eprop_enum_int_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); /* GladeEditorPropertyClass */ static void glade_eprop_enum_int_load (GladeEditorProperty *eprop, GladeProperty *property); static GtkWidget * glade_eprop_enum_int_create_input (GladeEditorProperty *eprop); typedef struct { GType type; GtkWidget *combo_box; GtkWidget *entry; guint focus_out_idle; } GladeEPropEnumIntPrivate; enum { PROP_0, PROP_TYPE }; enum { COLUMN_ENUM_TEXT, COLUMN_VALUE_INT, N_COLUMNS }; G_DEFINE_TYPE_WITH_PRIVATE (GladeEPropEnumInt, glade_eprop_enum_int, GLADE_TYPE_EDITOR_PROPERTY); static void glade_eprop_enum_int_init (GladeEPropEnumInt *eprop) { } static void glade_eprop_enum_int_class_init (GladeEPropEnumIntClass *klass) { GladeEditorPropertyClass *eprop_class = GLADE_EDITOR_PROPERTY_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); eprop_class->load = glade_eprop_enum_int_load; eprop_class->create_input = glade_eprop_enum_int_create_input; object_class->finalize = glade_eprop_enum_int_finalize; object_class->set_property = glade_eprop_enum_int_set_property; g_object_class_install_property (object_class, PROP_TYPE, g_param_spec_gtype ("type", _("Type"), _("Type"), G_TYPE_NONE, G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY)); } static void glade_eprop_enum_int_load (GladeEditorProperty *eprop, GladeProperty *property) { GladeEPropEnumInt *eprop_enum = GLADE_EPROP_ENUM_INT (eprop); GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (eprop_enum); /* Chain up first */ GLADE_EDITOR_PROPERTY_CLASS (glade_eprop_enum_int_parent_class)->load (eprop, property); if (property) { GEnumClass *enum_class; gint value; guint i; gboolean found = FALSE; enum_class = g_type_class_ref (priv->type); value = g_value_get_int (glade_property_inline_value (property)); /* * If we find the value in our enum, then set the active item, otherwise * set the entry text */ for (i = 0; i < enum_class->n_values; i++) { if (enum_class->values[i].value == value) { found = TRUE; break; } } if (found) { gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_box), i); } else { gchar *text = g_strdup_printf ("%d", value); gtk_entry_set_text (GTK_ENTRY (priv->entry), text); g_free (text); } g_type_class_unref (enum_class); } } static const gchar * string_from_value (GType etype, gint val) { GEnumClass *eclass; const gchar *string = NULL; guint i; g_return_val_if_fail ((eclass = g_type_class_ref (etype)) != NULL, NULL); for (i = 0; i < eclass->n_values; i++) { if (val == eclass->values[i].value) { if (glade_type_has_displayable_values (etype)) { if (!glade_displayable_value_is_disabled (etype, eclass->values[i].value_nick)) string = glade_get_displayable_value (etype, eclass->values[i].value_nick); } else { string = eclass->values[i].value_nick; } break; } } g_type_class_unref (eclass); return string; } static gboolean value_from_string (GType etype, const gchar *string, gint *value) { GEnumClass *eclass; GEnumValue *ev; gchar *endptr; gint val = 0; gboolean found = FALSE; /* Try a number first */ val = strtoul (string, &endptr, 0); if (endptr != string) found = TRUE; if (!found) { eclass = g_type_class_ref (etype); ev = g_enum_get_value_by_name (eclass, string); if (!ev) ev = g_enum_get_value_by_nick (eclass, string); if (ev) { val = ev->value; found = TRUE; } if (!found && string && string[0]) { /* Try Displayables */ string = glade_get_value_from_displayable (etype, string); if (string) { ev = g_enum_get_value_by_name (eclass, string); if (!ev) ev = g_enum_get_value_by_nick (eclass, string); if (ev) { val = ev->value; found = TRUE; } } } g_type_class_unref (eclass); } if (found) *value = val; return found; } static void glade_eprop_enum_int_changed_combo (GtkWidget *combo_box, GladeEditorProperty *eprop) { GladeEPropEnumInt *eprop_enum = GLADE_EPROP_ENUM_INT (eprop); GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (eprop_enum); gint ival; GtkTreeModel *tree_model; GtkTreeIter iter; GValue val = { 0, }; gboolean error = FALSE; if (glade_editor_property_loading (eprop)) return; tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)); if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter)) { gtk_tree_model_get (tree_model, &iter, COLUMN_VALUE_INT, &ival, -1); } else { const char *text = gtk_entry_get_text (GTK_ENTRY (priv->entry)); if (!value_from_string (priv->type, text, &ival)) error = TRUE; } if (error) { gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->entry), GTK_ENTRY_ICON_SECONDARY, "dialog-warning-symbolic"); } else { gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->entry), GTK_ENTRY_ICON_SECONDARY, NULL); } if (!error) { g_value_init (&val, G_TYPE_INT); g_value_set_int (&val, ival); glade_editor_property_commit_no_callback (eprop, &val); g_value_unset (&val); } } static gchar * glade_eprop_enum_int_format_entry_cb (GtkComboBox *combo, const gchar *path, GladeEPropEnumInt *eprop_enum) { GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (eprop_enum); GtkTreeIter iter; GtkTreeModel *model; gint value; const char *text; gchar *endptr; gboolean is_number = FALSE; model = gtk_combo_box_get_model (combo); /* Check if we currently have a number in the entry */ text = gtk_entry_get_text (GTK_ENTRY (priv->entry)); value = strtoul (text, &endptr, 0); if (endptr != text) is_number = TRUE; /* Get the selected value */ gtk_tree_model_get_iter_from_string (model, &iter, path); gtk_tree_model_get (model, &iter, COLUMN_VALUE_INT, &value, -1); /* If we're currently in focus, and we have a number in the entry * already, then we dont want to change it. * * E.g. If the user types "1" and we change it to "Pony" right away, * then we ignore that the user might want to enter "10" * * After focusing out, we'll force refresh this so that we settle * on displaying an enum value if one is valid. */ if (is_number && gtk_widget_has_focus (priv->entry)) return g_strdup_printf ("%d", value); /* Now format a nice displayable value if possible */ text = string_from_value (priv->type, value); if (text) return g_strdup (text); return g_strdup_printf ("%d", value); } static gboolean glade_eprop_enum_int_focus_out_idle (gpointer user_data) { GladeEPropEnumInt *eprop_enum = GLADE_EPROP_ENUM_INT (user_data); GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (eprop_enum); /* If the editor is no longer loaded with a property (i.e. the user changed * focus by selecting another widget), then just return. */ if (glade_editor_property_get_property (GLADE_EDITOR_PROPERTY (eprop_enum))) { /* After focusing out, ensure we have a valid selection here if an entered * number matches an enum value. Do this by provoking the combobox to * format it's value. */ g_signal_emit_by_name (priv->combo_box, "changed"); } priv->focus_out_idle = 0; return FALSE; } static gboolean glade_eprop_enum_int_entry_focus_out (GtkWidget *widget, GdkEvent *event, GladeEPropEnumInt *eprop_enum) { GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (eprop_enum); /* Queue the focus out idle, we may want to reformat the entry here */ if (priv->focus_out_idle == 0) priv->focus_out_idle = g_idle_add (glade_eprop_enum_int_focus_out_idle, eprop_enum); return FALSE; } static GtkWidget * glade_eprop_enum_int_create_input (GladeEditorProperty *eprop) { GladeEPropEnumInt *eprop_enum = GLADE_EPROP_ENUM_INT (eprop); GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (eprop_enum); GtkListStore *list_store; GtkTreeIter iter; guint i; GEnumClass *enum_class; enum_class = g_type_class_ref (priv->type); list_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, /* COLUMN_ENUM_TEXT */ G_TYPE_INT); /* COLUMN_VALUE_INT */ if (!glade_type_has_displayable_values (priv->type)) g_warning ("No displayable value found for type %s", g_type_name (priv->type)); gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter); for (i = 0; i < enum_class->n_values; i++) { if (glade_displayable_value_is_disabled (priv->type, enum_class->values[i].value_nick)) continue; gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, COLUMN_ENUM_TEXT, string_from_value (priv->type, enum_class->values[i].value), COLUMN_VALUE_INT, enum_class->values[i].value, -1); } priv->combo_box = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL (list_store)); priv->entry = gtk_bin_get_child (GTK_BIN (priv->combo_box)); gtk_widget_set_halign (priv->combo_box, GTK_ALIGN_FILL); gtk_widget_set_valign (priv->combo_box, GTK_ALIGN_CENTER); gtk_widget_set_hexpand (priv->combo_box, TRUE); gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (priv->combo_box), 0); g_signal_connect (G_OBJECT (priv->combo_box), "changed", G_CALLBACK (glade_eprop_enum_int_changed_combo), eprop); g_signal_connect (G_OBJECT (priv->combo_box), "format-entry-text", G_CALLBACK (glade_eprop_enum_int_format_entry_cb), eprop); g_signal_connect_after (G_OBJECT (priv->entry), "focus-out-event", G_CALLBACK (glade_eprop_enum_int_entry_focus_out), eprop); glade_util_remove_scroll_events (priv->combo_box); g_type_class_unref (enum_class); return priv->combo_box; } static void glade_eprop_enum_int_finalize (GObject *object) { GladeEPropEnumInt *self = GLADE_EPROP_ENUM_INT(object); GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (self); /* Be safe, dont leave idles hanging around */ if (priv->focus_out_idle != 0) g_source_remove (priv->focus_out_idle); } static void glade_eprop_enum_int_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GladeEPropEnumInt *self = GLADE_EPROP_ENUM_INT(object); GladeEPropEnumIntPrivate *priv = glade_eprop_enum_int_get_instance_private (self); switch (property_id) { case PROP_TYPE : priv->type = g_value_get_gtype (value); break; default: /* We don't have any other property... */ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } GladeEditorProperty * glade_eprop_enum_int_new (GladePropertyClass *pclass, GType type, gboolean use_command) { GladeEditorProperty *eprop = g_object_new (GLADE_TYPE_EPROP_ENUM_INT, "property-class", pclass, "use-command", use_command, "type", type, NULL); return eprop; }