Gimp/plug-ins/script-fu/script-fu-script.c
Michael Natterer d62e75a41f Move GimpParamSpecString from libgimp back to app
It's just too weird to be public. Remove its properties from the wire
protocol and from pluginrc. Instead, have all GParamSpecs' flags on
the wire and in pluginrc, so we can use stuff like
GIMP_PARAM_NO_VALIDATE.

Port the remaining few places to GIMP_PROC_ARG_STRING().

I'm sure something is broken now wrt UTF-8 validation,
will add tighter checks in the next commit.
2019-08-19 12:54:52 +02:00

854 lines
25 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "tinyscheme/scheme-private.h"
#include "script-fu-types.h"
#include "script-fu-script.h"
#include "script-fu-scripts.h"
#include "script-fu-utils.h"
#include "script-fu-intl.h"
/*
* Local Functions
*/
static gboolean script_fu_script_param_init (SFScript *script,
const GimpValueArray *args,
SFArgType type,
gint n);
/*
* Function definitions
*/
SFScript *
script_fu_script_new (const gchar *name,
const gchar *menu_label,
const gchar *blurb,
const gchar *author,
const gchar *copyright,
const gchar *date,
const gchar *image_types,
gint n_args)
{
SFScript *script;
script = g_slice_new0 (SFScript);
script->name = g_strdup (name);
script->menu_label = g_strdup (menu_label);
script->blurb = g_strdup (blurb);
script->author = g_strdup (author);
script->copyright = g_strdup (copyright);
script->date = g_strdup (date);
script->image_types = g_strdup (image_types);
script->n_args = n_args;
script->args = g_new0 (SFArg, script->n_args);
return script;
}
void
script_fu_script_free (SFScript *script)
{
gint i;
g_return_if_fail (script != NULL);
g_free (script->name);
g_free (script->blurb);
g_free (script->menu_label);
g_free (script->author);
g_free (script->copyright);
g_free (script->date);
g_free (script->image_types);
for (i = 0; i < script->n_args; i++)
{
SFArg *arg = &script->args[i];
g_free (arg->label);
switch (arg->type)
{
case SF_IMAGE:
case SF_DRAWABLE:
case SF_LAYER:
case SF_CHANNEL:
case SF_VECTORS:
case SF_DISPLAY:
case SF_COLOR:
case SF_TOGGLE:
break;
case SF_VALUE:
case SF_STRING:
case SF_TEXT:
g_free (arg->default_value.sfa_value);
g_free (arg->value.sfa_value);
break;
case SF_ADJUSTMENT:
break;
case SF_FILENAME:
case SF_DIRNAME:
g_free (arg->default_value.sfa_file.filename);
g_free (arg->value.sfa_file.filename);
break;
case SF_FONT:
g_free (arg->default_value.sfa_font);
g_free (arg->value.sfa_font);
break;
case SF_PALETTE:
g_free (arg->default_value.sfa_palette);
g_free (arg->value.sfa_palette);
break;
case SF_PATTERN:
g_free (arg->default_value.sfa_pattern);
g_free (arg->value.sfa_pattern);
break;
case SF_GRADIENT:
g_free (arg->default_value.sfa_gradient);
g_free (arg->value.sfa_gradient);
break;
case SF_BRUSH:
g_free (arg->default_value.sfa_brush.name);
g_free (arg->value.sfa_brush.name);
break;
case SF_OPTION:
g_slist_free_full (arg->default_value.sfa_option.list,
(GDestroyNotify) g_free);
break;
case SF_ENUM:
g_free (arg->default_value.sfa_enum.type_name);
break;
}
}
g_free (script->args);
g_slice_free (SFScript, script);
}
void
script_fu_script_install_proc (GimpPlugIn *plug_in,
SFScript *script,
GimpRunFunc run_func)
{
GimpProcedure *procedure;
const gchar *menu_label = NULL;
gint i;
g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
g_return_if_fail (script != NULL);
g_return_if_fail (run_func != NULL);
/* Allow scripts with no menus */
if (strncmp (script->menu_label, "<None>", 6) != 0)
menu_label = script->menu_label;
procedure = gimp_procedure_new (plug_in, script->name,
GIMP_TEMPORARY,
run_func, script, NULL);
gimp_procedure_set_image_types (procedure, script->image_types);
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));
for (i = 0; i < script->n_args; i++)
{
GParamSpec *pspec = NULL;
switch (script->args[i].type)
{
case SF_IMAGE:
pspec = gimp_param_spec_image_id ("image",
"Image",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_DRAWABLE:
pspec = gimp_param_spec_drawable_id ("drawable",
"Drawable",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_LAYER:
pspec = gimp_param_spec_layer_id ("layer",
"Layer",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_CHANNEL:
pspec = gimp_param_spec_channel_id ("channel",
"Channel",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_VECTORS:
pspec = gimp_param_spec_vectors_id ("vectors",
"Vectors",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_DISPLAY:
pspec = gimp_param_spec_display_id ("display",
"Display",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_COLOR:
pspec = gimp_param_spec_rgb ("color",
"Color",
script->args[i].label,
TRUE, NULL,
G_PARAM_READWRITE);
break;
case SF_TOGGLE:
pspec = g_param_spec_boolean ("toggle",
"Toggle",
script->args[i].label,
FALSE,
G_PARAM_READWRITE);
break;
case SF_VALUE:
pspec = g_param_spec_string ("value",
"Value",
script->args[i].label,
NULL,
G_PARAM_READWRITE);
break;
case SF_STRING:
case SF_TEXT:
pspec = g_param_spec_string ("string",
"String",
script->args[i].label,
NULL,
G_PARAM_READWRITE);
break;
case SF_ADJUSTMENT:
pspec = g_param_spec_double ("value",
"Value",
script->args[i].label,
-G_MAXDOUBLE, G_MAXDOUBLE, 0,
G_PARAM_READWRITE);
break;
case SF_FILENAME:
pspec = g_param_spec_string ("filename",
"Filename",
script->args[i].label,
NULL,
G_PARAM_READWRITE |
GIMP_PARAM_NO_VALIDATE);
break;
case SF_DIRNAME:
pspec = g_param_spec_string ("dirname",
"Dirname",
script->args[i].label,
NULL,
G_PARAM_READWRITE |
GIMP_PARAM_NO_VALIDATE);
break;
case SF_FONT:
pspec = g_param_spec_string ("Font",
"font",
script->args[i].label,
NULL,
G_PARAM_READWRITE);
break;
case SF_PALETTE:
pspec = g_param_spec_string ("palette",
"Palette",
script->args[i].label,
NULL,
G_PARAM_READWRITE);
break;
case SF_PATTERN:
pspec = g_param_spec_string ("pattern",
"Pattern",
script->args[i].label,
NULL,
G_PARAM_READWRITE);
break;
case SF_BRUSH:
pspec = g_param_spec_string ("brush",
"Brush",
script->args[i].label,
NULL,
G_PARAM_READWRITE);
break;
case SF_GRADIENT:
pspec = g_param_spec_string ("gradient",
"Gradient",
script->args[i].label,
NULL,
G_PARAM_READWRITE);
break;
case SF_OPTION:
pspec = g_param_spec_int ("option",
"Option",
script->args[i].label,
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
break;
case SF_ENUM:
pspec = g_param_spec_int ("enum",
"Enum",
script->args[i].label,
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
break;
}
gimp_procedure_add_argument (procedure, pspec);
}
gimp_plug_in_add_temp_procedure (plug_in, procedure);
g_object_unref (procedure);
}
void
script_fu_script_uninstall_proc (GimpPlugIn *plug_in,
SFScript *script)
{
g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
g_return_if_fail (script != NULL);
gimp_plug_in_remove_temp_procedure (plug_in, script->name);
}
gchar *
script_fu_script_get_title (SFScript *script)
{
gchar *title;
gchar *tmp;
g_return_val_if_fail (script != NULL, NULL);
/* strip mnemonics from the menupath */
title = gimp_strip_uline (script->menu_label);
/* if this looks like a full menu path, use only the last part */
if (title[0] == '<' && (tmp = strrchr (title, '/')) && tmp[1])
{
tmp = g_strdup (tmp + 1);
g_free (title);
title = tmp;
}
/* cut off ellipsis */
tmp = (strstr (title, "..."));
if (! tmp)
/* U+2026 HORIZONTAL ELLIPSIS */
tmp = strstr (title, "\342\200\246");
if (tmp && tmp == (title + strlen (title) - 3))
*tmp = '\0';
return title;
}
void
script_fu_script_reset (SFScript *script,
gboolean reset_ids)
{
gint i;
g_return_if_fail (script != NULL);
for (i = 0; i < script->n_args; i++)
{
SFArgValue *value = &script->args[i].value;
SFArgValue *default_value = &script->args[i].default_value;
switch (script->args[i].type)
{
case SF_IMAGE:
case SF_DRAWABLE:
case SF_LAYER:
case SF_CHANNEL:
case SF_VECTORS:
case SF_DISPLAY:
if (reset_ids)
value->sfa_image = default_value->sfa_image;
break;
case SF_COLOR:
value->sfa_color = default_value->sfa_color;
break;
case SF_TOGGLE:
value->sfa_toggle = default_value->sfa_toggle;
break;
case SF_VALUE:
case SF_STRING:
case SF_TEXT:
g_free (value->sfa_value);
value->sfa_value = g_strdup (default_value->sfa_value);
break;
case SF_ADJUSTMENT:
value->sfa_adjustment.value = default_value->sfa_adjustment.value;
break;
case SF_FILENAME:
case SF_DIRNAME:
g_free (value->sfa_file.filename);
value->sfa_file.filename = g_strdup (default_value->sfa_file.filename);
break;
case SF_FONT:
g_free (value->sfa_font);
value->sfa_font = g_strdup (default_value->sfa_font);
break;
case SF_PALETTE:
g_free (value->sfa_palette);
value->sfa_palette = g_strdup (default_value->sfa_palette);
break;
case SF_PATTERN:
g_free (value->sfa_pattern);
value->sfa_pattern = g_strdup (default_value->sfa_pattern);
break;
case SF_GRADIENT:
g_free (value->sfa_gradient);
value->sfa_gradient = g_strdup (default_value->sfa_gradient);
break;
case SF_BRUSH:
g_free (value->sfa_brush.name);
value->sfa_brush.name = g_strdup (default_value->sfa_brush.name);
value->sfa_brush.opacity = default_value->sfa_brush.opacity;
value->sfa_brush.spacing = default_value->sfa_brush.spacing;
value->sfa_brush.paint_mode = default_value->sfa_brush.paint_mode;
break;
case SF_OPTION:
value->sfa_option.history = default_value->sfa_option.history;
break;
case SF_ENUM:
value->sfa_enum.history = default_value->sfa_enum.history;
break;
}
}
}
gint
script_fu_script_collect_standard_args (SFScript *script,
const GimpValueArray *args)
{
gint params_consumed = 0;
g_return_val_if_fail (script != NULL, 0);
/* the first parameter may be a DISPLAY id */
if (script_fu_script_param_init (script,
args, SF_DISPLAY,
params_consumed))
{
params_consumed++;
}
/* an IMAGE id may come first or after the DISPLAY id */
if (script_fu_script_param_init (script,
args, SF_IMAGE,
params_consumed))
{
params_consumed++;
/* and may be followed by a DRAWABLE, LAYER, CHANNEL or
* VECTORS id
*/
if (script_fu_script_param_init (script,
args, SF_DRAWABLE,
params_consumed) ||
script_fu_script_param_init (script,
args, SF_LAYER,
params_consumed) ||
script_fu_script_param_init (script,
args, SF_CHANNEL,
params_consumed) ||
script_fu_script_param_init (script,
args, SF_VECTORS,
params_consumed))
{
params_consumed++;
}
}
return params_consumed;
}
gchar *
script_fu_script_get_command (SFScript *script)
{
GString *s;
gint i;
g_return_val_if_fail (script != NULL, NULL);
s = g_string_new ("(");
g_string_append (s, script->name);
for (i = 0; i < script->n_args; i++)
{
SFArgValue *arg_value = &script->args[i].value;
g_string_append_c (s, ' ');
switch (script->args[i].type)
{
case SF_IMAGE:
case SF_DRAWABLE:
case SF_LAYER:
case SF_CHANNEL:
case SF_VECTORS:
case SF_DISPLAY:
g_string_append_printf (s, "%d", arg_value->sfa_image);
break;
case SF_COLOR:
{
guchar r, g, b;
gimp_rgb_get_uchar (&arg_value->sfa_color, &r, &g, &b);
g_string_append_printf (s, "'(%d %d %d)",
(gint) r, (gint) g, (gint) b);
}
break;
case SF_TOGGLE:
g_string_append (s, arg_value->sfa_toggle ? "TRUE" : "FALSE");
break;
case SF_VALUE:
g_string_append (s, arg_value->sfa_value);
break;
case SF_STRING:
case SF_TEXT:
{
gchar *tmp;
tmp = script_fu_strescape (arg_value->sfa_value);
g_string_append_printf (s, "\"%s\"", tmp);
g_free (tmp);
}
break;
case SF_ADJUSTMENT:
{
gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (buffer, sizeof (buffer),
arg_value->sfa_adjustment.value);
g_string_append (s, buffer);
}
break;
case SF_FILENAME:
case SF_DIRNAME:
{
gchar *tmp;
tmp = script_fu_strescape (arg_value->sfa_file.filename);
g_string_append_printf (s, "\"%s\"", tmp);
g_free (tmp);
}
break;
case SF_FONT:
g_string_append_printf (s, "\"%s\"", arg_value->sfa_font);
break;
case SF_PALETTE:
g_string_append_printf (s, "\"%s\"", arg_value->sfa_palette);
break;
case SF_PATTERN:
g_string_append_printf (s, "\"%s\"", arg_value->sfa_pattern);
break;
case SF_GRADIENT:
g_string_append_printf (s, "\"%s\"", arg_value->sfa_gradient);
break;
case SF_BRUSH:
{
gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (buffer, sizeof (buffer),
arg_value->sfa_brush.opacity);
g_string_append_printf (s, "'(\"%s\" %s %d %d)",
arg_value->sfa_brush.name,
buffer,
arg_value->sfa_brush.spacing,
arg_value->sfa_brush.paint_mode);
}
break;
case SF_OPTION:
g_string_append_printf (s, "%d", arg_value->sfa_option.history);
break;
case SF_ENUM:
g_string_append_printf (s, "%d", arg_value->sfa_enum.history);
break;
}
}
g_string_append_c (s, ')');
return g_string_free (s, FALSE);
}
gchar *
script_fu_script_get_command_from_params (SFScript *script,
const GimpValueArray *args)
{
GString *s;
gint i;
g_return_val_if_fail (script != NULL, NULL);
s = g_string_new ("(");
g_string_append (s, script->name);
for (i = 0; i < script->n_args; i++)
{
GValue *value = gimp_value_array_index (args, i + 1);
g_string_append_c (s, ' ');
switch (script->args[i].type)
{
case SF_IMAGE:
case SF_DRAWABLE:
case SF_LAYER:
case SF_CHANNEL:
case SF_VECTORS:
case SF_DISPLAY:
g_string_append_printf (s, "%d", g_value_get_int (value));
break;
case SF_COLOR:
{
GimpRGB color;
guchar r, g, b;
gimp_value_get_rgb (value, &color);
gimp_rgb_get_uchar (&color, &r, &g, &b);
g_string_append_printf (s, "'(%d %d %d)",
(gint) r, (gint) g, (gint) b);
}
break;
case SF_TOGGLE:
g_string_append_printf (s, (g_value_get_boolean (value) ?
"TRUE" : "FALSE"));
break;
case SF_VALUE:
g_string_append (s, g_value_get_string (value));
break;
case SF_STRING:
case SF_TEXT:
case SF_FILENAME:
case SF_DIRNAME:
{
gchar *tmp;
tmp = script_fu_strescape (g_value_get_string (value));
g_string_append_printf (s, "\"%s\"", tmp);
g_free (tmp);
}
break;
case SF_ADJUSTMENT:
{
gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (buffer, sizeof (buffer), g_value_get_double (value));
g_string_append (s, buffer);
}
break;
case SF_FONT:
case SF_PALETTE:
case SF_PATTERN:
case SF_GRADIENT:
case SF_BRUSH:
g_string_append_printf (s, "\"%s\"", g_value_get_string (value));
break;
case SF_OPTION:
case SF_ENUM:
g_string_append_printf (s, "%d", g_value_get_int (value));
break;
}
}
g_string_append_c (s, ')');
return g_string_free (s, FALSE);
}
/*
* Local Functions
*/
static gboolean
script_fu_script_param_init (SFScript *script,
const GimpValueArray *args,
SFArgType type,
gint n)
{
SFArg *arg = &script->args[n];
if (script->n_args > n &&
arg->type == type &&
gimp_value_array_length (args) > n + 1)
{
GValue *value = gimp_value_array_index (args, n + 1);
switch (type)
{
case SF_IMAGE:
if (GIMP_VALUE_HOLDS_IMAGE_ID (value))
{
arg->value.sfa_image = gimp_value_get_image_id (value);
return TRUE;
}
break;
case SF_DRAWABLE:
if (GIMP_VALUE_HOLDS_DRAWABLE_ID (value))
{
arg->value.sfa_drawable = gimp_value_get_drawable_id (value);
return TRUE;
}
break;
case SF_LAYER:
if (GIMP_VALUE_HOLDS_LAYER_ID (value))
{
arg->value.sfa_layer = gimp_value_get_layer_id (value);
return TRUE;
}
break;
case SF_CHANNEL:
if (GIMP_VALUE_HOLDS_CHANNEL_ID (value))
{
arg->value.sfa_channel = gimp_value_get_channel_id (value);
return TRUE;
}
break;
case SF_VECTORS:
if (GIMP_VALUE_HOLDS_VECTORS_ID (value))
{
arg->value.sfa_vectors = gimp_value_get_vectors_id (value);
return TRUE;
}
break;
case SF_DISPLAY:
if (GIMP_VALUE_HOLDS_DISPLAY_ID (value))
{
arg->value.sfa_display = gimp_value_get_display_id (value);
return TRUE;
}
break;
default:
break;
}
}
return FALSE;
}