[gimp/tito: 2/3] Bug 708174 - Improve the original search dialog patch.



commit 11557680b8863578998807590616ffd0ab925797
Author: Jehan <jehan girinstud io>
Date:   Thu Sep 26 06:02:59 2013 +1200

    Bug 708174 - Improve the original search dialog patch.
    
    Fix various bugs, improve code design and efficiency, change feature
    name, update the feature up to our standards (now uses GIMP preferences,
    session management, less overwhelming settings...).
    Also now action history is tightly tied to GimpAction and logs all
    action activation (however it activates, and the show_unavailable
    parameter also applies to history).
    Search algorithm greatly improved with basic tokenization, better
    ordering, filtering, etc.

 app/actions/dialogs-actions.c      |    8 +-
 app/actions/help-actions.c         |    8 +-
 app/actions/help-commands.c        |   15 +
 app/actions/help-commands.h        |   10 +-
 app/config/gimpguiconfig.c         |   23 +
 app/config/gimpguiconfig.h         |    5 +
 app/config/gimprc-blurbs.h         |    6 +
 app/dialogs/action-search-dialog.c | 1381 +++++++++++++++---------------------
 app/dialogs/action-search-dialog.h |    3 +-
 app/dialogs/dialogs-constructors.c |   18 +-
 app/dialogs/dialogs-constructors.h |    8 +-
 app/dialogs/dialogs.c              |    4 +-
 app/dialogs/preferences-dialog.c   |   29 +-
 app/gui/gui.c                      |    3 +
 app/widgets/Makefile.am            |    2 +
 app/widgets/gimpaction-history.c   |  374 ++++++++++
 app/widgets/gimpaction-history.h   |   42 ++
 app/widgets/gimpaction.c           |   13 +
 menus/image-menu.xml.in            |    2 +-
 19 files changed, 1115 insertions(+), 839 deletions(-)
---
diff --git a/app/actions/dialogs-actions.c b/app/actions/dialogs-actions.c
index 7358256..c7686f8 100644
--- a/app/actions/dialogs-actions.c
+++ b/app/actions/dialogs-actions.c
@@ -267,13 +267,7 @@ static const GimpStringActionEntry dialogs_toplevel_actions[] =
     NULL,
     NC_("dialogs-action", "About GIMP"),
     "gimp-about-dialog",
-    GIMP_HELP_ABOUT_DIALOG },
-
-  { "dialogs-action-search", GTK_STOCK_FIND,
-    NC_("dialogs-action", "_Search and Run a Command"), NULL,
-    NC_("dialogs-action", "Search commands by keyword, and run them"),
-    "gimp-action-search-dialog",
-    GIMP_HELP_ACTION_SEARCH_DIALOG }
+    GIMP_HELP_ABOUT_DIALOG }
 };
 
 
diff --git a/app/actions/help-actions.c b/app/actions/help-actions.c
index fecce13..f76330d 100644
--- a/app/actions/help-actions.c
+++ b/app/actions/help-actions.c
@@ -47,7 +47,13 @@ static const GimpActionEntry help_actions[] =
     NC_("help-action", "_Context Help"), "<shift>F1",
     NC_("help-action", "Show the help for a specific user interface item"),
     G_CALLBACK (help_context_help_cmd_callback),
-    GIMP_HELP_HELP_CONTEXT }
+    GIMP_HELP_HELP_CONTEXT },
+
+  { "help-action-search", GTK_STOCK_FIND,
+    NC_("help-action", "_Search and Run a Command"), "slash",
+    NC_("help-action", "Search commands by keyword, and run them"),
+    G_CALLBACK (help_search_actions_cmd_callback),
+    GIMP_HELP_ACTION_SEARCH_DIALOG }
 };
 
 
diff --git a/app/actions/help-commands.c b/app/actions/help-commands.c
index 82d8a2f..1d4af8d 100644
--- a/app/actions/help-commands.c
+++ b/app/actions/help-commands.c
@@ -26,6 +26,7 @@
 
 #include "core/gimpprogress.h"
 
+#include "widgets/gimpdialogfactory.h"
 #include "widgets/gimphelp.h"
 
 #include "actions.h"
@@ -53,3 +54,17 @@ help_context_help_cmd_callback (GtkAction *action,
 
   gimp_context_help (widget);
 }
+
+void
+help_search_actions_cmd_callback (GtkAction *action,
+                                  gpointer   data)
+{
+  Gimp        *gimp;
+  GtkWidget *widget;
+  return_if_no_widget (widget, data);
+
+  gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (),
+                                  gtk_widget_get_screen (widget),
+                                  NULL,
+                                  "gimp-action-search-dialog", -1, TRUE);
+}
diff --git a/app/actions/help-commands.h b/app/actions/help-commands.h
index 7d94206..c7507e1 100644
--- a/app/actions/help-commands.h
+++ b/app/actions/help-commands.h
@@ -19,10 +19,12 @@
 #define __HELP_COMMANDS_H__
 
 
-void   help_help_cmd_callback         (GtkAction *action,
-                                       gpointer   data);
-void   help_context_help_cmd_callback (GtkAction *action,
-                                       gpointer   data);
+void   help_help_cmd_callback           (GtkAction *action,
+                                         gpointer   data);
+void   help_context_help_cmd_callback   (GtkAction *action,
+                                         gpointer   data);
+void   help_search_actions_cmd_callback (GtkAction *action,
+                                         gpointer   data);
 
 
 #endif /* __HELP_COMMANDS_H__ */
diff --git a/app/config/gimpguiconfig.c b/app/config/gimpguiconfig.c
index ff5dff7..5fd934a 100644
--- a/app/config/gimpguiconfig.c
+++ b/app/config/gimpguiconfig.c
@@ -67,6 +67,8 @@ enum
   PROP_SHOW_HELP_BUTTON,
   PROP_HELP_LOCALES,
   PROP_HELP_BROWSER,
+  PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS,
+  PROP_ACTION_HISTORY_SIZE,
   PROP_USER_MANUAL_ONLINE,
   PROP_USER_MANUAL_ONLINE_URI,
   PROP_DOCK_WINDOW_HINT,
@@ -228,6 +230,15 @@ gimp_gui_config_class_init (GimpGuiConfigClass *klass)
                                  GIMP_TYPE_HELP_BROWSER_TYPE,
                                  DEFAULT_HELP_BROWSER,
                                  GIMP_PARAM_STATIC_STRINGS);
+  /* As a default, we hide unavailable actions. */
+  GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS,
+                                    "search-show-unavailable-actions", SEARCH_SHOW_UNAVAILABLE_BLURB,
+                                    FALSE,
+                                    GIMP_PARAM_STATIC_STRINGS);
+  GIMP_CONFIG_INSTALL_PROP_INT (object_class, PROP_ACTION_HISTORY_SIZE,
+                                "action-history-size", ACTION_HISTORY_SIZE_BLURB,
+                                0, 1000, 100,
+                                GIMP_PARAM_STATIC_STRINGS);
   GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_USER_MANUAL_ONLINE,
                                     "user-manual-online",
                                     USER_MANUAL_ONLINE_BLURB,
