Issue #9228: make GEGL Operation tool's operations their own actions too.

Though the GEGL Operation tool is still useful as a generic dialog, let's
generate also per-operation (the ones not ignored and not already special-cased
in the rest of the GUI) tools and actions.

These "tools" are mostly hidden (e.g. not selectable in toolbox where it would
be a bit useless as they would show with the generic GEGL icon or none), but
they can be searched with the action search, shortcuts can be assigned and they
can be added to menus.
This commit is contained in:
Jehan 2023-04-13 19:08:44 +02:00
parent 60354a0e59
commit d51c64ec06
4 changed files with 169 additions and 39 deletions

View file

@ -147,7 +147,13 @@ gimp_tool_dialog_new (GimpToolInfo *tool_info,
gimp_dialog_add_buttons_valist (GIMP_DIALOG (dialog), args);
va_end (args);
identifier = g_strconcat (gimp_object_get_name (tool_info), "-dialog", NULL);
/* We don't register a foreign dialog for every generated GEGL op tool. Simply
* use the base GimpGeglTool dialog for them all.
*/
if (g_str_has_prefix (gimp_object_get_name (tool_info), "gimp-gegl-op-"))
identifier = g_strdup ("gimp-gegl-tool-dialog");
else
identifier = g_strconcat (gimp_object_get_name (tool_info), "-dialog", NULL);
gimp_dialog_factory_add_foreign (gimp_dialog_factory_get_singleton (),
identifier,

View file

@ -748,7 +748,10 @@ gimp_tools_register (GType tool_type,
gimp_tool_item_set_visible (GIMP_TOOL_ITEM (tool_info), visible);
/* hack to hide the operation tool entirely */
if (tool_type == GIMP_TYPE_OPERATION_TOOL)
if (tool_type == GIMP_TYPE_OPERATION_TOOL ||
/* Don't show the generated GEGL tools either. */
(tool_type == GIMP_TYPE_GEGL_TOOL &&
g_strcmp0 (identifier, "gimp-gegl-tool") != 0))
tool_info->hidden = TRUE;
/* hack to not require experimental tools to be present in toolrc */

View file

@ -29,8 +29,12 @@
#include "tools-types.h"
#include "core/gimp.h"
#include "core/gimptoolinfo.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimppropwidgets.h"
#include "widgets/gimpwidgets-utils.h"
#include "gimpfilteroptions.h"
#include "gimpgegltool.h"
@ -49,16 +53,18 @@ enum
/* local function prototypes */
static void gimp_gegl_tool_control (GimpTool *tool,
GimpToolAction action,
GimpDisplay *display);
static void gimp_gegl_tool_control (GimpTool *tool,
GimpToolAction action,
GimpDisplay *display);
static void gimp_gegl_tool_dialog (GimpFilterTool *filter_tool);
static GList * gimp_get_geglopclasses (void);
static void gimp_gegl_tool_halt (GimpGeglTool *gegl_tool);
static void gimp_gegl_tool_dialog (GimpFilterTool *filter_tool);
static void gimp_gegl_tool_operation_changed (GtkWidget *widget,
GimpGeglTool *gegl_tool);
static void gimp_gegl_tool_halt (GimpGeglTool *gegl_tool);
static void gimp_gegl_tool_operation_changed (GtkWidget *widget,
GimpGeglTool *gegl_tool);
G_DEFINE_TYPE (GimpGeglTool, gimp_gegl_tool, GIMP_TYPE_OPERATION_TOOL)
@ -70,6 +76,13 @@ void
gimp_gegl_tool_register (GimpToolRegisterCallback callback,
gpointer data)
{
GimpGeglToolClass *klass;
Gimp *gimp = GIMP (data);
GList *opclasses;
GList *iter;
klass = g_type_class_ref (GIMP_TYPE_GEGL_TOOL);
(* callback) (GIMP_TYPE_GEGL_TOOL,
GIMP_TYPE_FILTER_OPTIONS,
gimp_color_options_gui,
@ -81,6 +94,76 @@ gimp_gegl_tool_register (GimpToolRegisterCallback callback,
NULL, GIMP_HELP_TOOL_GEGL,
GIMP_ICON_GEGL,
data);
/* We generate hidden tools for every non-ignored GEGL operation, allowing
* people to easily access their custom GEGL operations.
*/
opclasses = gimp_get_geglopclasses ();
for (iter = opclasses; iter; iter = iter->next)
{
GeglOperationClass *opclass = GEGL_OPERATION_CLASS (iter->data);
const gchar *icon_name = NULL;
const gchar *op_name = opclass->name;
const gchar *title;
const gchar *desc;
gchar *formatted_op_name;
gchar *action_name;
gchar *identifier;
gchar *label;
gint i = 2;
formatted_op_name = g_strdup (opclass->name);
gimp_make_valid_action_name (formatted_op_name);
action_name = g_strdup_printf ("tools-gegl-op-%s", formatted_op_name);
while (g_action_group_has_action (G_ACTION_GROUP (gimp->app), action_name))
{
/* In the off-chance that after formatting to a valid action name, 2
* operations end up generating the same action name.
*/
g_free (action_name);
action_name = g_strdup_printf ("tools-gegl-op-%s-%d", formatted_op_name, i++);
}
g_free (formatted_op_name);
/* gimp_tool_info_get_action_name() expects a name starting with "gimp-"
* and ending with "-tool".
*/
identifier = g_strdup_printf ("gimp-%s-tool", action_name + strlen ("tools-"));
g_hash_table_replace (klass->generated_ops, action_name, g_strdup (op_name));
if (g_str_has_prefix (op_name, "gegl:"))
icon_name = GIMP_ICON_GEGL;
if (g_str_has_prefix (op_name, "gegl:"))
op_name += strlen ("gegl:");
title = gegl_operation_class_get_key (opclass, "title");
desc = gegl_operation_class_get_key (opclass, "description");
if (title)
label = g_strdup_printf ("%s (%s)", title, op_name);
else
label = g_strdup (op_name);
(* callback) (GIMP_TYPE_GEGL_TOOL,
GIMP_TYPE_FILTER_OPTIONS,
gimp_color_options_gui,
0,
identifier,
label, desc ? desc : label,
label, NULL,
NULL, GIMP_HELP_TOOL_GEGL,
icon_name,
data);
g_free (label);
g_free (identifier);
}
g_list_free (opclasses);
g_type_class_unref (klass);
}
static void
@ -92,6 +175,12 @@ gimp_gegl_tool_class_init (GimpGeglToolClass *klass)
tool_class->control = gimp_gegl_tool_control;
filter_tool_class->dialog = gimp_gegl_tool_dialog;
/* Store the mapping from tool identifier to operation name.
* This data is leaking, otherwise we'd have to register a dynamic type with a
* class_finalize() class method.
**/
klass->generated_ops = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
}
static void
@ -400,9 +489,10 @@ gimp_get_geglopclasses (void)
static void
gimp_gegl_tool_dialog (GimpFilterTool *filter_tool)
{
GimpGeglTool *tool = GIMP_GEGL_TOOL (filter_tool);
GimpOperationTool *o_tool = GIMP_OPERATION_TOOL (filter_tool);
GtkListStore *store;
GimpGeglTool *tool = GIMP_GEGL_TOOL (filter_tool);
GimpOperationTool *o_tool = GIMP_OPERATION_TOOL (filter_tool);
GimpToolInfo *tool_info = GIMP_TOOL (tool)->tool_info;
GtkListStore *store = NULL;
GtkCellRenderer *cell;
GtkWidget *main_vbox;
GtkWidget *hbox;
@ -411,6 +501,14 @@ gimp_gegl_tool_dialog (GimpFilterTool *filter_tool)
GtkWidget *options_box;
GList *opclasses;
GList *iter;
gchar *action_name;
gchar *show_op_name = NULL;
GimpGeglToolClass *klass;
klass = GIMP_GEGL_TOOL_GET_CLASS (tool);
action_name = gimp_tool_info_get_action_name (tool_info);
show_op_name = g_hash_table_lookup (klass->generated_ops, action_name);
g_free (action_name);
GIMP_FILTER_TOOL_CLASS (parent_class)->dialog (filter_tool);
@ -425,8 +523,9 @@ gimp_gegl_tool_dialog (GimpFilterTool *filter_tool)
gtk_box_reorder_child (GTK_BOX (main_vbox), hbox, 0);
gtk_widget_show (hbox);
store = gtk_list_store_new (N_COLUMNS,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
if (show_op_name == NULL)
store = gtk_list_store_new (N_COLUMNS,
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
opclasses = gimp_get_geglopclasses ();
@ -438,6 +537,9 @@ gimp_gegl_tool_dialog (GimpFilterTool *filter_tool)
const gchar *title;
gchar *label;
if (show_op_name != NULL && g_strcmp0 (show_op_name, op_name) != 0)
continue;
if (g_str_has_prefix (opclass->name, "gegl:"))
icon_name = GIMP_ICON_GEGL;
@ -451,40 +553,57 @@ gimp_gegl_tool_dialog (GimpFilterTool *filter_tool)
else
label = g_strdup (op_name);
gtk_list_store_insert_with_values (store, NULL, -1,
COLUMN_NAME, opclass->name,
COLUMN_LABEL, label,
COLUMN_ICON_NAME, icon_name,
-1);
if (store != NULL)
gtk_list_store_insert_with_values (store, NULL, -1,
COLUMN_NAME, opclass->name,
COLUMN_LABEL, label,
COLUMN_ICON_NAME, icon_name,
-1);
else
gimp_operation_tool_set_operation (GIMP_OPERATION_TOOL (tool),
opclass->name,
label,
gegl_operation_class_get_key (opclass, "description"),
NULL,
icon_name,
GIMP_HELP_TOOL_GEGL);
g_free (label);
}
if (show_op_name != NULL)
break;
}
g_list_free (opclasses);
combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
g_object_unref (store);
gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
gtk_widget_show (combo);
/* We either show the full combo or we found a specific operation. */
g_return_if_fail (store != NULL || iter != NULL);
cell = gtk_cell_renderer_pixbuf_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell,
"icon-name", COLUMN_ICON_NAME);
if (store != NULL)
{
combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
g_object_unref (store);
gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
gtk_widget_show (combo);
cell = gtk_cell_renderer_text_new ();
g_object_set (cell,
"ellipsize", PANGO_ELLIPSIZE_MIDDLE,
NULL);
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell,
"text", COLUMN_LABEL);
cell = gtk_cell_renderer_pixbuf_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell,
"icon-name", COLUMN_ICON_NAME);
g_signal_connect (combo, "changed",
G_CALLBACK (gimp_gegl_tool_operation_changed),
tool);
cell = gtk_cell_renderer_text_new ();
g_object_set (cell,
"ellipsize", PANGO_ELLIPSIZE_MIDDLE,
NULL);
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell,
"text", COLUMN_LABEL);
tool->operation_combo = combo;
g_signal_connect (combo, "changed",
G_CALLBACK (gimp_gegl_tool_operation_changed),
tool);
tool->operation_combo = combo;
}
tool->description_label = gtk_label_new ("");
gtk_label_set_line_wrap (GTK_LABEL (tool->description_label), TRUE);

View file

@ -45,6 +45,8 @@ struct _GimpGeglTool
struct _GimpGeglToolClass
{
GimpOperationToolClass parent_class;
GHashTable *generated_ops;
};