mirror of https://github.com/GNOME/gimp.git
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:
parent
fc8f6c127f
commit
5903e53d51
|
@ -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 }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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__ */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
@ -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__ */
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 */
|
||||||
/******************/
|
/******************/
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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__ */
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
Loading…
Reference in New Issue