[gimp] ScriptFu: script-fu-register-filter for GimpImageProcedure.
- From: Lloyd Konneker <lkonneker src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] ScriptFu: script-fu-register-filter for GimpImageProcedure.
- Date: Tue, 2 Aug 2022 08:46:59 +0000 (UTC)
commit 12c0c180366eefcf767dcd4b18695a13b219dcbf
Author: lloyd konneker <konnekerl gmail com>
Date: Fri Jul 15 13:50:36 2022 -0400
ScriptFu: script-fu-register-filter for GimpImageProcedure.
Resolves #8382
Also v2 scripts infer and set sensitivity to drawables
Add two test plugins clothify-v3.scm and test-sphere-v3.scm.
Temporary, to be removed when 3.0 ships.
Some refactoring (extracting methods, moving functions to new files).
Some drive-by fixes to script-fu-arg.c revealed by using GimpProcedureDialog.
plug-ins/script-fu/libscriptfu/Makefile.am | 10 +-
plug-ins/script-fu/libscriptfu/meson.build | 6 +-
plug-ins/script-fu/libscriptfu/scheme-wrapper.c | 135 +++--
plug-ins/script-fu/libscriptfu/scheme-wrapper.h | 1 +
plug-ins/script-fu/libscriptfu/script-fu-arg.c | 47 +-
plug-ins/script-fu/libscriptfu/script-fu-command.c | 150 +++++
plug-ins/script-fu/libscriptfu/script-fu-command.h | 31 ++
plug-ins/script-fu/libscriptfu/script-fu-dialog.c | 179 ++++++
plug-ins/script-fu/libscriptfu/script-fu-dialog.h | 31 ++
plug-ins/script-fu/libscriptfu/script-fu-enums.h | 38 +-
.../script-fu/libscriptfu/script-fu-interface.c | 1 -
.../script-fu/libscriptfu/script-fu-proc-factory.c | 1 -
.../script-fu/libscriptfu/script-fu-register.c | 462 ++++++++++++++++
.../script-fu/libscriptfu/script-fu-register.h | 30 +
.../script-fu/libscriptfu/script-fu-run-func.c | 217 ++++++++
.../script-fu/libscriptfu/script-fu-run-func.h | 33 ++
plug-ins/script-fu/libscriptfu/script-fu-script.c | 294 ++++++++--
plug-ins/script-fu/libscriptfu/script-fu-script.h | 12 +-
plug-ins/script-fu/libscriptfu/script-fu-scripts.c | 610 ++++-----------------
plug-ins/script-fu/libscriptfu/script-fu-scripts.h | 11 +-
plug-ins/script-fu/libscriptfu/script-fu-types.h | 2 +
plug-ins/script-fu/scripts/Makefile.am | 14 +-
plug-ins/script-fu/scripts/clothify-v3.scm | 84 +++
plug-ins/script-fu/scripts/meson.build | 2 +
plug-ins/script-fu/scripts/plug-in-compat.init | 13 +
plug-ins/script-fu/scripts/test-sphere-v3.scm | 174 ++++++
.../scripts/test/always-fail/always-fail.scm | 30 +
.../test/call-always-fail/call-always-fail.scm | 29 +
po-script-fu/POTFILES.skip | 1 +
29 files changed, 2022 insertions(+), 626 deletions(-)
---
diff --git a/plug-ins/script-fu/libscriptfu/Makefile.am b/plug-ins/script-fu/libscriptfu/Makefile.am
index 748f0a7f82..277564708c 100644
--- a/plug-ins/script-fu/libscriptfu/Makefile.am
+++ b/plug-ins/script-fu/libscriptfu/Makefile.am
@@ -123,7 +123,15 @@ libgimp_scriptfu_@GIMP_API_VERSION@_la_SOURCES = \
script-fu-proc-factory.h \
script-fu-proc-factory.c \
script-fu-arg.c \
- script-fu-arg.h
+ script-fu-arg.h \
+ script-fu-command.h \
+ script-fu-command.c \
+ script-fu-dialog.h \
+ script-fu-dialog.c \
+ script-fu-register.h \
+ script-fu-register.c \
+ script-fu-run-func.h \
+ script-fu-run-func.c
EXTRA_libgimp_scriptfu_@GIMP_API_VERSION@_la_DEPENDENCIES = $(scriptfu_def)
diff --git a/plug-ins/script-fu/libscriptfu/meson.build b/plug-ins/script-fu/libscriptfu/meson.build
index 74f5df5b97..7f5708956f 100644
--- a/plug-ins/script-fu/libscriptfu/meson.build
+++ b/plug-ins/script-fu/libscriptfu/meson.build
@@ -15,7 +15,11 @@ libscriptfu_sources = [
'script-fu-compat.c',
'script-fu-lib.c',
'script-fu-proc-factory.c',
- 'script-fu-arg.c'
+ 'script-fu-arg.c',
+ 'script-fu-register.c',
+ 'script-fu-dialog.c',
+ 'script-fu-run-func.c',
+ 'script-fu-command.c'
]
# !! just "library(...)" which means shared versus static depends on configuration of project.
diff --git a/plug-ins/script-fu/libscriptfu/scheme-wrapper.c b/plug-ins/script-fu/libscriptfu/scheme-wrapper.c
index 6c2a7d236b..c3f0256ff0 100644
--- a/plug-ins/script-fu/libscriptfu/scheme-wrapper.c
+++ b/plug-ins/script-fu/libscriptfu/scheme-wrapper.c
@@ -51,6 +51,10 @@
static void ts_init_constants (scheme *sc);
static void ts_init_enum (scheme *sc,
GType enum_type);
+
+static void ts_define_procedure (scheme *sc,
+ const gchar *symbol_name,
+ TsWrapperFunc func);
static void ts_init_procedures (scheme *sc,
gboolean register_scipts);
static void convert_string (gchar *str);
@@ -67,6 +71,8 @@ static pointer script_fu_marshal_procedure_call_deprecated (scheme *sc,
static pointer script_fu_register_call (scheme *sc,
pointer a);
+static pointer script_fu_register_call_filter (scheme *sc,
+ pointer a);
static pointer script_fu_menu_register_call (scheme *sc,
pointer a);
static pointer script_fu_quit_call (scheme *sc,
@@ -83,6 +89,7 @@ typedef struct
gint value;
} NamedConstant;
+/* LHS is text in a script, RHS is constant defined in C. */
static const NamedConstant script_constants[] =
{
/* Useful values from libgimpbase/gimplimits.h */
@@ -103,6 +110,8 @@ static const NamedConstant script_constants[] =
{ "UNIT-PICA", GIMP_UNIT_PICA },
/* Script-Fu types */
+
+ /* Arg types. */
{ "SF-IMAGE", SF_IMAGE },
{ "SF-DRAWABLE", SF_DRAWABLE },
{ "SF-LAYER", SF_LAYER },
@@ -125,6 +134,36 @@ static const NamedConstant script_constants[] =
{ "SF-ENUM", SF_ENUM },
{ "SF-DISPLAY", SF_DISPLAY },
+ /* PDB procedure drawable_arity, i.e. sensitivity.
+ * Used with script-fu-register-filter.
+ *
+ * This declares the arity of the algorithm,
+ * and not the signature of the PDB procedure.
+ * Since v3, PDB procedures that are image procedures,
+ * take a container of drawables.
+ * This only describes how many drawables the container *should* hold.
+ *
+ * For calls invoked by a user, this describes
+ * how many drawables the user is expected to select,
+ * which disables/enables the menu item for the procedure.
+ *
+ * Procedures are also called from other procedures.
+ * A call from another procedure may in fact
+ * pass more drawables than declared for drawable_arity.
+ * That is a programming error on behalf of the caller.
+ * A well-written callee that is passed more drawables than declared
+ * should return an error instead of processing any of the drawables.
+ *
+ * Similarly for fewer than declared.
+ */
+
+ /* Requires two drawables. Often an operation between them, yielding a new drawable */
+ { "SF-TWO-OR-MORE-DRAWABLE", SF_TWO_OR_MORE_DRAWABLE },
+ /* Often processed independently, sequentially, with side effects on the drawables. */
+ { "SF-ONE-OR-MORE-DRAWABLE", SF_ONE_OR_MORE_DRAWABLE },
+ /* Requires exactly one drawable. */
+ { "SF-ONE-DRAWABLE", SF_ONE_DRAWABLE },
+
/* For SF-ADJUSTMENT */
{ "SF-SLIDER", SF_SLIDER },
{ "SF-SPINNER", SF_SPINNER },
@@ -242,6 +281,8 @@ ts_interpret_stdin (void)
gint
ts_interpret_string (const gchar *expr)
{
+ gint result;
+
#if DEBUG_SCRIPTS
sc.print_output = 1;
sc.tracing = 1;
@@ -249,7 +290,10 @@ ts_interpret_string (const gchar *expr)
sc.vptr->load_string (&sc, (char *) expr);
- return sc.retcode;
+ result = sc.retcode;
+
+ g_debug ("ts_interpret_string returns: %i", result);
+ return result;
}
const gchar *
@@ -421,6 +465,27 @@ ts_init_enum (scheme *sc,
g_type_class_unref (enum_class);
}
+/* Define a symbol into interpreter state,
+ * bound to a foreign function, language C, defined here in ScriptFu source.
+ */
+static void
+ts_define_procedure (scheme *sc,
+ const gchar *symbol_name,
+ TsWrapperFunc func)
+{
+ pointer symbol;
+
+ symbol = sc->vptr->mk_symbol (sc, symbol_name);
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_foreign_func (sc, func));
+ sc->vptr->setimmutable (symbol);
+}
+
+
+/* Define, into interpreter state,
+ * 1) Scheme functions that call wrapper functions in C here in ScriptFu.
+ * 2) Scheme functions wrapping every procedure in the PDB.
+ */
static void
ts_init_procedures (scheme *sc,
gboolean register_scripts)
@@ -428,55 +493,30 @@ ts_init_procedures (scheme *sc,
gchar **proc_list;
gint num_procs;
gint i;
- pointer symbol;
#if USE_DL
- symbol = sc->vptr->mk_symbol (sc,"load-extension");
- sc->vptr->scheme_define (sc, sc->global_env, symbol,
- sc->vptr->mk_foreign_func (sc, scm_load_ext));
- sc->vptr->setimmutable (symbol);
+/* scm_load_ext not same as other wrappers, defined in tinyscheme/dynload */
+ts_define_procedure (sc, "load-extension", scm_load_ext);
#endif
- symbol = sc->vptr->mk_symbol (sc, "script-fu-register");
- sc->vptr->scheme_define (sc, sc->global_env, symbol,
- sc->vptr->mk_foreign_func (sc,
- register_scripts ?
- script_fu_register_call :
- script_fu_nil_call));
- sc->vptr->setimmutable (symbol);
-
- symbol = sc->vptr->mk_symbol (sc, "script-fu-menu-register");
- sc->vptr->scheme_define (sc, sc->global_env, symbol,
- sc->vptr->mk_foreign_func (sc,
- register_scripts ?
- script_fu_menu_register_call :
- script_fu_nil_call));
- sc->vptr->setimmutable (symbol);
-
- symbol = sc->vptr->mk_symbol (sc, "script-fu-quit");
- sc->vptr->scheme_define (sc, sc->global_env, symbol,
- sc->vptr->mk_foreign_func (sc, script_fu_quit_call));
- sc->vptr->setimmutable (symbol);
-
- /* register normal database execution procedure */
- symbol = sc->vptr->mk_symbol (sc, "gimp-proc-db-call");
- sc->vptr->scheme_define (sc, sc->global_env, symbol,
- sc->vptr->mk_foreign_func (sc,
- script_fu_marshal_procedure_call_strict));
- sc->vptr->setimmutable (symbol);
+ if (register_scripts)
+ {
+ ts_define_procedure (sc, "script-fu-register", script_fu_register_call);
+ ts_define_procedure (sc, "script-fu-register-filter", script_fu_register_call_filter);
+ ts_define_procedure (sc, "script-fu-menu-register", script_fu_menu_register_call);
+ }
+ else
+ {
+ ts_define_procedure (sc, "script-fu-register", script_fu_nil_call);
+ ts_define_procedure (sc, "script-fu-register-filter", script_fu_nil_call);
+ ts_define_procedure (sc, "script-fu-menu-register", script_fu_nil_call);
+ }
- /* register permissive and deprecated db execution procedure; see comment below */
- symbol = sc->vptr->mk_symbol (sc, "-gimp-proc-db-call");
- sc->vptr->scheme_define (sc, sc->global_env, symbol,
- sc->vptr->mk_foreign_func (sc,
- script_fu_marshal_procedure_call_permissive));
- sc->vptr->setimmutable (symbol);
+ ts_define_procedure (sc, "script-fu-quit", script_fu_quit_call);
- symbol = sc->vptr->mk_symbol (sc, "--gimp-proc-db-call");
- sc->vptr->scheme_define (sc, sc->global_env, symbol,
- sc->vptr->mk_foreign_func (sc,
- script_fu_marshal_procedure_call_deprecated));
- sc->vptr->setimmutable (symbol);
+ ts_define_procedure (sc, "gimp-proc-db-call", script_fu_marshal_procedure_call_strict);
+ ts_define_procedure (sc, "-gimp-proc-db-call", script_fu_marshal_procedure_call_permissive);
+ ts_define_procedure (sc, "--gimp-proc-db-call", script_fu_marshal_procedure_call_deprecated);
proc_list = gimp_pdb_query_procedures (gimp_get_pdb (),
".*", ".*", ".*", ".*",
@@ -1598,6 +1638,13 @@ script_fu_register_call (scheme *sc,
return script_fu_add_script (sc, a);
}
+static pointer
+script_fu_register_call_filter (scheme *sc,
+ pointer a)
+{
+ return script_fu_add_script_filter (sc, a);
+}
+
static pointer
script_fu_menu_register_call (scheme *sc,
pointer a)
diff --git a/plug-ins/script-fu/libscriptfu/scheme-wrapper.h b/plug-ins/script-fu/libscriptfu/scheme-wrapper.h
index 46bb9d28f7..53645bd2fd 100644
--- a/plug-ins/script-fu/libscriptfu/scheme-wrapper.h
+++ b/plug-ins/script-fu/libscriptfu/scheme-wrapper.h
@@ -21,6 +21,7 @@
#include "tinyscheme/scheme.h"
typedef void (*TsCallbackFunc) (void);
+typedef pointer (*TsWrapperFunc) (scheme*, pointer);
void tinyscheme_init (GList *path,
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-arg.c b/plug-ins/script-fu/libscriptfu/script-fu-arg.c
index d24c9e586e..9d11d2bf20 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-arg.c
+++ b/plug-ins/script-fu/libscriptfu/script-fu-arg.c
@@ -450,6 +450,7 @@ script_fu_arg_append_repr_from_gvalue (SFArg *arg,
GString *result_string,
GValue *gvalue)
{
+ g_debug("script_fu_arg_append_repr_from_gvalue %s", arg->label);
switch (arg->type)
{
case SF_IMAGE:
@@ -504,16 +505,24 @@ script_fu_arg_append_repr_from_gvalue (SFArg *arg,
case SF_FILENAME:
case SF_DIRNAME:
{
- gchar * filepath = NULL;
+ gchar * filepath = "error in file arg";
- if (g_value_get_gtype (gvalue) == G_TYPE_FILE)
+ /* sanity: GValue initialized. */
+ if (G_VALUE_HOLDS_GTYPE(gvalue))
{
- filepath = g_file_get_path (g_value_get_object (gvalue));
- /* Not escape special chars for whitespace or double quote. */
+ if (g_value_get_gtype (gvalue) == G_TYPE_FILE)
+ {
+ filepath = g_file_get_path (g_value_get_object (gvalue));
+ /* Not escape special chars for whitespace or double quote. */
+ }
+ else
+ {
+ g_warning ("Expecting GFile in gvalue.");
+ }
}
else
{
- g_warning ("Expecting GFile in gvalue.");
+ g_warning ("Expecting GValue holding a GType");
}
g_string_append_printf (result_string, "\"%s\"", filepath);
@@ -539,7 +548,19 @@ script_fu_arg_append_repr_from_gvalue (SFArg *arg,
case SF_OPTION:
case SF_ENUM:
- g_string_append_printf (result_string, "%d", g_value_get_int (gvalue));
+ /* When sanity test fails, return an arbitrary int.
+ * Fails when GimpConfig or GimpProcedureDialog does not support GParamEnum.
+ */
+ if (G_VALUE_HOLDS_INT (gvalue))
+ {
+ g_string_append_printf (result_string, "%d", g_value_get_int (gvalue));
+ }
+ else
+ {
+ g_warning ("Expecting GValue holding an int.");
+ g_string_append (result_string, "1"); /* Arbitrarily a common int. */
+ }
+
break;
}
}
@@ -684,12 +705,16 @@ script_fu_arg_reset_name_generator (void)
* It is unique among all names returned between resets of the generator.
* Thus name meets uniquity for names of properties of one object.
*
+ * !!! GimpImageProcedures already have properties for convenience arguments,
+ * e.g. a property named "image" "n_drawables" and "drawables"
+ * So we avoid that name clash by starting with "otherImage"
+ *
* The name means nothing to human readers of the spec.
- * The nick is descriptive.
+ * Instead, the nick is descriptive for human readers.
*
- * The returned string is owned by the generator
- * and is only stable until the next call to the generator.
- * That is, the caller should copy it (usually by creating a GParamSpec.)
+ * The returned string is owned by the generator, a constant.
+ * The caller need not copy it,
+ * but usually does by creating a GParamSpec.
*/
void
script_fu_arg_generate_name_and_nick (SFArg *arg,
@@ -702,7 +727,7 @@ script_fu_arg_generate_name_and_nick (SFArg *arg,
switch (arg->type)
{
case SF_IMAGE:
- name = "image";
+ name = "otherImage"; /* !!! Avoid name clash. */
break;
case SF_DRAWABLE:
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-command.c
b/plug-ins/script-fu/libscriptfu/script-fu-command.c
new file mode 100644
index 0000000000..296e25d88a
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-command.c
@@ -0,0 +1,150 @@
+/* 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 <libgimp/gimpui.h>
+
+#include "script-fu-types.h" /* SFScript */
+#include "script-fu-lib.h"
+#include "script-fu-script.h"
+
+#include "script-fu-command.h"
+
+
+/* Methods for interpreting commands.
+ *
+ * Usually there is a stack of calls similar to:
+ * script_fu_run_image_procedure (outer run func)
+ * calls script_fu_interpret_image_proc
+ * calls script_fu_run_command
+ * calls ts_interpret_string
+ * calls the inner run func in Scheme
+ *
+ * but script_fu_run_command is also called directly for loading scripts.
+ *
+ * FUTURE: see also similar code in script-fu-interface.c
+ * which could be migrated here.
+ */
+
+
+/* Interpret a command.
+ *
+ * When errors during interpretation:
+ * 1) set the error message from tinyscheme into GError at given handle.
+ * 2) return FALSE
+ * otherwise, return TRUE and discard any result of interpretation
+ * ScriptFu return values only have a GimpPDBStatus,
+ * since ScriptFu plugin scripts can only be declared returning void.
+ *
+ * While interpreting, any errors from further calls to the PDB
+ * can show error dialogs in any GIMP gui,
+ * unless the caller has taken responsibility with a prior call to
+ * gimp_plug_in_set_pdb_error_handler
+ *
+ * FIXME: see script_fu_run_procedure.
+ * It does not call gimp_plug_in_set_pdb_error_handler for NON-INTERACTIVE mode.
+ */
+gboolean
+script_fu_run_command (const gchar *command,
+ GError **error)
+{
+ GString *output;
+ gboolean success = FALSE;
+
+ g_debug ("script_fu_run_command: %s", command);
+ output = g_string_new (NULL);
+ script_fu_redirect_output_to_gstr (output);
+
+ if (script_fu_interpret_string (command))
+ {
+ g_set_error (error, GIMP_PLUG_IN_ERROR, 0, "%s", output->str);
+ }
+ else
+ {
+ success = TRUE;
+ }
+
+ g_string_free (output, TRUE);
+
+ return success;
+}
+
+
+
+/* Interpret a script that defines a GimpImageProcedure.
+ *
+ * Similar to v2 code in script-fu-interface.c, except:
+ * 1) builds a command from a GValueArray from a GimpConfig,
+ * instead of from local array of SFArg.
+ * 2) adds actual args image, drawable, etc. for GimpImageProcedure
+ */
+GimpValueArray *
+script_fu_interpret_image_proc (
+ GimpProcedure *procedure,
+ SFScript *script,
+ GimpImage *image,
+ guint n_drawables,
+ GimpDrawable **drawables,
+ const GimpValueArray *args)
+{
+ gchar *command;
+ GimpValueArray *result = NULL;
+ gboolean interpretation_result;
+ GError *error = NULL;
+
+ command = script_fu_script_get_command_for_image_proc (script, image, n_drawables, drawables, args);
+
+ /* Take responsibility for handling errors from the scripts further calls to PDB.
+ * ScriptFu does not show an error dialog, but forwards errors back to GIMP.
+ * This only tells GIMP that ScriptFu itself will forward GimpPDBStatus errors from
+ * this scripts calls to the PDB.
+ * The onus is on this script's called PDB procedures to return errors in the GimpPDBStatus.
+ * Any that do not, but for example only call gimp-message, are breaching contract.
+ */
+ gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
+ GIMP_PDB_ERROR_HANDLER_PLUGIN);
+
+ interpretation_result = script_fu_run_command (command, &error);
+ g_free (command);
+ if (! interpretation_result)
+ {
+ /* This is to the console.
+ * script->name not localized.
+ * error->message expected to be localized.
+ * GIMP will later display "PDB procedure failed: <message>" localized.
+ */
+ g_warning ("While executing %s: %s",
+ script->name,
+ error->message);
+ /* A GError was allocated and this will take ownership. */
+ result = gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_EXECUTION_ERROR,
+ error);
+ }
+ else
+ {
+ result = gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_SUCCESS,
+ NULL);
+ }
+
+ gimp_plug_in_set_pdb_error_handler (gimp_get_plug_in (),
+ GIMP_PDB_ERROR_HANDLER_INTERNAL);
+
+ return result;
+}
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-command.h
b/plug-ins/script-fu/libscriptfu/script-fu-command.h
new file mode 100644
index 0000000000..08a39ba8b6
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-command.h
@@ -0,0 +1,31 @@
+/* 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/>.
+ */
+
+ #ifndef __SCRIPT_FU_COMMAND_H__
+ #define __SCRIPT_FU_COMMAND_H__
+
+gboolean script_fu_run_command (const gchar *command,
+ GError **error);
+
+GimpValueArray *script_fu_interpret_image_proc (GimpProcedure *procedure,
+ SFScript *script,
+ GimpImage *image,
+ guint n_drawables,
+ GimpDrawable **drawables,
+ const GimpValueArray *args);
+
+#endif /* __SCRIPT_FU_COMMAND_H__ */
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-dialog.c
b/plug-ins/script-fu/libscriptfu/script-fu-dialog.c
new file mode 100644
index 0000000000..af11c37eac
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-dialog.c
@@ -0,0 +1,179 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * script-fu-dialog.c
+ * Copyright (C) 2022 Lloyd Konneker
+ *
+ * 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 <libgimp/gimpui.h>
+
+#include "script-fu-types.h" /* SFScript */
+#include "script-fu-script.h" /* get_title */
+#include "script-fu-command.h"
+
+#include "script-fu-dialog.h"
+
+
+/* An informal class that shows a dialog for a script then runs the script.
+ * It is internal to libscriptfu.
+ *
+ * The dialog is modal for the script:
+ * OK button hides the dialog then runs the script once.
+ *
+ * The dialog is non-modal with respect to the GIMP app GUI, which remains responsive.
+ *
+ * When called from plugin extension-script-fu, the dialog is modal on the extension:
+ * although GIMP app continues responsive, a user choosing a menu item
+ * that is also implemented by a script and extension-script-fu
+ * will not show a dialog until the first called script finishes.
+ */
+
+/* FUTURE: delete this after v3 is stable. */
+#define DEBUG_CONFIG_PROPERTIES FALSE
+
+#if DEBUG_CONFIG_PROPERTIES
+static void
+dump_properties (GimpProcedureConfig *config)
+{
+ GParamSpec **pspecs;
+ guint n_pspecs;
+
+ pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (config),
+ &n_pspecs);
+ for (guint i = 1; i < n_pspecs; i++)
+ g_printerr ("%s %s\n", pspecs[i]->name, G_PARAM_SPEC_TYPE_NAME (pspecs[i]));
+ g_free (pspecs);
+}
+#endif
+
+/* Run a dialog for a procedure, then interpret the script.
+ *
+ * Run dialog: create config, create dialog for config, show dialog, and return a config.
+ * Interpret: marshal config into Scheme text for function call, then interpret script.
+ *
+ * One widget per param of the procedure.
+ * Require the procedure registered with params of GTypes
+ * corresponding to SFType the author declared in script-fu-register call.
+ *
+ * Require initial_args is not NULL or empty.
+ * A caller must ensure a dialog is needed because args is not empty.
+ */
+GimpValueArray*
+script_fu_dialog_run (GimpProcedure *procedure,
+ SFScript *script,
+ GimpImage *image,
+ guint n_drawables,
+ GimpDrawable **drawables,
+ const GimpValueArray *initial_args)
+
+{
+ GimpValueArray *result = NULL;
+ GimpProcedureDialog *dialog = NULL;
+ GimpProcedureConfig *config = NULL;
+ gboolean not_canceled;
+
+ if ( (! G_IS_OBJECT (procedure)) || script == NULL)
+ return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, NULL);
+
+ if ( gimp_value_array_length (initial_args) < 1)
+ return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, NULL);
+
+ /* We don't prevent concurrent dialogs as in script-fu-interface.c.
+ * For extension-script-fu, Gimp is already preventing concurrent dialogs.
+ * For gimp-script-fu-interpreter, each plugin is a separate process
+ * so concurrent dialogs CAN occur.
+ */
+ /* There is no progress widget in GimpProcedureDialog.
+ * Also, we don't need to update the progress in Gimp UI,
+ * because Gimp shows progress: the name of all called PDB procedures.
+ */
+
+ /* Script's menu label */
+ gimp_ui_init (script_fu_script_get_title (script));
+
+ config = gimp_procedure_create_config (procedure);
+#if DEBUG_CONFIG_PROPERTIES
+ dump_properties (config);
+ g_debug ("Len of initial_args %i", gimp_value_array_length (initial_args) );
+#endif
+
+ /* Get saved settings (last values) into the config.
+ * Since run mode is INTERACTIVE, initial_args is moot.
+ * Instead, last used values or default values populate the config.
+ */
+ gimp_procedure_config_begin_run (config, NULL, GIMP_RUN_INTERACTIVE, initial_args);
+
+ /* Create a dialog having properties (describing arguments of the procedure)
+ * taken from the config.
+ *
+ * Title dialog with the menu item, not the procedure name.
+ * Assert menu item is localized.
+ */
+ dialog = (GimpProcedureDialog*) gimp_procedure_dialog_new (
+ procedure,
+ config,
+ script_fu_script_get_title (script));
+ /* dialog has no widgets except standard buttons. */
+
+ /* It is possible to create custom widget where the provided widget is not adequate.
+ * Then gimp_procedure_dialog_fill_list will create the rest.
+ * For now, the provided widgets should be adequate.
+ */
+
+ /* NULL means create widgets for all properties of the procedure
+ * that we have not already created widgets for.
+ */
+ gimp_procedure_dialog_fill_list (dialog, NULL);
+
+ not_canceled = gimp_procedure_dialog_run (dialog);
+ /* Assert config holds validated arg values from a user interaction. */
+ if (not_canceled)
+ {
+ /* initial_args is declared const.
+ * To avoid compiler warning "discarding const"
+ * copy initial_args to a writeable copy.
+ */
+ GimpValueArray *final_args = (GimpValueArray*) g_value_array_copy ((GValueArray*) initial_args);
+ /* FIXME the above is deprecated.
+ * Non-deprecated, but doesn't work:
+ * GimpValueArray *final_args = (GimpValueArray*) g_array_copy ((GArray*) initial_args);
+ * Maybe we need a gimp_value_array_copy method?
+ */
+
+ /* Store config's values into final_args. */
+ gimp_procedure_config_get_values (config, final_args);
+
+ result = script_fu_interpret_image_proc (procedure, script, image, n_drawables, drawables, final_args);
+ }
+ else
+ {
+ result = gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, NULL);
+ }
+
+ gtk_widget_destroy ((GtkWidget*) dialog);
+
+ /* Persist config aka settings for the next run of the plugin.
+ * Passing the GimpPDBStatus from result[0].
+ * We must have a matching end_run for the begin_run, regardless of status.
+ */
+ gimp_procedure_config_end_run (config, g_value_get_enum (gimp_value_array_index (result, 0)));
+
+ g_object_unref (config);
+
+ return result;
+}
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-dialog.h
b/plug-ins/script-fu/libscriptfu/script-fu-dialog.h
new file mode 100644
index 0000000000..69f33ba768
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-dialog.h
@@ -0,0 +1,31 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * script-fu-dialog.h
+ * Copyright (C) 2022 Lloyd Konneker
+ *
+ * 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/>.
+ */
+
+ #ifndef __SCRIPT_FU_DIALOG_H__
+ #define __SCRIPT_FU_DIALOG_H__
+
+GimpValueArray *script_fu_dialog_run (GimpProcedure *procedure,
+ SFScript *script,
+ GimpImage *image,
+ guint n_drawables,
+ GimpDrawable **drawables,
+ const GimpValueArray *args);
+
+#endif /* __SCRIPT_FU_DIALOG_H__ */
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-enums.h
b/plug-ins/script-fu/libscriptfu/script-fu-enums.h
index 581441fb41..120f7a921f 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-enums.h
+++ b/plug-ins/script-fu/libscriptfu/script-fu-enums.h
@@ -18,8 +18,11 @@
#ifndef __SCRIPT_FU_ENUMS_H__
#define __SCRIPT_FU_ENUMS_H__
-/* Typedefs for script-fu argument types */
+/* Note these are C names with underbar.
+ * The Scheme names are usually the same with hyphen substituted for underbar.
+ */
+/* script-fu argument types */
typedef enum
{
SF_IMAGE = 0,
@@ -51,4 +54,37 @@ typedef enum
SF_SPINNER
} SFAdjustmentType;
+/* This enum is local to ScriptFu
+ * but the notion is general to other plugins.
+ *
+ * A GimpImageProcedure has drawable arity > 1.
+ * A GimpProcedure often does not take any drawables, i.e. arity zero.
+ * Some GimpProcedure may take drawables i.e. arity > 0,
+ * but the procedure's menu item is always sensitive,
+ * and the drawable can be chosen in the plugin's dialog.
+ *
+ * Script author does not use SF-NO-DRAWABLE, for now.
+ *
+ * Scripts of class GimpProcedure are declared by script-fu-register.
+ * Their GUI is handled by ScriptFu, script-fu-interface.c
+ * An author does not declare drawable_arity.
+ *
+ * Scripts of class GimpImageProcedure are declared by script-fu-register-filter.
+ * Their GUI is handled by libgimpui, GimpProcedureDialog.
+ * Their drawable_arity is declared by the author of the script.
+ *
+ * For backward compatibility, GIMP deprecates but allows PDB procedures
+ * to take a single drawable, and sets their sensitivity automatically.
+ * Their drawable_arity is inferred by ScriptFu.
+ * FUTURE insist that an author use script-fu-register-filter (not script-fu-register)
+ * for GimpImageProcedure taking image and one or more drawables.
+ */
+typedef enum
+{
+ SF_NO_DRAWABLE = 0, /* GimpProcedure. */
+ SF_ONE_DRAWABLE, /* GimpImageProcedure, but only process one layer */
+ SF_ONE_OR_MORE_DRAWABLE, /* GimpImageProcedure, multilayer capable */
+ SF_TWO_OR_MORE_DRAWABLE, /* GimpImageProcedure, requires at least two drawables. */
+} SFDrawableArity;
+
#endif /* __SCRIPT_FU_ENUMS__ */
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-interface.c
b/plug-ins/script-fu/libscriptfu/script-fu-interface.c
index 4bfa3a7803..7f3e1e698b 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-interface.c
+++ b/plug-ins/script-fu/libscriptfu/script-fu-interface.c
@@ -28,7 +28,6 @@
#include <windows.h>
#endif
-#include "tinyscheme/scheme-private.h"
#include "scheme-wrapper.h"
#include "script-fu-types.h"
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.c
b/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.c
index 5c74a755ae..614f837fe4 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.c
+++ b/plug-ins/script-fu/libscriptfu/script-fu-proc-factory.c
@@ -88,7 +88,6 @@ script_fu_proc_factory_make_PLUGIN (GimpPlugIn *plug_in,
procedure = script_fu_script_create_PDB_procedure (
plug_in,
script,
- script_fu_script_proc, /* run_func */
GIMP_PDB_PROC_TYPE_PLUGIN);
script_fu_add_menu_to_procedure (procedure, script);
}
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-register.c
b/plug-ins/script-fu/libscriptfu/script-fu-register.c
new file mode 100644
index 0000000000..42a2b5fc1f
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-register.c
@@ -0,0 +1,462 @@
+/* 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 <glib.h>
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "tinyscheme/scheme-private.h"
+
+#include "script-fu-types.h"
+#include "script-fu-script.h"
+#include "script-fu-register.h"
+
+/* Methods for a script's call to script-fu-register or script-fu-register-filter.
+ * Such calls declare a PDB procedure, that ScriptFu will register in the PDB,
+ * that the script implements by its inner run func.
+ * These methods are only creating structs local to ScriptFu, used later to register.
+ */
+
+
+
+/* Traverse Scheme argument list
+ * creating a new SFScript with metadata, but empty SFArgs (formal arg specs)
+ *
+ * Takes a handle to a pointer into the argument list.
+ * Advances the pointer past the metadata args.
+ *
+ * Returns new SFScript.
+ */
+SFScript*
+script_fu_script_new_from_metadata_args (scheme *sc,
+ pointer *handle)
+{
+ SFScript *script;
+ const gchar *name;
+ const gchar *menu_label;
+ const gchar *blurb;
+ const gchar *author;
+ const gchar *copyright;
+ const gchar *date;
+ const gchar *image_types;
+ guint n_args;
+
+ /* dereference handle into local pointer. */
+ pointer a = *handle;
+
+ g_debug ("script_fu_script_new_from_metadata_args");
+
+ /* Require list_length starting at a is >=7
+ * else strange parsing errors at plugin query time.
+ */
+
+ name = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ menu_label = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ blurb = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ author = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ copyright = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ date = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ if (sc->vptr->is_pair (a))
+ {
+ image_types = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ {
+ image_types = sc->vptr->string_value (a);
+ a = sc->NIL;
+ }
+
+ /* Store local, advanced pointer at handle from caller. */
+ *handle = a;
+
+ /* Calculate supplied number of formal arguments of the PDB procedure,
+ * each takes three actual args from Scheme call.
+ */
+ n_args = sc->vptr->list_length (sc, a) / 3;
+
+ /* This allocates empty array of SFArg. Hereafter, script knows its n_args. */
+ script = script_fu_script_new (name,
+ menu_label,
+ blurb,
+ author,
+ copyright,
+ date,
+ image_types,
+ n_args);
+ return script;
+}
+
+/* Traverse suffix of Scheme argument list,
+ * creating SFArgs (formal arg specs) from triplets.
+ *
+ * Takes a handle to a pointer into the argument list.
+ * Advances the pointer past the triplets.
+ * Changes state of SFScript.args[]
+ *
+ * Returns a foreign_error or NIL.
+ */
+pointer
+script_fu_script_create_formal_args (scheme *sc,
+ pointer *handle,
+ SFScript *script)
+{
+ /* dereference handle into local pointer. */
+ pointer a = *handle;
+
+ g_debug ("script_fu_script_create_formal_args");
+
+ for (guint i = 0; i < script->n_args; i++)
+ {
+ SFArg *arg = &script->args[i];
+
+ if (a != sc->NIL)
+ {
+ if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: argument types must be integer values", 0);
+
+ arg->type = sc->vptr->ivalue (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ return foreign_error (sc, "script-fu-register: missing type specifier", 0);
+
+ if (a != sc->NIL)
+ {
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: argument labels must be strings", 0);
+
+ arg->label = g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ return foreign_error (sc, "script-fu-register: missing arguments label", 0);
+
+ if (a != sc->NIL)
+ {
+ switch (arg->type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: default IDs must be integer values", 0);
+
+ arg->default_value.sfa_image =
+ sc->vptr->ivalue (sc->vptr->pair_car (a));
+ break;
+
+ case SF_COLOR:
+ if (sc->vptr->is_string (sc->vptr->pair_car (a)))
+ {
+ if (! gimp_rgb_parse_css (&arg->default_value.sfa_color,
+ sc->vptr->string_value (sc->vptr->pair_car (a)),
+ -1))
+ return foreign_error (sc, "script-fu-register: invalid default color name", 0);
+
+ gimp_rgb_set_alpha (&arg->default_value.sfa_color, 1.0);
+ }
+ else if (sc->vptr->is_list (sc, sc->vptr->pair_car (a)) &&
+ sc->vptr->list_length(sc, sc->vptr->pair_car (a)) == 3)
+ {
+ pointer color_list;
+ guchar r, g, b;
+
+ color_list = sc->vptr->pair_car (a);
+ r = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
+ color_list = sc->vptr->pair_cdr (color_list);
+ g = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
+ color_list = sc->vptr->pair_cdr (color_list);
+ b = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
+
+ gimp_rgb_set_uchar (&arg->default_value.sfa_color, r, g, b);
+ }
+ else
+ {
+ return foreign_error (sc, "script-fu-register: color defaults must be a list of 3 integers
or a color name", 0);
+ }
+ break;
+
+ case SF_TOGGLE:
+ if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: toggle default must be an integer value", 0);
+
+ arg->default_value.sfa_toggle =
+ (sc->vptr->ivalue (sc->vptr->pair_car (a))) ? TRUE : FALSE;
+ break;
+
+ case SF_VALUE:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: value defaults must be string values", 0);
+
+ arg->default_value.sfa_value =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_STRING:
+ case SF_TEXT:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: string defaults must be string values", 0);
+
+ arg->default_value.sfa_value =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_ADJUSTMENT:
+ {
+ pointer adj_list;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: adjustment defaults must be a list", 0);
+
+ adj_list = sc->vptr->pair_car (a);
+ arg->default_value.sfa_adjustment.value =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.lower =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.upper =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.step =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.page =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.digits =
+ sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.type =
+ sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
+ }
+ break;
+
+ case SF_FILENAME:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: filename defaults must be string values", 0);
+ /* fallthrough */
+
+ case SF_DIRNAME:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: dirname defaults must be string values", 0);
+
+ arg->default_value.sfa_file.filename =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+
+#ifdef G_OS_WIN32
+ {
+ /* Replace POSIX slashes with Win32 backslashes. This
+ * is just so script-fus can be written with only
+ * POSIX directory separators.
+ */
+ gchar *filename = arg->default_value.sfa_file.filename;
+
+ while (*filename)
+ {
+ if (*filename == '/')
+ *filename = G_DIR_SEPARATOR;
+
+ filename++;
+ }
+ }
+#endif
+ break;
+
+ case SF_FONT:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: font defaults must be string values", 0);
+
+ arg->default_value.sfa_font =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_PALETTE:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: palette defaults must be string values", 0);
+
+ arg->default_value.sfa_palette =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_PATTERN:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: pattern defaults must be string values", 0);
+
+ arg->default_value.sfa_pattern =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_BRUSH:
+ {
+ pointer brush_list;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: brush defaults must be a list", 0);
+
+ brush_list = sc->vptr->pair_car (a);
+ arg->default_value.sfa_brush.name =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (brush_list)));
+
+ brush_list = sc->vptr->pair_cdr (brush_list);
+ arg->default_value.sfa_brush.opacity =
+ sc->vptr->rvalue (sc->vptr->pair_car (brush_list));
+
+ brush_list = sc->vptr->pair_cdr (brush_list);
+ arg->default_value.sfa_brush.spacing =
+ sc->vptr->ivalue (sc->vptr->pair_car (brush_list));
+
+ brush_list = sc->vptr->pair_cdr (brush_list);
+ arg->default_value.sfa_brush.paint_mode =
+ sc->vptr->ivalue (sc->vptr->pair_car (brush_list));
+ }
+ break;
+
+ case SF_GRADIENT:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: gradient defaults must be string values", 0);
+
+ arg->default_value.sfa_gradient =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_OPTION:
+ {
+ pointer option_list;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: option defaults must be a list", 0);
+
+ for (option_list = sc->vptr->pair_car (a);
+ option_list != sc->NIL;
+ option_list = sc->vptr->pair_cdr (option_list))
+ {
+ arg->default_value.sfa_option.list =
+ g_slist_append (arg->default_value.sfa_option.list,
+ g_strdup (sc->vptr->string_value
+ (sc->vptr->pair_car (option_list))));
+ }
+ }
+ break;
+
+ case SF_ENUM:
+ {
+ pointer option_list;
+ const gchar *val;
+ gchar *type_name;
+ GEnumValue *enum_value;
+ GType enum_type;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: enum defaults must be a list", 0);
+
+ option_list = sc->vptr->pair_car (a);
+ if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
+ return foreign_error (sc, "script-fu-register: first element in enum defaults must be a
type-name", 0);
+
+ val = sc->vptr->string_value (sc->vptr->pair_car (option_list));
+
+ if (g_str_has_prefix (val, "Gimp"))
+ type_name = g_strdup (val);
+ else
+ type_name = g_strconcat ("Gimp", val, NULL);
+
+ enum_type = g_type_from_name (type_name);
+ if (! G_TYPE_IS_ENUM (enum_type))
+ {
+ g_free (type_name);
+ return foreign_error (sc, "script-fu-register: first element in enum defaults must be
the name of a registered type", 0);
+ }
+
+ arg->default_value.sfa_enum.type_name = type_name;
+
+ option_list = sc->vptr->pair_cdr (option_list);
+ if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
+ return foreign_error (sc, "script-fu-register: second element in enum defaults must be a
string", 0);
+
+ enum_value =
+ g_enum_get_value_by_nick (g_type_class_peek (enum_type),
+ sc->vptr->string_value (sc->vptr->pair_car (option_list)));
+ if (enum_value)
+ arg->default_value.sfa_enum.history = enum_value->value;
+ }
+ break;
+ }
+
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ {
+ return foreign_error (sc, "script-fu-register: missing default argument", 0);
+ }
+ } /* end for */
+
+ /* Store local, advanced pointer at handle from caller. */
+ *handle = a;
+
+ return sc->NIL;
+}
+
+/* Traverse next arg in Scheme argument list.
+ * Set SFScript.drawable_arity from the argument.
+ * Used only by script-fu-register-filter.
+ *
+ * Return foreign_error or NIL.
+ */
+pointer
+script_fu_script_parse_drawable_arity_arg (scheme *sc,
+ pointer *handle,
+ SFScript *script)
+{
+ /* dereference handle into local pointer. */
+ pointer a = *handle;
+
+ /* argument must be an int, usually a symbol from enum e.g. SF-MULTIPLE-DRAWABLE */
+ if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register-filter: drawable arity must be integer value", 0);
+ script->drawable_arity = sc->vptr->ivalue (sc->vptr->pair_car (a));
+
+ /* Advance the pointer into script. */
+ a = sc->vptr->pair_cdr (a);
+ *handle = a;
+ return sc->NIL;
+}
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-register.h
b/plug-ins/script-fu/libscriptfu/script-fu-register.h
new file mode 100644
index 0000000000..1773a55982
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-register.h
@@ -0,0 +1,30 @@
+/* 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/>.
+ */
+
+#ifndef __SCRIPT_FU_REGISTER_H__
+#define __SCRIPT_FU_REGISTER_H__
+
+pointer script_fu_script_create_formal_args (scheme *sc,
+ pointer *handle,
+ SFScript *script);
+SFScript *script_fu_script_new_from_metadata_args (scheme *sc,
+ pointer *handle);
+pointer script_fu_script_parse_drawable_arity_arg (scheme *sc,
+ pointer *handle,
+ SFScript *script);
+
+#endif /* __SCRIPT_FU_REGISTER_H__ */
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-run-func.c
b/plug-ins/script-fu/libscriptfu/script-fu-run-func.c
new file mode 100644
index 0000000000..b2e6cbc02b
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-run-func.c
@@ -0,0 +1,217 @@
+/* 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 <glib.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "scheme-wrapper.h" /* type "pointer" */
+
+#include "script-fu-types.h"
+#include "script-fu-interface.h" /* ScriptFu's GUI implementation. */
+#include "script-fu-dialog.h" /* Gimp's GUI implementation. */
+#include "script-fu-script.h"
+#include "script-fu-scripts.h" /* script_fu_find_script */
+#include "script-fu-command.h"
+
+#include "script-fu-run-func.h"
+
+/* Outer run_funcs
+ * One each for GimpProcedure and GimpImageProcedure.
+ * These are called from Gimp, with two different signatures.
+ * These form and interpret "commands" which are calls to inner run_funcs
+ * defined in Scheme by a script.
+
+ * These return the result of interpretation,
+ * in a GimpValueArray whose only element is a status.
+ * !!! ScriptFu does not let authors define procedures that return values.
+ */
+
+/* run_func for a GimpImageProcedure
+ *
+ * Type is GimpRunImageFunc.
+ *
+ * Uses Gimp's config and gui.
+ *
+ * Since 3.0
+ */
+GimpValueArray *
+script_fu_run_image_procedure ( GimpProcedure *procedure, /* GimpImageProcedure */
+ GimpRunMode run_mode,
+ GimpImage *image,
+ guint n_drawables,
+ GimpDrawable **drawables,
+ const GimpValueArray *other_args,
+ gpointer data)
+{
+
+ GimpValueArray *result = NULL;
+ SFScript *script;
+
+ g_debug ("script_fu_run_image_procedure");
+ script = script_fu_find_script (gimp_procedure_get_name (procedure));
+
+ if (! script)
+ return gimp_procedure_new_return_values (procedure, GIMP_PDB_CALLING_ERROR, NULL);
+
+ ts_set_run_mode (run_mode);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ {
+ if (gimp_value_array_length (other_args) > 0)
+ {
+ /* Let user choose "other" args in a dialog, then interpret. Maintain a config. */
+ result = script_fu_dialog_run (procedure, script, image, n_drawables, drawables, other_args);
+ }
+ else
+ {
+ /* No "other" args for user to choose. No config to maintain. */
+ result = script_fu_interpret_image_proc (procedure, script, image, n_drawables, drawables,
other_args);
+ }
+ break;
+ }
+ case GIMP_RUN_NONINTERACTIVE:
+ {
+ /* A call from another PDB procedure.
+ * Use the given other_args, without interacting with user.
+ * Since no user interaction, no config to maintain.
+ */
+ result = script_fu_interpret_image_proc (procedure, script, image, n_drawables, drawables,
other_args);
+ break;
+ }
+ case GIMP_RUN_WITH_LAST_VALS:
+ {
+ /* User invoked from a menu "Filter>Run with last values".
+ * Do not show dialog. other_args are already last values, from a config.
+ */
+ result = script_fu_interpret_image_proc (procedure, script, image, n_drawables, drawables,
other_args);
+ break;
+ }
+ default:
+ {
+ result = gimp_procedure_new_return_values (procedure, GIMP_PDB_CALLING_ERROR, NULL);
+ }
+ }
+ return result;
+}
+
+
+/* run_func for a GimpProcedure.
+ *
+ * Type is GimpRunFunc
+ *
+ * Uses ScriptFu's own GUI implementation, and retains settings locally.
+ *
+ * Since prior to 3.0 but formerly named script_fu_script_proc
+ */
+GimpValueArray *
+script_fu_run_procedure (GimpProcedure *procedure,
+ const GimpValueArray *args,
+ gpointer data)
+{
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ SFScript *script;
+ GimpRunMode run_mode;
+ GError *error = NULL;
+
+ script = script_fu_find_script (gimp_procedure_get_name (procedure));
+
+ if (! script)
+ return gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_CALLING_ERROR,
+ NULL);
+
+ run_mode = GIMP_VALUES_GET_ENUM (args, 0);
+
+ ts_set_run_mode (run_mode);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ {
+ gint min_args = 0;
+
+ /* First, try to collect the standard script arguments... */
+ min_args = script_fu_script_collect_standard_args (script, args);
+
+ /* ...then acquire the rest of arguments (if any) with a dialog */
+ if (script->n_args > min_args)
+ {
+ status = script_fu_interface (script, min_args);
+ break;
+ }
+ /* otherwise (if the script takes no more arguments), skip
+ * this part and run the script directly (fallthrough)
+ */
+ }
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there */
+ if (gimp_value_array_length (args) != (script->n_args + 1))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gchar *command;
+
+ command = script_fu_script_get_command_from_params (script, args);
+
+ /* run the command through the interpreter */
+ if (! script_fu_run_command (command, &error))
+ {
+ return gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_EXECUTION_ERROR,
+ error);
+ }
+
+ g_free (command);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ {
+ gchar *command;
+
+ /* First, try to collect the standard script arguments */
+ script_fu_script_collect_standard_args (script, args);
+
+ command = script_fu_script_get_command (script);
+
+ /* run the command through the interpreter */
+ if (! script_fu_run_command (command, &error))
+ {
+ return gimp_procedure_new_return_values (procedure,
+ GIMP_PDB_EXECUTION_ERROR,
+ error);
+ }
+
+ g_free (command);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return gimp_procedure_new_return_values (procedure, status, NULL);
+}
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-run-func.h
b/plug-ins/script-fu/libscriptfu/script-fu-run-func.h
new file mode 100644
index 0000000000..86b6380c07
--- /dev/null
+++ b/plug-ins/script-fu/libscriptfu/script-fu-run-func.h
@@ -0,0 +1,33 @@
+/* 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/>.
+ */
+
+#ifndef __SCRIPT_FU_RUN_FUNC_H__
+#define __SCRIPT_FU_RUN_FUNC_H__
+
+GimpValueArray *script_fu_run_procedure (GimpProcedure *procedure,
+ const GimpValueArray *args,
+ gpointer data);
+
+GimpValueArray *script_fu_run_image_procedure (GimpProcedure *procedure,
+ GimpRunMode run_mode,
+ GimpImage *image,
+ guint n_drawables,
+ GimpDrawable **drawables,
+ const GimpValueArray *args,
+ gpointer data);
+
+#endif /* __SCRIPT_FU_RUN_FUNC__ */
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-script.c
b/plug-ins/script-fu/libscriptfu/script-fu-script.c
index 5a93cdbc27..97e956278d 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-script.c
+++ b/plug-ins/script-fu/libscriptfu/script-fu-script.c
@@ -27,6 +27,8 @@
#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"
@@ -34,14 +36,25 @@
* Local Functions
*/
-static gboolean script_fu_script_param_init (SFScript *script,
- const GimpValueArray *args,
- SFArgType type,
- gint n);
-
-
-
-
+static gboolean script_fu_script_param_init (SFScript *script,
+ const GimpValueArray *args,
+ 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
*/
@@ -71,6 +84,8 @@ script_fu_script_new (const gchar *name,
script->n_args = n_args;
script->args = g_new0 (SFArg, script->n_args);
+ script->drawable_arity = SF_NO_DRAWABLE; /* default */
+
return script;
}
@@ -106,18 +121,15 @@ script_fu_script_free (SFScript *script)
*/
void
script_fu_script_install_proc (GimpPlugIn *plug_in,
- SFScript *script,
- GimpRunFunc run_func)
+ SFScript *script)
{
GimpProcedure *procedure;
g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
g_return_if_fail (script != NULL);
- g_return_if_fail (run_func != NULL);
procedure = script_fu_script_create_PDB_procedure (plug_in,
script,
- run_func,
GIMP_PDB_PROC_TYPE_TEMPORARY);
gimp_plug_in_add_temp_procedure (plug_in, procedure);
@@ -126,67 +138,74 @@ script_fu_script_install_proc (GimpPlugIn *plug_in,
/*
- * Create and return a GimpProcedure.
+ * 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,
- GimpRunFunc run_func,
GimpPDBProcType plug_in_type)
{
GimpProcedure *procedure;
- const gchar *menu_label = NULL;
- g_debug ("script_fu_script_create_PDB_procedure: %s of type %i", script->name, plug_in_type);
+ 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);
- /* Allow scripts with no menus */
- if (strncmp (script->menu_label, "<None>", 6) != 0)
- menu_label = script->menu_label;
+ 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,
- run_func, script, NULL);
+ procedure = gimp_procedure_new (plug_in, script->name,
+ plug_in_type,
+ script_fu_run_procedure,
+ script, NULL);
- gimp_procedure_set_image_types (procedure, script->image_types);
+ script_fu_script_set_proc_metadata (procedure, script);
- if (menu_label && strlen (menu_label))
- gimp_procedure_set_menu_label (procedure, menu_label);
+ 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));
- gimp_procedure_set_documentation (procedure,
- script->blurb,
- NULL,
- script->name);
- gimp_procedure_set_attribution (procedure,
- script->author,
- script->copyright,
- script->date);
+ script_fu_script_set_proc_args (procedure, script, 0);
- 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));
+ /* !!! Author did not declare drawable arity, it was inferred. */
+ script_fu_script_set_drawable_sensitivity (procedure, script);
+ }
- script_fu_arg_reset_name_generator ();
- for (gint i = 0; 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);
- }
return procedure;
}
@@ -292,6 +311,10 @@ script_fu_script_collect_standard_args (SFScript *script,
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)
{
@@ -343,6 +366,96 @@ script_fu_script_get_command_from_params (SFScript *script,
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,
+ const GimpValueArray *args)
+{
+ GString *s;
+
+ g_return_val_if_fail (script != NULL, 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);
+
+ /* args contains the "other" args
+ * Iterate over the GimpValueArray.
+ * But script->args should be the same length, and types should match.
+ */
+ for (guint i = 0; i < gimp_value_array_length (args); i++)
+ {
+ GValue *value = gimp_value_array_index (args, i);
+ g_string_append_c (s, ' ');
+ script_fu_arg_append_repr_from_gvalue (&script->args[i],
+ s,
+ value);
+ }
+
+ g_string_append_c (s, ')');
+
+ 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
@@ -431,3 +544,80 @@ script_fu_script_param_init (SFScript *script,
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_DRAWABLES |
+ GIMP_PROCEDURE_SENSITIVE_DRAWABLE );
+ 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");
+ }
+}
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-script.h
b/plug-ins/script-fu/libscriptfu/script-fu-script.h
index a9901cd200..0547685f01 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-script.h
+++ b/plug-ins/script-fu/libscriptfu/script-fu-script.h
@@ -30,8 +30,7 @@ SFScript * script_fu_script_new (const gchar *name,
void script_fu_script_free (SFScript *script);
void script_fu_script_install_proc (GimpPlugIn *plug_in,
- SFScript *script,
- GimpRunFunc run_func);
+ SFScript *script);
void script_fu_script_uninstall_proc (GimpPlugIn *plug_in,
SFScript *script);
@@ -45,12 +44,17 @@ gint script_fu_script_collect_standard_args (SFScript *scrip
gchar * script_fu_script_get_command (SFScript *script);
gchar * script_fu_script_get_command_from_params (SFScript *script,
const GimpValueArray *args);
+gchar * script_fu_script_get_command_for_image_proc (
+ SFScript *script,
+ GimpImage *image,
+ guint n_drawables,
+ GimpDrawable **drawables,
+ const GimpValueArray *args);
GimpProcedure * script_fu_script_create_PDB_procedure (GimpPlugIn *plug_in,
SFScript *script,
- GimpRunFunc run_func,
GimpPDBProcType plug_in_type);
-
+void script_fu_script_infer_drawable_arity (SFScript *script);
#endif /* __SCRIPT_FU_SCRIPT__ */
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-scripts.c
b/plug-ins/script-fu/libscriptfu/script-fu-scripts.c
index 914ea1a6c6..cbb54f3a48 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-scripts.c
+++ b/plug-ins/script-fu/libscriptfu/script-fu-scripts.c
@@ -18,7 +18,6 @@
#include "config.h"
#include <string.h>
-
#include <glib.h>
#ifdef G_OS_WIN32
@@ -31,14 +30,12 @@
#include "tinyscheme/scheme-private.h"
-#include "scheme-wrapper.h"
-
#include "script-fu-types.h"
-
-#include "script-fu-interface.h"
#include "script-fu-script.h"
#include "script-fu-scripts.h"
#include "script-fu-utils.h"
+#include "script-fu-register.h"
+#include "script-fu-command.h"
#include "script-fu-intl.h"
@@ -47,8 +44,6 @@
* Local Functions
*/
-static gboolean script_fu_run_command (const gchar *command,
- GError **error);
static void script_fu_load_directory (GFile *directory);
static void script_fu_load_script (GFile *file);
static gboolean script_fu_install_script (gpointer foo,
@@ -63,6 +58,8 @@ static gchar * script_fu_menu_map (const gchar *menu_pat
static gint script_fu_menu_compare (gconstpointer a,
gconstpointer b);
+static void script_fu_try_map_menu (SFScript *script);
+static void script_fu_append_script_to_tree (SFScript *script);
/*
* Local variables
@@ -78,14 +75,14 @@ static GList *script_menu_list = NULL;
/* Traverse list of paths, finding .scm files.
* Load and eval any found script texts.
- * Script texts will call Scheme functions script-fu-register()
- * and script-fu-menu-register(),
+ * Script texts will call Scheme functions script-fu-register
+ * and script-fu-menu-register,
* which insert a SFScript record into script_tree,
* and insert a SFMenu record into script_menu_list.
* These are side effects on the state of the outer (SF) interpreter.
*
* Return the tree of scripts, as well as keeping a local pointer to the tree.
- * The other result (script_menu_list) is not returned, see script_fu_get_menu_list().
+ * The other result (script_menu_list) is not returned, see script_fu_get_menu_list.
*
* Caller should free script_tree and script_menu_list,
* This should only be called once.
@@ -126,7 +123,7 @@ script_fu_find_scripts_into_tree ( GimpPlugIn *plug_in,
/*
* Return list of SFMenu for recently loaded scripts.
- * List is non-empty only after a call to script_fu_find_scripts_into_tree().
+ * List is non-empty only after a call to script_fu_find_scripts_into_tree.
*/
GList *
script_fu_get_menu_list (void)
@@ -157,393 +154,99 @@ script_fu_find_scripts (GimpPlugIn *plug_in,
script_menu_list = NULL;
}
+
+
+/* For a script's call to script-fu-register.
+ * Traverse Scheme argument list creating a new SFScript
+ * whose drawable_arity is SF_PROC_ORDINARY.
+ *
+ * Return NIL or a foreign_error
+ */
pointer
script_fu_add_script (scheme *sc,
pointer a)
{
SFScript *script;
- 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;
- gint i;
+ pointer args_error;
- /* Check the length of a */
+ /* Check metadata args args are present */
if (sc->vptr->list_length (sc, a) < 7)
- {
- g_message (_("Too few arguments to 'script-fu-register' call"));
- return sc->NIL;
- }
-
- /* Find the script name */
- name = sc->vptr->string_value (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
-
- /* Find the script menu_label */
- menu_label = sc->vptr->string_value (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
-
- /* Find the script blurb */
- blurb = sc->vptr->string_value (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
-
- /* Find the script author */
- author = sc->vptr->string_value (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
-
- /* Find the script copyright */
- copyright = sc->vptr->string_value (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
-
- /* Find the script date */
- date = sc->vptr->string_value (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
-
- /* Find the script image types */
- if (sc->vptr->is_pair (a))
- {
- image_types = sc->vptr->string_value (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
- }
- else
- {
- image_types = sc->vptr->string_value (a);
- a = sc->NIL;
- }
-
- /* Check the supplied number of arguments */
- n_args = sc->vptr->list_length (sc, a) / 3;
-
- /* Create a new script */
- script = script_fu_script_new (name,
- menu_label,
- blurb,
- author,
- copyright,
- date,
- image_types,
- n_args);
-
- for (i = 0; i < script->n_args; i++)
- {
- SFArg *arg = &script->args[i];
-
- if (a != sc->NIL)
- {
- if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: argument types must be integer values", 0);
-
- arg->type = sc->vptr->ivalue (sc->vptr->pair_car (a));
- a = sc->vptr->pair_cdr (a);
- }
- else
- return foreign_error (sc, "script-fu-register: missing type specifier", 0);
-
- if (a != sc->NIL)
- {
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: argument labels must be strings", 0);
-
- arg->label = g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
- a = sc->vptr->pair_cdr (a);
- }
- else
- return foreign_error (sc, "script-fu-register: missing arguments label", 0);
-
- if (a != sc->NIL)
- {
- switch (arg->type)
- {
- case SF_IMAGE:
- case SF_DRAWABLE:
- case SF_LAYER:
- case SF_CHANNEL:
- case SF_VECTORS:
- case SF_DISPLAY:
- if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: default IDs must be integer values", 0);
-
- arg->default_value.sfa_image =
- sc->vptr->ivalue (sc->vptr->pair_car (a));
- break;
-
- case SF_COLOR:
- if (sc->vptr->is_string (sc->vptr->pair_car (a)))
- {
- if (! gimp_rgb_parse_css (&arg->default_value.sfa_color,
- sc->vptr->string_value (sc->vptr->pair_car (a)),
- -1))
- return foreign_error (sc, "script-fu-register: invalid default color name", 0);
-
- gimp_rgb_set_alpha (&arg->default_value.sfa_color, 1.0);
- }
- else if (sc->vptr->is_list (sc, sc->vptr->pair_car (a)) &&
- sc->vptr->list_length(sc, sc->vptr->pair_car (a)) == 3)
- {
- pointer color_list;
- guchar r, g, b;
-
- color_list = sc->vptr->pair_car (a);
- r = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
- color_list = sc->vptr->pair_cdr (color_list);
- g = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
- color_list = sc->vptr->pair_cdr (color_list);
- b = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
-
- gimp_rgb_set_uchar (&arg->default_value.sfa_color, r, g, b);
- }
- else
- {
- return foreign_error (sc, "script-fu-register: color defaults must be a list of 3 integers
or a color name", 0);
- }
- break;
-
- case SF_TOGGLE:
- if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: toggle default must be an integer value", 0);
-
- arg->default_value.sfa_toggle =
- (sc->vptr->ivalue (sc->vptr->pair_car (a))) ? TRUE : FALSE;
- break;
-
- case SF_VALUE:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: value defaults must be string values", 0);
-
- arg->default_value.sfa_value =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
- break;
-
- case SF_STRING:
- case SF_TEXT:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: string defaults must be string values", 0);
-
- arg->default_value.sfa_value =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
- break;
-
- case SF_ADJUSTMENT:
- {
- pointer adj_list;
-
- if (!sc->vptr->is_list (sc, a))
- return foreign_error (sc, "script-fu-register: adjustment defaults must be a list", 0);
-
- adj_list = sc->vptr->pair_car (a);
- arg->default_value.sfa_adjustment.value =
- sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+ return foreign_error (sc, "script-fu-register: Not enough arguments", 0);
- adj_list = sc->vptr->pair_cdr (adj_list);
- arg->default_value.sfa_adjustment.lower =
- sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+ /* pass handle to pointer into script (on the stack) */
+ script = script_fu_script_new_from_metadata_args (sc, &a);
- adj_list = sc->vptr->pair_cdr (adj_list);
- arg->default_value.sfa_adjustment.upper =
- sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
-
- adj_list = sc->vptr->pair_cdr (adj_list);
- arg->default_value.sfa_adjustment.step =
- sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
-
- adj_list = sc->vptr->pair_cdr (adj_list);
- arg->default_value.sfa_adjustment.page =
- sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+ /* Require drawable_arity defaults to SF_PROC_ORDINARY.
+ * script-fu-register specifies an ordinary GimpProcedure.
+ * We may go on to infer a different arity.
+ */
+ g_assert (script->drawable_arity == SF_NO_DRAWABLE);
- adj_list = sc->vptr->pair_cdr (adj_list);
- arg->default_value.sfa_adjustment.digits =
- sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
+ args_error = script_fu_script_create_formal_args (sc, &a, script);
+ if (args_error != sc->NIL)
+ return args_error;
- adj_list = sc->vptr->pair_cdr (adj_list);
- arg->default_value.sfa_adjustment.type =
- sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
- }
- break;
+ /* fill all values from defaults */
+ script_fu_script_reset (script, TRUE);
- case SF_FILENAME:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: filename defaults must be string values", 0);
- /* fallthrough */
+ /* Infer whether the script really requires one drawable,
+ * so that later we can set the sensitivity.
+ * For backward compatibility:
+ * v2 script-fu-register does not require author to declare drawable arity.
+ */
+ script_fu_script_infer_drawable_arity (script);
- case SF_DIRNAME:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: dirname defaults must be string values", 0);
+ script->proc_class = GIMP_TYPE_PROCEDURE;
- arg->default_value.sfa_file.filename =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ script_fu_try_map_menu (script);
+ script_fu_append_script_to_tree (script);
+ return sc->NIL;
+}
-#ifdef G_OS_WIN32
- {
- /* Replace POSIX slashes with Win32 backslashes. This
- * is just so script-fus can be written with only
- * POSIX directory separators.
- */
- gchar *filename = arg->default_value.sfa_file.filename;
-
- while (*filename)
- {
- if (*filename == '/')
- *filename = G_DIR_SEPARATOR;
-
- filename++;
- }
- }
-#endif
- break;
-
- case SF_FONT:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: font defaults must be string values", 0);
-
- arg->default_value.sfa_font =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
- break;
-
- case SF_PALETTE:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: palette defaults must be string values", 0);
-
- arg->default_value.sfa_palette =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
- break;
-
- case SF_PATTERN:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: pattern defaults must be string values", 0);
-
- arg->default_value.sfa_pattern =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
- break;
-
- case SF_BRUSH:
- {
- pointer brush_list;
-
- if (!sc->vptr->is_list (sc, a))
- return foreign_error (sc, "script-fu-register: brush defaults must be a list", 0);
-
- brush_list = sc->vptr->pair_car (a);
- arg->default_value.sfa_brush.name =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (brush_list)));
-
- brush_list = sc->vptr->pair_cdr (brush_list);
- arg->default_value.sfa_brush.opacity =
- sc->vptr->rvalue (sc->vptr->pair_car (brush_list));
-
- brush_list = sc->vptr->pair_cdr (brush_list);
- arg->default_value.sfa_brush.spacing =
- sc->vptr->ivalue (sc->vptr->pair_car (brush_list));
-
- brush_list = sc->vptr->pair_cdr (brush_list);
- arg->default_value.sfa_brush.paint_mode =
- sc->vptr->ivalue (sc->vptr->pair_car (brush_list));
- }
- break;
-
- case SF_GRADIENT:
- if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
- return foreign_error (sc, "script-fu-register: gradient defaults must be string values", 0);
-
- arg->default_value.sfa_gradient =
- g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
- break;
-
- case SF_OPTION:
- {
- pointer option_list;
-
- if (!sc->vptr->is_list (sc, a))
- return foreign_error (sc, "script-fu-register: option defaults must be a list", 0);
-
- for (option_list = sc->vptr->pair_car (a);
- option_list != sc->NIL;
- option_list = sc->vptr->pair_cdr (option_list))
- {
- arg->default_value.sfa_option.list =
- g_slist_append (arg->default_value.sfa_option.list,
- g_strdup (sc->vptr->string_value
- (sc->vptr->pair_car (option_list))));
- }
- }
- break;
-
- case SF_ENUM:
- {
- pointer option_list;
- const gchar *val;
- gchar *type_name;
- GEnumValue *enum_value;
- GType enum_type;
-
- if (!sc->vptr->is_list (sc, a))
- return foreign_error (sc, "script-fu-register: enum defaults must be a list", 0);
-
- option_list = sc->vptr->pair_car (a);
- if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
- return foreign_error (sc, "script-fu-register: first element in enum defaults must be a
type-name", 0);
-
- val = sc->vptr->string_value (sc->vptr->pair_car (option_list));
-
- if (g_str_has_prefix (val, "Gimp"))
- type_name = g_strdup (val);
- else
- type_name = g_strconcat ("Gimp", val, NULL);
-
- enum_type = g_type_from_name (type_name);
- if (! G_TYPE_IS_ENUM (enum_type))
- {
- g_free (type_name);
- return foreign_error (sc, "script-fu-register: first element in enum defaults must be
the name of a registered type", 0);
- }
-
- arg->default_value.sfa_enum.type_name = type_name;
-
- option_list = sc->vptr->pair_cdr (option_list);
- if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
- return foreign_error (sc, "script-fu-register: second element in enum defaults must be a
string", 0);
-
- enum_value =
- g_enum_get_value_by_nick (g_type_class_peek (enum_type),
- sc->vptr->string_value (sc->vptr->pair_car (option_list)));
- if (enum_value)
- arg->default_value.sfa_enum.history = enum_value->value;
- }
- break;
- }
+/* For a script's call to script-fu-register-filter.
+ * Traverse Scheme argument list creating a new SFScript
+ * whose drawable_arity is SF_PROC_IMAGE_MULTIPLE_DRAWABLE or
+ * SF_PROC_IMAGE_SINGLE_DRAWABLE
+ *
+ * Same as script-fu-register, except one more arg for drawable_arity.
+ *
+ * Return NIL or a foreign_error
+ */
+pointer
+script_fu_add_script_filter (scheme *sc,
+ pointer a)
+{
+ SFScript *script;
+ pointer args_error; /* a foreign_error or NIL. */
- a = sc->vptr->pair_cdr (a);
- }
- else
- {
- return foreign_error (sc, "script-fu-register: missing default argument", 0);
- }
- }
+ /* Check metadata args args are present.
+ * Has one more arg than script-fu-register.
+ */
+ if (sc->vptr->list_length (sc, a) < 8)
+ return foreign_error (sc, "script-fu-register-filter: Not enough arguments", 0);
- /* fill all values from defaults */
- script_fu_script_reset (script, TRUE);
+ /* pass handle i.e. "&a" ("a" of type "pointer" is on the stack) */
+ script = script_fu_script_new_from_metadata_args (sc, &a);
- if (script->menu_label[0] == '<')
- {
- gchar *mapped = script_fu_menu_map (script->menu_label);
+ /* Check semantic error: a script declaring it takes an image must specify
+ * image types. Otherwise the script's menu item will be enabled
+ * even when no images exist.
+ */
+ if (g_strcmp0(script->image_types, "")==0)
+ return foreign_error (sc, "script-fu-register-filter: A filter must declare image types.", 0);
- if (mapped)
- {
- g_free (script->menu_label);
- script->menu_label = mapped;
- }
- }
+ args_error = script_fu_script_parse_drawable_arity_arg (sc, &a, script);
+ if (args_error != sc->NIL)
+ return args_error;
- {
- GList *list = g_tree_lookup (script_tree, script->menu_label);
+ args_error = script_fu_script_create_formal_args (sc, &a, script);
+ if (args_error != sc->NIL)
+ return args_error;
- g_tree_insert (script_tree, (gpointer) script->menu_label,
- g_list_append (list, script));
- }
+ script->proc_class = GIMP_TYPE_IMAGE_PROCEDURE;
+ script_fu_try_map_menu (script);
+ script_fu_append_script_to_tree (script);
return sc->NIL;
}
@@ -594,31 +297,6 @@ script_fu_add_menu (scheme *sc,
/* private functions */
-static gboolean
-script_fu_run_command (const gchar *command,
- GError **error)
-{
- GString *output;
- gboolean success = FALSE;
-
- g_debug ("script_fu_run_command: %s", command);
- output = g_string_new (NULL);
- ts_register_output_func (ts_gstring_output_func, output);
-
- if (ts_interpret_string (command))
- {
- g_set_error (error, GIMP_PLUG_IN_ERROR, 0, "%s", output->str);
- }
- else
- {
- success = TRUE;
- }
-
- g_string_free (output, TRUE);
-
- return success;
-}
-
static void
script_fu_load_directory (GFile *directory)
{
@@ -717,8 +395,7 @@ script_fu_install_script (gpointer foo G_GNUC_UNUSED,
const gchar* name = script->name;
if (script_fu_is_defined (name))
- script_fu_script_install_proc (plug_in, script,
- script_fu_script_proc);
+ script_fu_script_install_proc (plug_in, script);
else
g_warning ("Run function not defined, or does not match PDB procedure name: %s", name);
}
@@ -766,105 +443,7 @@ script_fu_remove_script (gpointer foo G_GNUC_UNUSED,
return FALSE;
}
-/* This is the outer "run func" for this plugin.
- * When called, the name of the inner run func (code in Scheme language)
- * is the first element of the value array.
- * Form a command (text in Scheme language) that is a call to the the inner run func,
- * evaluate it, and return the result, marshalled into a GimpValueArray.
- *
- * In the name 'script_fu_script_proc', 'proc' is a verb meaning 'process the script'
- */
-GimpValueArray *
-script_fu_script_proc (GimpProcedure *procedure,
- const GimpValueArray *args,
- gpointer data)
-{
- GimpPDBStatusType status = GIMP_PDB_SUCCESS;
- SFScript *script;
- GimpRunMode run_mode;
- GError *error = NULL;
- script = script_fu_find_script (gimp_procedure_get_name (procedure));
-
- if (! script)
- return gimp_procedure_new_return_values (procedure,
- GIMP_PDB_CALLING_ERROR,
- NULL);
-
- run_mode = GIMP_VALUES_GET_ENUM (args, 0);
-
- ts_set_run_mode (run_mode);
-
- switch (run_mode)
- {
- case GIMP_RUN_INTERACTIVE:
- {
- gint min_args = 0;
-
- /* First, try to collect the standard script arguments... */
- min_args = script_fu_script_collect_standard_args (script, args);
-
- /* ...then acquire the rest of arguments (if any) with a dialog */
- if (script->n_args > min_args)
- {
- status = script_fu_interface (script, min_args);
- break;
- }
- /* otherwise (if the script takes no more arguments), skip
- * this part and run the script directly (fallthrough)
- */
- }
-
- case GIMP_RUN_NONINTERACTIVE:
- /* Make sure all the arguments are there */
- if (gimp_value_array_length (args) != (script->n_args + 1))
- status = GIMP_PDB_CALLING_ERROR;
-
- if (status == GIMP_PDB_SUCCESS)
- {
- gchar *command;
-
- command = script_fu_script_get_command_from_params (script, args);
-
- /* run the command through the interpreter */
- if (! script_fu_run_command (command, &error))
- {
- return gimp_procedure_new_return_values (procedure,
- GIMP_PDB_EXECUTION_ERROR,
- error);
- }
-
- g_free (command);
- }
- break;
-
- case GIMP_RUN_WITH_LAST_VALS:
- {
- gchar *command;
-
- /* First, try to collect the standard script arguments */
- script_fu_script_collect_standard_args (script, args);
-
- command = script_fu_script_get_command (script);
-
- /* run the command through the interpreter */
- if (! script_fu_run_command (command, &error))
- {
- return gimp_procedure_new_return_values (procedure,
- GIMP_PDB_EXECUTION_ERROR,
- error);
- }
-
- g_free (command);
- }
- break;
-
- default:
- break;
- }
-
- return gimp_procedure_new_return_values (procedure, status, NULL);
-}
/* this is a GTraverseFunction */
static gboolean
@@ -1004,3 +583,32 @@ script_fu_is_defined (const gchar * name)
}
return result;
}
+
+
+/* Side effects on script. */
+static void
+script_fu_try_map_menu (SFScript *script)
+{
+ if (script->menu_label[0] == '<')
+ {
+ gchar *mapped = script_fu_menu_map (script->menu_label);
+
+ if (mapped)
+ {
+ g_free (script->menu_label);
+ script->menu_label = mapped;
+ }
+ }
+}
+
+/* Append to ordered tree.
+ * Side effects on script_tree.
+ */
+static void
+script_fu_append_script_to_tree (SFScript *script)
+{
+ GList *list = g_tree_lookup (script_tree, script->menu_label);
+
+ g_tree_insert (script_tree, (gpointer) script->menu_label,
+ g_list_append (list, script));
+}
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-scripts.h
b/plug-ins/script-fu/libscriptfu/script-fu-scripts.h
index b81e779ab1..7441c2424b 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-scripts.h
+++ b/plug-ins/script-fu/libscriptfu/script-fu-scripts.h
@@ -18,11 +18,12 @@
#ifndef __SCRIPT_FU_SCRIPTS_H__
#define __SCRIPT_FU_SCRIPTS_H__
-
void script_fu_find_scripts (GimpPlugIn *plug_in,
GList *path);
-pointer script_fu_add_script (scheme *sc,
- pointer a);
+pointer script_fu_add_script (scheme *sc,
+ pointer a);
+pointer script_fu_add_script_filter (scheme *sc,
+ pointer a);
pointer script_fu_add_menu (scheme *sc,
pointer a);
@@ -30,9 +31,7 @@ GTree * script_fu_find_scripts_into_tree (GimpPlugIn *plug_in,
GList *path);
SFScript * script_fu_find_script (const gchar *name);
GList * script_fu_get_menu_list (void);
-GimpValueArray * script_fu_script_proc (GimpProcedure *procedure,
- const GimpValueArray *args,
- gpointer data);
+
gboolean script_fu_is_defined (const gchar *name);
#endif /* __SCRIPT_FU_SCRIPTS__ */
diff --git a/plug-ins/script-fu/libscriptfu/script-fu-types.h
b/plug-ins/script-fu/libscriptfu/script-fu-types.h
index 981e450e0e..d74c5e6b40 100644
--- a/plug-ins/script-fu/libscriptfu/script-fu-types.h
+++ b/plug-ins/script-fu/libscriptfu/script-fu-types.h
@@ -101,6 +101,8 @@ typedef struct
gint n_args;
SFArg *args;
+ SFDrawableArity drawable_arity;
+ GType proc_class; /* GimpProcedure or GimpImageProcedure. */
} SFScript;
typedef struct
diff --git a/plug-ins/script-fu/scripts/Makefile.am b/plug-ins/script-fu/scripts/Makefile.am
index c9564d8f78..1f0ce2be29 100644
--- a/plug-ins/script-fu/scripts/Makefile.am
+++ b/plug-ins/script-fu/scripts/Makefile.am
@@ -57,18 +57,26 @@ scripts = \
weave.scm \
xach-effect.scm
+# FIXME: when 3.0 ships, ship only the v3 versions
+# of clothify.scm and test-sphere.scm
+# During dev of 2.99, install old and new so devs can compare their GUIs.
test_scripts = \
- contactsheet.scm \
- test-sphere.scm
+ contactsheet.scm \
+ test-sphere.scm \
+ clothify-v3.scm
+
# scripts interpreted by gimp-script-fu-interpreter
# Each installs to a subdir of /plug-ins
# Each should have a shebang and execute permission
independent_scripts = \
- ts-helloworld.scm
+ ts-helloworld.scm \
+ test-sphere-v3.scm
ts_helloworlddir = $(gimpplugindir)/plug-ins/ts-helloworld
ts_helloworld_SCRIPTS = ts-helloworld.scm
+test_sphere_v3dir = $(gimpplugindir)/plug-ins/test-sphere-v3
+test_sphere_v3_SCRIPTS = test-sphere-v3.scm
scriptdata_DATA = $(scripts)
diff --git a/plug-ins/script-fu/scripts/clothify-v3.scm b/plug-ins/script-fu/scripts/clothify-v3.scm
new file mode 100644
index 0000000000..00207dec9b
--- /dev/null
+++ b/plug-ins/script-fu/scripts/clothify-v3.scm
@@ -0,0 +1,84 @@
+; CLOTHIFY version 1.02
+; Gives the current layer in the indicated image a cloth-like texture.
+; Process invented by Zach Beane (Xath irc gimp net)
+;
+; Tim Newsome <drz froody bloke com> 4/11/97
+
+; v3>>> Adapted to take many drawables, but only handle the first
+; v3>>> drawables is-a vector, and there is no formal arg for its length i.e. n_drawables
+
+(define (script-fu-clothify-v3 timg drawables bx by azimuth elevation depth)
+ (let* (
+ (tdrawable (aref drawables 0)) v3>>> only the first drawable
+ (width (car (gimp-drawable-get-width tdrawable)))
+ (height (car (gimp-drawable-get-height tdrawable)))
+ (img (car (gimp-image-new width height RGB)))
+; (layer-two (car (gimp-layer-new img width height RGB-IMAGE "Y Dots" 100 LAYER-MODE-MULTIPLY)))
+ (layer-one (car (gimp-layer-new img width height RGB-IMAGE "X Dots" 100 LAYER-MODE-NORMAL)))
+ (layer-two 0)
+ (bump-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable img)
+
+ (gimp-image-insert-layer img layer-one 0 0)
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill layer-one FILL-BACKGROUND)
+
+ (plug-in-noisify RUN-NONINTERACTIVE img layer-one FALSE 0.7 0.7 0.7 0.7)
+
+ (set! layer-two (car (gimp-layer-copy layer-one 0)))
+ (gimp-layer-set-mode layer-two LAYER-MODE-MULTIPLY)
+ (gimp-image-insert-layer img layer-two 0 0)
+
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img layer-one bx TRUE FALSE)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img layer-two by FALSE TRUE)
+ (gimp-image-flatten img)
+ (set! bump-layer (car (gimp-image-get-active-layer img)))
+
+ (plug-in-c-astretch RUN-NONINTERACTIVE img bump-layer)
+ (plug-in-noisify RUN-NONINTERACTIVE img bump-layer FALSE 0.2 0.2 0.2 0.2)
+
+ (plug-in-bump-map RUN-NONINTERACTIVE img tdrawable bump-layer azimuth elevation depth 0 0 0 0 FALSE
FALSE 0)
+ (gimp-image-delete img)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+
+ ; well-behaved requires error if more than one drawable
+ ( if (> (vector-length drawables) 1 )
+ (begin
+ ; Msg to status bar, need not be acknowledged by any user
+ (gimp-message "Received more than one drawable.")
+ ; Msg propagated in a GError to Gimp's error dialog that must be acknowledged
+ (write "Received more than one drawable.")
+ ; Indicate err to programmed callers
+ #f)
+ #t
+ )
+ )
+)
+
+; v3 >>> no image or drawable declared.
+; v3 >>> SF-ONE-DRAWABLE means contracts to process only one drawable
+(script-fu-register-filter "script-fu-clothify-v3"
+ _"_Clothify v3..."
+ _"Add a cloth-like texture to the selected region (or alpha)"
+ "Tim Newsome <drz froody bloke com>"
+ "Tim Newsome"
+ "4/11/97"
+ "RGB* GRAY*"
+ SF-ONE-DRAWABLE
+ SF-ADJUSTMENT _"Blur X" '(9 3 100 1 10 0 1)
+ SF-ADJUSTMENT _"Blur Y" '(9 3 100 1 10 0 1)
+ SF-ADJUSTMENT _"Azimuth" '(135 0 360 1 10 1 0)
+ SF-ADJUSTMENT _"Elevation" '(45 0 90 1 10 1 0)
+ SF-ADJUSTMENT _"Depth" '(3 1 50 1 10 0 1)
+)
+
+(script-fu-menu-register "script-fu-clothify-v3"
+ "<Image>/Filters/Artistic")
diff --git a/plug-ins/script-fu/scripts/meson.build b/plug-ins/script-fu/scripts/meson.build
index 5dc33b52b1..2feae66d3e 100644
--- a/plug-ins/script-fu/scripts/meson.build
+++ b/plug-ins/script-fu/scripts/meson.build
@@ -53,6 +53,7 @@ scripts = [
'waves-anim.scm',
'weave.scm',
'xach-effect.scm',
+ 'clothify-v3.scm'
]
if not stable
@@ -75,6 +76,7 @@ install_data(
scripts_independent = [
{ 'name': 'ts-helloworld' },
+ { 'name': 'test-sphere-v3' },
]
foreach plugin : scripts_independent
diff --git a/plug-ins/script-fu/scripts/plug-in-compat.init b/plug-ins/script-fu/scripts/plug-in-compat.init
index dd68c2c24f..4eb8dd9198 100644
--- a/plug-ins/script-fu/scripts/plug-in-compat.init
+++ b/plug-ins/script-fu/scripts/plug-in-compat.init
@@ -22,3 +22,16 @@
(- 255 (caddr dest-color-1)) (- 255 (caddr dest-color-2)))
(gimp-levels layer HISTOGRAM-VALUE 0 255 1.0 255 0)
)
+
+; since 3.0 a layer selection can be many,
+; so PDB methods to get selections return an int and a GObjectArray,
+; which in Scheme is a list containing a numeric and a vector.
+; and the word "active" changed to "selected".
+; Formerly, such PDB methods returned a list of one element, the ID of a layer.
+
+; A compatible replacement for gimp-image-get-active-layer.
+; This should be used only when you know the image has only one layer.
+; In other situations, you may break a contract to process all selected layers.
+(define (gimp-image-get-active-layer img)
+ (list (aref (cadr (gimp-image-get-selected-layers img)) 0))
+)
diff --git a/plug-ins/script-fu/scripts/test-sphere-v3.scm b/plug-ins/script-fu/scripts/test-sphere-v3.scm
new file mode 100644
index 0000000000..d9a696194b
--- /dev/null
+++ b/plug-ins/script-fu/scripts/test-sphere-v3.scm
@@ -0,0 +1,174 @@
+#!/usr/bin/env gimp-script-fu-interpreter-3.0
+
+; v3 >>> Has shebang, is interpreter
+
+; This is a a test script to test Script-Fu parameter API.
+
+; For GIMP 3: uses GimpImageProcedure, GimpProcedureDialog, GimpConfig
+
+; See also test-sphere.scm, for GIMP 2, from which this is derived
+; Diffs marked with ; v3 >>>
+
+
+; v3 >>> signature of GimpImageProcedure
+; drawables is a vector
+(define (script-fu-test-sphere-v3
+ image
+ drawables
+ radius
+ light
+ shadow
+ bg-color
+ sphere-color
+ brush
+ text
+ multi-text
+ pattern
+ gradient
+ gradient-reverse
+ font
+ size
+ unused-palette
+ unused-filename
+ unused-orientation
+ unused-interpolation
+ unused-dirname
+ unused-image
+ unused-layer
+ unused-channel
+ unused-drawable)
+ (let* (
+ (width (* radius 3.75))
+ (height (* radius 2.5))
+ (img (car (gimp-image-new width height RGB)))
+ (drawable (car (gimp-layer-new img width height RGB-IMAGE
+ "Sphere Layer" 100 LAYER-MODE-NORMAL)))
+ (radians (/ (* light *pi*) 180))
+ (cx (/ width 2))
+ (cy (/ height 2))
+ (light-x (+ cx (* radius (* 0.6 (cos radians)))))
+ (light-y (- cy (* radius (* 0.6 (sin radians)))))
+ (light-end-x (+ cx (* radius (cos (+ *pi* radians)))))
+ (light-end-y (- cy (* radius (sin (+ *pi* radians)))))
+ (offset (* radius 0.1))
+ (text-extents (gimp-text-get-extents-fontname multi-text
+ size PIXELS
+ font))
+ (x-position (- cx (/ (car text-extents) 2)))
+ (y-position (- cy (/ (cadr text-extents) 2)))
+ (shadow-w 0)
+ (shadow-x 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+ (gimp-context-set-foreground sphere-color)
+ (gimp-context-set-background bg-color)
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+ (gimp-context-set-background '(20 20 20))
+
+ (if (and
+ (or (and (>= light 45) (<= light 75))
+ (and (<= light 135) (>= light 105)))
+ (= shadow TRUE))
+ (let ((shadow-w (* (* radius 2.5) (cos (+ *pi* radians))))
+ (shadow-h (* radius 0.5))
+ (shadow-x cx)
+ (shadow-y (+ cy (* radius 0.65))))
+ (if (< shadow-w 0)
+ (begin (set! shadow-x (+ cx shadow-w))
+ (set! shadow-w (- shadow-w))))
+
+ (gimp-context-set-feather TRUE)
+ (gimp-context-set-feather-radius 7.5 7.5)
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE shadow-x shadow-y shadow-w shadow-h)
+ (gimp-context-set-pattern pattern)
+ (gimp-drawable-edit-fill drawable FILL-PATTERN)))
+
+ (gimp-context-set-feather FALSE)
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE (- cx radius) (- cy radius)
+ (* 2 radius) (* 2 radius))
+
+ (gimp-context-set-gradient-fg-bg-rgb)
+ (gimp-drawable-edit-gradient-fill drawable
+ GRADIENT-RADIAL offset
+ FALSE 0 0
+ TRUE
+ light-x light-y
+ light-end-x light-end-y)
+
+ (gimp-selection-none img)
+
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE 10 10 50 50)
+
+ (gimp-context-set-gradient gradient)
+ (gimp-context-set-gradient-reverse gradient-reverse)
+ (gimp-drawable-edit-gradient-fill drawable
+ GRADIENT-LINEAR offset
+ FALSE 0 0
+ TRUE
+ 10 10
+ 30 60)
+
+ (gimp-selection-none img)
+
+ (gimp-context-set-foreground '(0 0 0))
+ (gimp-floating-sel-anchor (car (gimp-text-fontname img drawable
+ x-position y-position
+ multi-text
+ 0 TRUE
+ size PIXELS
+ font)))
+
+ (gimp-image-undo-enable img)
+ (gimp-display-new img)
+
+ (gimp-context-pop)
+ )
+)
+
+; v3 >>> use script-fu-register-filter
+; v3 >>> menu item is v3, alongside old one
+; v3 >>> not yet localized
+
+(script-fu-register-filter "script-fu-test-sphere-v3"
+ "Sphere v3..."
+ "Test script-fu-register-filter and GimpProcedureDialog: needs 2 selected layers."
+ "Spencer Kimball, Sven Neumann"
+ "Spencer Kimball"
+ "1996, 1998"
+ "*" ; image types any
+ SF-TWO-OR-MORE-DRAWABLE ; v3 >>> additional argument
+ SF-ADJUSTMENT "Radius (in pixels)" (list 100 1 5000 1 10 0 SF-SPINNER)
+ SF-ADJUSTMENT "Lighting (degrees)" (list 45 0 360 1 10 1 SF-SLIDER)
+ SF-TOGGLE "Shadow" TRUE
+ SF-COLOR "Background color" "white"
+ SF-COLOR "Sphere color" "red"
+ SF-BRUSH "Brush" '("2. Hardness 100" 100 44 0)
+ SF-STRING "Text" "Tiny-Fu rocks!"
+ SF-TEXT "Multi-line text" "Hello,\nWorld!"
+ SF-PATTERN "Pattern" "Maple Leaves"
+ SF-GRADIENT "Gradient" "Deep Sea"
+ SF-TOGGLE "Gradient reverse" FALSE
+ SF-FONT "Font" "Agate"
+ SF-ADJUSTMENT "Font size (pixels)" '(50 1 1000 1 10 0 1)
+ SF-PALETTE "Palette" "Default"
+ SF-FILENAME "Environment map"
+ (string-append gimp-data-directory
+ "/scripts/images/beavis.jpg")
+ SF-OPTION "Orientation" '("Horizontal"
+ "Vertical")
+ SF-ENUM "Interpolation" '("InterpolationType" "linear")
+ SF-DIRNAME "Output directory" "/var/tmp/"
+ SF-IMAGE "Image" -1
+ SF-LAYER "Layer" -1
+ SF-CHANNEL "Channel" -1
+ SF-DRAWABLE "Drawable" -1
+ SF-VECTORS "Vectors" -1
+)
+
+(script-fu-menu-register "script-fu-test-sphere-v3"
+ "<Image>/Filters/Development/Script-Fu/Test")
diff --git a/plug-ins/script-fu/scripts/test/always-fail/always-fail.scm
b/plug-ins/script-fu/scripts/test/always-fail/always-fail.scm
new file mode 100644
index 0000000000..04a523d9c2
--- /dev/null
+++ b/plug-ins/script-fu/scripts/test/always-fail/always-fail.scm
@@ -0,0 +1,30 @@
+#!/usr/bin/env gimp-script-fu-interpreter-3.0
+
+; A script that always fails
+;
+; Setup: copy this file w/ executable permission, and its parent dir to /plug-ins
+; Example: to ~/.gimp-2.99/plug-ins/always-fail/always-fail.scm
+
+; Expect "Test>Always fail" in the menus
+; Expect when chosen, message on GIMP message bar "Failing"
+; Expect a dialog in GIMP app that requires an OK
+
+(define (script-fu-always-fail)
+ (begin
+ (gimp-message "Failing")
+ ; since last expression, the result, and should mean error
+ #f
+ )
+)
+
+(script-fu-register "script-fu-always-fail"
+ "Always fail"
+ _"Expect error dialog in Gimp, or PDB execution error when called by another"
+ "lkk"
+ "lkk"
+ "2022"
+ "" ; requires no image
+ ; no arguments or dialog
+)
+
+(script-fu-menu-register "script-fu-always-fail" "<Image>/Test")
diff --git a/plug-ins/script-fu/scripts/test/call-always-fail/call-always-fail.scm
b/plug-ins/script-fu/scripts/test/call-always-fail/call-always-fail.scm
new file mode 100644
index 0000000000..a72fec67d5
--- /dev/null
+++ b/plug-ins/script-fu/scripts/test/call-always-fail/call-always-fail.scm
@@ -0,0 +1,29 @@
+#!/usr/bin/env gimp-script-fu-interpreter-3.0
+
+; A script that calls a script that always fails
+;
+; Setup: copy this file w/ executable permission, and its parent dir to /plug-ins
+; Example: to ~/.gimp-2.99/plug-ins/always-fail/always-fail.scm
+
+; Expect "Test>Call always fail" in the menus
+; Expect when chosen, message on GIMP message bar "Failing" (from script-fu-always-fail)
+; Expect a dialog in GIMP app that requires an OK
+
+(define (script-fu-call-always-fail)
+ ; call a script that always fails
+ (script-fu-always-fail)
+ ; we have not checked the result and declaring the error on our own.
+ ; since this is the last expression, the #f from the call should propogate.
+)
+
+(script-fu-register "script-fu-call-always-fail"
+ "Call always fail"
+ _"Expect error dialog in Gimp, having concatenated error messages"
+ "lkk"
+ "lkk"
+ "2022"
+ "" ; requires no image
+ ; no arguments or dialog
+)
+
+(script-fu-menu-register "script-fu-call-always-fail" "<Image>/Test")
diff --git a/po-script-fu/POTFILES.skip b/po-script-fu/POTFILES.skip
index 7c7f9c1ba3..b61a44621f 100644
--- a/po-script-fu/POTFILES.skip
+++ b/po-script-fu/POTFILES.skip
@@ -54,6 +54,7 @@ plug-ins/script-fu/scripts/test/test1/test3.scm
plug-ins/script-fu/scripts/test/test4/test4.scm
plug-ins/script-fu/scripts/test/test7/test7.scm
plug-ins/script-fu/scripts/test/test8/test8.scm
+plug-ins/script-fu/scripts/clothify-v3.scm
plug-ins/selection-to-path
plug-ins/twain
plug-ins/ui
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]