[gimp] app: new GimpSearchPopup widget.



commit 6a9d449eac943dc8e0357ee41387c5e357d39f5c
Author: Jehan <jehan girinstud io>
Date:   Sun Sep 6 20:30:39 2015 +0200

    app: new GimpSearchPopup widget.
    
    Based on GimpPopup as parent, this is a generic search popup widget,
    which can display any list of action. The results construction logics
    is not part of the widget, and is built through a callback instead,
    which could allow to use it to create different dialogs.

 app/widgets/Makefile.am       |    2 +
 app/widgets/gimpsearchpopup.c |  766 +++++++++++++++++++++++++++++++++++++++++
 app/widgets/gimpsearchpopup.h |   74 ++++
 3 files changed, 842 insertions(+), 0 deletions(-)
---
diff --git a/app/widgets/Makefile.am b/app/widgets/Makefile.am
index a352bba..91c964f 100644
--- a/app/widgets/Makefile.am
+++ b/app/widgets/Makefile.am
@@ -302,6 +302,8 @@ libappwidgets_a_sources = \
        gimpsavedialog.h                \
        gimpscalebutton.c               \
        gimpscalebutton.h               \
+       gimpsearchpopup.c               \
+       gimpsearchpopup.h               \
        gimpselectiondata.c             \
        gimpselectiondata.h             \
        gimpselectioneditor.c           \
