PIKApp/app/actions/pikageglprocedure.c

469 lines
17 KiB
C

/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* pikageglprocedure.c
* Copyright (C) 2016-2018 Michael Natterer <mitch@gimp.org>
*
* 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 <gegl.h>
#include <gtk/gtk.h>
#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"
#include "actions-types.h"
#include "config/pikaguiconfig.h"
#include "operations/pika-operation-config.h"
#include "operations/pikaoperationsettings.h"
#include "core/pika.h"
#include "core/pika-memsize.h"
#include "core/pikacontainer.h"
#include "core/pikacontext.h"
#include "core/pikadisplay.h"
#include "core/pikadrawable-operation.h"
#include "core/pikaimage.h"
#include "core/pikalayermask.h"
#include "core/pikaparamspecs.h"
#include "core/pikasettings.h"
#include "core/pikatoolinfo.h"
#include "tools/pikaoperationtool.h"
#include "tools/tool_manager.h"
#include "pikageglprocedure.h"
#include "pika-intl.h"
static void pika_gegl_procedure_finalize (GObject *object);
static gint64 pika_gegl_procedure_get_memsize (PikaObject *object,
gint64 *gui_size);
static gchar * pika_gegl_procedure_get_description (PikaViewable *viewable,
gchar **tooltip);
static const gchar * pika_gegl_procedure_get_menu_label (PikaProcedure *procedure);
static gboolean pika_gegl_procedure_get_sensitive (PikaProcedure *procedure,
PikaObject *object,
const gchar **reason);
static PikaValueArray * pika_gegl_procedure_execute (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaValueArray *args,
GError **error);
static void pika_gegl_procedure_execute_async (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaValueArray *args,
PikaDisplay *display);
G_DEFINE_TYPE (PikaGeglProcedure, pika_gegl_procedure,
PIKA_TYPE_PROCEDURE)
#define parent_class pika_gegl_procedure_parent_class
static void
pika_gegl_procedure_class_init (PikaGeglProcedureClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass);
PikaViewableClass *viewable_class = PIKA_VIEWABLE_CLASS (klass);
PikaProcedureClass *proc_class = PIKA_PROCEDURE_CLASS (klass);
object_class->finalize = pika_gegl_procedure_finalize;
pika_object_class->get_memsize = pika_gegl_procedure_get_memsize;
viewable_class->default_icon_name = "pika-gegl";
viewable_class->get_description = pika_gegl_procedure_get_description;
proc_class->get_menu_label = pika_gegl_procedure_get_menu_label;
proc_class->get_sensitive = pika_gegl_procedure_get_sensitive;
proc_class->execute = pika_gegl_procedure_execute;
proc_class->execute_async = pika_gegl_procedure_execute_async;
}
static void
pika_gegl_procedure_init (PikaGeglProcedure *proc)
{
}
static void
pika_gegl_procedure_finalize (GObject *object)
{
PikaGeglProcedure *proc = PIKA_GEGL_PROCEDURE (object);
g_clear_object (&proc->default_settings);
g_clear_pointer (&proc->operation, g_free);
g_clear_pointer (&proc->menu_label, g_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gint64
pika_gegl_procedure_get_memsize (PikaObject *object,
gint64 *gui_size)
{
PikaGeglProcedure *proc = PIKA_GEGL_PROCEDURE (object);
gint64 memsize = 0;
memsize += pika_string_get_memsize (proc->operation);
memsize += pika_string_get_memsize (proc->menu_label);
return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
static gchar *
pika_gegl_procedure_get_description (PikaViewable *viewable,
gchar **tooltip)
{
PikaProcedure *procedure = PIKA_PROCEDURE (viewable);
if (tooltip)
*tooltip = g_strdup (pika_procedure_get_blurb (procedure));
return g_strdup (pika_procedure_get_label (procedure));
}
static const gchar *
pika_gegl_procedure_get_menu_label (PikaProcedure *procedure)
{
PikaGeglProcedure *proc = PIKA_GEGL_PROCEDURE (procedure);
if (proc->menu_label)
return proc->menu_label;
return PIKA_PROCEDURE_CLASS (parent_class)->get_menu_label (procedure);
}
static gboolean
pika_gegl_procedure_get_sensitive (PikaProcedure *procedure,
PikaObject *object,
const gchar **reason)
{
PikaImage *image = PIKA_IMAGE (object);
GList *drawables = NULL;
gboolean sensitive = FALSE;
if (image)
drawables = pika_image_get_selected_drawables (image);
if (g_list_length (drawables) == 1)
{
PikaDrawable *drawable = drawables->data;
PikaItem *item;
if (PIKA_IS_LAYER_MASK (drawable))
item = PIKA_ITEM (pika_layer_mask_get_layer (PIKA_LAYER_MASK (drawable)));
else
item = PIKA_ITEM (drawable);
sensitive = ! pika_item_is_content_locked (item, NULL);
if (pika_viewable_get_children (PIKA_VIEWABLE (drawable)))
sensitive = FALSE;
}
g_list_free (drawables);
return sensitive;
}
static PikaValueArray *
pika_gegl_procedure_execute (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaValueArray *args,
GError **error)
{
PikaImage *image;
gint n_drawables;
GObject **drawables;
GObject *config;
image = g_value_get_object (pika_value_array_index (args, 1));
n_drawables = g_value_get_int (pika_value_array_index (args, 2));
drawables = pika_value_get_object_array (pika_value_array_index (args, 3));
config = g_value_get_object (pika_value_array_index (args, 4));
if (n_drawables == 1)
{
GeglNode *node;
node = gegl_node_new_child (NULL,
"operation",
PIKA_GEGL_PROCEDURE (procedure)->operation,
NULL);
pika_drawable_apply_operation_with_config (PIKA_DRAWABLE (drawables[0]),
progress,
pika_procedure_get_label (procedure),
node, config);
g_object_unref (node);
pika_image_flush (image);
return pika_procedure_get_return_values (procedure, TRUE, NULL);
}
return pika_procedure_get_return_values (procedure, FALSE, NULL);
}
static void
pika_gegl_procedure_execute_async (PikaProcedure *procedure,
Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaValueArray *args,
PikaDisplay *display)
{
PikaGeglProcedure *gegl_procedure = PIKA_GEGL_PROCEDURE (procedure);
PikaRunMode run_mode;
PikaObject *settings;
PikaTool *active_tool;
const gchar *tool_name;
run_mode = g_value_get_enum (pika_value_array_index (args, 0));
settings = g_value_get_object (pika_value_array_index (args, 4));
if (! settings &&
(run_mode != PIKA_RUN_INTERACTIVE ||
PIKA_GUI_CONFIG (pika->config)->filter_tool_use_last_settings))
{
/* if we didn't get settings passed, get the last used settings */
GType config_type;
PikaContainer *container;
config_type = G_VALUE_TYPE (pika_value_array_index (args, 4));
container = pika_operation_config_get_container (pika, config_type,
(GCompareFunc)
pika_settings_compare);
settings = pika_container_get_child_by_index (container, 0);
/* only use the settings if they are automatically created
* "last used" values, not if they were saved explicitly and
* have a zero timestamp; and if they are not a separator.
*/
if (settings &&
(PIKA_SETTINGS (settings)->time == 0 ||
! pika_object_get_name (settings)))
{
settings = NULL;
}
}
if (run_mode == PIKA_RUN_NONINTERACTIVE ||
run_mode == PIKA_RUN_WITH_LAST_VALS)
{
if (settings || run_mode == PIKA_RUN_NONINTERACTIVE)
{
PikaValueArray *return_vals;
g_value_set_object (pika_value_array_index (args, 4), settings);
return_vals = pika_procedure_execute (procedure, pika, context, progress,
args, NULL);
pika_value_array_unref (return_vals);
return;
}
pika_message (pika,
G_OBJECT (progress), PIKA_MESSAGE_WARNING,
_("There are no last settings for '%s', "
"showing the filter dialog instead."),
pika_procedure_get_label (procedure));
}
if (! strcmp (gegl_procedure->operation, "pika:brightness-contrast"))
{
tool_name = "pika-brightness-contrast-tool";
}
else if (! strcmp (gegl_procedure->operation, "pika:curves"))
{
tool_name = "pika-curves-tool";
}
else if (! strcmp (gegl_procedure->operation, "pika:levels"))
{
tool_name = "pika-levels-tool";
}
else if (! strcmp (gegl_procedure->operation, "pika:threshold"))
{
tool_name = "pika-threshold-tool";
}
else if (! strcmp (gegl_procedure->operation, "pika:offset"))
{
tool_name = "pika-offset-tool";
}
else
{
tool_name = "pika-operation-tool";
}
active_tool = tool_manager_get_active (pika);
/* do not use the passed context because we need to set the active
* tool on the global user context
*/
context = pika_get_user_context (pika);
if (strcmp (pika_object_get_name (active_tool->tool_info), tool_name))
{
PikaToolInfo *tool_info = pika_get_tool_info (pika, tool_name);
if (PIKA_IS_TOOL_INFO (tool_info))
pika_context_set_tool (context, tool_info);
}
else
{
pika_context_tool_changed (context);
}
active_tool = tool_manager_get_active (pika);
if (! strcmp (pika_object_get_name (active_tool->tool_info), tool_name))
{
/* Remember the procedure that created this tool, because
* we can't just switch to an operation tool using
* pika_context_set_tool(), we also have to go through the
* initialization code below, otherwise we end up with a
* dummy tool that does nothing. See bug #776370.
*/
g_object_set_data_full (G_OBJECT (active_tool), "pika-gegl-procedure",
g_object_ref (procedure),
(GDestroyNotify) g_object_unref);
if (! strcmp (tool_name, "pika-operation-tool"))
{
pika_operation_tool_set_operation (PIKA_OPERATION_TOOL (active_tool),
gegl_procedure->operation,
pika_procedure_get_label (procedure),
pika_procedure_get_label (procedure),
pika_procedure_get_label (procedure),
pika_viewable_get_icon_name (PIKA_VIEWABLE (procedure)),
pika_procedure_get_help_id (procedure));
}
tool_manager_initialize_active (pika, display);
if (settings)
pika_filter_tool_set_config (PIKA_FILTER_TOOL (active_tool),
PIKA_CONFIG (settings));
}
}
/* public functions */
PikaProcedure *
pika_gegl_procedure_new (Pika *pika,
PikaRunMode default_run_mode,
PikaObject *default_settings,
const gchar *operation,
const gchar *name,
const gchar *menu_label,
const gchar *tooltip,
const gchar *icon_name,
const gchar *help_id)
{
PikaProcedure *procedure;
PikaGeglProcedure *gegl_procedure;
GType config_type;
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (operation != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (menu_label != NULL, NULL);
config_type = pika_operation_config_get_type (pika, operation, icon_name,
PIKA_TYPE_OPERATION_SETTINGS);
procedure = g_object_new (PIKA_TYPE_GEGL_PROCEDURE, NULL);
gegl_procedure = PIKA_GEGL_PROCEDURE (procedure);
gegl_procedure->operation = g_strdup (operation);
gegl_procedure->default_run_mode = default_run_mode;
gegl_procedure->menu_label = g_strdup (menu_label);
if (default_settings)
gegl_procedure->default_settings = g_object_ref (default_settings);
pika_object_set_name (PIKA_OBJECT (procedure), name);
pika_viewable_set_icon_name (PIKA_VIEWABLE (procedure), icon_name);
pika_procedure_set_help (procedure,
tooltip,
tooltip,
help_id);
pika_procedure_set_static_attribution (procedure,
"author", "copyright", "date");
pika_procedure_add_argument (procedure,
pika_param_spec_enum ("run-mode",
"Run mode",
"Run mode",
PIKA_TYPE_RUN_MODE,
PIKA_RUN_INTERACTIVE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_image ("image",
"Image",
"Input image",
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_int ("num-drawables",
"N drawables",
"The number of drawables",
0, G_MAXINT32, 0,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
pika_param_spec_object_array ("drawables",
"Drawables",
"Input drawables",
PIKA_TYPE_DRAWABLE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_object ("settings",
"Settings",
"Settings",
config_type,
PIKA_PARAM_READWRITE));
return procedure;
}