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); + 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) 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. * - * 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: 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 . + */ + +#include "config.h" + +#include + +#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: " 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 . + */ + + #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 . + */ + +#include "config.h" + +#include + +#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 . + */ + + #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 #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 . + */ + +#include "config.h" + +#include + +#ifdef G_OS_WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include + +#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 . + */ + +#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 . + */ + +#include "config.h" + +#include +#include + +#include +#include + +#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 . + */ + +#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); - - /* Allow scripts with no menus */ - if (strncmp (script->menu_label, "", 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, "", 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 - #include #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)); +} 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 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 " + "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" + "/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" + "/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" "/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" "/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