diff --git a/app/widgets/gimpsearchpopup.c b/app/widgets/gimpsearchpopup.c
new file mode 100644
index 0000000..fdaaeb8
--- /dev/null
+++ b/app/widgets/gimpsearchpopup.c
@@ -0,0 +1,766 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpsearchpopup.c
+ * Copyright (C) 2015 Jehan <jehan at girinstud.io>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "core/gimp.h"
+
+#include "gimpaction.h"
+#include "gimppopup.h"
+#include "gimpsearchpopup.h"
+#include "gimpuimanager.h"
+
+#include "gimp-intl.h"
+
+enum
+{
+  COLUMN_ICON,
+  COLUMN_MARKUP,
+  COLUMN_TOOLTIP,
+  COLUMN_ACTION,
+  COLUMN_SENSITIVE,
+  COLUMN_SECTION,
+  N_COL
+};
+
+enum
+{
+    PROP_0,
+    PROP_GIMP,
+    PROP_CALLBACK,
+    PROP_CALLBACK_DATA
+};
+
+struct _GimpSearchPopupPrivate
+{
+  Gimp                    *gimp;
+  GtkWidget               *keyword_entry;
+  GtkWidget               *results_list;
+  GtkWidget               *list_view;
+
+  gint                     window_height;
+
+  GimpSearchPopupCallback  build_results;
+  gpointer                 build_results_data;
+};
+
+/* GObject handlers */
+static void       gimp_search_popup_constructed         (GObject            *object);
+static void       gimp_search_popup_set_property        (GObject            *object,
+                                                         guint               property_id,
+                                                         const GValue       *value,
+                                                         GParamSpec         *pspec);
+static void       gimp_search_popup_get_property        (GObject            *object,
+                                                         guint               property_id,
+                                                         GValue             *value,
+                                                         GParamSpec         *pspec);
+
+/* GimpPopup handlers */
+static void       gimp_search_popup_confirm              (GimpPopup *popup);
+
+/* Signal handlers on the search entry */
+static gboolean   keyword_entry_on_key_press_event       (GtkWidget         *widget,
+                                                          GdkEventKey       *event,
+                                                          GimpSearchPopup   *popup);
+static gboolean   keyword_entry_on_key_release_event     (GtkWidget         *widget,
+                                                          GdkEventKey       *event,
+                                                          GimpSearchPopup   *popup);
+/* Signal handlers on the results list */
+static gboolean   results_list_on_key_press_event        (GtkWidget         *widget,
+                                                          GdkEventKey       *kevent,
+                                                          GimpSearchPopup   *popup);
+static void       results_list_on_row_activated          (GtkTreeView       *treeview,
+                                                          GtkTreePath       *path,
+                                                          GtkTreeViewColumn *col,
+                                                          GimpSearchPopup   *popup);
+/* Signal handlers on the popup */
+static gboolean   gimp_search_popup_on_configured        (GtkWindow         *window,
+                                                          GdkEvent          *event,
+                                                          GimpSearchPopup   *popup);
+/* Utils */
+static void       gimp_search_popup_run_selected         (GimpSearchPopup   *popup);
+static void       gimp_search_popup_setup_results        (GtkWidget        **results_list,
+                                                          GtkWidget        **list_view);
+
+static gchar    * gimp_search_popup_find_accel_label     (GtkAction         *action);
+static gboolean   gimp_search_popup_view_accel_find_func (GtkAccelKey       *key,
+                                                          GClosure          *closure,
+                                                          gpointer           data);
+
+
+G_DEFINE_TYPE (GimpSearchPopup, gimp_search_popup, GIMP_TYPE_POPUP)
+
+#define parent_class gimp_search_popup_parent_class
+
+static void
+gimp_search_popup_class_init (GimpSearchPopupClass *klass)
+{
+  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+  GimpPopupClass *popup_class = GIMP_POPUP_CLASS (klass);
+  GtkBindingSet  *binding_set;
+
+  object_class->constructed  = gimp_search_popup_constructed;
+  object_class->set_property = gimp_search_popup_set_property;
+  object_class->get_property = gimp_search_popup_get_property;
+
+  popup_class->confirm       = gimp_search_popup_confirm;
+
+  /**
+   * GimpSearchPopup:gimp:
+   *
+   * The #Gimp object.
+   */
+  g_object_class_install_property (object_class, PROP_GIMP,
+                                   g_param_spec_object ("gimp",
+                                                        NULL, NULL,
+                                                        G_TYPE_OBJECT,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY));
+  /**
+   * GimpSearchPopup:callback:
+   *
+   * The #GimpSearchPopupCallback used to fill in results.
+   */
+  g_object_class_install_property (object_class, PROP_CALLBACK,
+                                   g_param_spec_pointer ("callback", NULL, NULL,
+                                                         GIMP_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT_ONLY));
+  /**
+   * GimpSearchPopup:callback-data:
+   *
+   * The #GPointer fed as last parameter to the #GimpSearchPopupCallback.
+   */
+  g_object_class_install_property (object_class, PROP_CALLBACK_DATA,
+                                   g_param_spec_pointer ("callback-data", NULL, NULL,
+                                                         GIMP_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT_ONLY));
+
+  /* We don't want space to activate actions in the search widget, since
+   * we allow search with spaces in it. */
+  binding_set = gtk_binding_set_by_class (g_type_class_peek (GIMP_TYPE_POPUP));
+
+  gtk_binding_entry_remove (binding_set, GDK_KEY_space, 0);
+  gtk_binding_entry_remove (binding_set, GDK_KEY_KP_Space, 0);
+
+  g_type_class_add_private (klass, sizeof (GimpSearchPopupPrivate));
+}
+
+static void
+gimp_search_popup_init (GimpSearchPopup *search_popup)
+{
+  search_popup->priv = G_TYPE_INSTANCE_GET_PRIVATE (search_popup,
+                                                    GIMP_TYPE_SEARCH_POPUP,
+                                                    GimpSearchPopupPrivate);
+}
+
+/************ Public Functions ****************/
+
+/**
+ * gimp_search_popup_new:
+ * @gimp:          #Gimp object.
+ * @role:          the role to give to the #GtkWindow.
+ * @title:         the #GtkWindow title.
+ * @callback:      the #GimpSearchPopupCallback used to fill in results.
+ * @callback_data: data fed to @callback.
+ *
+ * Returns: a new #GimpSearchPopup.
+ */
+GtkWidget *
+gimp_search_popup_new (Gimp                    *gimp,
+                       const gchar             *role,
+                       const gchar             *title,
+                       GimpSearchPopupCallback  callback,
+                       gpointer                 callback_data)
+{
+  GtkWidget    *widget;
+
+  widget = g_object_new (GIMP_TYPE_SEARCH_POPUP,
+                         "type",          GTK_WINDOW_TOPLEVEL,
+                         "decorated",     TRUE,
+                         "modal",         TRUE,
+                         "role",          role,
+                         "title",         title,
+
+                         "gimp",          gimp,
+                         "callback",      callback,
+                         "callback-data", callback_data,
+                         NULL);
+
+
+  return widget;
+}
+
+/**
+ * gimp_search_popup_add_result:
+ * @popup:   the #GimpSearchPopup.
+ * @action:  a #GtkAction to add in results list.
+ * @section: the section to add @action.
+ *
+ * Adds @action in the @popup's results at @section.
+ * The section only indicates relative order. If you want some items
+ * to appear before other, simply use lower @section.
+ */
+void
+gimp_search_popup_add_result (GimpSearchPopup *popup,
+                              GtkAction       *action,
+                              gint             section)
+{
+  GtkTreeIter   iter;
+  GtkTreeIter   next_section;
+  GtkListStore *store;
+  GtkTreeModel *model;
+  gchar        *markup;
+  gchar        *action_name;
+  gchar        *label;
+  gchar        *escaped_label = NULL;
+  const gchar  *icon_name;
+  gchar        *accel_string;
+  gchar        *escaped_accel = NULL;
+  gboolean      has_shortcut = FALSE;
+  const gchar  *tooltip;
+  gchar        *escaped_tooltip = NULL;
+  gboolean      has_tooltip  = FALSE;
+
+  label = g_strstrip (gimp_strip_uline (gtk_action_get_label (action)));
+
+  if (! label || strlen (label) == 0)
+    {
+      g_free (label);
+      return;
+    }
+
+  escaped_label = g_markup_escape_text (label, -1);
+
+  if (GTK_IS_TOGGLE_ACTION (action))
+    {
+      if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+        icon_name = "gtk-ok";
+      else
+        icon_name = "gtk-no";
+    }
+  else
+    {
+      icon_name = gtk_action_get_icon_name (action);
+    }
+
+  accel_string = gimp_search_popup_find_accel_label (action);
+  if (accel_string != NULL)
+    {
+      escaped_accel = g_markup_escape_text (accel_string, -1);
+      has_shortcut = TRUE;
+    }
+
+  tooltip = gtk_action_get_tooltip (action);
+  if (tooltip != NULL)
+    {
+      escaped_tooltip = g_markup_escape_text (tooltip, -1);
+      has_tooltip = TRUE;
+    }
+
+  markup = g_strdup_printf ("%s<small>%s%s%s<span weight='light'>%s</span></small>",
+                            escaped_label,
+                            has_shortcut ? " | " : "",
+                            has_shortcut ? escaped_accel : "",
+                            has_tooltip ? "\n" : "",
+                            has_tooltip ? escaped_tooltip : "");
+
+  action_name = g_markup_escape_text (gtk_action_get_name (action), -1);
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (popup->priv->results_list));
+  store = GTK_LIST_STORE (model);
+  if (gtk_tree_model_get_iter_first (model, &next_section))
+    {
+      while (TRUE)
+        {
+          gint iter_section;
+
+          gtk_tree_model_get (model, &next_section,
+                              COLUMN_SECTION, &iter_section, -1);
+          if (iter_section > section)
+            {
+              gtk_list_store_insert_before (store, &iter, &next_section);
+              break;
+            }
+          else if (! gtk_tree_model_iter_next (model, &next_section))
+            {
+              gtk_list_store_append (store, &iter);
+              break;
+            }
+        }
+    }
+  else
+    {
+      gtk_list_store_append (store, &iter);
+    }
+
+  gtk_list_store_set (store, &iter,
+                      COLUMN_ICON,      icon_name,
+                      COLUMN_MARKUP,    markup,
+                      COLUMN_TOOLTIP,   action_name,
+                      COLUMN_ACTION,    action,
+                      COLUMN_SECTION,   section,
+                      COLUMN_SENSITIVE, gtk_action_is_sensitive (action),
+                      -1);
+
+  g_free (accel_string);
+  g_free (markup);
+  g_free (action_name);
+  g_free (label);
+  g_free (escaped_accel);
+  g_free (escaped_label);
+  g_free (escaped_tooltip);
+}
+
+/************ Private Functions ****************/
+
+static void
+gimp_search_popup_constructed (GObject *object)
+{
+  GimpSearchPopup *popup  = GIMP_SEARCH_POPUP (object);
+  GdkScreen       *screen = gdk_screen_get_default ();
+  GtkWidget       *main_vbox;
+
+  G_OBJECT_CLASS (parent_class)->constructed (object);
+
+  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (popup), main_vbox);
+  gtk_widget_show (main_vbox);
+
+  popup->priv->keyword_entry = gtk_entry_new ();
+  gtk_entry_set_icon_from_icon_name (GTK_ENTRY (popup->priv->keyword_entry),
+                                     GTK_ENTRY_ICON_PRIMARY, "edit-find");
+  gtk_box_pack_start (GTK_BOX (main_vbox),
+                      popup->priv->keyword_entry,
+                      FALSE, FALSE, 0);
+  gtk_widget_show (popup->priv->keyword_entry);
+
+  gimp_search_popup_setup_results (&popup->priv->results_list,
+                                   &popup->priv->list_view);
+  gtk_box_pack_start (GTK_BOX (main_vbox),
+                      popup->priv->list_view, TRUE, TRUE, 0);
+
+  gtk_widget_set_events (GTK_WIDGET (object),
+                         GDK_KEY_RELEASE_MASK  |
+                         GDK_KEY_PRESS_MASK    |
+                         GDK_BUTTON_PRESS_MASK |
+                         GDK_SCROLL_MASK);
+
+  g_signal_connect (popup->priv->keyword_entry, "key-press-event",
+                    G_CALLBACK (keyword_entry_on_key_press_event),
+                    popup);
+  g_signal_connect (popup->priv->keyword_entry, "key-release-event",
+                    G_CALLBACK (keyword_entry_on_key_release_event),
+                    popup);
+
+  g_signal_connect (popup->priv->results_list, "key-press-event",
+                    G_CALLBACK (results_list_on_key_press_event),
+                    popup);
+  g_signal_connect (popup->priv->results_list, "row-activated",
+                    G_CALLBACK (results_list_on_row_activated),
+                    popup);
+
+  g_signal_connect (popup, "configure-event",
+                    G_CALLBACK (gimp_search_popup_on_configured),
+                    popup);
+
+  popup->priv->window_height = gdk_screen_get_height (screen) / 2;
+}
+
+static void
+gimp_search_popup_set_property (GObject      *object,
+                                guint         property_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GimpSearchPopup *search_popup = GIMP_SEARCH_POPUP (object);
+
+  switch (property_id)
+    {
+    case PROP_GIMP:
+      search_popup->priv->gimp = g_value_get_object (value);
+      break;
+    case PROP_CALLBACK:
+      search_popup->priv->build_results = g_value_get_pointer (value);
+      break;
+    case PROP_CALLBACK_DATA:
+      search_popup->priv->build_results_data = g_value_get_pointer (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_search_popup_get_property (GObject      *object,
+                                guint         property_id,
+                                GValue       *value,
+                                GParamSpec   *pspec)
+{
+  GimpSearchPopup *search_popup = GIMP_SEARCH_POPUP (object);
+
+  switch (property_id)
+    {
+    case PROP_GIMP:
+      g_value_set_object (value, search_popup->priv->gimp);
+      break;
+    case PROP_CALLBACK:
+      g_value_set_pointer (value, search_popup->priv->build_results);
+      break;
+    case PROP_CALLBACK_DATA:
+      g_value_set_pointer (value, search_popup->priv->build_results_data);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_search_popup_confirm (GimpPopup *popup)
+{
+  GimpSearchPopup *search_popup = GIMP_SEARCH_POPUP (popup);
+
+  gimp_search_popup_run_selected (search_popup);
+}
+
+static gboolean
+keyword_entry_on_key_press_event (GtkWidget       *widget,
+                                  GdkEventKey     *event,
+                                  GimpSearchPopup *popup)
+{
+  gboolean event_processed = FALSE;
+
+  if (event->keyval == GDK_KEY_Down &&
+      gtk_widget_get_visible (popup->priv->list_view))
+    {
+      GtkTreeView *tree_view = GTK_TREE_VIEW (popup->priv->results_list);
+
+      /* When hitting the down key while editing, select directly the
+       * second item, since the first could have run directly with
+       * Enter. */
+      gtk_tree_selection_select_path (gtk_tree_view_get_selection (tree_view),
+                                      gtk_tree_path_new_from_string ("1"));
+      gtk_widget_grab_focus (GTK_WIDGET (popup->priv->results_list));
+      event_processed = TRUE;
+    }
+
+  return event_processed;
+}
+
+static gboolean
+keyword_entry_on_key_release_event (GtkWidget       *widget,
+                                    GdkEventKey     *event,
+                                    GimpSearchPopup *popup)
+{
+  GtkTreeView *tree_view = GTK_TREE_VIEW (popup->priv->results_list);
+  gchar       *entry_text;
+  gint         width;
+
+  /* These keys are already managed by key bindings. */
+  if (event->keyval == GDK_KEY_Escape   ||
+      event->keyval == GDK_KEY_Return   ||
+      event->keyval == GDK_KEY_KP_Enter ||
+      event->keyval == GDK_KEY_ISO_Enter)
+    {
+      return FALSE;
+    }
+
+  gtk_window_get_size (GTK_WINDOW (popup), &width, NULL);
+  entry_text = g_strstrip (gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1));
+
+  if (strcmp (entry_text, "") != 0)
+    {
+      gtk_window_resize (GTK_WINDOW (popup),
+                         width, popup->priv->window_height);
+      gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (tree_view)));
+      gtk_widget_show_all (popup->priv->list_view);
+      popup->priv->build_results (popup, entry_text,
+                                  popup->priv->build_results_data);
+      gtk_tree_selection_select_path (gtk_tree_view_get_selection (tree_view),
+                                      gtk_tree_path_new_from_string ("0"));
+    }
+  else if (strcmp (entry_text, "") == 0 && (event->keyval == GDK_KEY_Down))
+    {
+      gtk_window_resize (GTK_WINDOW (popup),
+                         width, popup->priv->window_height);
+      gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (tree_view)));
+      gtk_widget_show_all (popup->priv->list_view);
+      popup->priv->build_results (popup, NULL,
+                                  popup->priv->build_results_data);
+      gtk_tree_selection_select_path (gtk_tree_view_get_selection (tree_view),
+                                      gtk_tree_path_new_from_string ("0"));
+    }
+  else
+    {
+      GtkTreeSelection *selection;
+      GtkTreeModel     *model;
+      GtkTreeIter       iter;
+
+      selection = gtk_tree_view_get_selection (tree_view);
+      gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+      if (gtk_tree_selection_get_selected (selection, &model, &iter))
+        {
+          GtkTreePath *path;
+
+          path = gtk_tree_model_get_path (model, &iter);
+          gtk_tree_selection_unselect_path (selection, path);
+
+          gtk_tree_path_free (path);
+        }
+
+      gtk_widget_hide (popup->priv->list_view);
+      gtk_window_resize (GTK_WINDOW (popup), width, 1);
+    }
+
+  g_free (entry_text);
+
+  return TRUE;
+}
+
+static gboolean
+results_list_on_key_press_event (GtkWidget       *widget,
+                                 GdkEventKey     *kevent,
+                                 GimpSearchPopup *popup)
+{
+  /* These keys are already managed by key bindings. */
+  g_return_if_fail (kevent->keyval != GDK_KEY_Escape   &&
+                    kevent->keyval != GDK_KEY_Return   &&
+                    kevent->keyval != GDK_KEY_KP_Enter &&
+                    kevent->keyval != GDK_KEY_ISO_Enter);
+
+  switch (kevent->keyval)
+    {
+    case GDK_KEY_Up:
+      {
+        gboolean          event_processed = FALSE;
+        GtkTreeSelection *selection;
+        GtkTreeModel     *model;
+        GtkTreeIter       iter;
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (popup->priv->results_list));
+        gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+        if (gtk_tree_selection_get_selected (selection, &model, &iter))
+          {
+            GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
+
+            if (strcmp (gtk_tree_path_to_string (path), "0") == 0)
+              {
+                gint start_pos;
+                gint end_pos;
+
+                gtk_editable_get_selection_bounds (GTK_EDITABLE (popup->priv->keyword_entry),
+                                                   &start_pos, &end_pos);
+                gtk_widget_grab_focus ((GTK_WIDGET (popup->priv->keyword_entry)));
+                gtk_editable_select_region (GTK_EDITABLE (popup->priv->keyword_entry),
+                                            start_pos, end_pos);
+
+                event_processed = TRUE;
+              }
+
+            gtk_tree_path_free (path);
+          }
+
+        return event_processed;
+      }
+    case GDK_KEY_Down:
+      {
+        return FALSE;
+      }
+    default:
+      {
+        gint start_pos;
+        gint end_pos;
+
+        gtk_editable_get_selection_bounds (GTK_EDITABLE (popup->priv->keyword_entry),
+                                           &start_pos, &end_pos);
+        gtk_widget_grab_focus ((GTK_WIDGET (popup->priv->keyword_entry)));
+        gtk_editable_select_region (GTK_EDITABLE (popup->priv->keyword_entry),
+                                    start_pos, end_pos);
+        gtk_widget_event (GTK_WIDGET (popup->priv->keyword_entry),
+                          (GdkEvent *) kevent);
+      }
+    }
+
+  return FALSE;
+}
+
+static void
+results_list_on_row_activated (GtkTreeView       *treeview,
+                               GtkTreePath       *path,
+                               GtkTreeViewColumn *col,
+                               GimpSearchPopup   *popup)
+{
+  gimp_search_popup_run_selected (popup);
+}
+
+static gboolean
+gimp_search_popup_on_configured (GtkWindow       *window,
+                                 GdkEvent        *event,
+                                 GimpSearchPopup *popup)
+{
+  if (gtk_widget_get_visible (GTK_WIDGET (window)) &&
+      gtk_widget_get_visible (popup->priv->list_view))
+    {
+      gtk_window_get_size (GTK_WINDOW (popup),
+                           NULL, &popup->priv->window_height);
+    }
+
+  return FALSE;
+}
+
+static void
+gimp_search_popup_run_selected (GimpSearchPopup *popup)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreeIter       iter;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (popup->priv->results_list));
+  gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      GtkAction *action;
+
+      gtk_tree_model_get (model, &iter, COLUMN_ACTION, &action, -1);
+
+      if (gtk_action_is_sensitive (action))
+        {
+          gtk_action_activate (action);
+          /* Close the search popup on activation. */
+          GIMP_POPUP_CLASS (parent_class)->cancel (GIMP_POPUP (popup));
+        }
+
+      g_object_unref (action);
+    }
+}
+
+static void
+gimp_search_popup_setup_results (GtkWidget **results_list,
+                                 GtkWidget **list_view)
+{
+  gint                wid1 = 100;
+  GtkListStore       *store;
+  GtkCellRenderer    *cell;
+  GtkTreeViewColumn  *column;
+
+  *list_view = GTK_WIDGET (gtk_scrolled_window_new (NULL, NULL));
+  store = gtk_list_store_new (N_COL,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              GTK_TYPE_ACTION,
+                              G_TYPE_BOOLEAN,
+                              G_TYPE_INT);
+  *results_list = GTK_WIDGET (gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)));
+  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (*results_list), FALSE);
+#ifdef GIMP_UNSTABLE
+  gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (*results_list),
+                                    COLUMN_TOOLTIP);
+#endif
+
+  cell = gtk_cell_renderer_pixbuf_new ();
+  column = gtk_tree_view_column_new_with_attributes (NULL, cell,
+                                                     "icon-name", COLUMN_ICON,
+                                                     "sensitive", COLUMN_SENSITIVE,
+                                                     NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (*results_list), column);
+  gtk_tree_view_column_set_min_width (column, 22);
+
+  cell = gtk_cell_renderer_text_new ();
+  column = gtk_tree_view_column_new_with_attributes (NULL, cell,
+                                                     "markup",    COLUMN_MARKUP,
+                                                     "sensitive", COLUMN_SENSITIVE,
+                                                     NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (*results_list), column);
+  gtk_tree_view_column_set_max_width (column, wid1);
+
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (*list_view),
+                                  GTK_POLICY_NEVER,
+                                  GTK_POLICY_AUTOMATIC);
+
+  gtk_container_add (GTK_CONTAINER (*list_view), *results_list);
+  g_object_unref (G_OBJECT (store));
+}
+
+static gchar *
+gimp_search_popup_find_accel_label (GtkAction *action)
+{
+  guint            accel_key     = 0;
+  GdkModifierType  accel_mask    = 0;
+  GClosure        *accel_closure = NULL;
+  gchar           *accel_string;
+  GtkAccelGroup   *accel_group;
+  GimpUIManager   *manager;
+
+  manager       = gimp_ui_managers_from_name ("<Image>")->data;
+  accel_group   = gtk_ui_manager_get_accel_group (GTK_UI_MANAGER (manager));
+  accel_closure = gtk_action_get_accel_closure (action);
+
+  if (accel_closure)
+   {
+     GtkAccelKey *key;
+
+     key = gtk_accel_group_find (accel_group,
+                                 gimp_search_popup_view_accel_find_func,
+                                 accel_closure);
+     if (key            &&
+         key->accel_key &&
+         key->accel_flags & GTK_ACCEL_VISIBLE)
+       {
+         accel_key  = key->accel_key;
+         accel_mask = key->accel_mods;
+       }
+   }
+
+  accel_string = gtk_accelerator_get_label (accel_key, accel_mask);
+
+  if (strcmp (g_strstrip (accel_string), "") == 0)
+    {
+      /* The value returned by gtk_accelerator_get_label() must be freed after use. */
+      g_free (accel_string);
+      accel_string = NULL;
+    }
+
+  return accel_string;
+}
+
+static gboolean
+gimp_search_popup_view_accel_find_func (GtkAccelKey *key,
+                                        GClosure    *closure,
+                                        gpointer     data)
+{
+  return (GClosure *) data == closure;
+}
diff --git a/app/widgets/gimpsearchpopup.h b/app/widgets/gimpsearchpopup.h
new file mode 100644
index 0000000..e3098c0
--- /dev/null
+++ b/app/widgets/gimpsearchpopup.h
@@ -0,0 +1,74 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpsearchpopup.h
+ * Copyright (C) 2015 Jehan <jehan at girinstud.io>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_SEARCH_POPUP_H__
+#define __GIMP_SEARCH_POPUP_H__
+
+#include "gimppopup.h"
+
+#define GIMP_TYPE_SEARCH_POPUP            (gimp_search_popup_get_type ())
+#define GIMP_SEARCH_POPUP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SEARCH_POPUP, 
GimpSearchPopup))
+#define GIMP_SEARCH_POPUP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SEARCH_POPUP, 
GimpSearchPopupClass))
+#define GIMP_IS_SEARCH_POPUP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SEARCH_POPUP))
+#define GIMP_IS_SEARCH_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SEARCH_POPUP))
+#define GIMP_SEARCH_POPUP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SEARCH_POPUP, 
GimpSearchPopupClass))
+
+/**
+ * GimpSearchPopupCallback:
+ * @popup:  the #GimpSearchPopup to operate on.
+ * @search: the text searched.
+ * 
+ * Callback used by @popup to fill in its result list.
+ * It should make use of gimp_search_popup_add_result() to fill in
+ * results.
+ */ 
+typedef struct _GimpSearchPopup           GimpSearchPopup;
+typedef struct _GimpSearchPopupClass      GimpSearchPopupClass;
+typedef struct _GimpSearchPopupPrivate    GimpSearchPopupPrivate;
+
+typedef void   (*GimpSearchPopupCallback) (GimpSearchPopup  *popup,
+                                           const gchar      *search,
+                                           gpointer          data);
+
+struct _GimpSearchPopup
+{
+  GimpPopup               parent_instance;
+
+  GimpSearchPopupPrivate *priv;
+};
+
+struct _GimpSearchPopupClass
+{
+  GimpPopupClass          parent_class;
+};
+
+GType       gimp_search_popup_get_type   (void);
+
+GtkWidget * gimp_search_popup_new        (Gimp                    *gimp,
+                                          const gchar             *role,
+                                          const gchar             *title,
+                                          GimpSearchPopupCallback  callback,
+                                          gpointer                 callback_data);
+
+void        gimp_search_popup_add_result (GimpSearchPopup *popup,
+                                          GtkAction       *action,
+                                          gint             section);
+
+#endif  /*  __GIMP_SEARCH_POPUP_H__  */


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