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.
This commit is contained in:
Jehan 2013-09-26 06:02:59 +12:00 committed by Michael Natterer
parent fc8f6c127f
commit 5903e53d51
19 changed files with 1140 additions and 864 deletions

View File

@ -267,13 +267,7 @@ static const GimpStringActionEntry dialogs_toplevel_actions[] =
NULL, NULL,
NC_("dialogs-action", "About GIMP"), NC_("dialogs-action", "About GIMP"),
"gimp-about-dialog", "gimp-about-dialog",
GIMP_HELP_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 }
}; };

View File

@ -47,7 +47,13 @@ static const GimpActionEntry help_actions[] =
NC_("help-action", "_Context Help"), "<shift>F1", NC_("help-action", "_Context Help"), "<shift>F1",
NC_("help-action", "Show the help for a specific user interface item"), NC_("help-action", "Show the help for a specific user interface item"),
G_CALLBACK (help_context_help_cmd_callback), 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 }
}; };

View File

@ -26,6 +26,7 @@
#include "core/gimpprogress.h" #include "core/gimpprogress.h"
#include "widgets/gimpdialogfactory.h"
#include "widgets/gimphelp.h" #include "widgets/gimphelp.h"
#include "actions.h" #include "actions.h"
@ -53,3 +54,17 @@ help_context_help_cmd_callback (GtkAction *action,
gimp_context_help (widget); 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);
}

View File

@ -19,10 +19,12 @@
#define __HELP_COMMANDS_H__ #define __HELP_COMMANDS_H__
void help_help_cmd_callback (GtkAction *action, void help_help_cmd_callback (GtkAction *action,
gpointer data); gpointer data);
void help_context_help_cmd_callback (GtkAction *action, void help_context_help_cmd_callback (GtkAction *action,
gpointer data); gpointer data);
void help_search_actions_cmd_callback (GtkAction *action,
gpointer data);
#endif /* __HELP_COMMANDS_H__ */ #endif /* __HELP_COMMANDS_H__ */

View File

