[glade] GladeAdaptorChooser: created new widget to choose an adaptor. Used in GladePlaceholder and GladeDesi



commit d1b11560a31d49b77052a2e56ec63dfc09d3ed7b
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date:   Tue May 13 18:16:32 2014 -0300

    GladeAdaptorChooser: created new widget to choose an adaptor.
    Used in GladePlaceholder and GladeDesignView to quickly create new widgets and objects.
    
    This should be more than enough to close Bug 708146 "Catalog search entry"

 gladeui/Makefile.am                     |    3 +
 gladeui/glade-adaptor-chooser.c         |  440 +++++++++++++++++++++++++++++++
 gladeui/glade-adaptor-chooser.h         |   75 ++++++
 gladeui/glade-adaptor-chooser.ui        |   93 +++++++
 gladeui/glade-design-view.c             |   45 ++++
 gladeui/glade-placeholder.c             |   52 ++++-
 gladeui/gladeui-resources.gresource.xml |    1 +
 7 files changed, 707 insertions(+), 2 deletions(-)
---
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index 132e3df..c63b6a3 100644
--- a/gladeui/Makefile.am
+++ b/gladeui/Makefile.am
@@ -59,6 +59,7 @@ BUILT_SOURCES = \
 UI_FILES =                             \
        glade-editor.ui                 \
        glade-project-properties.ui     \
+       glade-adaptor-chooser.ui        \
        glade-property-label.ui
 
 GRAPHICS_FILES = \
@@ -83,6 +84,7 @@ libgladeui_2_la_SOURCES = \
        glade-accumulators.c \
        glade-app.c \
        glade-base-editor.c \
+       glade-adaptor-chooser.c \
        glade-builtins.c \
        glade-catalog.c \
        glade-cell-renderer-icon.c \
@@ -185,6 +187,7 @@ libgladeuiinclude_HEADERS = \
 
 noinst_HEADERS = \
        glade-accumulators.h \
+       glade-adaptor-chooser.h \
        glade-design-layout.h \
        glade-design-private.h \
        glade-marshallers.h \
