[glade] Bug 351645 - Added GladeEPropEnumInt



commit 598a6f98a716b27050ae7118f11736571e5891f9
Author: Tristan Van Berkom <tristan vanberkom codethink co uk>
Date:   Sat Oct 29 22:15:58 2016 +0900

    Bug 351645 - Added GladeEPropEnumInt
    
    An enhanced enum integer editor based on patch contributed by Lukas K <lu 0x83 eu>

 plugins/gtk+/Makefile.am            |    2 +
 plugins/gtk+/glade-eprop-enum-int.c |  443 +++++++++++++++++++++++++++++++++++
 plugins/gtk+/glade-eprop-enum-int.h |   31 +++
 3 files changed, 476 insertions(+), 0 deletions(-)
---
diff --git a/plugins/gtk+/Makefile.am b/plugins/gtk+/Makefile.am
index 4390c7e..a134771 100644
--- a/plugins/gtk+/Makefile.am
+++ b/plugins/gtk+/Makefile.am
@@ -160,6 +160,7 @@ libgladegtk_la_SOURCES =            \
        glade-stack-switcher-editor.c   \
        glade-store-editor.c            \
        glade-string-list.c             \
+       glade-eprop-enum-int.c          \
        glade-text-view-editor.c        \
        glade-tool-button-editor.c      \
        glade-tool-item-group-editor.c  \
@@ -243,6 +244,7 @@ noinst_HEADERS =                    \
        glade-stack-switcher-editor.h   \
        glade-store-editor.h            \
        glade-string-list.h             \
+       glade-eprop-enum-int.h          \
        glade-text-view-editor.h        \
        glade-tool-button-editor.h      \
        glade-tool-item-group-editor.h  \
diff --git a/plugins/gtk+/glade-eprop-enum-int.c b/plugins/gtk+/glade-eprop-enum-int.c
new file mode 100644
index 0000000..e7bb65d
--- /dev/null
+++ b/plugins/gtk+/glade-eprop-enum-int.c
@@ -0,0 +1,443 @@
+
+#include <config.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include <gladeui/glade.h>
+#include <glib/gi18n-lib.h>
+
+#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;
+}
diff --git a/plugins/gtk+/glade-eprop-enum-int.h b/plugins/gtk+/glade-eprop-enum-int.h
new file mode 100644
index 0000000..b5df6a1
--- /dev/null
+++ b/plugins/gtk+/glade-eprop-enum-int.h
@@ -0,0 +1,31 @@
+#ifndef __GLADE_EPROP_ENUM_INT_H__
+#define __GLADE_EPROP_ENUM_INT_H__
+
+#include <glib-object.h>
+#include <gladeui/glade.h>
+
+G_BEGIN_DECLS
+
+#define GLADE_TYPE_EPROP_ENUM_INT            (glade_eprop_enum_int_get_type())
+#define GLADE_EPROP_ENUM_INT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_ENUM_INT, 
GladeEPropEnumInt))
+#define GLADE_EPROP_ENUM_INT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_ENUM_INT, 
GladeEPropEnumIntClass))
+#define GLADE_IS_EPROP_ENUM_INT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_ENUM_INT))
+#define GLADE_IS_EPROP_ENUM_INT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_ENUM_INT))
+#define GLADE_EPROP_ENUM_INT_GET_CLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_ENUM_INT, 
GladeEPropEnumIntClass))
+
+typedef struct
+{
+  GladeEditorProperty eprop;
+} GladeEPropEnumInt;
+
+typedef struct {
+       GladeEditorPropertyClass eprop_class;
+} GladeEPropEnumIntClass;
+
+GladeEditorProperty *glade_eprop_enum_int_new (GladePropertyClass *pclass,
+                                              GType               type,
+                                              gboolean            use_command);
+
+G_END_DECLS
+
+#endif   /* __GLADE_EPROP_ENUM_INT_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]