@ -67,6 +67,8 @@ enum
PROP_SHOW_HELP_BUTTON, PROP_SHOW_HELP_BUTTON,
PROP_HELP_LOCALES, PROP_HELP_LOCALES,
PROP_HELP_BROWSER, PROP_HELP_BROWSER,
PROP_SEARCH_SHOW_UNAVAILABLE_ACTIONS,
PROP_ACTION_HISTORY_SIZE,
PROP_USER_MANUAL_ONLINE, PROP_USER_MANUAL_ONLINE,
PROP_USER_MANUAL_ONLINE_URI, PROP_USER_MANUAL_ONLINE_URI,
PROP_DOCK_WINDOW_HINT, PROP_DOCK_WINDOW_HINT,
@ -228,6 +230,15 @@ gimp_gui_config_class_init (GimpGuiConfigClass *klass)
GIMP_TYPE_HELP_BROWSER_TYPE, GIMP_TYPE_HELP_BROWSER_TYPE,
DEFAULT_HELP_BROWSER, DEFAULT_HELP_BROWSER,
GIMP_PARAM_STATIC_STRINGS); 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, GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_USER_MANUAL_ONLINE,
"user-manual-online", "user-manual-online",
USER_MANUAL_ONLINE_BLURB, USER_MANUAL_ONLINE_BLURB,
@ -432,6 +443,12 @@ gimp_gui_config_set_property (GObject *object,
case PROP_HELP_BROWSER: case PROP_HELP_BROWSER:
gui_config->help_browser = g_value_get_enum (value); gui_config->help_browser = g_value_get_enum (value);
break; 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: case PROP_USER_MANUAL_ONLINE:
gui_config->user_manual_online = g_value_get_boolean (value); gui_config->user_manual_online = g_value_get_boolean (value);
break; break;
@ -558,6 +575,12 @@ gimp_gui_config_get_property (GObject *object,
case PROP_HELP_BROWSER: case PROP_HELP_BROWSER:
g_value_set_enum (value, gui_config->help_browser); g_value_set_enum (value, gui_config->help_browser);
break; 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: case PROP_USER_MANUAL_ONLINE:
g_value_set_boolean (value, gui_config->user_manual_online); g_value_set_boolean (value, gui_config->user_manual_online);
break; break;

View File

@ -61,6 +61,11 @@ struct _GimpGuiConfig
gboolean show_help_button; gboolean show_help_button;
gchar *help_locales; gchar *help_locales;
GimpHelpBrowserType help_browser; GimpHelpBrowserType help_browser;
/* Action Search preferences. */
gboolean search_show_unavailable;
gint action_history_size;
gchar *web_browser; gchar *web_browser;
gboolean user_manual_online; gboolean user_manual_online;
gchar *user_manual_online_uri; gchar *user_manual_online_uri;

View File

@ -474,4 +474,10 @@ N_("When enabled, uses OpenCL for some operations.")
"Bugs in event history buffer are frequent so in case of cursor " \ "Bugs in event history buffer are frequent so in case of cursor " \
"offset problems turning it off helps." "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__ */ #endif /* __GIMP_RC_BLURBS_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,6 @@
#ifndef __ACTION_SEARCH_DIALOG_H__ #ifndef __ACTION_SEARCH_DIALOG_H__
#define __ACTION_SEARCH_DIALOG_H__ #define __ACTION_SEARCH_DIALOG_H__
GtkWidget * action_search_dialog_create (Gimp *gimp);
GtkWidget * action_search_dialog_create (void);
#endif /* __ACTION_SEARCH_DIALOG_H__ */ #endif /* __ACTION_SEARCH_DIALOG_H__ */

View File

@ -132,6 +132,15 @@ dialogs_file_export_new (GimpDialogFactory *factory,
return file_save_dialog_new (context->gimp, TRUE); return file_save_dialog_new (context->gimp, TRUE);
} }
GtkWidget *
dialogs_action_search_get (GimpDialogFactory *factory,
GimpContext *context,
GimpUIManager *ui_manager,
gint view_size)
{
return action_search_dialog_create (context->gimp);
}
GtkWidget * GtkWidget *
dialogs_preferences_get (GimpDialogFactory *factory, dialogs_preferences_get (GimpDialogFactory *factory,
GimpContext *context, GimpContext *context,
@ -195,15 +204,6 @@ dialogs_about_get (GimpDialogFactory *factory,
return about_dialog_create (context); return about_dialog_create (context);
} }
GtkWidget *
dialogs_action_search_get (GimpDialogFactory *factory,
GimpContext *context,
GimpUIManager *ui_manager,
gint view_size)
{
return action_search_dialog_create ();
}
GtkWidget * GtkWidget *
dialogs_error_get (GimpDialogFactory *factory, dialogs_error_get (GimpDialogFactory *factory,
GimpContext *context, GimpContext *context,

View File

@ -41,6 +41,10 @@ GtkWidget * dialogs_file_export_new (GimpDialogFactory *factory,
GimpContext *context, GimpContext *context,
GimpUIManager *ui_manager, GimpUIManager *ui_manager,
gint view_size); gint view_size);
GtkWidget * dialogs_action_search_get (GimpDialogFactory *factory,
GimpContext *context,
GimpUIManager *ui_manager,
gint view_size);
GtkWidget * dialogs_preferences_get (GimpDialogFactory *factory, GtkWidget * dialogs_preferences_get (GimpDialogFactory *factory,
GimpContext *context, GimpContext *context,
GimpUIManager *ui_manager, GimpUIManager *ui_manager,
@ -69,10 +73,6 @@ GtkWidget * dialogs_about_get (GimpDialogFactory *factory,
GimpContext *context, GimpContext *context,
GimpUIManager *ui_manager, GimpUIManager *ui_manager,
gint view_size); gint view_size);
GtkWidget * dialogs_action_search_get (GimpDialogFactory *factory,
GimpContext *context,
GimpUIManager *ui_manager,
gint view_size);
GtkWidget * dialogs_error_get (GimpDialogFactory *factory, GtkWidget * dialogs_error_get (GimpDialogFactory *factory,
GimpContext *context, GimpContext *context,
GimpUIManager *ui_manager, GimpUIManager *ui_manager,

View File

@ -264,6 +264,8 @@ static const GimpDialogFactoryEntry entries[] =
dialogs_file_export_new, FALSE, TRUE, TRUE), dialogs_file_export_new, FALSE, TRUE, TRUE),
/* singleton toplevels */ /* singleton toplevels */
TOPLEVEL ("gimp-action-search-dialog",
dialogs_action_search_get, TRUE, TRUE, TRUE),
TOPLEVEL ("gimp-preferences-dialog", TOPLEVEL ("gimp-preferences-dialog",
dialogs_preferences_get, TRUE, TRUE, FALSE), dialogs_preferences_get, TRUE, TRUE, FALSE),
TOPLEVEL ("gimp-input-devices-dialog", TOPLEVEL ("gimp-input-devices-dialog",
@ -278,8 +280,6 @@ static const GimpDialogFactoryEntry entries[] =
dialogs_tips_get, TRUE, FALSE, FALSE), dialogs_tips_get, TRUE, FALSE, FALSE),
TOPLEVEL ("gimp-about-dialog", TOPLEVEL ("gimp-about-dialog",
dialogs_about_get, TRUE, FALSE, FALSE), dialogs_about_get, TRUE, FALSE, FALSE),
TOPLEVEL ("gimp-action-search-dialog",
dialogs_action_search_get, TRUE, FALSE, FALSE),
TOPLEVEL ("gimp-error-dialog", TOPLEVEL ("gimp-error-dialog",
dialogs_error_get, TRUE, FALSE, FALSE), dialogs_error_get, TRUE, FALSE, FALSE),
TOPLEVEL ("gimp-close-all-dialog", TOPLEVEL ("gimp-close-all-dialog",

View File

@ -53,6 +53,7 @@
#include "widgets/gimptooleditor.h" #include "widgets/gimptooleditor.h"
#include "widgets/gimpwidgets-constructors.h" #include "widgets/gimpwidgets-constructors.h"
#include "widgets/gimpwidgets-utils.h" #include "widgets/gimpwidgets-utils.h"
#include "widgets/gimpaction-history.h"
#include "menus/menus.h" #include "menus/menus.h"
@ -113,6 +114,8 @@ static void prefs_devices_save_callback (GtkWidget *widget,
Gimp *gimp); Gimp *gimp);
static void prefs_devices_clear_callback (GtkWidget *widget, static void prefs_devices_clear_callback (GtkWidget *widget,
Gimp *gimp); Gimp *gimp);
static void prefs_search_empty_callback (GtkWidget *widget,
gpointer user_data);
static void prefs_tool_options_save_callback (GtkWidget *widget, static void prefs_tool_options_save_callback (GtkWidget *widget,
Gimp *gimp); Gimp *gimp);
static void prefs_tool_options_clear_callback (GtkWidget *widget, static void prefs_tool_options_clear_callback (GtkWidget *widget,
@ -645,6 +648,13 @@ prefs_devices_clear_callback (GtkWidget *widget,
} }
} }
static void
prefs_search_empty_callback (GtkWidget *widget,
gpointer user_data)
{
gimp_action_history_empty ();
}
static void static void
prefs_tool_options_save_callback (GtkWidget *widget, prefs_tool_options_save_callback (GtkWidget *widget,
Gimp *gimp) Gimp *gimp)
@ -1670,10 +1680,27 @@ prefs_dialog_new (Gimp *gimp,
_("H_elp browser to use:"), _("H_elp browser to use:"),
GTK_TABLE (table), 0, size_group); 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); g_object_unref (size_group);
size_group = NULL; size_group = NULL;
/******************/ /******************/
/* Tool Options */ /* Tool Options */
/******************/ /******************/

View File

@ -61,6 +61,7 @@
#include "widgets/gimpuimanager.h" #include "widgets/gimpuimanager.h"
#include "widgets/gimpwidgets-utils.h" #include "widgets/gimpwidgets-utils.h"
#include "widgets/gimplanguagestore-parser.h" #include "widgets/gimplanguagestore-parser.h"
#include "widgets/gimpaction-history.h"
#include "actions/actions.h" #include "actions/actions.h"
#include "actions/windows-commands.h" #include "actions/windows-commands.h"
@ -492,6 +493,7 @@ gui_restore_after_callback (Gimp *gimp,
gimp, gimp,
gui_config->tearoff_menus); gui_config->tearoff_menus);
gimp_ui_manager_update (image_ui_manager, gimp); gimp_ui_manager_update (image_ui_manager, gimp);
gimp_action_history_init (gui_config);
#ifdef GDK_WINDOWING_QUARTZ #ifdef GDK_WINDOWING_QUARTZ
{ {
@ -660,6 +662,7 @@ gui_exit_after_callback (Gimp *gimp,
gui_show_tooltips_notify, gui_show_tooltips_notify,
gimp); gimp);
gimp_action_history_exit (GIMP_GUI_CONFIG (gimp->config));
g_object_unref (image_ui_manager); g_object_unref (image_ui_manager);
image_ui_manager = NULL; image_ui_manager = NULL;

View File

@ -223,6 +223,8 @@ libappwidgets_a_sources = \
gimplanguagestore.h \ gimplanguagestore.h \
gimplanguagestore-parser.c \ gimplanguagestore-parser.c \
gimplanguagestore-parser.h \ gimplanguagestore-parser.h \
gimpaction-history.c \
gimpaction-history.h \
gimplayertreeview.c \ gimplayertreeview.c \
gimplayertreeview.h \ gimplayertreeview.h \
gimpmenudock.c \ gimpmenudock.c \

View File

@ -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;
}
}

View File

@ -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__ */

View File

@ -37,6 +37,7 @@
#include "core/gimpviewable.h" #include "core/gimpviewable.h"
#include "gimpaction.h" #include "gimpaction.h"
#include "gimpaction-history.h"
#include "gimpview.h" #include "gimpview.h"
#include "gimpviewrenderer.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_finalize (GObject *object);
static void gimp_action_set_property (GObject *object, static void gimp_action_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -85,6 +87,7 @@ gimp_action_class_init (GimpActionClass *klass)
GtkActionClass *action_class = GTK_ACTION_CLASS (klass); GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
GimpRGB black; GimpRGB black;
object_class->constructed = gimp_action_constructed;
object_class->finalize = gimp_action_finalize; object_class->finalize = gimp_action_finalize;
object_class->set_property = gimp_action_set_property; object_class->set_property = gimp_action_set_property;
object_class->get_property = gimp_action_get_property; object_class->get_property = gimp_action_get_property;
@ -138,6 +141,16 @@ gimp_action_init (GimpAction *action)
NULL); NULL);
} }
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 static void
gimp_action_finalize (GObject *object) gimp_action_finalize (GObject *object)
{ {

View File

@ -750,7 +750,7 @@
<menuitem action="dialogs-tips" /> <menuitem action="dialogs-tips" />
<menuitem action="dialogs-about" /> <menuitem action="dialogs-about" />
<separator /> <separator />
<menuitem action="dialogs-action-search" /> <menuitem action="help-action-search" />
<separator /> <separator />
<placeholder name="Programming" /> <placeholder name="Programming" />
<separator /> <separator />