@@ -432,6 +443,12 @@ gimp_gui_config_set_property (GObject      *object,
     case PROP_HELP_BROWSER:
       gui_config->help_browser = g_value_get_enum (value);
       break;
+    case PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS:
+      gui_config->search_show_unavailable = g_value_get_boolean (value);
+      break;
+    case PROP_ACTION_HISTORY_SIZE:
+      gui_config->action_history_size = g_value_get_int (value);
+      break;
     case PROP_USER_MANUAL_ONLINE:
       gui_config->user_manual_online = g_value_get_boolean (value);
       break;
@@ -558,6 +575,12 @@ gimp_gui_config_get_property (GObject    *object,
     case PROP_HELP_BROWSER:
       g_value_set_enum (value, gui_config->help_browser);
       break;
+    case PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS:
+      g_value_set_boolean (value, gui_config->search_show_unavailable);
+      break;
+    case PROP_ACTION_HISTORY_SIZE:
+      g_value_set_int (value, gui_config->action_history_size);
+      break;
     case PROP_USER_MANUAL_ONLINE:
       g_value_set_boolean (value, gui_config->user_manual_online);
       break;
diff --git a/app/config/gimpguiconfig.h b/app/config/gimpguiconfig.h
index e7eed9f..6cbe87a 100644
--- a/app/config/gimpguiconfig.h
+++ b/app/config/gimpguiconfig.h
@@ -61,6 +61,11 @@ struct _GimpGuiConfig
   gboolean             show_help_button;
   gchar               *help_locales;
   GimpHelpBrowserType  help_browser;
+
+  /* Action Search preferences. */
+  gboolean             search_show_unavailable;
+  gint                 action_history_size;
+
   gchar               *web_browser;
   gboolean             user_manual_online;
   gchar               *user_manual_online_uri;
diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h
index 6c2acd0..093a6e2 100644
--- a/app/config/gimprc-blurbs.h
+++ b/app/config/gimprc-blurbs.h
@@ -474,4 +474,10 @@ N_("When enabled, uses OpenCL for some operations.")
 "Bugs in event history buffer are frequent so in case of cursor " \
 "offset problems turning it off helps."
 
+#define SEARCH_SHOW_UNAVAILABLE_BLURB \
+"When enabled, a search of actions will also return inactive actions."
+
+#define ACTION_HISTORY_SIZE_BLURB \
+"The maximum number of actions saved in history."
+
 #endif  /* __GIMP_RC_BLURBS_H__ */
diff --git a/app/dialogs/action-search-dialog.c b/app/dialogs/action-search-dialog.c
index 9c0f1b1..b12629b 100644
--- a/app/dialogs/action-search-dialog.c
+++ b/app/dialogs/action-search-dialog.c
@@ -18,244 +18,277 @@
 #include "config.h"
 
 #include <ctype.h>
-#include <stdlib.h>
-
-#include <glib/gstdio.h>
 
+#include <gegl.h>
 #include <gtk/gtk.h>
 
 #include <gdk/gdkkeysyms.h>
 
 #include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
 
 #include "dialogs-types.h"
 
-#include "widgets/gimpuimanager.h"
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+
 #include "widgets/gimpaction.h"
+#include "widgets/gimpaction-history.h"
+#include "widgets/gimpdialogfactory.h"
+#include "widgets/gimphelp-ids.h"
+#include "widgets/gimpsessioninfo.h"
+#include "widgets/gimpspinscale.h"
+#include "widgets/gimpuimanager.h"
 
 #include "action-search-dialog.h"
 
 #include "gimp-intl.h"
 
-#define  MAX_HISTORY_ACTIONS 20
-#define  DEFAULT_HEIGHT 1
-
-gboolean            action_search_run_result_action             (void);
-static GtkWidget *  action_search_setup_results_list            (void);
-static gboolean     action_search_search_dialog                 (void);
-static gboolean     action_search_is_action_match               (GtkAction         *action,
-                                                                 const gchar*       keyword);
-static void         action_search_add_to_results_list           (const gchar       *label,
-                                                                 const gchar       *tooltip,
-                                                                 GtkAction*         action);
-static void         action_search_search_history_and_actions    (const gchar       *keyword);
-
-static void         action_search_update_history                (GtkAction         *action);
-static void         action_search_read_history                  (void);
-static void         action_search_fill_history                  (void);
-static void         action_search_clear_history                 (void);
-
-static void         action_search_preferences_dialog            (void);
-static void         action_search_set_default_preferences       (void);
-static void         action_search_update_preferences            (void);
-static void         action_search_write_preferences             (void);
-static void         action_search_read_preferences              (void);
-static void         action_search_set_prefereces_ui_values      (void);
-
-gboolean            action_search_initializer                   (void);
-void                action_search_finalizer                     (void);
-static void         action_search_context_menu                  (void);
-
-static GtkWidget *  action_search_dialog;
-static GtkWidget *  results_list;
-static GtkWidget *  keyword_entry;
-
-static gchar            *history_file_path;
-static gchar            *preference_file_path;
-static gint              cur_no_of_his_actions;
-static gboolean          first_time = TRUE;
-static gint              tmp_x, tmp_y;
-static gint              par_x, par_y;
-static gint              par_height, par_width;
-
-enum RES_COL {
+typedef struct
+{
+  GtkWidget     *dialog;
+
+  GimpGuiConfig *config;
+  GtkWidget     *keyword_entry;
+  GtkWidget     *results_list;
+  GtkWidget     *list_view;
+
+  gint           x;
+  gint           y;
+  gint           width;
+  gint           height;
+} SearchDialog;
+
+static void         key_released                           (GtkWidget         *widget,
+                                                            GdkEventKey       *event,
+                                                            SearchDialog      *private);
+static gboolean     result_selected                        (GtkWidget         *widget,
+                                                            GdkEventKey       *pKey,
+                                                            SearchDialog      *private);
+static void         row_activated                          (GtkTreeView       *treeview,
+                                                            GtkTreePath       *path,
+                                                            GtkTreeViewColumn *col,
+                                                            SearchDialog      *private);
+static gboolean     action_search_view_accel_find_func     (GtkAccelKey       *key,
+                                                            GClosure          *closure,
+                                                            gpointer           data);
+static gchar*       action_search_find_accel_label         (GtkAction         *action);
+static void         action_search_add_to_results_list      (GtkAction         *action,
+                                                            SearchDialog      *private,
+                                                            gint               section);
+static void         action_search_run_selected             (SearchDialog      *private);
+static void         action_search_history_and_actions      (const gchar       *keyword,
+                                                            SearchDialog      *private);
+static gboolean     action_fuzzy_match                     (gchar             *string,
+                                                            gchar             *key);
+static gchar *      action_search_normalize_string         (const gchar       *str);
+static gboolean     action_search_match_keyword            (GtkAction         *action,
+                                                            const gchar*       keyword,
+                                                            gint              *section,
+                                                            gboolean           match_fuzzy);
+
+static void         action_search_finalizer                (SearchDialog      *private);
+
+static gboolean     window_configured                      (GtkWindow         *window,
+                                                            GdkEvent          *event,
+                                                            SearchDialog      *private);
+static void         window_shown                           (GtkWidget        *widget,
+                                                            SearchDialog      *private);
+static void         action_search_setup_results_list       (GtkWidget        **results_list,
+                                                            GtkWidget        **list_view);
+static void         search_dialog_free                     (SearchDialog      *private);
+
+enum ResultColumns {
   RESULT_ICON,
   RESULT_DATA,
   RESULT_ACTION,
   IS_SENSITIVE,
+  RESULT_SECTION,
   N_COL
 };
 
-static struct HISTORY {
-  GtkAction        *history_action;
-  gint               count;
-} history[MAX_HISTORY_ACTIONS];
-
-static struct HISTORY_ACTION_NAME {
-  char             *action_name;
-  gint               no;
-} name[MAX_HISTORY_ACTIONS];
-
-static struct PREFERENCES {
-  gint               POSITION;
-  gfloat             POSITION_X;
-  gfloat             POSITION_Y;
-  gint               NO_OF_RESULTS;
-  gfloat             WIDTH;
-  gboolean          SHOW_INSENSITIVE;
-  gdouble           OPACITY;
-} PREF;
-
-static struct ACTION_SEARCH_PREF_UI {
-  GtkWidget        *specify_radio;
-  GtkWidget        *pos_x_hbox;
-  GtkWidget        *pos_y_hbox;
-  GtkWidget        *right_top_radio;
-  GtkWidget        *middle_radio;
-  GtkWidget        *pos_x_spin_button;
-  GtkWidget        *pos_y_spin_button;
-  GtkWidget        *no_of_results_spin_button;
-  GtkWidget        *width_spin_button;
-  GtkWidget        *opacity_spin_button;
-  GtkWidget        *show_insensitive_check_button;
-} PREF_UI;
+/* Public Functions */
 
 GtkWidget *
-action_search_dialog_create (void)
+action_search_dialog_create (Gimp *gimp)
 {
-  if (! action_search_initializer ())
-    g_message ("Tito action_search_initializer failed");
+  static SearchDialog  *private       = NULL;
+  GimpSessionInfo      *session_info  = NULL;
+  GdkScreen            *screen        = gdk_screen_get_default ();
+  gint                  screen_width  = gdk_screen_get_width (screen);
+  gint                  screen_height = gdk_screen_get_height (screen);
+  GdkWindow            *par_window    = gdk_screen_get_active_window (screen);
+  gint                  parent_height, parent_width;
+  gint                  parent_x, parent_y;
 
-  action_search_search_dialog ();
-  return action_search_dialog;
-}
+  gdk_window_get_geometry (par_window, &parent_x, &parent_y, &parent_width, &parent_height, NULL);
 
-static void
-modify_position_spins (void)
-{
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (PREF_UI.specify_radio)))
-    {
-      gtk_widget_set_sensitive (PREF_UI.pos_x_hbox, TRUE);
-      gtk_widget_set_sensitive (PREF_UI.pos_y_hbox, TRUE);
-    }
-  else
+  if (! private)
     {
-      gtk_widget_set_sensitive (PREF_UI.pos_x_hbox, FALSE);
-      gtk_widget_set_sensitive (PREF_UI.pos_y_hbox, FALSE);
+      GtkWidget     *action_search_dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      GimpGuiConfig *config               = GIMP_GUI_CONFIG (gimp->config);
+      GtkWidget     *main_vbox, *main_hbox;
+
+      private = g_slice_new0 (SearchDialog);
+      g_object_weak_ref (G_OBJECT (action_search_dialog),
+                         (GWeakNotify) search_dialog_free, private);
+
+      private->dialog = action_search_dialog;
+      private->config = config;
+
+      gtk_window_set_role (GTK_WINDOW (action_search_dialog), "gimp-action-search-dialog");
+      gtk_window_set_title (GTK_WINDOW (action_search_dialog), _("Search Actions"));
+
+      main_vbox = gtk_vbox_new (FALSE, 2);
+      gtk_container_add (GTK_CONTAINER (action_search_dialog), main_vbox);
+      gtk_widget_show (main_vbox);
+
+      main_hbox = gtk_hbox_new (FALSE, 2);
+      gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, FALSE, TRUE, 0);
+      gtk_widget_show (main_hbox);
+
+      private->keyword_entry = gtk_entry_new ();
+      gtk_entry_set_icon_from_stock (GTK_ENTRY (private->keyword_entry), GTK_ENTRY_ICON_PRIMARY, 
GTK_STOCK_FIND);
+      gtk_widget_show (private->keyword_entry);
+      gtk_box_pack_start (GTK_BOX (main_hbox), private->keyword_entry, TRUE, TRUE, 0);
+
+      action_search_setup_results_list (&private->results_list, &private->list_view);
+      gtk_box_pack_start (GTK_BOX (main_vbox), private->list_view, TRUE, TRUE, 0);
+
+      gtk_widget_set_events (private->dialog,
+                             GDK_KEY_RELEASE_MASK | GDK_KEY_PRESS_MASK |
+                             GDK_BUTTON_PRESS_MASK | GDK_SCROLL_MASK);
+
+      g_signal_connect (private->results_list, "row-activated", (GCallback) row_activated, private);
+      g_signal_connect (private->keyword_entry, "key-release-event", G_CALLBACK (key_released), private);
+      g_signal_connect (private->results_list, "key_press_event", G_CALLBACK (result_selected), private);
+      g_signal_connect (private->dialog, "event", G_CALLBACK (window_configured), private);
+      g_signal_connect (private->dialog, "show", G_CALLBACK (window_shown), private);
+      g_signal_connect (private->dialog, "delete_event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
     }
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON (PREF_UI.pos_x_spin_button),
-                            (gdouble) (PREF.POSITION_X/par_width * 100));
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON (PREF_UI.pos_y_spin_button),
-                            (gdouble) (PREF.POSITION_Y/par_height * 100));
-  gtk_spin_button_set_range (GTK_SPIN_BUTTON (PREF_UI.pos_x_spin_button),
-                            (gdouble) 0,
-                            (gdouble) (100 - gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(PREF_UI.width_spin_button))));
-}
 
-static void
-action_search_clear_history_button_clicked (GtkButton *button,
-                                            gpointer   user_data)
-{
-  action_search_clear_history ();
-}
+  /* Move the window to the previous session's position using session management. */
+  private->x      = -1;
+  private->y      = -1;
+  private->width  = -1;
+  /* Height is the only value not reused since it is too variable because of the result list. */
+  private->height = parent_height / 2;
 
-static void
-restore_defaults_button_clicked (GtkButton *button,
-                                 gpointer   user_data)
-{
-  action_search_set_default_preferences ();
-  action_search_set_prefereces_ui_values ();
-}
+  session_info =
+    gimp_dialog_factory_find_session_info (gimp_dialog_factory_get_singleton(),
+                                           "gimp-action-search-dialog");
 
-static void
-action_search_set_prefereces_ui_values (void)
-{
-  if (PREF.POSITION == 0)
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (PREF_UI.right_top_radio), TRUE);
-  else if (PREF.POSITION == 1)
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (PREF_UI.middle_radio), TRUE);
-  else
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (PREF_UI.specify_radio), TRUE);
+  if (session_info)
+    {
+      private->x     = gimp_session_info_get_x (session_info);
+      private->y     = gimp_session_info_get_y (session_info);
+      private->width = gimp_session_info_get_width (session_info);
+    }
 
-  modify_position_spins ();
+  if (private->width < 0)
+    {
+      private->width = parent_width / 2;
+    }
+  else if (private->width > screen_width)
+    {
+      private->width = parent_width;
+    }
 
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON (PREF_UI.no_of_results_spin_button), (gdouble) 
PREF.NO_OF_RESULTS);
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON (PREF_UI.width_spin_button), (gdouble) PREF.WIDTH);
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON (PREF_UI.opacity_spin_button), (gdouble) PREF.OPACITY * 100);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (PREF_UI.show_insensitive_check_button), 
PREF.SHOW_INSENSITIVE);
+  if (private->x < 0 || private->x + private->width > screen_width)
+    {
+      private->x = parent_x + (parent_width - private->width) / 2;
+    }
 
-}
+  if (private->y < 0 || private->y + private->height > screen_height)
+    {
+      private->y = parent_y + (parent_height - private->height) / 2 ;
+    }
 
-static gboolean
-on_focus_out (GtkWidget     *widget,
-              GdkEventFocus *event,
-              gpointer       data)
-{
-  if (! gtk_widget_is_focus (GTK_WIDGET (data)))
-    action_search_finalizer ();
+  gtk_window_set_default_size (GTK_WINDOW (private->dialog), private->width, 1);
+  gtk_widget_show (private->dialog);
 
-  return TRUE;
+  return private->dialog;
 }
 
-
+/* Private Functions */
 static void