diff --git a/gladeui/glade-adaptor-chooser.c b/gladeui/glade-adaptor-chooser.c
new file mode 100644
index 0000000..f4e4d97
--- /dev/null
+++ b/gladeui/glade-adaptor-chooser.c
@@ -0,0 +1,440 @@
+/*
+ * glade-adaptor-chooser.c
+ *
+ * Copyright (C) 2014 Juan Pablo Ugarte
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public 
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Authors:
+ *   Juan Pablo Ugarte <juanpablougarte gmail com>
+ */
+
+#include "glade-app.h"
+#include "gladeui-enum-types.h"
+#include "glade-adaptor-chooser.h"
+
+#include <string.h>
+
+enum
+{
+  COLUMN_ADAPTOR = 0,
+  COLUMN_NORMALIZED_NAME,
+  COLUMN_NORMALIZED_NAME_LEN,
+  N_COLUMN
+};
+
+struct _GladeAdaptorChooserPrivate
+{
+  GtkListStore       *store;
+  GtkTreeModelFilter *treemodelfilter;
+  GtkSearchEntry     *searchentry;
+  GtkEntryCompletion *entrycompletion;
+
+  /* Needed for gtk_tree_view_column_set_cell_data_func() */
+  GtkTreeViewColumn *column_icon;
+  GtkCellRenderer   *icon_cell;
+  GtkTreeViewColumn *column_adaptor;
+  GtkCellRenderer   *adaptor_cell;
+
+  /* Properties */
+  _GladeAdaptorChooserFlags flags;
+  GladeProject *project;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_SHOW_FLAGS,
+  PROP_PROJECT
+};
+
+enum
+{
+  ADAPTOR_SELECTED,
+
+  LAST_SIGNAL
+};
+
+static guint adaptor_chooser_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_PRIVATE (_GladeAdaptorChooser, _glade_adaptor_chooser, GTK_TYPE_BOX);
+
+static void
+_glade_adaptor_chooser_init (_GladeAdaptorChooser *chooser)
+{
+  chooser->priv = _glade_adaptor_chooser_get_instance_private (chooser);
+
+  chooser->priv->flags = GLADE_ADAPTOR_CHOOSER_WIDGET;
+  gtk_widget_init_template (GTK_WIDGET (chooser));
+}
+
+static void
+_glade_adaptor_chooser_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (_glade_adaptor_chooser_parent_class)->finalize (object);
+}
+
+static void
+_glade_adaptor_chooser_set_property (GObject      *object, 
+                                    guint         prop_id, 
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{ 
+  _GladeAdaptorChooserPrivate *priv;
+  
+  g_return_if_fail (GLADE_IS_ADAPTOR_CHOOSER (object));
+
+  priv = GLADE_ADAPTOR_CHOOSER (object)->priv;
+
+  switch (prop_id)
+    {
+      case PROP_SHOW_FLAGS:
+        priv->flags = g_value_get_flags (value);
+      break;
+      case PROP_PROJECT:
+        priv->project = g_value_get_object (value);
+      break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+_glade_adaptor_chooser_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  _GladeAdaptorChooserPrivate *priv;
+
+  g_return_if_fail (GLADE_IS_ADAPTOR_CHOOSER (object));
+
+  priv = GLADE_ADAPTOR_CHOOSER (object)->priv;
+
+  switch (prop_id)
+    {
+      case PROP_SHOW_FLAGS:
+        g_value_set_flags (value, priv->flags);
+      break;
+      case PROP_PROJECT:
+        g_value_set_object (value, priv->project);
+      break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static inline gchar *
+normalize_name (const gchar *name)
+{
+  gchar *normalized_name = g_utf8_normalize (name, -1, G_NORMALIZE_DEFAULT);
+  gchar *casefold_name = g_utf8_casefold (normalized_name, -1);
+  g_free (normalized_name);
+  return casefold_name;
+}
+
+static inline void
+store_append_adaptor (GtkListStore *store, GladeWidgetAdaptor *adaptor)
+{
+  gchar *normalized_name = normalize_name (glade_widget_adaptor_get_name (adaptor));
+
+  gtk_list_store_insert_with_values (store, NULL, -1,
+                                     COLUMN_ADAPTOR, adaptor,
+                                     COLUMN_NORMALIZED_NAME, normalized_name,
+                                     COLUMN_NORMALIZED_NAME_LEN, strlen (normalized_name),
+                                     -1);
+  g_free (normalized_name);
+}
+
+static inline void
+store_populate (GtkListStore            *store,
+                GladeProject            *project,
+                _GladeAdaptorChooserFlags flags)
+{
+  const gchar *catalog = NULL;
+  gint major, minor;
+  GList *l;
+
+  for (l = glade_app_get_catalogs (); l; l = g_list_next (l))
+    {
+      GList *groups = glade_catalog_get_widget_groups (GLADE_CATALOG (l->data));
+
+      for (; groups; groups = g_list_next (groups))
+        {
+          GladeWidgetGroup *group = GLADE_WIDGET_GROUP (groups->data);
+          const GList *adaptors;
+
+          for (adaptors = glade_widget_group_get_adaptors (group); adaptors;
+               adaptors = g_list_next (adaptors))
+            {
+              GladeWidgetAdaptor *adaptor = adaptors->data;
+              GType type = glade_widget_adaptor_get_object_type (adaptor);
+
+              /* Skip deprecated adaptors and according to flags */
+              if (GWA_DEPRECATED (adaptor) ||
+                  (flags & GLADE_ADAPTOR_CHOOSER_SKIP_TOPLEVEL && GWA_IS_TOPLEVEL (adaptor)) ||
+                  !((flags & GLADE_ADAPTOR_CHOOSER_WIDGET && g_type_is_a (type, GTK_TYPE_WIDGET)) ||
+                    (flags & GLADE_ADAPTOR_CHOOSER_TOPLEVEL && GWA_IS_TOPLEVEL (adaptor))))
+                continue;
+
+              /* Skip classes not available in project target version */
+              if (project)
+                {
+                  const gchar *new_catalog = glade_widget_adaptor_get_catalog (adaptor);
+
+                  if (g_strcmp0 (catalog, new_catalog))
+                    {
+                      catalog = new_catalog;
+                      glade_project_get_target_version (project, catalog, &major, &minor);
+                    }
+
+                  if (!GWA_VERSION_CHECK (adaptor, major, minor))
+                    continue;
+                }
+              store_append_adaptor (store, adaptor);
+            }     
+        }
+    }
+}
+
+static void
+on_treeview_row_activated (GtkTreeView         *tree_view,
+                           GtkTreePath         *path,
+                           GtkTreeViewColumn   *column,
+                           _GladeAdaptorChooser *chooser)
+{
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view); 
+  GtkTreeIter iter;
+
+  if (gtk_tree_model_get_iter (model, &iter, path))
+    {
+      GladeWidgetAdaptor *adaptor;
+
+      gtk_tree_model_get (model, &iter, COLUMN_ADAPTOR, &adaptor, -1);
+
+      /* Emit selected signal */
+      g_signal_emit (chooser, adaptor_chooser_signals[ADAPTOR_SELECTED], 0, adaptor);
+
+      g_object_unref (adaptor);
+    }
+}
+
+static void
+on_searchentry_activate (GtkEntry *entry, _GladeAdaptorChooser *chooser)
+{
+  const gchar *text = gtk_entry_get_text (entry);
+  GladeWidgetAdaptor *adaptor;
+
+  /* try to find an adaptor by name */
+  if (!(adaptor = glade_widget_adaptor_get_by_name (text)))
+    {
+      GtkTreeModel *model = GTK_TREE_MODEL (chooser->priv->treemodelfilter);
+      gchar *normalized_name = normalize_name (text);
+      GtkTreeIter iter;
+      gboolean valid;
+      gint count = 0;
+
+      valid = gtk_tree_model_get_iter_first (model, &iter);
+
+      /* we could not find it check if we can find it by normalized name */
+      while (valid)
+        {
+          gchar *name;
+
+          gtk_tree_model_get (model, &iter, COLUMN_NORMALIZED_NAME, &name, -1);
+
+          if (g_strcmp0 (name, normalized_name) == 0)
+            {
+              gtk_tree_model_get (model, &iter, COLUMN_ADAPTOR, &adaptor, -1);
+              g_free (name);
+              break;
+            }
+
+          valid = gtk_tree_model_iter_next (model, &iter);
+          g_free (name);
+          count++;
+        }
+
+      /* if not, and there is only one row, then we select that one */
+      if (!adaptor && count == 1 && gtk_tree_model_get_iter_first (model, &iter))
+        gtk_tree_model_get (model, &iter, COLUMN_ADAPTOR, &adaptor, -1);
+
+      g_free (normalized_name);
+    }
+
+  if (adaptor)
+    g_signal_emit (chooser, adaptor_chooser_signals[ADAPTOR_SELECTED], 0, adaptor);
+}
+
+static gboolean
+chooser_match_func (_GladeAdaptorChooser *chooser,
+                    GtkTreeModel         *model,
+                    const gchar          *key,
+                    GtkTreeIter          *iter)
+{
+  gboolean visible;
+  gint name_len;
+  gchar *name;
+
+  if (!key || *key == '\0')
+    return TRUE;
+
+  gtk_tree_model_get (model, iter,
+                      COLUMN_NORMALIZED_NAME, &name,
+                      COLUMN_NORMALIZED_NAME_LEN, &name_len,
+                      -1);
+
+  visible = (g_strstr_len (name, name_len, key) != NULL);
+  g_free (name);
+
+  return visible;
+}
+
+static gboolean
+treemodelfilter_visible_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+  _GladeAdaptorChooserPrivate *priv = GLADE_ADAPTOR_CHOOSER (data)->priv;
+  gchar *key = normalize_name (gtk_entry_get_text (GTK_ENTRY (priv->searchentry)));
+  gboolean visible = chooser_match_func (data, model, key, iter);
+  g_free (key);
+  return visible;
+}
+
+static gboolean
+entrycompletion_match_func (GtkEntryCompletion *entry, const gchar *key, GtkTreeIter *iter, gpointer data)
+{
+  return chooser_match_func (data, gtk_entry_completion_get_model (entry), key, iter);
+}
+
+static void
+adaptor_cell_data_func (GtkTreeViewColumn *tree_column,
+                        GtkCellRenderer   *cell,
+                        GtkTreeModel      *tree_model,
+                        GtkTreeIter       *iter,
+                        gpointer           data)
+{
+  GladeWidgetAdaptor *adaptor;
+  gtk_tree_model_get (tree_model, iter, COLUMN_ADAPTOR, &adaptor, -1);
+
+  if (GPOINTER_TO_SIZE (data))
+    g_object_set (cell, "icon-name", glade_widget_adaptor_get_icon_name (adaptor), NULL);
+  else
+    g_object_set (cell, "text", glade_widget_adaptor_get_name (adaptor), NULL);
+
+  g_object_unref (adaptor);
+}
+
+static void
+_glade_adaptor_chooser_constructed (GObject *object)
+{
+  _GladeAdaptorChooser *chooser = GLADE_ADAPTOR_CHOOSER (object);
+  _GladeAdaptorChooserPrivate *priv = chooser->priv;
+
+  store_populate (priv->store, priv->project, priv->flags);
+
+  /* Set cell data function: this save us from alocating name and icon name for each adaptor. */
+  gtk_tree_view_column_set_cell_data_func (priv->column_icon,
+                                           priv->icon_cell,
+                                           adaptor_cell_data_func,
+                                           GSIZE_TO_POINTER (TRUE),
+                                           NULL);
+  gtk_tree_view_column_set_cell_data_func (priv->column_adaptor,
+                                           priv->adaptor_cell,
+                                           adaptor_cell_data_func,
+                                           GSIZE_TO_POINTER (FALSE),
+                                           NULL);
+  /* Set tree model filter function */
+  gtk_tree_model_filter_set_visible_func (priv->treemodelfilter,
+                                          treemodelfilter_visible_func,
+                                          chooser, NULL);
+  /* Set completion match function */
+  gtk_entry_completion_set_match_func (priv->entrycompletion,
+                                       entrycompletion_match_func,
+                                       chooser, NULL);
+}
+
+static GType
+_glade_adaptor_chooser_flags_get_type (void)
+{
+    static GType etype = 0;
+    if (G_UNLIKELY(etype == 0)) {
+        static const GFlagsValue values[] = {
+            { GLADE_ADAPTOR_CHOOSER_WIDGET, "GLADE_ADAPTOR_CHOOSER_WIDGET", "widget" },
+            { GLADE_ADAPTOR_CHOOSER_TOPLEVEL, "GLADE_ADAPTOR_CHOOSER_TOPLEVEL", "toplevel" },
+            { GLADE_ADAPTOR_CHOOSER_SKIP_TOPLEVEL, "GLADE_ADAPTOR_CHOOSER_SKIP_TOPLEVEL", "skip-toplevel" },
+            { 0, NULL, NULL }
+        };
+        etype = g_flags_register_static (g_intern_static_string ("_GladeAdaptorChooserFlag"), values);
+    }
+    return etype;
+}
+
+static void
+_glade_adaptor_chooser_class_init (_GladeAdaptorChooserClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = _glade_adaptor_chooser_finalize;
+  object_class->set_property = _glade_adaptor_chooser_set_property;
+  object_class->get_property = _glade_adaptor_chooser_get_property;
+  object_class->constructed = _glade_adaptor_chooser_constructed;
+
+  g_object_class_install_property (object_class,
+                                   PROP_SHOW_FLAGS,
+                                   g_param_spec_flags ("show-flags",
+                                                       "Show flags",
+                                                       "Widget adaptors show flags",
+                                                       _glade_adaptor_chooser_flags_get_type (),
+                                                       GLADE_ADAPTOR_CHOOSER_WIDGET,
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+                                   PROP_PROJECT,
+                                   g_param_spec_object ("project",
+                                                        "Glade Project",
+                                                        "If set, use project target version to skip 
unsupported classes",
+                                                        GLADE_TYPE_PROJECT,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  adaptor_chooser_signals[ADAPTOR_SELECTED] =
+    g_signal_new ("adaptor-selected", G_OBJECT_CLASS_TYPE (klass), 0, 0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1,
+                  GLADE_TYPE_WIDGET_ADAPTOR);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/gladeui/glade-adaptor-chooser.ui");
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, store);
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, treemodelfilter);
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, searchentry);
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, entrycompletion);
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, column_icon);
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, icon_cell);
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, column_adaptor);
+  gtk_widget_class_bind_template_child_private (widget_class, _GladeAdaptorChooser, adaptor_cell);
+  gtk_widget_class_bind_template_callback (widget_class, on_treeview_row_activated);
+  gtk_widget_class_bind_template_callback (widget_class, on_searchentry_activate);
+}
+
+GtkWidget *
+_glade_adaptor_chooser_new (_GladeAdaptorChooserFlags flags, GladeProject *project)
+{
+  return GTK_WIDGET (g_object_new (GLADE_TYPE_ADAPTOR_CHOOSER,
+                                   "show-flags", flags,
+                                   "project", project,
+                                   NULL));
+}
diff --git a/gladeui/glade-adaptor-chooser.h b/gladeui/glade-adaptor-chooser.h
new file mode 100644
index 0000000..8c33208
--- /dev/null
+++ b/gladeui/glade-adaptor-chooser.h
@@ -0,0 +1,75 @@
+/*
+ * glade-adaptor-chooser.h
+ *
+ * Copyright (C) 2014 Juan Pablo Ugarte
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public 
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Authors:
+ *   Juan Pablo Ugarte <juanpablougarte gmail com>
+ */
+
+#ifndef _GLADE_ADAPTOR_CHOOSER_H_
+#define _GLADE_ADAPTOR_CHOOSER_H_
+
+#include <gladeui/glade-widget-adaptor.h>
+
+G_BEGIN_DECLS
+
+#define GLADE_TYPE_ADAPTOR_CHOOSER             (_glade_adaptor_chooser_get_type ())
+#define GLADE_ADAPTOR_CHOOSER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GLADE_TYPE_ADAPTOR_CHOOSER, _GladeAdaptorChooser))
+#define GLADE_ADAPTOR_CHOOSER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), 
GLADE_TYPE_ADAPTOR_CHOOSER, _GladeAdaptorChooserClass))
+#define GLADE_IS_ADAPTOR_CHOOSER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GLADE_TYPE_ADAPTOR_CHOOSER))
+#define GLADE_IS_ADAPTOR_CHOOSER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GLADE_TYPE_ADAPTOR_CHOOSER))
+#define GLADE_ADAPTOR_CHOOSER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GLADE_TYPE_ADAPTOR_CHOOSER, _GladeAdaptorChooserClass))
+
+typedef struct _GladeAdaptorChooserClass   _GladeAdaptorChooserClass;
+typedef struct _GladeAdaptorChooser        _GladeAdaptorChooser;
+typedef struct _GladeAdaptorChooserPrivate _GladeAdaptorChooserPrivate;
+
+typedef enum
+{
+  GLADE_ADAPTOR_CHOOSER_WIDGET        = 1 << 0,
+  GLADE_ADAPTOR_CHOOSER_TOPLEVEL      = 1 << 1,
+  GLADE_ADAPTOR_CHOOSER_SKIP_TOPLEVEL = 1 << 2
+} _GladeAdaptorChooserFlags;
+
+struct _GladeAdaptorChooserClass
+{
+  GtkBoxClass parent_class;
+
+  /* Signals */ 
+  void (* padding1) (void);
+  void (* padding2) (void);
+  void (* padding3) (void);
+  void (* padding4) (void);
+};
+
+struct _GladeAdaptorChooser
+{
+  GtkBox parent_instance;
+
+  _GladeAdaptorChooserPrivate *priv;
+};
+
+GType _glade_adaptor_chooser_get_type (void) G_GNUC_CONST;
+
+GtkWidget *_glade_adaptor_chooser_new (_GladeAdaptorChooserFlags  flags,
+                                       GladeProject             *project);
+
+G_END_DECLS
+
+#endif /* _GLADE_ADAPTOR_CHOOSER_H_ */
+
diff --git a/gladeui/glade-adaptor-chooser.ui b/gladeui/glade-adaptor-chooser.ui
new file mode 100644
index 0000000..086053c
--- /dev/null
+++ b/gladeui/glade-adaptor-chooser.ui
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.1 -->
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <object class="GtkListStore" id="store">
+    <columns>
+      <!-- column-name adaptor -->
+      <column type="GObject"/>
+      <!-- column-name normalized-name -->
+      <column type="gchararray"/>
+      <!-- column-name normalized-name-len -->
+      <column type="gint"/>
+    </columns>
+  </object>
+  <object class="GtkTreeModelFilter" id="treemodelfilter">
+    <property name="child_model">store</property>
+  </object>
+  <object class="GtkEntryCompletion" id="entrycompletion">
+    <property name="model">treemodelfilter</property>
+    <property name="text_column">1</property>
+    <property name="inline_completion">True</property>
+    <property name="popup_completion">False</property>
+    <property name="popup_single_match">False</property>
+  </object>
+  <template class="_GladeAdaptorChooser" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkSearchEntry" id="searchentry">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="primary_icon_name">edit-find-symbolic</property>
+        <property name="primary_icon_activatable">False</property>
+        <property name="primary_icon_sensitive">False</property>
+        <property name="completion">entrycompletion</property>
+        <property name="input_hints">GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_NONE</property>
+        <signal name="activate" handler="on_searchentry_activate" swapped="no"/>
+        <signal name="search-changed" handler="gtk_tree_model_filter_refilter" object="treemodelfilter" 
swapped="yes"/>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkScrolledWindow" id="scrolledwindow">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="hscrollbar_policy">never</property>
+        <property name="shadow_type">in</property>
+        <property name="min_content_height">196</property>
+        <child>
+          <object class="GtkTreeView" id="treeview">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="model">treemodelfilter</property>
+            <property name="headers_visible">False</property>
+            <property name="enable_search">False</property>
+            <signal name="row-activated" handler="on_treeview_row_activated" swapped="no"/>
+            <child internal-child="selection">
+              <object class="GtkTreeSelection" id="treeview-selection"/>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="column_icon">
+                <property name="title" translatable="yes">column</property>
+                <child>
+                  <object class="GtkCellRendererPixbuf" id="icon_cell"/>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkTreeViewColumn" id="column_adaptor">
+                <property name="title" translatable="yes">column</property>
+                <child>
+                  <object class="GtkCellRendererText" id="adaptor_cell"/>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/gladeui/glade-design-view.c b/gladeui/glade-design-view.c
index a6445fa..598579b 100644
--- a/gladeui/glade-design-view.c
+++ b/gladeui/glade-design-view.c
@@ -41,6 +41,7 @@
 #include "glade-design-layout.h"
 #include "glade-design-private.h"
 #include "glade-path.h"
