/* 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 * * 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 "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; }