-key_released (GtkWidget *widget,
-              GdkEventKey *event,
-              gpointer func_data)
+key_released (GtkWidget    *widget,
+              GdkEventKey  *event,
+              SearchDialog *private)
 {
-  const gchar  *entry_text;
-  GtkWidget    *list_view = GTK_WIDGET (func_data);
+  gchar         *entry_text;
+  gint           width;
 
-  entry_text = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
+  gtk_window_get_size (GTK_WINDOW (private->dialog), &width, NULL);
+  entry_text = g_strstrip (gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1));
 
   switch (event->keyval)
-  {
-    case GDK_Escape:
-    {
-      action_search_finalizer ();
-      return;
-    }
-    case GDK_Return:
     {
-      action_search_run_result_action ();
-      return;
+      case GDK_Escape:
+        {
+          action_search_finalizer (private);
+          return;
+        }
+      case GDK_Return:
+        {
+          action_search_run_selected (private);
+          return;
+        }
     }
-  }
 
   if (strcmp (entry_text, "") != 0)
     {
-      gtk_window_resize (GTK_WINDOW (action_search_dialog), (PREF.WIDTH * par_width) / 100,
-                         PREF.NO_OF_RESULTS * 40 + 100);
-      gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (results_list))));
-      gtk_widget_show_all (list_view);
-      action_search_search_history_and_actions (entry_text);
-      gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (results_list)),
+      gtk_window_resize (GTK_WINDOW (private->dialog), width,
+                         private->height);
+      gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW 
(private->results_list))));
+      gtk_widget_show_all (private->list_view);
+      action_search_history_and_actions (entry_text, private);
+      gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (private->results_list)),
                                       gtk_tree_path_new_from_string ("0"));
     }
   else if (strcmp (entry_text, "") == 0 && (event->keyval == GDK_Down) )
     {
-      gtk_window_resize (GTK_WINDOW (action_search_dialog), (PREF.WIDTH * par_width) / 100,
-                         PREF.NO_OF_RESULTS * 40 + 100);
-      gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (results_list))));
-      gtk_widget_show_all (list_view);
-      action_search_search_history_and_actions (" ");
-      gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (results_list)),
+      gtk_window_resize (GTK_WINDOW (private->dialog), width,
+                         private->height);
+      gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW 
(private->results_list))));
+      gtk_widget_show_all (private->list_view);
+      action_search_history_and_actions (NULL, private);
+      gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (private->results_list)),
                                       gtk_tree_path_new_from_string ("0"));
 
     }
   else
     {
-      gtk_widget_hide (list_view);
-      gtk_window_resize (GTK_WINDOW (action_search_dialog),
-                         (PREF.WIDTH * par_width) / 100,
-                         DEFAULT_HEIGHT);
+      GtkTreeSelection *selection;
+      GtkTreeModel     *model;
+      GtkTreeIter       iter;
+
+      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (private->results_list));
+      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 (private->list_view);
+      gtk_window_resize (GTK_WINDOW (private->dialog),
+                         width, 1);
     }
+
+  g_free (entry_text);
 }
 
 static gboolean
-result_selected (GtkWidget   *widget,
-                 GdkEventKey *pKey,
-                 gpointer     func_data)
+result_selected (GtkWidget    *widget,
+                 GdkEventKey  *pKey,
+                 SearchDialog *private)
 {
   if (pKey->type == GDK_KEY_PRESS)
     {
@@ -263,22 +296,23 @@ result_selected (GtkWidget   *widget,
         {
           case GDK_Return:
             {
-              action_search_run_result_action ();
+              action_search_run_selected (private);
               break;
             }
           case GDK_Escape:
             {
-              action_search_finalizer ();
+              action_search_finalizer (private);
               return TRUE;
             }
           case GDK_Up:
             {
+              gboolean          event_processed = FALSE;
               GtkTreeSelection *selection;
               GtkTreeModel     *model;
               GtkTreePath      *path;
               GtkTreeIter       iter;
 
-              selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (results_list));
+              selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (private->results_list));
               gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 
               if (gtk_tree_selection_get_selected (selection, &model, &iter))
@@ -287,36 +321,59 @@ result_selected (GtkWidget   *widget,
 
                   if (strcmp (gtk_tree_path_to_string (path), "0") == 0)
                     {
-                      gtk_widget_grab_focus ((GTK_WIDGET (keyword_entry)));
-                      return TRUE;
+                      gint start_pos;
+                      gint end_pos;
+
+                      gtk_editable_get_selection_bounds (GTK_EDITABLE (private->keyword_entry), &start_pos, 
&end_pos);
+                      gtk_widget_grab_focus ((GTK_WIDGET (private->keyword_entry)));
+                      gtk_editable_select_region (GTK_EDITABLE (private->keyword_entry), start_pos, end_pos);
+
+                      event_processed = TRUE;
                     }
+                  gtk_tree_path_free (path);
                 }
+
+              return event_processed;
+            }
+          case GDK_Down:
+            {
+              return FALSE;
+            }
+          default:
+            {
+              gint start_pos;
+              gint end_pos;
+
+              gtk_editable_get_selection_bounds (GTK_EDITABLE (private->keyword_entry), &start_pos, 
&end_pos);
+              gtk_widget_grab_focus ((GTK_WIDGET (private->keyword_entry)));
+              gtk_editable_select_region (GTK_EDITABLE (private->keyword_entry), start_pos, end_pos);
+              gtk_widget_event (GTK_WIDGET (private->keyword_entry), (GdkEvent*) pKey);
             }
         }
     }
+
   return FALSE;
 }
 
-
 static void
 row_activated (GtkTreeView        *treeview,
                GtkTreePath        *path,
                GtkTreeViewColumn  *col,
-               gpointer            userdata)
+               SearchDialog       *private)
 {
-    action_search_run_result_action ();
+  action_search_run_selected (private);
 }
 
 static gboolean
-action_search_action_view_accel_find_func (GtkAccelKey *key,
-                                           GClosure    *closure,
-                                           gpointer     data)
+action_search_view_accel_find_func (GtkAccelKey *key,
+                                    GClosure    *closure,
+                                    gpointer     data)
 {
   return (GClosure *) data == closure;
 }
 
 static gchar*
-find_accel_label (GtkAction *action)
+action_search_find_accel_label (GtkAction *action)
 {
   guint            accel_key     = 0;
   GdkModifierType  accel_mask    = 0;
@@ -332,8 +389,9 @@ find_accel_label (GtkAction *action)
   if (accel_closure)
    {
      GtkAccelKey *key;
+
      key = gtk_accel_group_find (accel_group,
-                                 action_search_action_view_accel_find_func,
+                                 action_search_view_accel_find_func,
                                  accel_closure);
      if (key            &&
          key->accel_key &&
@@ -346,27 +404,44 @@ find_accel_label (GtkAction *action)
 
   accel_string = gtk_accelerator_get_label (accel_key, accel_mask);
 
-  return (strcmp (accel_string, "") == 0)?  accel_string : NULL;
-}
+  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 void
-action_search_add_to_results_list (const gchar *label,
-                                   const gchar *tooltip,
-                                   GtkAction   *action)
+action_search_add_to_results_list (GtkAction    *action,
+                                   SearchDialog *private,
+                                   gint          section)
 {
   GtkTreeIter   iter;
+  GtkTreeIter   next_section;
   GtkListStore *store;
+  GtkTreeModel *model;
   gchar        *markuptxt;
-  gchar        *accel_string = find_accel_label (action);
-  const gchar  *stock_id = gtk_action_get_stock_id (action);
-  char         *data = g_new (char, 1024);
-  char          shortcut[1024] = "";
-
-  if (data == NULL                ||
-      strchr (label, '@') != NULL ||
-      strchr (label, '&') != NULL)
-    return;
+  gchar        *label;
+  gchar        *escaped_label = NULL;
+  const gchar  *stock_id;
+  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))
     {
@@ -375,49 +450,85 @@ action_search_add_to_results_list (const gchar *label,
       else
         stock_id = GTK_STOCK_NO;
     }
-
-  if (accel_string == NULL)
-      strcpy (shortcut, "");
-  else if (strchr (accel_string, '<') != NULL)
-      strcpy (shortcut, "");
   else
     {
-      strcpy (shortcut, " | ");
-      strcat (shortcut, accel_string);
+      stock_id = gtk_action_get_stock_id (action);
     }
 
-  if (tooltip == NULL)
-      strcpy (data, "");
-  else if (strchr (tooltip, '<') != NULL)
-       strcpy (data, "");
+  accel_string = action_search_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;
+    }
+
+  markuptxt = 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 : "");
+
+  model = gtk_tree_view_get_model (GTK_TREE_VIEW (private->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,
+                              RESULT_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
     {
-      strcpy (data, "\n");
-      strcat (data, tooltip);
+      gtk_list_store_append (store, &iter);
     }
 
-  markuptxt = g_strdup_printf ("%s<small>%s<span weight='light'>%s</span></small>",
-                               label, shortcut, data);
-  store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (results_list)));
-  gtk_list_store_append (store, &iter);
   gtk_list_store_set (store, &iter,
                      RESULT_ICON, stock_id,
                      RESULT_DATA, markuptxt,
                      RESULT_ACTION, action,
+                     RESULT_SECTION, section,
                      IS_SENSITIVE, gtk_action_get_sensitive (action),
                      -1);
-  g_free (data);
+
+  g_free (accel_string);
+  g_free (markuptxt);
+  g_free (label);
+  g_free (escaped_accel);
+  g_free (escaped_label);
+  g_free (escaped_tooltip);
 }
 
-gboolean
-action_search_run_result_action (void)
+static void
+action_search_run_selected (SearchDialog *private)
 {
   GtkTreeSelection *selection;
   GtkTreeModel     *model;
   GtkTreeIter       iter;
 
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (results_list));
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (private->results_list));
   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
   if (gtk_tree_selection_get_selected (selection, &model, &iter))
     {
       GtkAction *action;
@@ -425,719 +536,373 @@ action_search_run_result_action (void)
       gtk_tree_model_get (model, &iter, RESULT_ACTION, &action, -1);
 
       if (! gtk_action_get_sensitive (action))
-        return FALSE;
+        return;
 
-      gtk_widget_hide (action_search_dialog);
+      action_search_finalizer (private);
       gtk_action_activate (action);
-      action_search_finalizer ();
-      action_search_update_history (action);
     }
 
-  return TRUE;
+  return;
 }
 
