mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 09:23:24 +00:00

This goes with our planned change of not making GimpProcedure arguments order relevant anymore regarding the PDB API. In particular, it means we don't want to use GimpValueArray for various procedure arguments API, but directly GimpProcedureConfig objects. This change will allow to add or reorder arguments in the future, so that we won't have to create new PDB procedures when adding new arguments, while still keeping PDB API stability.
651 lines
20 KiB
C
651 lines
20 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <libgimp/gimp.h>
|
|
|
|
#include "tinyscheme/scheme-private.h"
|
|
|
|
#include "script-fu-types.h"
|
|
#include "script-fu-arg.h"
|
|
#include "script-fu-script.h"
|
|
#include "script-fu-run-func.h"
|
|
|
|
#include "script-fu-intl.h"
|
|
|
|
|
|
/*
|
|
* Local Functions
|
|
*/
|
|
|
|
static gboolean script_fu_script_param_init (SFScript *script,
|
|
GParamSpec **pspecs,
|
|
guint n_pspecs,
|
|
GimpProcedureConfig *config,
|
|
SFArgType type,
|
|
gint n);
|
|
static void script_fu_script_set_proc_metadata (
|
|
GimpProcedure *procedure,
|
|
SFScript *script);
|
|
static void script_fu_script_set_proc_args (
|
|
GimpProcedure *procedure,
|
|
SFScript *script,
|
|
guint first_conveyed_arg);
|
|
static void script_fu_script_set_drawable_sensitivity (
|
|
GimpProcedure *procedure,
|
|
SFScript *script);
|
|
|
|
static void script_fu_command_append_drawables (
|
|
GString *s,
|
|
guint n_drawables,
|
|
GimpDrawable **drawables);
|
|
/*
|
|
* Function definitions
|
|
*/
|
|
|
|
SFScript *
|
|
script_fu_script_new (const gchar *name,
|
|
const gchar *menu_label,
|
|
const gchar *blurb,
|
|
const gchar *author,
|
|
const gchar *copyright,
|
|
const gchar *date,
|
|
const gchar *image_types,
|
|
gint n_args)
|
|
{
|
|
SFScript *script;
|
|
|
|
script = g_slice_new0 (SFScript);
|
|
|
|
script->name = g_strdup (name);
|
|
script->menu_label = g_strdup (menu_label);
|
|
script->blurb = g_strdup (blurb);
|
|
script->author = g_strdup (author);
|
|
script->copyright = g_strdup (copyright);
|
|
script->date = g_strdup (date);
|
|
script->image_types = g_strdup (image_types);
|
|
|
|
script->n_args = n_args;
|
|
script->args = g_new0 (SFArg, script->n_args);
|
|
|
|
script->drawable_arity = SF_NO_DRAWABLE; /* default */
|
|
|
|
return script;
|
|
}
|
|
|
|
void
|
|
script_fu_script_free (SFScript *script)
|
|
{
|
|
gint i;
|
|
|
|
g_return_if_fail (script != NULL);
|
|
|
|
g_free (script->name);
|
|
g_free (script->blurb);
|
|
g_free (script->menu_label);
|
|
g_free (script->author);
|
|
g_free (script->copyright);
|
|
g_free (script->date);
|
|
g_free (script->image_types);
|
|
|
|
for (i = 0; i < script->n_args; i++)
|
|
{
|
|
script_fu_arg_free (&script->args[i]);
|
|
}
|
|
|
|
g_free (script->args);
|
|
|
|
g_slice_free (SFScript, script);
|
|
}
|
|
|
|
|
|
/*
|
|
* From the script, create a temporary PDB procedure,
|
|
* and install it as owned by the scriptfu extension PDB proc.
|
|
*/
|
|
void
|
|
script_fu_script_install_proc (GimpPlugIn *plug_in,
|
|
SFScript *script)
|
|
{
|
|
GimpProcedure *procedure;
|
|
|
|
g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
|
|
g_return_if_fail (script != NULL);
|
|
|
|
procedure = script_fu_script_create_PDB_procedure (plug_in,
|
|
script,
|
|
GIMP_PDB_PROC_TYPE_TEMPORARY);
|
|
|
|
gimp_plug_in_add_temp_procedure (plug_in, procedure);
|
|
g_object_unref (procedure);
|
|
}
|
|
|
|
|
|
/*
|
|
* Create and return a GimpProcedure or its subclass GimpImageProcedure.
|
|
* Caller typically either:
|
|
* install it owned by self as TEMPORARY type procedure
|
|
* OR return it as the result of a create_procedure callback from GIMP (PLUGIN type procedure.)
|
|
*
|
|
* Caller must unref the procedure.
|
|
*
|
|
* Understands ScriptFu's internal run funcs for GimpProcedure and GimpImageProcedure
|
|
*/
|
|
GimpProcedure *
|
|
script_fu_script_create_PDB_procedure (GimpPlugIn *plug_in,
|
|
SFScript *script,
|
|
GimpPDBProcType plug_in_type)
|
|
{
|
|
GimpProcedure *procedure;
|
|
|
|
if (script->proc_class == GIMP_TYPE_IMAGE_PROCEDURE)
|
|
{
|
|
g_debug ("script_fu_script_create_PDB_procedure: %s, plugin type %i, image_proc",
|
|
script->name, plug_in_type);
|
|
|
|
procedure = gimp_image_procedure_new (plug_in, script->name,
|
|
plug_in_type,
|
|
(GimpRunImageFunc) script_fu_run_image_procedure,
|
|
script, /* user_data, pointer in extension-script-fu process */
|
|
NULL);
|
|
|
|
script_fu_script_set_proc_metadata (procedure, script);
|
|
|
|
/* Script author does not declare image, drawable in script-fu-register-filter,
|
|
* and we don't add to formal args in PDB.
|
|
* The convenience class GimpImageProcedure already has formal args:
|
|
* run_mode, image, n_drawables, drawables.
|
|
* "0" means not skip any arguments declared in the script.
|
|
*/
|
|
script_fu_script_set_proc_args (procedure, script, 0);
|
|
|
|
script_fu_script_set_drawable_sensitivity (procedure, script);
|
|
}
|
|
else
|
|
{
|
|
g_assert (script->proc_class == GIMP_TYPE_PROCEDURE);
|
|
g_debug ("script_fu_script_create_PDB_procedure: %s, plugin type %i, ordinary proc",
|
|
script->name, plug_in_type);
|
|
|
|
procedure = gimp_procedure_new (plug_in, script->name,
|
|
plug_in_type,
|
|
script_fu_run_procedure,
|
|
script, NULL);
|
|
|
|
script_fu_script_set_proc_metadata (procedure, script);
|
|
|
|
gimp_procedure_add_argument (procedure,
|
|
g_param_spec_enum ("run-mode",
|
|
"Run mode",
|
|
"The run mode",
|
|
GIMP_TYPE_RUN_MODE,
|
|
GIMP_RUN_INTERACTIVE,
|
|
G_PARAM_READWRITE));
|
|
|
|
script_fu_script_set_proc_args (procedure, script, 0);
|
|
|
|
/* !!! Author did not declare drawable arity, it was inferred. */
|
|
script_fu_script_set_drawable_sensitivity (procedure, script);
|
|
}
|
|
|
|
|
|
return procedure;
|
|
}
|
|
|
|
void
|
|
script_fu_script_uninstall_proc (GimpPlugIn *plug_in,
|
|
SFScript *script)
|
|
{
|
|
g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
|
|
g_return_if_fail (script != NULL);
|
|
|
|
gimp_plug_in_remove_temp_procedure (plug_in, script->name);
|
|
}
|
|
|
|
gchar *
|
|
script_fu_script_get_title (SFScript *script)
|
|
{
|
|
gchar *title;
|
|
gchar *tmp;
|
|
|
|
g_return_val_if_fail (script != NULL, NULL);
|
|
|
|
/* strip mnemonics from the menupath */
|
|
title = gimp_strip_uline (script->menu_label);
|
|
|
|
/* if this looks like a full menu path, use only the last part */
|
|
if (title[0] == '<' && (tmp = strrchr (title, '/')) && tmp[1])
|
|
{
|
|
tmp = g_strdup (tmp + 1);
|
|
|
|
g_free (title);
|
|
title = tmp;
|
|
}
|
|
|
|
/* cut off ellipsis */
|
|
tmp = (strstr (title, "..."));
|
|
if (! tmp)
|
|
/* U+2026 HORIZONTAL ELLIPSIS */
|
|
tmp = strstr (title, "\342\200\246");
|
|
|
|
if (tmp && tmp == (title + strlen (title) - 3))
|
|
*tmp = '\0';
|
|
|
|
return title;
|
|
}
|
|
|
|
void
|
|
script_fu_script_reset (SFScript *script,
|
|
gboolean reset_ids)
|
|
{
|
|
gint i;
|
|
|
|
g_return_if_fail (script != NULL);
|
|
|
|
for (i = 0; i < script->n_args; i++)
|
|
{
|
|
script_fu_arg_reset (&script->args[i], reset_ids);
|
|
}
|
|
}
|
|
|
|
gint
|
|
script_fu_script_collect_standard_args (SFScript *script,
|
|
GParamSpec **pspecs,
|
|
guint n_pspecs,
|
|
GimpProcedureConfig *config)
|
|
{
|
|
gint params_consumed = 0;
|
|
|
|
g_return_val_if_fail (script != NULL, 0);
|
|
|
|
/* the first parameter may be a DISPLAY id */
|
|
if (script_fu_script_param_init (script, pspecs, n_pspecs,
|
|
config, SF_DISPLAY,
|
|
params_consumed))
|
|
{
|
|
params_consumed++;
|
|
}
|
|
|
|
/* an IMAGE id may come first or after the DISPLAY id */
|
|
if (script_fu_script_param_init (script, pspecs, n_pspecs,
|
|
config, SF_IMAGE,
|
|
params_consumed))
|
|
{
|
|
params_consumed++;
|
|
|
|
/* and may be followed by a DRAWABLE, LAYER, CHANNEL or
|
|
* VECTORS id
|
|
*/
|
|
if (script_fu_script_param_init (script, pspecs, n_pspecs,
|
|
config, SF_DRAWABLE,
|
|
params_consumed) ||
|
|
script_fu_script_param_init (script, pspecs, n_pspecs,
|
|
config, SF_LAYER,
|
|
params_consumed) ||
|
|
script_fu_script_param_init (script, pspecs, n_pspecs,
|
|
config, SF_CHANNEL,
|
|
params_consumed) ||
|
|
script_fu_script_param_init (script, pspecs, n_pspecs,
|
|
config, SF_VECTORS,
|
|
params_consumed))
|
|
{
|
|
params_consumed++;
|
|
}
|
|
}
|
|
|
|
return params_consumed;
|
|
}
|
|
|
|
/* Methods that form "commands" i.e. texts in Scheme language
|
|
* that represent calls to the inner run func defined in a script.
|
|
*/
|
|
|
|
gchar *
|
|
script_fu_script_get_command (SFScript *script)
|
|
{
|
|
GString *s;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (script != NULL, NULL);
|
|
|
|
s = g_string_new ("(");
|
|
g_string_append (s, script->name);
|
|
|
|
for (i = 0; i < script->n_args; i++)
|
|
{
|
|
g_string_append_c (s, ' ');
|
|
|
|
script_fu_arg_append_repr_from_self (&script->args[i], s);
|
|
}
|
|
|
|
g_string_append_c (s, ')');
|
|
|
|
return g_string_free (s, FALSE);
|
|
}
|
|
|
|
gchar *
|
|
script_fu_script_get_command_from_params (SFScript *script,
|
|
GParamSpec **pspecs,
|
|
guint n_pspecs,
|
|
GimpProcedureConfig *config)
|
|
{
|
|
GString *s;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (script != NULL, NULL);
|
|
|
|
s = g_string_new ("(");
|
|
g_string_append (s, script->name);
|
|
|
|
for (i = 0; i < script->n_args; i++)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
GParamSpec *pspec = pspecs[i + 1];
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
g_object_get_property (G_OBJECT (config), pspec->name, &value);
|
|
|
|
g_string_append_c (s, ' ');
|
|
|
|
script_fu_arg_append_repr_from_gvalue (&script->args[i],
|
|
s,
|
|
&value);
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
g_string_append_c (s, ')');
|
|
|
|
return g_string_free (s, FALSE);
|
|
}
|
|
|
|
/* Append a literal representing a Scheme container of numerics
|
|
* where the numerics are the ID's of the given drawables.
|
|
* Container is scheme vector, meaning its elements are all the same type.
|
|
*/
|
|
static void
|
|
script_fu_command_append_drawables (GString *s,
|
|
guint n_drawables,
|
|
GimpDrawable **drawables)
|
|
{
|
|
/* Require non-empty array of drawables. */
|
|
g_assert (n_drawables > 0);
|
|
|
|
/* !!! leading space to separate from prior args.
|
|
* #() is scheme syntax for a vector.
|
|
*/
|
|
g_string_append (s, " #(" );
|
|
for (guint i=0; i < n_drawables; i++)
|
|
{
|
|
g_string_append_printf (s, " %d", gimp_item_get_id ((GimpItem*) drawables[i]));
|
|
}
|
|
g_string_append (s, ")" );
|
|
/* Ensure string is like: " #( 1 2 3)" */
|
|
}
|
|
|
|
|
|
gchar *
|
|
script_fu_script_get_command_for_image_proc (SFScript *script,
|
|
GimpImage *image,
|
|
guint n_drawables,
|
|
GimpDrawable **drawables,
|
|
GimpProcedureConfig *config)
|
|
{
|
|
GParamSpec **pspecs;
|
|
guint n_pspecs;
|
|
GString *s;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (script != NULL, NULL);
|
|
g_return_val_if_fail (GIMP_IS_PROCEDURE_CONFIG (config), NULL);
|
|
|
|
s = g_string_new ("(");
|
|
g_string_append (s, script->name);
|
|
|
|
/* The command has no run mode. */
|
|
|
|
/* scripts use integer ID's for Gimp objects. */
|
|
g_string_append_printf (s, " %d", gimp_image_get_id (image));
|
|
|
|
/* Not pass n_drawables.
|
|
* An author must use Scheme functions for length of container of drawables.
|
|
*/
|
|
|
|
/* Append text repr for a container of all drawable ID's.
|
|
* Even if script->drawable_arity = SF_PROC_IMAGE_SINGLE_DRAWABLE
|
|
* since that means the inner run func takes many but will only process one.
|
|
* We are not adapting to an inner run func that expects a single numeric.
|
|
*/
|
|
script_fu_command_append_drawables (s, n_drawables, drawables);
|
|
|
|
/* config contains the "other" args
|
|
* Iterate over the GimpValueArray.
|
|
* But script->args should be the same length, and types should match.
|
|
*/
|
|
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config), &n_pspecs);
|
|
|
|
/* config will have 1 additional property: "procedure". */
|
|
for (i = 1; i < n_pspecs; i++)
|
|
{
|
|
GParamSpec *pspec = pspecs[i];
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
g_string_append_c (s, ' ');
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
g_object_get_property (G_OBJECT (config), pspec->name, &value);
|
|
script_fu_arg_append_repr_from_gvalue (&script->args[i - 1], s, &value);
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
g_string_append_c (s, ')');
|
|
g_free (pspecs);
|
|
|
|
return g_string_free (s, FALSE);
|
|
}
|
|
|
|
/* Infer whether the script, defined using v2 script-fu-register,
|
|
* which does not specify the arity for drawables,
|
|
* is actually a script that takes one and only one drawable.
|
|
* Such plugins are deprecated in v3: each plugin must take container of drawables
|
|
* and declare its drawable arity via gimp_procedure_set_sensitivity_mask.
|
|
*/
|
|
void
|
|
script_fu_script_infer_drawable_arity (SFScript *script)
|
|
{
|
|
if ((script->n_args > 1) &&
|
|
script->args[0].type == SF_IMAGE &&
|
|
script->args[1].type == SF_DRAWABLE)
|
|
{
|
|
g_debug ("Inferring drawable arity one.");
|
|
script->drawable_arity = SF_ONE_DRAWABLE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Local Functions
|
|
*/
|
|
|
|
static gboolean
|
|
script_fu_script_param_init (SFScript *script,
|
|
GParamSpec **pspecs,
|
|
guint n_pspecs,
|
|
GimpProcedureConfig *config,
|
|
SFArgType type,
|
|
gint n)
|
|
{
|
|
SFArg *arg = &script->args[n];
|
|
|
|
if (script->n_args > n &&
|
|
arg->type == type &&
|
|
/* The first pspec is "procedure", the second is "run-mode". */
|
|
n_pspecs > n + 2)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
GParamSpec *pspec = pspecs[n + 2];
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
g_object_get_property (G_OBJECT (config), pspec->name, &value);
|
|
switch (type)
|
|
{
|
|
case SF_IMAGE:
|
|
if (GIMP_VALUE_HOLDS_IMAGE (&value))
|
|
{
|
|
GimpImage *image = g_value_get_object (&value);
|
|
|
|
arg->value.sfa_image = gimp_image_get_id (image);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case SF_DRAWABLE:
|
|
if (GIMP_VALUE_HOLDS_DRAWABLE (&value))
|
|
{
|
|
GimpItem *item = g_value_get_object (&value);
|
|
|
|
arg->value.sfa_drawable = gimp_item_get_id (item);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case SF_LAYER:
|
|
if (GIMP_VALUE_HOLDS_LAYER (&value))
|
|
{
|
|
GimpItem *item = g_value_get_object (&value);
|
|
|
|
arg->value.sfa_layer = gimp_item_get_id (item);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case SF_CHANNEL:
|
|
if (GIMP_VALUE_HOLDS_CHANNEL (&value))
|
|
{
|
|
GimpItem *item = g_value_get_object (&value);
|
|
|
|
arg->value.sfa_channel = gimp_item_get_id (item);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case SF_VECTORS:
|
|
if (GIMP_VALUE_HOLDS_VECTORS (&value))
|
|
{
|
|
GimpItem *item = g_value_get_object (&value);
|
|
|
|
arg->value.sfa_vectors = gimp_item_get_id (item);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case SF_DISPLAY:
|
|
if (GIMP_VALUE_HOLDS_DISPLAY (&value))
|
|
{
|
|
GimpDisplay *display = g_value_get_object (&value);
|
|
|
|
arg->value.sfa_display = gimp_display_get_id (display);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
script_fu_script_set_proc_metadata (GimpProcedure *procedure,
|
|
SFScript *script)
|
|
{
|
|
const gchar *menu_label = NULL;
|
|
|
|
/* Allow scripts with no menus */
|
|
if (strncmp (script->menu_label, "<None>", 6) != 0)
|
|
menu_label = script->menu_label;
|
|
|
|
gimp_procedure_set_image_types (procedure, script->image_types);
|
|
|
|
if (menu_label && strlen (menu_label))
|
|
gimp_procedure_set_menu_label (procedure, menu_label);
|
|
|
|
gimp_procedure_set_documentation (procedure,
|
|
script->blurb,
|
|
NULL,
|
|
script->name);
|
|
gimp_procedure_set_attribution (procedure,
|
|
script->author,
|
|
script->copyright,
|
|
script->date);
|
|
}
|
|
|
|
/* Convey formal arguments from SFArg to the PDB. */
|
|
static void
|
|
script_fu_script_set_proc_args (GimpProcedure *procedure,
|
|
SFScript *script,
|
|
guint first_conveyed_arg)
|
|
{
|
|
script_fu_arg_reset_name_generator ();
|
|
for (gint i = first_conveyed_arg; i < script->n_args; i++)
|
|
{
|
|
GParamSpec *pspec = NULL;
|
|
const gchar *name = NULL;
|
|
const gchar *nick = NULL;
|
|
|
|
script_fu_arg_generate_name_and_nick (&script->args[i], &name, &nick);
|
|
pspec = script_fu_arg_get_param_spec (&script->args[i],
|
|
name,
|
|
nick);
|
|
gimp_procedure_add_argument (procedure, pspec);
|
|
}
|
|
}
|
|
|
|
/* Convey drawable arity to the PDB.
|
|
* !!! Unless set, sensitivity defaults to drawable arity 1.
|
|
* See libgimp/gimpprocedure.c gimp_procedure_set_sensitivity_mask
|
|
*/
|
|
static void
|
|
script_fu_script_set_drawable_sensitivity (GimpProcedure *procedure, SFScript *script)
|
|
{
|
|
switch (script->drawable_arity)
|
|
{
|
|
case SF_TWO_OR_MORE_DRAWABLE:
|
|
gimp_procedure_set_sensitivity_mask (procedure,
|
|
GIMP_PROCEDURE_SENSITIVE_DRAWABLES);
|
|
break;
|
|
case SF_ONE_OR_MORE_DRAWABLE:
|
|
gimp_procedure_set_sensitivity_mask (procedure,
|
|
GIMP_PROCEDURE_SENSITIVE_DRAWABLE |
|
|
GIMP_PROCEDURE_SENSITIVE_DRAWABLES);
|
|
break;
|
|
case SF_ONE_DRAWABLE:
|
|
gimp_procedure_set_sensitivity_mask (procedure, GIMP_PROCEDURE_SENSITIVE_DRAWABLE);
|
|
break;
|
|
case SF_NO_DRAWABLE:
|
|
/* menu item always sensitive. */
|
|
break;
|
|
default:
|
|
/* Fail to set sensitivy mask. */
|
|
g_warning ("Unhandled case for SFDrawableArity");
|
|
}
|
|
}
|