gimp/plug-ins/script-fu/script-fu-script.c
Michael Natterer 392f00baf5 app, libgimp: get rid of all ID GTypes and ID param specs
Turn all ID param specs into object param specs (e.g. GimpParamImageID
becomes GimpParamImage) and convert between IDs and objects in
gimpgpparams.c directly above the the wire protocol, so all of app/,
libgimp/ and plug-ins/ can deal directly with objects down to the
lowest level and not care about IDs.

Use the actual object param specs for procedure arguments and return
values again instead of a plain g_param_spec_object() and bring back
the none_ok parameter.

This implies changing the PDB type checking functions to work on pure
integers instead of IDs (one can't check whether object creation is
possible if performing that check requires the object to already
exist).

For example gimp_foo_is_valid() becomes gimp_foo_id_is_valid() and is
not involved in automatic object creation magic at the protocol
level. Added wrappers which still say gimp_foo_is_valid() and take the
respective objects.

Adapted all code, and it all becomes nicer and less convoluted, even
the generated PDB wrappers in app/ and libgimp/.
2019-08-29 11:39:34 +02:00

874 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 ("image",
"Image",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_DRAWABLE:
pspec = gimp_param_spec_drawable ("drawable",
"Drawable",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_LAYER:
pspec = gimp_param_spec_layer ("layer",
"Layer",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_CHANNEL:
pspec = gimp_param_spec_channel ("channel",
"Channel",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_VECTORS:
pspec = gimp_param_spec_vectors ("vectors",
"Vectors",
script->args[i].label,
TRUE,
G_PARAM_READWRITE);
break;
case SF_DISPLAY:
pspec = gimp_param_spec_display ("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:
{
GObject *object = g_value_get_object (value);
gint id = -1;
if (object)
g_object_get (object, "id", &id, NULL);
g_string_append_printf (s, "%d", id);
}
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 (value))
{
GimpImage *image = g_value_get_object (value);
arg->value.sfa_image = gimp_image_get_id (image);
return TRUE;
}
break;
case SF_DRAWABLE:
if (GIMP_VALUE_HOLDS_DRAWABLE (value))
{
GimpItem *item = g_value_get_object (value);
arg->value.sfa_drawable = gimp_item_get_id (item);
return TRUE;
}
break;
case SF_LAYER:
if (GIMP_VALUE_HOLDS_LAYER (value))
{
GimpItem *item = g_value_get_object (value);
arg->value.sfa_layer = gimp_item_get_id (item);
return TRUE;
}
break;
case SF_CHANNEL:
if (GIMP_VALUE_HOLDS_CHANNEL (value))
{
GimpItem *item = g_value_get_object (value);
arg->value.sfa_channel = gimp_item_get_id (item);
return TRUE;
}
break;
case SF_VECTORS:
if (GIMP_VALUE_HOLDS_VECTORS (value))
{
GimpItem *item = g_value_get_object (value);
arg->value.sfa_vectors = gimp_item_get_id (item);
return TRUE;
}
break;
case SF_DISPLAY:
if (GIMP_VALUE_HOLDS_DISPLAY (value))
{
GimpDisplay *display = g_value_get_object (value);
arg->value.sfa_display = gimp_display_get_id (display);
return TRUE;
}
break;
default:
break;
}
}
return FALSE;
}