-
-void
-action_search_search_history_and_actions (const gchar *keyword)
+static void
+action_search_history_and_actions (const gchar  *keyword,
+                                   SearchDialog *private)
 {
   GList             *list;
   GimpUIManager     *manager;
-  gint               i = 0;
+  GList             *history_actions = NULL;
 
   manager = gimp_ui_managers_from_name ("<Image>")->data;
 
-  if (strcmp (keyword, "") == 0)
+  if (g_strcmp0 (keyword, "") == 0)
     return;
 
-  for (i = 0;i<cur_no_of_his_actions;i++)
-  {
-    if (history[i].history_action != NULL)
-      {
-        if (action_search_is_action_match (history[i].history_action, keyword))
-          action_search_add_to_results_list (gimp_strip_uline (gtk_action_get_label 
(history[i].history_action)),
-                                             gtk_action_get_tooltip (history[i].history_action),
-                                             history[i].history_action );
-      }
-  }
+  history_actions = gimp_action_history_search (keyword,
+                                                action_search_match_keyword,
+                                                private->config);
+
+  /* First put on top of the list any matching action of user history. */
+  for (list = history_actions; list; list = g_list_next (list))
+    {
+      action_search_add_to_results_list (GTK_ACTION (list->data), private, 0);
+    }
 
+  /* Now check other actions. */
   for (list = gtk_ui_manager_get_action_groups (GTK_UI_MANAGER (manager));
        list;
        list = g_list_next (list))
     {
-      GimpActionGroup *group = list->data;
-      GList           *actions;
       GList           *list2;
-
+      GimpActionGroup *group   = list->data;
+      GList           *actions = NULL;
 
       actions = gtk_action_group_list_actions (GTK_ACTION_GROUP (group));
       actions = g_list_sort (actions, (GCompareFunc) gimp_action_name_compare);
 
       for (list2 = actions; list2; list2 = g_list_next (list2))
         {
-          GtkAction       *action        = list2->data;
-          const gchar     *name;
-          gboolean        is_redundant = FALSE;
-          name         = gtk_action_get_name (action);
-
-
-          if (strstr (name, "-menu")  ||
-              strstr (name, "-popup") ||
-              strstr (name, "context") ||
-              strstr (name, "edit-undo") ||
-              name[0] == '<')
+          const gchar *name;
+          GtkAction   *action       = list2->data;
+          gboolean     is_redundant = FALSE;
+          gint         section;
+
+          name = gtk_action_get_name (action);
+
+          /* The action search dialog don't show any non-historized
+           * action, with the exception of "plug-in-repeat/reshow"
+           * actions.
+           * Logging them is meaningless (they may mean a different
+           * actual action each time), but they are still interesting
+           * as a search result.
+           */
+          if (gimp_action_history_excluded_action (name) &&
+              g_strcmp0 (name, "plug-in-repeat") != 0    &&
+              g_strcmp0 (name, "plug-in-reshow") != 0)
             continue;
 
-            if (! gtk_action_get_sensitive (action) && ! (PREF.SHOW_INSENSITIVE) )
-                continue;
+          if (! gtk_action_get_sensitive (action) && ! private->config->search_show_unavailable)
+            continue;
 
-          for (i = 0;i<cur_no_of_his_actions;i++)
+          if (action_search_match_keyword (action, keyword, &section, TRUE))
             {
-              if (history[i].history_action != NULL)
+              GList *list3;
+
+              /* A matching action. Check if we have not already added it as an history action. */
+              for (list3 = history_actions; list3; list3 = g_list_next (list3))
                 {
-                  if (strcmp (gtk_action_get_name (history[i].history_action), name) == 0)
+                  if (strcmp (gtk_action_get_name (GTK_ACTION (list3->data)), name) == 0)
                     {
                       is_redundant = TRUE;
                       break;
                     }
                 }
-            }
-
-          if (is_redundant)
-            continue;
 
-          if (action_search_is_action_match (action, keyword))
-            {
-              action_search_add_to_results_list (gimp_strip_uline (gtk_action_get_label (action)),
-                                        gtk_action_get_tooltip (action),
-                                        action);
+              if (! is_redundant)
+                {
+                  action_search_add_to_results_list (action, private, section);
+                }
             }
         }
+
       g_list_free (actions);
    }
-}
-
-static void action_search_fill_history (void)
-{
-  GList             *list;
-  GimpUIManager     *manager;
-  gint               i = 0;
-  manager = gimp_ui_managers_from_name ("<Image>")->data;
 
-  for (list = gtk_ui_manager_get_action_groups (GTK_UI_MANAGER (manager));
-       list;
-       list = g_list_next (list))
-      {
-        GimpActionGroup *group = list->data;
-        GList           *actions;
-        GList           *list2;
-
-         actions = gtk_action_group_list_actions (GTK_ACTION_GROUP (group));
-         actions = g_list_sort (actions, (GCompareFunc) gimp_action_name_compare);
-
-         for (list2 = actions; list2; list2 = g_list_next (list2))
-           {
-             GtkAction       *action        = list2->data;
-             const gchar     *action_name;
-             action_name        = gtk_action_get_name (action);
-
-             if (strstr (action_name, "-menu")   ||
-                 strstr (action_name, "-popup")  ||
-                 strstr (action_name, "context") ||
-                 action_name[0] == '<')
-                    continue;
-
-             for (i = 0;i<cur_no_of_his_actions;i++)
-               {
-                 if (name[i].action_name != NULL)
-                   {
-                     if (strcmp (name[i].action_name, action_name) == 0)
-                       {
-                         history[i].history_action = action;
-                         history[i].count = name[i].no;
-                       }
-                    }
-                }
-            }
-        }
+  g_list_free_full (history_actions, (GDestroyNotify) g_object_unref);
 }
 
+/**
+ * Fuzzy search matching.
+ * Returns: TRUE if all the letters of `key` are found in `string`,
+ * in the same order (even with intermediate letters).
+ */
 static gboolean
-fuzzy_search (gchar *string,
-              gchar *key)
+action_fuzzy_match (gchar *string,
+                    gchar *key)
 {
   gchar *remaining_string = string;
+
   if (strlen (key) == 0 )
-      return TRUE;
+    return TRUE;
 
   if ((remaining_string = strchr (string, key[0])) != NULL )
-      return fuzzy_search (remaining_string+1, key+1 );
+    return action_fuzzy_match (remaining_string + 1,
+                                            key + 1);
   else
     return FALSE;
 }
 
-static gboolean
-action_search_is_action_match (GtkAction *action,
-                               const gchar* keyword)
+/*
+ * Returns: a newly allocated lowercased string, which replaced any
+ * spacing characters into a single space and stripped out any leading
+ * and trailing space. */
+static gchar *
+action_search_normalize_string (const gchar *str)
 {
-  gchar *label, *tooltip, *key;
-  gint i;
-  gchar* space_pos;
-  label =   g_new (gchar, 1024);
-  key =     g_new (gchar, 1024);
-  tooltip = g_new (gchar, 1024);
-
-  strcpy (label, gimp_strip_uline (gtk_action_get_label (action)));
-  strcpy (key, keyword);
-
-  for (i = 0;i<strlen (label);i++)
-    label[i] = tolower (label[i]);
-  for (i = 0;i<strlen (key);i++)
-    key[i] = tolower (key[i]);
-  if (strlen (key) == 2)
-    {
-      space_pos = strchr (label, ' ');
-      if (space_pos!= NULL)
-        {
-          space_pos++;
-          if (key[0] == label[0] && key[1] == *space_pos)
-              return TRUE;
-        }
-    }
+  GRegex *spaces_regex;
+  gchar  *normalized_str;
+  gint    i;
 
-  if (strstr (label, key))
-    return TRUE;
-
-  if (fuzzy_search (label, key))
-    return TRUE;
+  spaces_regex = g_regex_new ("[ \n\t\r]+", 0, 0, NULL);
+  normalized_str = g_regex_replace_literal (spaces_regex, str, -1, 0, " ", 0, NULL);
 
-  if (strlen (key)>2 || strcmp (key, " ") == 0)
-    {
-      if (gtk_action_get_tooltip (action)!= NULL)
-        {
-          strcpy (tooltip, gtk_action_get_tooltip (action));
-          for (i = 0;i<strlen (tooltip);i++)
-            tooltip[i] = tolower (tooltip[i]);
+  g_regex_unref (spaces_regex);
 
-          if (strstr (tooltip, key))
-                return TRUE;
-        }
-    }
+  normalized_str = g_strstrip (normalized_str);
 
-  g_free (label);
-  g_free (key);
-  g_free (tooltip);
+  for (i = 0 ; i < strlen (normalized_str); i++)
+    normalized_str[i] = tolower (normalized_str[i]);
 
-  return FALSE;
+  return normalized_str;
 }
 
-static void
-action_search_read_history (void)
+static gboolean
+action_search_match_keyword (GtkAction   *action,
+                             const gchar *keyword,
+                             gint        *section,
+                             gboolean     match_fuzzy)
 {
-  gint i;
-  FILE *fp;
-  cur_no_of_his_actions = 0;
-
-  fp = fopen (history_file_path, "r");
-  if (fp == NULL)
-        return;
+  gboolean  matched = FALSE;
+  gchar    *key;
+  gchar    *label;
+  gchar    *tmp;
 
-  for (i = 0;i<MAX_HISTORY_ACTIONS;i++)
+  if (keyword == NULL)
     {
-      if (fscanf (fp, "%s  %d", name[i].action_name, &name[i].no) == EOF)
-        break;
-      cur_no_of_his_actions++;
+      /* As a special exception, a NULL keyword means
+         any action matches. */
+      if (section)
+        {
+          *section = 0;
+        }
+      return TRUE;
     }
 
-  fclose (fp);
-  action_search_fill_history ();
-}
-
-static gint
-compare (const void * a,
-              const void * b)
-{
-  struct HISTORY *p = (struct HISTORY *)a;
-  struct HISTORY *q = (struct HISTORY *)b;
-  return (q->count - p->count);
-}
-
-static void
-action_search_update_history (GtkAction *action)
-{
-  gint i;
-  FILE *fp;
-  gboolean is_present = FALSE;
+  key   = action_search_normalize_string (keyword);
+  tmp   = gimp_strip_uline (gtk_action_get_label (action));
+  label = action_search_normalize_string (tmp);
+  g_free (tmp);
 
-  fp = fopen (history_file_path, "w");
-  if (fp == NULL)
+  /* If keyword is two characters,
+     then match them with first letters of first and second word in the labels.
+     For instance 'gb' will list 'Gaussian Blur...' */
+  if (strlen (key) == 2)
     {
-      g_message ("Unable to open history file to write");
-     return;
-    }
+      gchar* space_pos;
 
-  for (i = 0;i<cur_no_of_his_actions;i++)
-    {
-      if (strcmp (gtk_action_get_name (action), gtk_action_get_name (history[i].history_action)) == 0)
+      space_pos = strchr (label, ' ');
+
+      if (space_pos != NULL)
         {
-          history[i].count++;
-          is_present = TRUE;
-          break;
+          space_pos++;
+
+          if (key[0] == label[0] && key[1] == *space_pos)
+            {
+              matched = TRUE;
+              if (section)
+                {
+                  *section = 1;
+                }
+            }
         }
     }
 
-  if (! is_present)
+  if (! matched)
     {
-      if (cur_no_of_his_actions == MAX_HISTORY_ACTIONS)
+      gchar *substr;
+
+      substr = strstr (label, key);
+      if (substr)
         {
-          history[MAX_HISTORY_ACTIONS-1].history_action = action;
-          history[MAX_HISTORY_ACTIONS-1].count = 1;
+          matched = TRUE;
+          if (section)
+            {
+              /* If the substring is the label start, this is a nicer match. */
+              *section = (substr == label)? 1 : 2;
+            }
         }
-      else
+      else if (strlen (key) > 2)
         {
-          history[cur_no_of_his_actions].history_action = action;
-          history[cur_no_of_his_actions++].count = 1;
-        }
-    }
-
-  qsort (history, cur_no_of_his_actions, sizeof (struct HISTORY), compare);
+          gchar *tooltip = NULL;
 
-  for (i = 0;i<cur_no_of_his_actions;i++)
-    {
-      if (history[i].history_action != NULL)
-          fprintf (fp, "%s  %d \n", gtk_action_get_name (history[i].history_action),
-                                   history[i].count);
-    }
-
-  fclose (fp);
-}
+          if (gtk_action_get_tooltip (action)!= NULL)
+            {
+              tooltip = action_search_normalize_string (gtk_action_get_tooltip (action));
 
-static void
-action_search_update_position (void)
-{
-  if (PREF.POSITION == 0)
-  {
-    PREF.POSITION_X = (1-PREF.WIDTH/100)*par_width+par_x;
-    PREF.POSITION_Y = 0.04*par_height+par_y;
-  }
-  else if (PREF.POSITION == 1)
-  {
-    PREF.POSITION_X = (par_width- PREF.WIDTH*par_width*.01)/2 + par_x;
-    PREF.POSITION_Y = 0.2*par_height + par_y;
-  }
-  else
-  {
-    PREF.POSITION_X = tmp_x*par_width/100 + par_x;
-    PREF.POSITION_Y = tmp_y*par_height/100 + par_y;
-  }
-  gtk_window_move (GTK_WINDOW (action_search_dialog), PREF.POSITION_X, PREF.POSITION_Y);
-}
+              if (strstr (tooltip, key))
+                {
+                  matched = TRUE;
+                  if (section)
+                    {
+                      *section = 3;
+                    }
+                }
+            }
 
