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.
This commit is contained in:
lloyd konneker 2022-07-15 13:50:36 -04:00 committed by Lloyd Konneker
parent 25f8b332e9
commit 12c0c18036
29 changed files with 2031 additions and 635 deletions

View File

@ -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)

View File

@ -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.

View File

@ -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);
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);
}
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);
ts_define_procedure (sc, "script-fu-quit", script_fu_quit_call);
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);
/* 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);
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)

View File

@ -21,6 +21,7 @@
#include "tinyscheme/scheme.h"
typedef void (*TsCallbackFunc) (void);
typedef pointer (*TsWrapperFunc) (scheme*, pointer);
void tinyscheme_init (GList *path,

View File

@ -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.
*
* The name means nothing to human readers of the spec.
* The nick is descriptive.
* !!! 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 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 name means nothing to human readers of the spec.
* Instead, the nick is descriptive for human readers.
*
* 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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,6 @@
#include <windows.h>
#endif
#include "tinyscheme/scheme-private.h"
#include "scheme-wrapper.h"
#include "script-fu-types.h"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
/* Allow scripts with no menus */
if (strncmp (script->menu_label, "<None>", 6) != 0)
menu_label = script->menu_label;
procedure = gimp_procedure_new (plug_in, script->name,
plug_in_type,
run_func, script, NULL);
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);
gimp_procedure_add_argument (procedure,
g_param_spec_enum ("run-mode",
"Run mode",
"The run mode",
GIMP_TYPE_RUN_MODE,
GIMP_RUN_INTERACTIVE,
G_PARAM_READWRITE));
script_fu_arg_reset_name_generator ();
for (gint i = 0; i < script->n_args; i++)
if (script->proc_class == GIMP_TYPE_IMAGE_PROCEDURE)
{
GParamSpec *pspec = NULL;
const gchar *name = NULL;
const gchar *nick = NULL;
g_debug ("script_fu_script_create_PDB_procedure: %s, plugin type %i, image_proc",
script->name, plug_in_type);
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);
procedure = gimp_image_procedure_new (
plug_in, script->name,
plug_in_type,
(GimpRunImageFunc) script_fu_run_image_procedure,
script, /* user_data, pointer in extension-script-fu process */
NULL);
script_fu_script_set_proc_metadata (procedure, script);
/* Script author does not declare image, drawable in script-fu-register-filter,
* and we don't add to formal args in PDB.
* The convenience class GimpImageProcedure already has formal args:
* run_mode, image, n_drawables, drawables.
* "0" means not skip any arguments declared in the script.
*/
script_fu_script_set_proc_args (procedure, script, 0);
script_fu_script_set_drawable_sensitivity (procedure, script);
}
else
{
g_assert (script->proc_class == GIMP_TYPE_PROCEDURE);
g_debug ("script_fu_script_create_PDB_procedure: %s, plugin type %i, ordinary proc",
script->name, plug_in_type);
procedure = gimp_procedure_new (plug_in, script->name,
plug_in_type,
script_fu_run_procedure,
script, NULL);
script_fu_script_set_proc_metadata (procedure, script);
gimp_procedure_add_argument (procedure,
g_param_spec_enum ("run-mode",
"Run mode",
"The run mode",
GIMP_TYPE_RUN_MODE,
GIMP_RUN_INTERACTIVE,
G_PARAM_READWRITE));
script_fu_script_set_proc_args (procedure, script, 0);
/* !!! Author did not declare drawable arity, it was inferred. */
script_fu_script_set_drawable_sensitivity (procedure, script);
}
return procedure;
}
@ -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");
}
}

View File

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

View File

@ -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;
}
return foreign_error (sc, "script-fu-register: Not enough arguments", 0);
/* Find the script name */
name = sc->vptr->string_value (sc->vptr->pair_car (a));
a = sc->vptr->pair_cdr (a);
/* pass handle to pointer into script (on the stack) */
script = script_fu_script_new_from_metadata_args (sc, &a);
/* Find the script menu_label */
menu_label = sc->vptr->string_value (sc->vptr->pair_car (a));
a = sc->vptr->pair_cdr (a);
/* 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);
/* 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));
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);
}
}
args_error = script_fu_script_create_formal_args (sc, &a, script);
if (args_error != sc->NIL)
return args_error;
/* fill all values from defaults */
script_fu_script_reset (script, TRUE);
if (script->menu_label[0] == '<')
{
gchar *mapped = script_fu_menu_map (script->menu_label);
/* 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);
if (mapped)
{
g_free (script->menu_label);
script->menu_label = mapped;
}
}
script->proc_class = GIMP_TYPE_PROCEDURE;
{
GList *list = g_tree_lookup (script_tree, script->menu_label);
script_fu_try_map_menu (script);
script_fu_append_script_to_tree (script);
return sc->NIL;
}
g_tree_insert (script_tree, (gpointer) script->menu_label,
g_list_append (list, script));
}
/* 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. */
/* 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);
/* pass handle i.e. "&a" ("a" of type "pointer" is on the stack) */
script = script_fu_script_new_from_metadata_args (sc, &a);
/* 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);
args_error = script_fu_script_parse_drawable_arity_arg (sc, &a, script);
if (args_error != sc->NIL)
return args_error;
args_error = script_fu_script_create_formal_args (sc, &a, script);
if (args_error != sc->NIL)
return args_error;
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));
}

View File

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

View File

@ -101,6 +101,8 @@ typedef struct
gint n_args;
SFArg *args;
SFDrawableArity drawable_arity;
GType proc_class; /* GimpProcedure or GimpImageProcedure. */
} SFScript;
typedef struct

View File

@ -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)

View File

@ -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")

View File

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

View File

@ -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))
)

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

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