+#include "glade-adaptor-chooser.h"
 
 #include <glib.h>
 #include <glib/gi18n.h>
@@ -365,6 +366,46 @@ glade_design_view_draw (GtkWidget *widget, cairo_t *cr)
 }
 
 static void
+on_chooser_adaptor_selected (_GladeAdaptorChooser *chooser,
+                             GladeWidgetAdaptor   *adaptor,
+                             GladeProject         *project)
+
+{
+  gtk_widget_hide (GTK_WIDGET (chooser));
+  glade_command_create (adaptor, NULL, NULL, project);
+  gtk_widget_destroy (GTK_WIDGET (chooser));
+}
+
+static gboolean
+glade_design_view_viewport_button_press (GtkWidget       *widget,
+                                         GdkEventButton  *event,
+                                         GladeDesignView *view)
+{
+  GladeDesignViewPrivate *priv = view->priv;
+  GdkRectangle rect = {event->x, event->y, 8, 8};
+  GtkWidget *pop, *chooser;
+  
+  if (event->type != GDK_2BUTTON_PRESS)
+    return FALSE;
+
+  pop = gtk_popover_new (widget);
+  gtk_popover_set_pointing_to (GTK_POPOVER (pop), &rect);
+  gtk_popover_set_position (GTK_POPOVER (pop), GTK_POS_BOTTOM);
+  
+  chooser = _glade_adaptor_chooser_new (GLADE_ADAPTOR_CHOOSER_TOPLEVEL,
+                                        priv->project);
+  g_signal_connect (chooser, "adaptor-selected",
+                    G_CALLBACK (on_chooser_adaptor_selected),
+                    priv->project);
+
+  gtk_container_add (GTK_CONTAINER (pop), chooser);
+  gtk_widget_show (chooser);
+  gtk_widget_show (pop);
+
+  return TRUE;
+}
+
+static void
 glade_design_view_init (GladeDesignView *view)
 {
   GtkWidget *viewport;
@@ -386,6 +427,10 @@ glade_design_view_init (GladeDesignView *view)
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
   viewport = gtk_viewport_new (NULL, NULL);
+  gtk_widget_add_events (viewport, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+  g_signal_connect (viewport, "button-press-event",
+                    G_CALLBACK (glade_design_view_viewport_button_press),
+                    view);
   gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
   gtk_container_add (GTK_CONTAINER (viewport), view->priv->layout_box);
   gtk_container_add (GTK_CONTAINER (view->priv->scrolled_window), viewport);
diff --git a/gladeui/glade-placeholder.c b/gladeui/glade-placeholder.c
index 40a4cb0..5fcee12 100644
--- a/gladeui/glade-placeholder.c
+++ b/gladeui/glade-placeholder.c
@@ -45,6 +45,7 @@
 #include "glade-cursor.h"
 #include "glade-widget.h"
 #include "glade-app.h"
+#include "glade-adaptor-chooser.h"
 #include <math.h>
 
 #include "glade-dnd.h"
@@ -380,6 +381,38 @@ glade_placeholder_motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
   return FALSE;
 }
 
+static void
+on_chooser_adaptor_selected (_GladeAdaptorChooser *chooser,
+                             GladeWidgetAdaptor  *adaptor,
+                             GladePlaceholder    *placeholder)
+
+{
+  gtk_widget_hide (GTK_WIDGET (chooser));
+  glade_command_create (adaptor, glade_placeholder_get_parent (placeholder),
+                        placeholder, glade_placeholder_get_project (placeholder));
+  gtk_widget_destroy (GTK_WIDGET (chooser));
+}
+
+static GtkWidget *
+glade_placeholder_popover_new (GladePlaceholder *placeholder, GtkWidget *relative_to)
+{
+  GtkWidget *pop = gtk_popover_new (relative_to);
+  GtkWidget *chooser;
+
+  chooser = _glade_adaptor_chooser_new (GLADE_ADAPTOR_CHOOSER_WIDGET |
+                                        GLADE_ADAPTOR_CHOOSER_SKIP_TOPLEVEL,
+                                        glade_placeholder_get_project (placeholder));
+
+  g_signal_connect (chooser, "adaptor-selected",
+                    G_CALLBACK (on_chooser_adaptor_selected),
+                    placeholder);
+  gtk_popover_set_position (GTK_POPOVER (pop), GTK_POS_BOTTOM);
+  gtk_container_add (GTK_CONTAINER (pop), chooser);
+  gtk_widget_show (chooser);
+
+  return pop;
+}
+
 static gboolean
 glade_placeholder_button_press (GtkWidget *widget, GdkEventButton *event)
 {
@@ -397,9 +430,9 @@ glade_placeholder_button_press (GtkWidget *widget, GdkEventButton *event)
   if (!gtk_widget_has_focus (widget))
     gtk_widget_grab_focus (widget);
 
-  if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
+  if (event->button == 1)
     {
-      if (adaptor != NULL)
+      if (event->type == GDK_BUTTON_PRESS && adaptor != NULL)
         {
           GladeWidget *parent = glade_placeholder_get_parent (placeholder);
 
@@ -414,6 +447,21 @@ glade_placeholder_button_press (GtkWidget *widget, GdkEventButton *event)
           glade_cursor_set (project, event->window, GLADE_CURSOR_SELECTOR);
           handled = TRUE;
         }
+      else if (event->type == GDK_2BUTTON_PRESS && adaptor == NULL)
+        {
+          GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
+          GladeWidget *toplevel = glade_widget_get_toplevel (glade_placeholder_get_parent (placeholder));
+          GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (glade_widget_get_object (toplevel)));
+          GtkWidget *pop = glade_placeholder_popover_new (placeholder, parent);
+          GdkRectangle rect = {0, 0, 8, 8};
+
+          gtk_widget_translate_coordinates (event_widget, parent,
+                                            event->x, event->y,
+                                            &rect.x, &rect.y);
+          gtk_popover_set_pointing_to (GTK_POPOVER (pop), &rect);
+          gtk_widget_show (pop);
+          handled = TRUE;
+        }
     }
 
   if (!handled && glade_popup_is_popup_event (event))
diff --git a/gladeui/gladeui-resources.gresource.xml b/gladeui/gladeui-resources.gresource.xml
index 2b811e2..fddfe9a 100644
--- a/gladeui/gladeui-resources.gresource.xml
+++ b/gladeui/gladeui-resources.gresource.xml
@@ -5,5 +5,6 @@
     <file compressed="true" preprocess="xml-stripblanks">glade-editor.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">glade-project-properties.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">glade-property-label.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">glade-adaptor-chooser.ui</file>
   </gresource>
 </gresources>


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