-void
-action_search_finalizer (void)
-{
-  gtk_widget_destroy (action_search_dialog);
-}
+          if (! matched && strchr (key, ' '))
+            {
+              gchar *key_copy  = g_strdup (key);
+              gchar *words = key_copy;
+              gchar *word;
 
-static void
-initialize_storage (void)
-{
-  gchar *dir_filename = g_build_filename (gimp_directory (), "tito", NULL);
+              matched = TRUE;
+              if (section)
+                {
+                  *section = 4;
+                }
 
-  g_mkdir (dir_filename,
-           S_IRUSR | S_IWUSR | S_IXUSR |
-           S_IRGRP | S_IXGRP |
-           S_IROTH | S_IXOTH);
+              while ((word = strsep (&words, " ")) != NULL)
+                {
+                  if (! strstr (label, word) && (! tooltip || ! strstr (tooltip, word)))
+                    {
+                      matched = FALSE;
+                      break;
+                    }
+                }
 
-  history_file_path = g_new (gchar, 1024);
-  strcpy (history_file_path, dir_filename);
+              g_free (key_copy);
+            }
 
-  preference_file_path = g_new (gchar, 1024);
-  strcpy (preference_file_path, dir_filename);
+          g_free (tooltip);
+        }
 
-  strcat (history_file_path, "/history");
-  strcat (preference_file_path, "/preferences");
+      if (! matched && match_fuzzy && action_fuzzy_match (label, key))
+        {
+          matched = TRUE;
+          if (section)
+            {
+              *section = 5;
+            }
+        }
+    }
 
-  g_free (dir_filename);
-}
+  g_free (label);
+  g_free (key);
 
-gboolean
-action_search_initializer (void)
-{
-  gint       i = 0;
-  GdkWindow *par_window = gdk_screen_get_active_window (gdk_screen_get_default ());
-
-  gdk_window_get_geometry (par_window, &par_x, &par_y, &par_width, &par_height, NULL);
-
-  if (first_time)
-  {
-    initialize_storage ();
-
-    for (i = 0;i<MAX_HISTORY_ACTIONS;i++)
-      {
-        name[i].action_name = g_new (char, 100);
-        strcpy (name[i].action_name, "");
-        name[i].no = 0;
-      }
-    first_time = FALSE;
-  }
-  action_search_read_preferences ();
-  action_search_read_history ();
-  gtk_accel_map_change_entry ("<Actions>/dialogs/dialogs-action-search", 'd', 0, FALSE);
-  return TRUE;
+  return matched;
 }
 
-static void
-action_search_clear_history (void)
+void
+action_search_finalizer (SearchDialog *private)
 {
-  FILE *fp;
-  fp = fopen (history_file_path, "w");
-  if (fp == NULL)
+  if (GTK_IS_WIDGET (private->dialog))
     {
-        g_message ("file not created");
-        return;
+      gtk_entry_set_text (GTK_ENTRY (private->keyword_entry), "");
+      gtk_widget_hide (private->list_view);
+      gtk_window_resize (GTK_WINDOW (private->dialog),
+                         private->width, 1);
+      gimp_dialog_factory_hide_dialog (private->dialog);
     }
-  fclose (fp);
-}
-
-static void
-action_search_set_default_preferences (void)
-{
- PREF.POSITION = 1;
- PREF.WIDTH = 40;
- PREF.POSITION_X = (1-0.4)*par_width+par_x;
- PREF.POSITION_Y = 0.04*par_height+par_y;
- PREF.NO_OF_RESULTS = 4;
- PREF.SHOW_INSENSITIVE = FALSE;
- PREF.OPACITY = 1;
- action_search_write_preferences ();
 }
 
-static void
-action_search_update_preferences (void)
+static gboolean
+window_configured (GtkWindow    *window,
+                   GdkEvent     *event,
+                   SearchDialog *private)
 {
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (PREF_UI.right_top_radio)))
-    PREF.POSITION = 0;
-  else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (PREF_UI.middle_radio)))
-    PREF.POSITION = 1;
-  else
+  if (event->type == GDK_CONFIGURE &&
+      gtk_widget_get_visible (GTK_WIDGET (window)))
     {
-      PREF.POSITION = 2;
-      tmp_x = (gfloat) gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (PREF_UI.pos_x_spin_button));
-      tmp_y = (gfloat) gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (PREF_UI.pos_y_spin_button));
-    }
-
-  PREF.NO_OF_RESULTS    = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(PREF_UI.no_of_results_spin_button));
-  PREF.WIDTH            = (gfloat)gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(PREF_UI.width_spin_button));
-  PREF.OPACITY          = (gdouble)gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON 
(PREF_UI.opacity_spin_button))/100;
-  PREF.SHOW_INSENSITIVE = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(PREF_UI.show_insensitive_check_button));
-
-  action_search_update_position ();
-  action_search_write_preferences ();
-  action_search_finalizer ();
-}
-
-static void
-action_search_write_preferences (void)
-{
- FILE *fp;
- fp = fopen (preference_file_path, "w");
-
- if (fp == NULL)
-  {
-    g_message ("Unable to open preferences file to write");
-   return;
-  }
-
- if (fp == NULL)
-    return;
- fprintf (fp, "%d %f %f %d %f %d %lf", PREF.POSITION, PREF.POSITION_X, PREF.POSITION_Y,
-                    PREF.NO_OF_RESULTS, PREF.WIDTH, PREF.SHOW_INSENSITIVE, PREF.OPACITY);
- fclose (fp);
-}
-
-static void
-action_search_read_preferences (void)
-{
- FILE *fp;
-
- fp = fopen (preference_file_path, "r");
+      gint           x, y, width, height;
 
- if (fp == NULL)
-   {
-     action_search_set_default_preferences ();
-     return;
-   }
-
- if (fscanf (fp, "%d %f %f %d %f %d %lf",
-             &PREF.POSITION, &PREF.POSITION_X, &PREF.POSITION_Y,
-             &PREF.NO_OF_RESULTS, &PREF.WIDTH, &PREF.SHOW_INSENSITIVE,
-             &PREF.OPACITY) == 0)
-   action_search_set_default_preferences ();
+      /* I don't use coordinates of GdkEventConfigure, because they are
+         relative to the parent window. */
+      gtk_window_get_position (window, &x, &y);
+      if (x < 0)
+        {
+          x = 0;
+        }
+      if (y < 0)
+        {
+          y = 0;
+        }
+      private->x = x;
+      private->y = y;
 
- fclose (fp);
-}
+      gtk_window_get_size (GTK_WINDOW (private->dialog), &width, &height);
+      private->width = width;
 
-static gboolean
-context_menu_invoked  (GtkWidget *widget,
-                       GdkEvent  *event,
-                       gpointer   user_data)
-{
-  action_search_context_menu ();
-  return TRUE;
-}
+      if (gtk_widget_get_visible  (private->list_view))
+        {
+          private->height = height;
+        }
+    }
 
-static void
-context_menu_handler (GtkMenuItem* menuitem,
-                      gpointer *data)
-{
-  if (strchr (gtk_menu_item_get_label (menuitem), 'r') != NULL)
-    action_search_preferences_dialog ();
-  else
-    gtk_widget_destroy (action_search_dialog);
+  return FALSE;
 }
 
 static void
