… with gimp_pdb_execute_procedure_by_name(). While using gimp_pdb_execute_procedure_by_name_args(), you would create the GValue-s manually, gimp_pdb_execute_procedure_by_name() is supposed to create them for us from common C/glib types. C arrays in particular don't have length information and we can't just G_VALUE_COLLECT() them (it ends up in broken array with length 0). By convention, the array length will be the previous parameter. So let's use this information and create the GValue with gimp_value_set_*_array() instead.
591 lines
18 KiB
C
591 lines
18 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 <stdarg.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <gobject/gvaluecollector.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
#include <gegl.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
#include "pdb-types.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimpitem.h"
|
|
#include "core/gimp-memsize.h"
|
|
#include "core/gimpcontext.h"
|
|
#include "core/gimpmarshal.h"
|
|
#include "core/gimpprogress.h"
|
|
|
|
#include "gimppdb.h"
|
|
#include "gimppdberror.h"
|
|
#include "gimpprocedure.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
enum
|
|
{
|
|
REGISTER_PROCEDURE,
|
|
UNREGISTER_PROCEDURE,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
|
|
static void gimp_pdb_finalize (GObject *object);
|
|
static gint64 gimp_pdb_get_memsize (GimpObject *object,
|
|
gint64 *gui_size);
|
|
static void gimp_pdb_real_register_procedure (GimpPDB *pdb,
|
|
GimpProcedure *procedure);
|
|
static void gimp_pdb_real_unregister_procedure (GimpPDB *pdb,
|
|
GimpProcedure *procedure);
|
|
static void gimp_pdb_entry_free (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data);
|
|
static gint64 gimp_pdb_entry_get_memsize (GList *procedures,
|
|
gint64 *gui_size);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpPDB, gimp_pdb, GIMP_TYPE_OBJECT)
|
|
|
|
#define parent_class gimp_pdb_parent_class
|
|
|
|
static guint gimp_pdb_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
static void
|
|
gimp_pdb_class_init (GimpPDBClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
|
|
|
|
gimp_pdb_signals[REGISTER_PROCEDURE] =
|
|
g_signal_new ("register-procedure",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpPDBClass, register_procedure),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1,
|
|
GIMP_TYPE_PROCEDURE);
|
|
|
|
gimp_pdb_signals[UNREGISTER_PROCEDURE] =
|
|
g_signal_new ("unregister-procedure",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpPDBClass, unregister_procedure),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1,
|
|
GIMP_TYPE_PROCEDURE);
|
|
|
|
object_class->finalize = gimp_pdb_finalize;
|
|
|
|
gimp_object_class->get_memsize = gimp_pdb_get_memsize;
|
|
|
|
klass->register_procedure = gimp_pdb_real_register_procedure;
|
|
klass->unregister_procedure = gimp_pdb_real_unregister_procedure;
|
|
}
|
|
|
|
static void
|
|
gimp_pdb_init (GimpPDB *pdb)
|
|
{
|
|
pdb->procedures = g_hash_table_new (g_str_hash, g_str_equal);
|
|
pdb->compat_proc_names = g_hash_table_new (g_str_hash, g_str_equal);
|
|
}
|
|
|
|
static void
|
|
gimp_pdb_finalize (GObject *object)
|
|
{
|
|
GimpPDB *pdb = GIMP_PDB (object);
|
|
|
|
if (pdb->procedures)
|
|
{
|
|
g_hash_table_foreach (pdb->procedures, gimp_pdb_entry_free, NULL);
|
|
g_hash_table_destroy (pdb->procedures);
|
|
pdb->procedures = NULL;
|
|
}
|
|
|
|
if (pdb->compat_proc_names)
|
|
{
|
|
g_hash_table_destroy (pdb->compat_proc_names);
|
|
pdb->compat_proc_names = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gint64
|
|
gimp_pdb_get_memsize (GimpObject *object,
|
|
gint64 *gui_size)
|
|
{
|
|
GimpPDB *pdb = GIMP_PDB (object);
|
|
gint64 memsize = 0;
|
|
|
|
memsize += gimp_g_hash_table_get_memsize_foreach (pdb->procedures,
|
|
(GimpMemsizeFunc)
|
|
gimp_pdb_entry_get_memsize,
|
|
gui_size);
|
|
memsize += gimp_g_hash_table_get_memsize (pdb->compat_proc_names, 0);
|
|
|
|
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
|
|
gui_size);
|
|
}
|
|
|
|
static void
|
|
gimp_pdb_real_register_procedure (GimpPDB *pdb,
|
|
GimpProcedure *procedure)
|
|
{
|
|
const gchar *name;
|
|
GList *list;
|
|
|
|
name = gimp_object_get_name (procedure);
|
|
|
|
list = g_hash_table_lookup (pdb->procedures, name);
|
|
|
|
g_hash_table_replace (pdb->procedures, (gpointer) name,
|
|
g_list_prepend (list, g_object_ref (procedure)));
|
|
}
|
|
|
|
static void
|
|
gimp_pdb_real_unregister_procedure (GimpPDB *pdb,
|
|
GimpProcedure *procedure)
|
|
{
|
|
const gchar *name;
|
|
GList *list;
|
|
|
|
name = gimp_object_get_name (procedure);
|
|
|
|
list = g_hash_table_lookup (pdb->procedures, name);
|
|
|
|
if (list)
|
|
{
|
|
list = g_list_remove (list, procedure);
|
|
|
|
if (list)
|
|
{
|
|
name = gimp_object_get_name (list->data);
|
|
g_hash_table_replace (pdb->procedures, (gpointer) name, list);
|
|
}
|
|
else
|
|
{
|
|
g_hash_table_remove (pdb->procedures, name);
|
|
}
|
|
|
|
g_object_unref (procedure);
|
|
}
|
|
}
|
|
|
|
|
|
/* public functions */
|
|
|
|
GimpPDB *
|
|
gimp_pdb_new (Gimp *gimp)
|
|
{
|
|
GimpPDB *pdb;
|
|
|
|
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
|
|
|
|
pdb = g_object_new (GIMP_TYPE_PDB,
|
|
"name", "pdb",
|
|
NULL);
|
|
|
|
pdb->gimp = gimp;
|
|
|
|
return pdb;
|
|
}
|
|
|
|
void
|
|
gimp_pdb_register_procedure (GimpPDB *pdb,
|
|
GimpProcedure *procedure)
|
|
{
|
|
g_return_if_fail (GIMP_IS_PDB (pdb));
|
|
g_return_if_fail (GIMP_IS_PROCEDURE (procedure));
|
|
|
|
if (! procedure->deprecated ||
|
|
pdb->gimp->pdb_compat_mode != GIMP_PDB_COMPAT_OFF)
|
|
{
|
|
g_signal_emit (pdb, gimp_pdb_signals[REGISTER_PROCEDURE], 0,
|
|
procedure);
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_pdb_unregister_procedure (GimpPDB *pdb,
|
|
GimpProcedure *procedure)
|
|
{
|
|
g_return_if_fail (GIMP_IS_PDB (pdb));
|
|
g_return_if_fail (GIMP_IS_PROCEDURE (procedure));
|
|
|
|
g_signal_emit (pdb, gimp_pdb_signals[UNREGISTER_PROCEDURE], 0,
|
|
procedure);
|
|
}
|
|
|
|
GimpProcedure *
|
|
gimp_pdb_lookup_procedure (GimpPDB *pdb,
|
|
const gchar *name)
|
|
{
|
|
GList *list;
|
|
|
|
g_return_val_if_fail (GIMP_IS_PDB (pdb), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
list = g_hash_table_lookup (pdb->procedures, name);
|
|
|
|
if (list)
|
|
return list->data;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
gimp_pdb_register_compat_proc_name (GimpPDB *pdb,
|
|
const gchar *old_name,
|
|
const gchar *new_name)
|
|
{
|
|
g_return_if_fail (GIMP_IS_PDB (pdb));
|
|
g_return_if_fail (old_name != NULL);
|
|
g_return_if_fail (new_name != NULL);
|
|
|
|
g_hash_table_insert (pdb->compat_proc_names,
|
|
(gpointer) old_name,
|
|
(gpointer) new_name);
|
|
}
|
|
|
|
const gchar *
|
|
gimp_pdb_lookup_compat_proc_name (GimpPDB *pdb,
|
|
const gchar *old_name)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_PDB (pdb), NULL);
|
|
g_return_val_if_fail (old_name != NULL, NULL);
|
|
|
|
return g_hash_table_lookup (pdb->compat_proc_names, old_name);
|
|
}
|
|
|
|
GimpValueArray *
|
|
gimp_pdb_execute_procedure_by_name_args (GimpPDB *pdb,
|
|
GimpContext *context,
|
|
GimpProgress *progress,
|
|
GError **error,
|
|
const gchar *name,
|
|
GimpValueArray *args)
|
|
{
|
|
GimpValueArray *return_vals = NULL;
|
|
GList *list;
|
|
|
|
g_return_val_if_fail (GIMP_IS_PDB (pdb), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
list = g_hash_table_lookup (pdb->procedures, name);
|
|
|
|
if (list == NULL)
|
|
{
|
|
GError *pdb_error = g_error_new (GIMP_PDB_ERROR,
|
|
GIMP_PDB_ERROR_PROCEDURE_NOT_FOUND,
|
|
_("Procedure '%s' not found"), name);
|
|
|
|
return_vals = gimp_procedure_get_return_values (NULL, FALSE, pdb_error);
|
|
g_propagate_error (error, pdb_error);
|
|
|
|
return return_vals;
|
|
}
|
|
|
|
g_return_val_if_fail (args != NULL, NULL);
|
|
|
|
for (; list; list = g_list_next (list))
|
|
{
|
|
GimpProcedure *procedure = list->data;
|
|
|
|
g_return_val_if_fail (GIMP_IS_PROCEDURE (procedure), NULL);
|
|
|
|
return_vals = gimp_procedure_execute (procedure,
|
|
pdb->gimp, context, progress,
|
|
args, error);
|
|
|
|
if (g_value_get_enum (gimp_value_array_index (return_vals, 0)) ==
|
|
GIMP_PDB_PASS_THROUGH)
|
|
{
|
|
/* If the return value is GIMP_PDB_PASS_THROUGH and there is
|
|
* a next procedure in the list, destroy the return values
|
|
* and run the next procedure.
|
|
*/
|
|
if (g_list_next (list))
|
|
{
|
|
gimp_value_array_unref (return_vals);
|
|
g_clear_error (error);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No GIMP_PDB_PASS_THROUGH, break out of the list of
|
|
* procedures and return the current return values.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
return return_vals;
|
|
}
|
|
|
|
GimpValueArray *
|
|
gimp_pdb_execute_procedure_by_name (GimpPDB *pdb,
|
|
GimpContext *context,
|
|
GimpProgress *progress,
|
|
GError **error,
|
|
const gchar *name,
|
|
...)
|
|
{
|
|
GimpProcedure *procedure;
|
|
GimpValueArray *args;
|
|
GimpValueArray *return_vals;
|
|
va_list va_args;
|
|
GType prev_value_type = G_TYPE_NONE;
|
|
gint prev_int_value = 0;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (GIMP_IS_PDB (pdb), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
procedure = gimp_pdb_lookup_procedure (pdb, name);
|
|
|
|
if (! procedure)
|
|
{
|
|
GError *pdb_error = g_error_new (GIMP_PDB_ERROR,
|
|
GIMP_PDB_ERROR_PROCEDURE_NOT_FOUND,
|
|
_("Procedure '%s' not found"), name);
|
|
|
|
return_vals = gimp_procedure_get_return_values (NULL, FALSE, pdb_error);
|
|
g_propagate_error (error, pdb_error);
|
|
|
|
return return_vals;
|
|
}
|
|
|
|
args = gimp_procedure_get_arguments (procedure);
|
|
|
|
va_start (va_args, name);
|
|
|
|
for (i = 0; i < procedure->num_args; i++)
|
|
{
|
|
GValue *value;
|
|
GType arg_type;
|
|
GType value_type;
|
|
gchar *error_msg = NULL;
|
|
|
|
arg_type = va_arg (va_args, GType);
|
|
|
|
if (arg_type == G_TYPE_NONE)
|
|
break;
|
|
|
|
value = gimp_value_array_index (args, i);
|
|
|
|
value_type = G_VALUE_TYPE (value);
|
|
|
|
/* G_TYPE_INT is widely abused for enums and booleans in
|
|
* old plug-ins, silently copy stuff into integers when enums
|
|
* and booleans are passed
|
|
*/
|
|
if (arg_type != value_type
|
|
|
|
&&
|
|
|
|
value_type == G_TYPE_INT
|
|
|
|
&&
|
|
|
|
(arg_type == G_TYPE_BOOLEAN ||
|
|
g_type_is_a (arg_type, G_TYPE_ENUM)))
|
|
{
|
|
arg_type = value_type;
|
|
}
|
|
|
|
if (arg_type != value_type)
|
|
{
|
|
GError *pdb_error;
|
|
const gchar *expected = g_type_name (value_type);
|
|
const gchar *got = g_type_name (arg_type);
|
|
|
|
gimp_value_array_unref (args);
|
|
|
|
pdb_error = g_error_new (GIMP_PDB_ERROR,
|
|
GIMP_PDB_ERROR_INVALID_ARGUMENT,
|
|
_("Procedure '%s' has been called with a "
|
|
"wrong type for argument #%d. "
|
|
"Expected %s, got %s."),
|
|
gimp_object_get_name (procedure),
|
|
i + 1, expected, got);
|
|
|
|
return_vals = gimp_procedure_get_return_values (procedure,
|
|
FALSE, pdb_error);
|
|
g_propagate_error (error, pdb_error);
|
|
|
|
va_end (va_args);
|
|
|
|
return return_vals;
|
|
}
|
|
|
|
if (GIMP_VALUE_HOLDS_INT32_ARRAY (value) ||
|
|
GIMP_VALUE_HOLDS_INT16_ARRAY (value) ||
|
|
GIMP_VALUE_HOLDS_UINT8_ARRAY (value) ||
|
|
GIMP_VALUE_HOLDS_FLOAT_ARRAY (value) ||
|
|
GIMP_VALUE_HOLDS_RGB_ARRAY (value) ||
|
|
GIMP_VALUE_HOLDS_STRING_ARRAY (value) ||
|
|
GIMP_VALUE_HOLDS_OBJECT_ARRAY (value))
|
|
{
|
|
/* Array arguments don't have their size information when they
|
|
* are set by core code, in C array form.
|
|
* By convention, the previous argument has to be the array
|
|
* size argument.
|
|
*/
|
|
g_return_val_if_fail (prev_value_type == G_TYPE_INT && prev_int_value >= 0, NULL);
|
|
|
|
if (GIMP_VALUE_HOLDS_INT32_ARRAY (value))
|
|
gimp_value_set_int32_array (value,
|
|
(const gint32 *) va_arg (va_args, gpointer),
|
|
prev_int_value);
|
|
else if (GIMP_VALUE_HOLDS_INT16_ARRAY (value))
|
|
gimp_value_set_int16_array (value,
|
|
(const gint16 *) va_arg (va_args, gpointer),
|
|
prev_int_value);
|
|
else if (GIMP_VALUE_HOLDS_UINT8_ARRAY (value))
|
|
gimp_value_set_uint8_array (value,
|
|
(const guint8 *) va_arg (va_args, gpointer),
|
|
prev_int_value);
|
|
else if (GIMP_VALUE_HOLDS_FLOAT_ARRAY (value))
|
|
gimp_value_set_float_array (value,
|
|
(const gdouble *) va_arg (va_args, gpointer),
|
|
prev_int_value);
|
|
else if (GIMP_VALUE_HOLDS_RGB_ARRAY (value))
|
|
gimp_value_set_rgb_array (value,
|
|
(const GimpRGB *) va_arg (va_args, gpointer),
|
|
prev_int_value);
|
|
else if (GIMP_VALUE_HOLDS_STRING_ARRAY (value))
|
|
gimp_value_set_string_array (value,
|
|
(const gchar **) va_arg (va_args, gpointer),
|
|
prev_int_value);
|
|
else if (GIMP_VALUE_HOLDS_OBJECT_ARRAY (value))
|
|
gimp_value_set_object_array (value, GIMP_TYPE_ITEM,
|
|
va_arg (va_args, gpointer),
|
|
prev_int_value);
|
|
}
|
|
else
|
|
{
|
|
G_VALUE_COLLECT (value, va_args, G_VALUE_NOCOPY_CONTENTS, &error_msg);
|
|
}
|
|
|
|
if (error_msg)
|
|
{
|
|
GError *pdb_error = g_error_new_literal (GIMP_PDB_ERROR,
|
|
GIMP_PDB_ERROR_INTERNAL_ERROR,
|
|
error_msg);
|
|
g_warning ("%s: %s", G_STRFUNC, error_msg);
|
|
g_free (error_msg);
|
|
|
|
gimp_value_array_unref (args);
|
|
|
|
return_vals = gimp_procedure_get_return_values (procedure,
|
|
FALSE, pdb_error);
|
|
g_propagate_error (error, pdb_error);
|
|
|
|
va_end (va_args);
|
|
|
|
return return_vals;
|
|
}
|
|
|
|
prev_value_type = value_type;
|
|
if (prev_value_type == G_TYPE_INT)
|
|
prev_int_value = g_value_get_int (value);
|
|
}
|
|
|
|
va_end (va_args);
|
|
|
|
return_vals = gimp_pdb_execute_procedure_by_name_args (pdb, context,
|
|
progress, error,
|
|
name, args);
|
|
|
|
gimp_value_array_unref (args);
|
|
|
|
return return_vals;
|
|
}
|
|
|
|
/**
|
|
* gimp_pdb_get_deprecated_procedures:
|
|
* @pdb:
|
|
*
|
|
* Returns: A new #GList with the deprecated procedures. Free with
|
|
* g_list_free().
|
|
**/
|
|
GList *
|
|
gimp_pdb_get_deprecated_procedures (GimpPDB *pdb)
|
|
{
|
|
GList *result = NULL;
|
|
GList *procs;
|
|
GList *iter;
|
|
|
|
g_return_val_if_fail (GIMP_IS_PDB (pdb), NULL);
|
|
|
|
procs = g_hash_table_get_values (pdb->procedures);
|
|
|
|
for (iter = procs;
|
|
iter;
|
|
iter = g_list_next (iter))
|
|
{
|
|
GList *proc_list = iter->data;
|
|
|
|
/* Only care about the first procedure in the list */
|
|
GimpProcedure *procedure = GIMP_PROCEDURE (proc_list->data);
|
|
|
|
if (procedure->deprecated)
|
|
result = g_list_prepend (result, procedure);
|
|
}
|
|
|
|
result = g_list_sort (result, (GCompareFunc) gimp_procedure_name_compare);
|
|
|
|
g_list_free (procs);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* private functions */
|
|
|
|
static void
|
|
gimp_pdb_entry_free (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
if (value)
|
|
g_list_free_full (value, (GDestroyNotify) g_object_unref);
|
|
}
|
|
|
|
static gint64
|
|
gimp_pdb_entry_get_memsize (GList *procedures,
|
|
gint64 *gui_size)
|
|
{
|
|
return gimp_g_list_get_memsize_foreach (procedures,
|
|
(GimpMemsizeFunc)
|
|
gimp_object_get_memsize,
|
|
gui_size);
|
|
}
|