-action_search_context_menu (void)
+window_shown (GtkWidget    *widget,
+              SearchDialog *private)
 {
-  GtkWidget *preferences_menuitem;
-  GtkWidget *close_menuitem;
-  GtkWidget *context_menu;
-
-  context_menu = gtk_menu_new ();
-  preferences_menuitem =  gtk_image_menu_item_new_from_stock (GTK_STOCK_PREFERENCES, NULL);
-  close_menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLOSE, NULL);
-
-  gtk_menu_shell_append (GTK_MENU_SHELL (context_menu), preferences_menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (context_menu), close_menuitem);
-
-  gtk_widget_show (context_menu);
-  gtk_widget_show (preferences_menuitem);
-  gtk_widget_show (close_menuitem);
-
-  g_signal_connect (preferences_menuitem, "activate", G_CALLBACK (context_menu_handler), NULL);
-  g_signal_connect (close_menuitem, "activate", G_CALLBACK (context_menu_handler), NULL);
-
-  gtk_menu_popup (GTK_MENU (context_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
+  gtk_window_move (GTK_WINDOW (widget),
+                   private->x, private->y);
 }
 
 static void
-action_search_preferences_dialog (void)
-{
-  GtkWidget   *pref_dialog;
-  GtkWidget   *top_hbox;
-
-  GtkWidget   *position_frame;
-  GtkWidget   *position_vbox;
-  GtkWidget   *pos_x_label;
-  GtkWidget   *pos_y_label;
-  GtkWidget   *specify_alignment_x;
-  GtkWidget   *specify_alignment_y;
-
-  GtkWidget   *display_frame;
-  GtkWidget   *display_vbox;
-  GtkWidget   *no_of_results_hbox;
-  GtkWidget   *width_hbox;
-  GtkWidget   *opacity_hbox;
-  GtkWidget   *no_of_results_label;
-  GtkWidget   *width_label;
-  GtkWidget   *opacity_label;
-
-  GtkWidget   *bottom_hbox;
-  GtkWidget   *action_search_clear_history_button;
-  GtkWidget   *restore_defaults_button;
-
-  pref_dialog = gtk_dialog_new_with_buttons ("Tito preferences",
-                                             NULL,
-                                             GTK_DIALOG_MODAL,
-                                             GTK_STOCK_OK,
-                                             GTK_RESPONSE_ACCEPT,
-                                             GTK_STOCK_CANCEL,
-                                             GTK_RESPONSE_REJECT,
-                                             NULL);
-
-  gtk_window_set_position (GTK_WINDOW (pref_dialog), GTK_WIN_POS_CENTER_ALWAYS);
-  top_hbox = gtk_hbox_new (FALSE, 10);
-  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (pref_dialog))), top_hbox, FALSE, 
FALSE, 2);
-
-  position_frame = gtk_frame_new ("Postion");
-  position_vbox = gtk_vbox_new (TRUE, 2);
-
-  gtk_frame_set_shadow_type (GTK_FRAME (position_frame), GTK_SHADOW_ETCHED_IN);
-
-  PREF_UI.right_top_radio = gtk_radio_button_new_with_label (NULL, "Right-Top");
-  PREF_UI.middle_radio = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON 
(PREF_UI.right_top_radio), "Middle");
-  PREF_UI.specify_radio = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON 
(PREF_UI.right_top_radio), "Specify");
-  PREF_UI.pos_x_hbox = gtk_hbox_new (FALSE, 1);
-  PREF_UI.pos_y_hbox = gtk_hbox_new (FALSE, 1);
-  specify_alignment_x = gtk_alignment_new (1, 0, 0, 0);
-  specify_alignment_y = gtk_alignment_new (1, 0, 0, 0);
-  pos_x_label = gtk_label_new ("x:");
-  pos_y_label = gtk_label_new ("y:");
-  PREF_UI.pos_x_spin_button = gtk_spin_button_new_with_range (0, 100-PREF.WIDTH, 1);
-  PREF_UI.pos_y_spin_button = gtk_spin_button_new_with_range (0, 50, 1);
-
-  gtk_box_pack_start (GTK_BOX (top_hbox), position_frame, FALSE, FALSE, 2);
-  gtk_container_add (GTK_CONTAINER (position_frame), position_vbox);
-  gtk_box_pack_start (GTK_BOX (position_vbox), PREF_UI.right_top_radio, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (position_vbox), PREF_UI.middle_radio, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (position_vbox), PREF_UI.specify_radio, TRUE, TRUE, 2);
-
-  gtk_box_pack_start (GTK_BOX (position_vbox), specify_alignment_x, TRUE, TRUE, 1);
-  gtk_container_add (GTK_CONTAINER (specify_alignment_x), PREF_UI.pos_x_hbox);
-  gtk_box_pack_start (GTK_BOX (PREF_UI.pos_x_hbox), pos_x_label, TRUE, TRUE, 1);
-  gtk_box_pack_start (GTK_BOX (PREF_UI.pos_x_hbox), PREF_UI.pos_x_spin_button, TRUE, TRUE, 1);
-
-  gtk_box_pack_start (GTK_BOX (position_vbox), specify_alignment_y, TRUE, TRUE, 1);
-  gtk_container_add (GTK_CONTAINER (specify_alignment_y), PREF_UI.pos_y_hbox);
-  gtk_box_pack_start (GTK_BOX (PREF_UI.pos_y_hbox), pos_y_label, TRUE, TRUE, 1);
-  gtk_box_pack_start (GTK_BOX (PREF_UI.pos_y_hbox), PREF_UI.pos_y_spin_button, TRUE, TRUE, 1);
-
-  display_frame = gtk_frame_new ("Display");
-  display_vbox = gtk_vbox_new (TRUE, 2);
-
-  gtk_frame_set_shadow_type (GTK_FRAME (display_frame), GTK_SHADOW_ETCHED_IN);
-
-  no_of_results_hbox = gtk_hbox_new (FALSE, 2);
-  width_hbox = gtk_hbox_new (FALSE, 2);
-  opacity_hbox = gtk_hbox_new (FALSE, 2);
-  no_of_results_label = gtk_label_new ("Results height:");
-  PREF_UI.no_of_results_spin_button = gtk_spin_button_new_with_range (2, 10, 1);
-  width_label = gtk_label_new ("Tito Width:");
-  PREF_UI.width_spin_button = gtk_spin_button_new_with_range (20, 60, 1);
-  opacity_label = gtk_label_new ("Tito Opacity:");
-  PREF_UI.opacity_spin_button = gtk_spin_button_new_with_range (40, 100, 10);
-  PREF_UI.show_insensitive_check_button = gtk_check_button_new_with_label ("Show unavailable actions");
-  action_search_clear_history_button = gtk_button_new_with_label ("Clear history");
-  restore_defaults_button = gtk_button_new_with_label ("Restore defaults");
-
-  gtk_box_pack_start (GTK_BOX (top_hbox), display_frame, FALSE, FALSE, 2);
-  gtk_container_add (GTK_CONTAINER (display_frame), display_vbox);
-  gtk_box_pack_start (GTK_BOX (display_vbox), no_of_results_hbox, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (no_of_results_hbox), no_of_results_label, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (no_of_results_hbox), PREF_UI.no_of_results_spin_button, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (display_vbox), width_hbox, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (width_hbox), width_label, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (width_hbox), PREF_UI.width_spin_button, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (display_vbox), opacity_hbox, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (opacity_hbox), opacity_label, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (opacity_hbox), PREF_UI.opacity_spin_button, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (display_vbox), PREF_UI.show_insensitive_check_button, TRUE, TRUE, 2);
-
-  bottom_hbox = gtk_hbox_new (TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (pref_dialog))), bottom_hbox, FALSE, 
FALSE, 2);
-  gtk_box_pack_start (GTK_BOX (bottom_hbox), action_search_clear_history_button, TRUE, TRUE, 2);
-  gtk_box_pack_start (GTK_BOX (bottom_hbox), restore_defaults_button, TRUE, TRUE, 2);
-
-  action_search_set_prefereces_ui_values ();
-  gtk_widget_show_all (pref_dialog);
-
-  g_signal_connect (PREF_UI.right_top_radio, "toggled", G_CALLBACK (modify_position_spins), NULL);
-  g_signal_connect (PREF_UI.middle_radio, "toggled", G_CALLBACK (modify_position_spins), NULL);
-  g_signal_connect (PREF_UI.specify_radio, "toggled", G_CALLBACK (modify_position_spins), NULL);
-  g_signal_connect (action_search_clear_history_button, "clicked", G_CALLBACK 
(action_search_clear_history_button_clicked), NULL);
-  g_signal_connect (restore_defaults_button, "clicked", G_CALLBACK (restore_defaults_button_clicked), NULL);
-
-  if (gtk_dialog_run (GTK_DIALOG (pref_dialog)) == GTK_RESPONSE_ACCEPT)
-    action_search_update_preferences ();
-
-  gtk_widget_destroy (pref_dialog);
-}
-
-static GtkWidget*
-action_search_setup_results_list (void)
+action_search_setup_results_list (GtkWidget **results_list,
+                                  GtkWidget **list_view)
 {
   gint                wid1 = 100;
-  GtkWidget          *sc_win;
   GtkListStore       *store;
   GtkCellRenderer    *cell1;
   GtkCellRenderer    *cell_renderer;
   GtkTreeViewColumn  *column1, *column2;
 
-  sc_win = gtk_scrolled_window_new (NULL, NULL);
-  store = gtk_list_store_new (N_COL, G_TYPE_STRING, G_TYPE_STRING, GTK_TYPE_ACTION, G_TYPE_BOOLEAN);
-  results_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
-  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (results_list), FALSE);
+  *list_view = GTK_WIDGET (gtk_scrolled_window_new (NULL, NULL));
+  store = gtk_list_store_new (N_COL, 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);
 
   cell1 = gtk_cell_renderer_pixbuf_new ();
   column1 = gtk_tree_view_column_new_with_attributes (NULL,
-                                                  cell1,
-                                                  "stock_id", RESULT_ICON,
-                                                  NULL);
-  gtk_tree_view_append_column (GTK_TREE_VIEW (results_list), column1);
+                                                      cell1,
+                                                      "stock_id", RESULT_ICON,
+                                                      NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (*results_list), column1);
   gtk_tree_view_column_add_attribute (column1, cell1, "sensitive", IS_SENSITIVE);
   gtk_tree_view_column_set_min_width (column1, 22);
 
   cell_renderer = gtk_cell_renderer_text_new ();
   column2 = gtk_tree_view_column_new_with_attributes (NULL,
-                                                  cell_renderer,
-                                                  "markup", RESULT_DATA,
-                                                  NULL);
+                                                      cell_renderer,
+                                                      "markup", RESULT_DATA,
+                                                      NULL);
   gtk_tree_view_column_add_attribute (column2, cell_renderer, "sensitive", IS_SENSITIVE);
-  gtk_tree_view_append_column (GTK_TREE_VIEW (results_list), column2);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (*results_list), column2);
   gtk_tree_view_column_set_max_width (column2, wid1);
 
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sc_win),
-                                 GTK_POLICY_NEVER,
-                                 GTK_POLICY_AUTOMATIC);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (*list_view),
+                                  GTK_POLICY_NEVER,
+                                  GTK_POLICY_AUTOMATIC);
 
-  gtk_container_add (GTK_CONTAINER (sc_win), results_list);
+  gtk_container_add (GTK_CONTAINER (*list_view), *results_list);
   g_object_unref (G_OBJECT (store));
-
-  return sc_win;
 }
 
-static gboolean
-action_search_search_dialog (void)
+static void
+search_dialog_free (SearchDialog *private)
 {
-  GtkWidget *main_vbox, *main_hbox;
-  GtkWidget *preferences_image;
-  GtkWidget *preferences_button;
-  GtkWidget *list_view;
-
-  action_search_dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
-  gtk_window_set_decorated (GTK_WINDOW (action_search_dialog), FALSE);
-  gtk_window_set_default_size (GTK_WINDOW (action_search_dialog), (PREF.WIDTH/100)*par_width, 
DEFAULT_HEIGHT);
-  action_search_update_position ();
-  gtk_window_set_opacity (GTK_WINDOW (action_search_dialog), PREF.OPACITY);
-
-  main_vbox = gtk_vbox_new (FALSE, 2);
-  gtk_container_add (GTK_CONTAINER (action_search_dialog), main_vbox);
-  gtk_widget_show (main_vbox);
-
-  main_hbox = gtk_hbox_new (FALSE, 2);
-  gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, FALSE, TRUE, 0);
-  gtk_widget_show (main_hbox);
-
-  keyword_entry = gtk_entry_new ();
-  gtk_entry_set_has_frame (GTK_ENTRY (keyword_entry), FALSE);
-  gtk_entry_set_icon_from_stock (GTK_ENTRY (keyword_entry), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
-  gtk_widget_show (keyword_entry);
-  gtk_box_pack_start (GTK_BOX (main_hbox), keyword_entry, TRUE, TRUE, 0);
-
-  preferences_image = gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU);
-  preferences_button = gtk_button_new ();
-  gtk_button_set_image (GTK_BUTTON (preferences_button), preferences_image);
-  gtk_widget_show (preferences_image);
-  gtk_widget_show (preferences_button);
-  gtk_box_pack_end (GTK_BOX (main_hbox), preferences_button, FALSE, TRUE, 0);
-
-  list_view = action_search_setup_results_list ();
-  gtk_box_pack_start (GTK_BOX (main_vbox), list_view, TRUE, TRUE, 0);
-
-
-  gtk_widget_set_events (action_search_dialog, GDK_KEY_RELEASE_MASK);
-  gtk_widget_set_events (action_search_dialog, GDK_KEY_PRESS_MASK);
-  gtk_widget_set_events (action_search_dialog, GDK_BUTTON_PRESS_MASK);
-  gtk_widget_set_events (preferences_button, GDK_BUTTON_PRESS_MASK);
-
-  g_signal_connect (results_list, "row-activated", (GCallback) row_activated, NULL);
-  g_signal_connect (keyword_entry, "key-release-event", G_CALLBACK (key_released), list_view);
-  g_signal_connect (results_list, "key_press_event", G_CALLBACK (result_selected), NULL);
-  g_signal_connect (preferences_button, "clicked", G_CALLBACK (context_menu_invoked), NULL);
-  g_signal_connect (action_search_dialog, "focus-out-event", G_CALLBACK (on_focus_out), preferences_button);
-
-  gtk_widget_show (action_search_dialog);
-
-  return TRUE;
+  g_slice_free (SearchDialog, private);
 }
diff --git a/app/dialogs/action-search-dialog.h b/app/dialogs/action-search-dialog.h
index 162efe4..6bc2c40 100644
--- a/app/dialogs/action-search-dialog.h
+++ b/app/dialogs/action-search-dialog.h
@@ -18,7 +18,6 @@
 #ifndef __ACTION_SEARCH_DIALOG_H__
 #define __ACTION_SEARCH_DIALOG_H__
 
-
-GtkWidget * action_search_dialog_create (void);
+GtkWidget * action_search_dialog_create (Gimp *gimp);
 
 #endif /* __ACTION_SEARCH_DIALOG_H__ */
diff --git a/app/dialogs/dialogs-constructors.c b/app/dialogs/dialogs-constructors.c
index 09e2448..706d638 100644
--- a/app/dialogs/dialogs-constructors.c
+++ b/app/dialogs/dialogs-constructors.c
@@ -133,6 +133,15 @@ dialogs_file_export_new (GimpDialogFactory *factory,
 }
 
 GtkWidget *
+dialogs_action_search_get (GimpDialogFactory *factory,
+                           GimpContext       *context,
+                           GimpUIManager     *ui_manager,
+                           gint               view_size)
+{
+  return action_search_dialog_create (context->gimp);
+}
+
+GtkWidget *
 dialogs_preferences_get (GimpDialogFactory *factory,
                          GimpContext       *context,
                          GimpUIManager     *ui_manager,
@@ -196,15 +205,6 @@ dialogs_about_get (GimpDialogFactory *factory,
 }
 
 GtkWidget *
-dialogs_action_search_get (GimpDialogFactory *factory,
-                           GimpContext       *context,
-                           GimpUIManager     *ui_manager,
-                           gint               view_size)
-{
-  return action_search_dialog_create ();
-}
-
-GtkWidget *
 dialogs_error_get (GimpDialogFactory *factory,
                    GimpContext       *context,
                    GimpUIManager     *ui_manager,
diff --git a/app/dialogs/dialogs-constructors.h b/app/dialogs/dialogs-constructors.h
index 53b8d53..bb0a526 100644
--- a/app/dialogs/dialogs-constructors.h
+++ b/app/dialogs/dialogs-constructors.h
@@ -41,6 +41,10 @@ GtkWidget * dialogs_file_export_new        (GimpDialogFactory *factory,
                                             GimpContext       *context,
                                             GimpUIManager     *ui_manager,
                                             gint               view_size);
+GtkWidget * dialogs_action_search_get      (GimpDialogFactory *factory,
+                                            GimpContext       *context,
+                                            GimpUIManager     *ui_manager,
+                                            gint               view_size);
 GtkWidget * dialogs_preferences_get        (GimpDialogFactory *factory,
                                             GimpContext       *context,
                                             GimpUIManager     *ui_manager,
@@ -69,10 +73,6 @@ GtkWidget * dialogs_about_get              (GimpDialogFactory *factory,
                                             GimpContext       *context,
                                             GimpUIManager     *ui_manager,
                                             gint               view_size);
-GtkWidget * dialogs_action_search_get      (GimpDialogFactory *factory,
-                                            GimpContext       *context,
-                                            GimpUIManager     *ui_manager,
-                                            gint               view_size);
 GtkWidget * dialogs_error_get              (GimpDialogFactory *factory,
                                             GimpContext       *context,
                                             GimpUIManager     *ui_manager,
diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c
index 9a4f393..42ec649 100644
--- a/app/dialogs/dialogs.c
+++ b/app/dialogs/dialogs.c
@@ -264,6 +264,8 @@ static const GimpDialogFactoryEntry entries[] =
             dialogs_file_export_new,        FALSE, TRUE, TRUE),
 
   /*  singleton toplevels  */
+  TOPLEVEL ("gimp-action-search-dialog",
+            dialogs_action_search_get,      TRUE,  TRUE, TRUE),
   TOPLEVEL ("gimp-preferences-dialog",
             dialogs_preferences_get,        TRUE, TRUE,  FALSE),
   TOPLEVEL ("gimp-input-devices-dialog",
@@ -278,8 +280,6 @@ static const GimpDialogFactoryEntry entries[] =
             dialogs_tips_get,               TRUE, FALSE, FALSE),
   TOPLEVEL ("gimp-about-dialog",
             dialogs_about_get,              TRUE, FALSE, FALSE),
-  TOPLEVEL ("gimp-action-search-dialog",
-            dialogs_action_search_get,      TRUE, FALSE, FALSE),
   TOPLEVEL ("gimp-error-dialog",
             dialogs_error_get,              TRUE, FALSE, FALSE),
   TOPLEVEL ("gimp-close-all-dialog",
diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c
index be14267..efb8624 100644
--- a/app/dialogs/preferences-dialog.c
+++ b/app/dialogs/preferences-dialog.c
@@ -53,6 +53,7 @@
 #include "widgets/gimptooleditor.h"
 #include "widgets/gimpwidgets-constructors.h"
 #include "widgets/gimpwidgets-utils.h"
+#include "widgets/gimpaction-history.h"
 
 #include "menus/menus.h"
 
@@ -113,6 +114,8 @@ static void   prefs_devices_save_callback         (GtkWidget  *widget,
                                                    Gimp       *gimp);
 static void   prefs_devices_clear_callback        (GtkWidget  *widget,
                                                    Gimp       *gimp);
+static void   prefs_search_empty_callback         (GtkWidget  *widget,
+                                                   gpointer    user_data);
 static void   prefs_tool_options_save_callback    (GtkWidget  *widget,
                                                    Gimp       *gimp);
 static void   prefs_tool_options_clear_callback   (GtkWidget  *widget,
@@ -646,6 +649,13 @@ prefs_devices_clear_callback (GtkWidget *widget,
 }
 
 static void
+prefs_search_empty_callback (GtkWidget  *widget,
+                             gpointer    user_data)
+{
+  gimp_action_history_empty ();
+}
+
+static void
 prefs_tool_options_save_callback (GtkWidget *widget,
                                   Gimp      *gimp)
 {
@@ -1670,10 +1680,27 @@ prefs_dialog_new (Gimp       *gimp,
                             _("H_elp browser to use:"),
                             GTK_TABLE (table), 0, size_group);
 
+  /* Action Search */
+  vbox2 = prefs_frame_new (_("Action Search"), GTK_CONTAINER (vbox), FALSE);
+  table = prefs_table_new (1, GTK_CONTAINER (vbox2));
+
+  prefs_check_button_add (object, "search-show-unavailable-actions",
+                          _("Show _unavailable actions"),
+                          GTK_BOX (vbox2));
+  prefs_spin_button_add (object, "action-history-size", 1.0, 10.0, 0,
+                         _("Maximum History Size:"),
+                         GTK_TABLE (table), 0, size_group);
+
+  button = prefs_button_add (GTK_STOCK_CLEAR,
+                             _("Clear Action History"),
+                             GTK_BOX (vbox2));
+  g_signal_connect (button, "clicked",
+                    G_CALLBACK (prefs_search_empty_callback),
+                    NULL);
+
   g_object_unref (size_group);
   size_group = NULL;
 
-
   /******************/
   /*  Tool Options  */
   /******************/
diff --git a/app/gui/gui.c b/app/gui/gui.c
index 40e471e..7eb3d44 100644
--- a/app/gui/gui.c
+++ b/app/gui/gui.c
@@ -61,6 +61,7 @@
 #include "widgets/gimpuimanager.h"
 #include "widgets/gimpwidgets-utils.h"
 #include "widgets/gimplanguagestore-parser.h"
+#include "widgets/gimpaction-history.h"
 
 #include "actions/actions.h"
 #include "actions/windows-commands.h"
@@ -492,6 +493,7 @@ gui_restore_after_callback (Gimp               *gimp,
                                                     gimp,
                                                     gui_config->tearoff_menus);
   gimp_ui_manager_update (image_ui_manager, gimp);
+  gimp_action_history_init (gui_config);
 
 #ifdef GDK_WINDOWING_QUARTZ
   {
@@ -660,6 +662,7 @@ gui_exit_after_callback (Gimp     *gimp,
                                         gui_show_tooltips_notify,
                                         gimp);
 
+  gimp_action_history_exit (GIMP_GUI_CONFIG (gimp->config));
   g_object_unref (image_ui_manager);
   image_ui_manager = NULL;
 
diff --git a/app/widgets/Makefile.am b/app/widgets/Makefile.am
index 80bb889..3948910 100644
--- a/app/widgets/Makefile.am
+++ b/app/widgets/Makefile.am
@@ -223,6 +223,8 @@ libappwidgets_a_sources = \
        gimplanguagestore.h             \
        gimplanguagestore-parser.c      \
        gimplanguagestore-parser.h      \
+       gimpaction-history.c    \
+       gimpaction-history.h    \
        gimplayertreeview.c             \
        gimplayertreeview.h             \
        gimpmenudock.c                  \
diff --git a/app/widgets/gimpaction-history.c b/app/widgets/gimpaction-history.c
new file mode 100644
index 0000000..4da02bd
--- /dev/null
+++ b/app/widgets/gimpaction-history.c
@@ -0,0 +1,374 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 2013  Jehan <jehan at girinstud.io>
+ *
+ * gimpaction-history.c
+ *
+ * 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 <string.h>
+#include <ctype.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "widgets-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "gimpuimanager.h"
+#include "gimpaction.h"
+#include "gimpaction-history.h"
+
+#define GIMP_ACTION_HISTORY_FILENAME "action_history"
+
+typedef struct {
+  GtkAction    *action;
+  gchar        *name;
+  gint          count;
+} GimpActionHistoryItem;
+
+static struct {
+  GimpGuiConfig *config;
+  GList         *items;
+} history;
+
+static void gimp_action_history_item_free         (GimpActionHistoryItem *item);
+
+static gint gimp_action_history_init_compare_func (GimpActionHistoryItem *a,
+                                                   GimpActionHistoryItem *b);
+static gint gimp_action_history_compare_func      (GimpActionHistoryItem *a,
+                                                   GimpActionHistoryItem *b);
+
+static void gimp_action_insert                    (const gchar           *action_name,
+                                                   gint                   count);
+
+/*  public functions  */
+
+void
+gimp_action_history_init (GimpGuiConfig *config)
+{
+  gchar *history_file_path;
+  gint   count;
+  FILE  *fp;
+  gint   i;
+
+  if (history.items != NULL)
+    {
+      g_warning ("%s: must be run only once.", G_STRFUNC);
+      return;
+    }
+
+  history.config    = config;
+  history_file_path = g_build_filename (gimp_directory (),
+                                        GIMP_ACTION_HISTORY_FILENAME,
+                                        NULL);
+  fp = fopen (history_file_path, "r");
+  if (fp == NULL)
+    /* Probably a first use case. Not necessarily an error. */
+    return;
+
+  for (i = 0; i < config->action_history_size; i++)
+    {
+      /* Let's assume an action name will never be more than 256 character. */
+      gchar action_name[256];
+
+      if (fscanf (fp, "%s %d", action_name, &count) == EOF)
+        break;
+
+      gimp_action_insert (action_name, count);
+    }
+
+  if (count > 1)
+    {
+      GList *actions = history.items;
+
+      for (; actions && i; actions = g_list_next (actions), i--)
+        {
+          GimpActionHistoryItem *action = actions->data;
+
+          action->count -= count - 1;
+        }
+    }
+
+  g_free (history_file_path);
+
+  fclose (fp);
+}
+
+void
+gimp_action_history_exit (GimpGuiConfig *config)
+{
+  GList *actions = history.items;
+  gchar *history_file_path;
+  gint   min_count = 0;
+  FILE  *fp;
+  gint   i = config->action_history_size;
+
+  /* If we have more items than current history size, trim the history
+     and move down all count so that 1 is lower. */
+  for (; actions && i; actions = g_list_next (actions), i--)
+    ;
+  if (actions)
+    {
+      GimpActionHistoryItem *action = actions->data;
+
+      min_count = action->count - 1;
+    }
+
+  actions = history.items;
+  i       = config->action_history_size;
+
+  history_file_path = g_build_filename (gimp_directory (),
+                                        GIMP_ACTION_HISTORY_FILENAME,
+                                        NULL);
+
+  fp = fopen (history_file_path, "w");
+  for (; actions && i; actions = g_list_next (actions), i--)
+    {
+      GimpActionHistoryItem *action = actions->data;
+
+      fprintf (fp, "%s %d \n", action->name, action->count - min_count);
+    }
+
+  gimp_action_history_empty ();
+  fclose (fp);
+  g_free (history_file_path);
+}
+
+/* gimp_action_history_excluded_action:
+ *
+ * Returns whether an action should be excluded from history.
+ */
+gboolean
+gimp_action_history_excluded_action (const gchar *action_name)
+{
+  return (g_str_has_suffix (action_name, "-menu")           ||
+          g_str_has_suffix (action_name, "-popup")          ||
+          g_str_has_suffix (action_name, "-set")            ||
+          g_str_has_suffix (action_name, "-accel")          ||
+          g_str_has_prefix (action_name, "context-")        ||
+          g_str_has_prefix (action_name, "plug-in-recent-") ||
+          g_strcmp0 (action_name, "plug-in-repeat") == 0    ||
+          g_strcmp0 (action_name, "plug-in-reshow") == 0    ||
+          g_strcmp0 (action_name, "help-action-search") == 0);
+}
+
+/* Callback run on the `activate` signal of an action.
+   It allows us to log all used action. */
+void
+gimp_action_history_activate_callback (GtkAction *action,
+                                       gpointer   user_data)
+{
+  GList                 *actions;
+  GimpActionHistoryItem *history_item;
+  const gchar           *action_name;
+  gint                   previous_count = 0;
+
+  action_name = gtk_action_get_name (action);
+
+  /* Some specific actions are of no log interest. */
+  if (gimp_action_history_excluded_action (action_name))
+    return;
+
+  for (actions = history.items; actions; actions = g_list_next (actions))
+    {
+      history_item = actions->data;
+
+      if (g_strcmp0 (action_name, history_item->name) == 0)
+        {
+          GimpActionHistoryItem *next_history_item = g_list_next (actions) ?
+            g_list_next (actions)->data : NULL;
+
+          /* Is there any other item with the same count?
+             We don't want to leave any count gap to always accept new items.
+             This means that if we increment the only item with a given count,
+             we must decrement the next item.
+             Other consequence is that an item with higher count won't be
+             incremented at all if no other items have the same count. */
+          if (previous_count == history_item->count ||
+              (next_history_item && next_history_item->count == history_item->count))
+            {
+              history_item->count++;
+              /* Remove then reinsert to reorder. */
+              history.items = g_list_remove (history.items, history_item);
+              history.items = g_list_insert_sorted (history.items, history_item,
+                                                    (GCompareFunc) gimp_action_history_compare_func);
+            }
+          else if (previous_count != 0                   &&
+                   previous_count != history_item->count)
+            {
+              GimpActionHistoryItem *previous_history_item = g_list_previous (actions)->data;
+
+              history_item->count++;
+              /* Remove then reinsert to reorder. */
+              history.items = g_list_remove (history.items, history_item);
+              history.items = g_list_insert_sorted (history.items, history_item,
+                                                    (GCompareFunc) gimp_action_history_compare_func);
+
+              previous_history_item->count--;
+              /* Remove then reinsert to reorder. */
+              history.items = g_list_remove (history.items, previous_history_item);
+              history.items = g_list_insert_sorted (history.items, previous_history_item,
+                                                    (GCompareFunc) gimp_action_history_compare_func);
+            }
+          return;
+        }
+
+      previous_count = history_item->count;
+    }
+
+  /* If we are here, this action is not logged yet. */
+  history_item = g_malloc0 (sizeof (GimpActionHistoryItem));
+
+  history_item->action = g_object_ref (action);
+  history_item->name = g_strdup (action_name);
+  history_item->count = 1;
+  history.items = g_list_insert_sorted (history.items,
+                                        history_item,
+                                        (GCompareFunc) gimp_action_history_compare_func);
+}
+
+void
+gimp_action_history_empty (void)
+{
+  g_list_free_full (history.items, (GDestroyNotify) gimp_action_history_item_free);
+  history.items = NULL;
+}
+
+/* Search all history actions which match "keyword"
+   with function match_func(action, keyword).
+
+   @return a list of GtkAction*, to free with:
+   g_list_free_full (result, (GDestroyNotify) g_object_unref);
+  */
+GList*
+gimp_action_history_search (const gchar         *keyword,
+                            GimpActionMatchFunc  match_func,
+                            GimpGuiConfig       *config)
+{
+  GList                 *actions;
+  GimpActionHistoryItem *history_item;
+  GtkAction             *action;
+  GList                 *search_result = NULL;
+  gint                   i             = config->action_history_size;
+
+  for (actions = history.items; actions && i; actions = g_list_next (actions), i--)
+    {
+      history_item = actions->data;
+      action = history_item->action;
+
+      if (! gtk_action_get_sensitive (action) && ! config->search_show_unavailable)
+        continue;
+
+      if (match_func (action, keyword, NULL, FALSE))
+        search_result = g_list_prepend (search_result, g_object_ref (action));
+    }
+
+  return g_list_reverse (search_result);
+}
+
+/*  private functions  */
+
+static void
+gimp_action_history_item_free (GimpActionHistoryItem *item)
+{
+  g_object_unref (item->action);
+  g_free (item->name);
+  g_free (item);
+}
+
+/* Compare function used at list initialization.
+   We use a slightly different compare function as for runtime insert,
+   because we want to keep history file order for equal values. */
+static gint
+gimp_action_history_init_compare_func (GimpActionHistoryItem *a,
+                                       GimpActionHistoryItem *b)
+{
+  return (a->count <= b->count);
+}
+
+/* Compare function used when updating the list.
+   There is no equality case. If they have the same count,
+   I ensure that the first action (last inserted) will be before. */
+static gint
+gimp_action_history_compare_func (GimpActionHistoryItem *a,
+                                  GimpActionHistoryItem *b)
+{
+  return (a->count < b->count);
+}
+
+static void
+gimp_action_insert (const gchar *action_name,
+                    gint         count)
+{
+  GList             *action_groups;
+  GimpUIManager     *manager;
+
+  /* We do not insert some categories of actions. */
+  if (gimp_action_history_excluded_action (action_name))
+    return;
+
+  manager = gimp_ui_managers_from_name ("<Image>")->data;
+
+  for (action_groups = gtk_ui_manager_get_action_groups (GTK_UI_MANAGER (manager));
+       action_groups;
+       action_groups = g_list_next (action_groups))
+    {
+      GimpActionGroup *group = action_groups->data;
+      GList           *actions;
+      GList           *list2;
+      gboolean         found = FALSE;
+
+      actions = gtk_action_group_list_actions (GTK_ACTION_GROUP (group));
+      actions = g_list_sort (actions, (GCompareFunc) gimp_action_name_compare);
+
+      for (list2 = actions; list2; list2 = g_list_next (list2))
+        {
+          GtkAction *action             = list2->data;
+          gint       unavailable_action = -1;
+
+          unavailable_action = strcmp (action_name, gtk_action_get_name (action));
+
+          if (unavailable_action == 0)
+            {
+              /* We found our action. */
+              GimpActionHistoryItem *new_action = g_malloc0 (sizeof (GimpActionHistoryItem));
+
+              new_action->action = g_object_ref (action);
+              new_action->name = g_strdup (action_name);
+              new_action->count = count;
+              history.items = g_list_insert_sorted (history.items,
+                                                    new_action,
+                                                    (GCompareFunc) gimp_action_history_init_compare_func);
+              found = TRUE;
+              break;
+            }
+          else if (unavailable_action < 0)
+            {
+              /* Since the actions list is sorted, it means we passed
+                 all possible actions already and it is not in this group. */
+              break;
+            }
+
+        }
+      g_list_free (actions);
+
+      if (found)
+        break;
+   }
+}
diff --git a/app/widgets/gimpaction-history.h b/app/widgets/gimpaction-history.h
new file mode 100644
index 0000000..c0ca984
--- /dev/null
+++ b/app/widgets/gimpaction-history.h
@@ -0,0 +1,42 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 2013  Jehan <jehan at girinstud.io>
+ *
+ * gimpaction-history.h
+ *
+ * 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_ACTION_HISTORY_H__
+#define __GIMP_ACTION_HISTORY_H__
+
+typedef gboolean   (* GimpActionMatchFunc)       (GtkAction           *action,
+                                                  const gchar         *keyword,
+                                                  gint                *section,
+                                                  gboolean             match_fuzzy);
+
+void       gimp_action_history_init              (GimpGuiConfig       *config);
+void       gimp_action_history_exit              (GimpGuiConfig       *config);
+
+void       gimp_action_history_activate_callback (GtkAction           *action,
+                                                  gpointer             user_data);
+
+void       gimp_action_history_empty             (void);
+
+GList *    gimp_action_history_search            (const gchar         *keyword,
+                                                  GimpActionMatchFunc  match_func,
+                                                  GimpGuiConfig       *config);
+
+gboolean   gimp_action_history_excluded_action   (const gchar         *action_name);
+
+#endif  /* __GIMP_ACTION_HISTORY_H__ */
diff --git a/app/widgets/gimpaction.c b/app/widgets/gimpaction.c
index ff3db4c..ffc68fb 100644
--- a/app/widgets/gimpaction.c
+++ b/app/widgets/gimpaction.c
@@ -37,6 +37,7 @@
 #include "core/gimpviewable.h"
 
 #include "gimpaction.h"
+#include "gimpaction-history.h"
 #include "gimpview.h"
 #include "gimpviewrenderer.h"
 
@@ -52,6 +53,7 @@ enum
 };
 
 
+static void   gimp_action_constructed       (GObject          *object);
 static void   gimp_action_finalize          (GObject          *object);
 static void   gimp_action_set_property      (GObject          *object,
                                              guint             prop_id,
@@ -85,6 +87,7 @@ gimp_action_class_init (GimpActionClass *klass)
   GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
   GimpRGB         black;
 
+  object_class->constructed   = gimp_action_constructed;
   object_class->finalize      = gimp_action_finalize;
   object_class->set_property  = gimp_action_set_property;
   object_class->get_property  = gimp_action_get_property;
@@ -139,6 +142,16 @@ gimp_action_init (GimpAction *action)
 }
 
 static void
+gimp_action_constructed (GObject *object)
+{
+  GimpAction *action = GIMP_ACTION (object);
+
+  g_signal_connect (action, "activate",
+                    (GCallback) gimp_action_history_activate_callback,
+                    NULL);
+}
+
+static void
 gimp_action_finalize (GObject *object)
 {
   GimpAction *action = GIMP_ACTION (object);
diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in
index 5fffd97..4e6c790 100644
--- a/menus/image-menu.xml.in
+++ b/menus/image-menu.xml.in
@@ -750,7 +750,7 @@
       <menuitem action="dialogs-tips" />
       <menuitem action="dialogs-about" />
       <separator />
-      <menuitem action="dialogs-action-search" />
+      <menuitem action="help-action-search" />
       <separator />
       <placeholder name="Programming" />
       <separator />



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