/* 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 . */ #include "config.h" #include #include #include #include "libgimpbase/gimpbase.h" #include "libgimpcolor/gimpcolor.h" #include "libgimpconfig/gimpconfig.h" #include "libgimpwidgets/gimpwidgets.h" #include "tools-types.h" #include "gegl/gimp-gegl-config.h" #include "gegl/gimp-gegl-utils.h" #include "core/gimpchannel.h" #include "core/gimpcontainer.h" #include "core/gimpcontext.h" #include "core/gimpdrawable.h" #include "core/gimperror.h" #include "core/gimpimage.h" #include "core/gimpimagemap.h" #include "core/gimplist.h" #include "core/gimpparamspecs-duplicate.h" #include "core/gimppickable.h" #include "core/gimpsettings.h" #include "widgets/gimpbuffersourcebox.h" #include "widgets/gimphelp-ids.h" #include "widgets/gimppickablebutton.h" #include "widgets/gimppropgui.h" #include "display/gimpdisplay.h" #include "display/gimptoolgui.h" #include "gimpcoloroptions.h" #include "gimpoperationtool.h" #include "gimp-intl.h" typedef struct _AuxInput AuxInput; struct _AuxInput { GeglNode *node; GtkWidget *box; }; /* local function prototypes */ static void gimp_operation_tool_finalize (GObject *object); static gboolean gimp_operation_tool_initialize (GimpTool *tool, GimpDisplay *display, GError **error); static void gimp_operation_tool_control (GimpTool *tool, GimpToolAction action, GimpDisplay *display); static gchar * gimp_operation_tool_get_operation (GimpImageMapTool *im_tool, gchar **title, gchar **description, gchar **undo_desc, gchar **icon_name, gchar **help_id); static void gimp_operation_tool_dialog (GimpImageMapTool *im_tool); static void gimp_operation_tool_reset (GimpImageMapTool *im_tool); static GtkWidget * gimp_operation_tool_get_settings_ui (GimpImageMapTool *im_tool, GimpContainer *settings, GFile *settings_file, const gchar *import_dialog_title, const gchar *export_dialog_title, const gchar *file_dialog_help_id, GFile *default_folder, GtkWidget **settings_box); static void gimp_operation_tool_color_picked (GimpImageMapTool *im_tool, gpointer identifier, gdouble x, gdouble y, const Babl *sample_format, const GimpRGB *color); static void gimp_operation_tool_sync_op (GimpOperationTool *op_tool, GimpDrawable *drawable); static AuxInput * gimp_operation_tool_aux_input_new (GimpOperationTool *tool, GeglNode *operation, const gchar *input_pad, const gchar *label); static void gimp_operation_tool_aux_input_clear (AuxInput *input); static void gimp_operation_tool_aux_input_free (AuxInput *input); G_DEFINE_TYPE (GimpOperationTool, gimp_operation_tool, GIMP_TYPE_IMAGE_MAP_TOOL) #define parent_class gimp_operation_tool_parent_class void gimp_operation_tool_register (GimpToolRegisterCallback callback, gpointer data) { (* callback) (GIMP_TYPE_OPERATION_TOOL, GIMP_TYPE_COLOR_OPTIONS, gimp_color_options_gui, GIMP_CONTEXT_PROP_MASK_FOREGROUND | GIMP_CONTEXT_PROP_MASK_BACKGROUND, "gimp-operation-tool", _("GEGL Operation"), _("Operation Tool: Use an arbitrary GEGL operation"), N_("_GEGL Operation..."), NULL, NULL, GIMP_HELP_TOOL_GEGL, GIMP_STOCK_GEGL, data); } static void gimp_operation_tool_class_init (GimpOperationToolClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass); GimpImageMapToolClass *im_tool_class = GIMP_IMAGE_MAP_TOOL_CLASS (klass); object_class->finalize = gimp_operation_tool_finalize; tool_class->initialize = gimp_operation_tool_initialize; tool_class->control = gimp_operation_tool_control; im_tool_class->get_operation = gimp_operation_tool_get_operation; im_tool_class->dialog = gimp_operation_tool_dialog; im_tool_class->reset = gimp_operation_tool_reset; im_tool_class->get_settings_ui = gimp_operation_tool_get_settings_ui; im_tool_class->color_picked = gimp_operation_tool_color_picked; } static void gimp_operation_tool_init (GimpOperationTool *tool) { GIMP_IMAGE_MAP_TOOL_GET_CLASS (tool)->settings_name = NULL; /* XXX hack */ } static void gimp_operation_tool_finalize (GObject *object) { GimpOperationTool *tool = GIMP_OPERATION_TOOL (object); if (tool->operation) { g_free (tool->operation); tool->operation = NULL; } if (tool->title) { g_free (tool->title); tool->title = NULL; } if (tool->description) { g_free (tool->description); tool->description = NULL; } if (tool->undo_desc) { g_free (tool->undo_desc); tool->undo_desc = NULL; } if (tool->icon_name) { g_free (tool->icon_name); tool->icon_name = NULL; } if (tool->help_id) { g_free (tool->help_id); tool->help_id = NULL; } g_list_free_full (tool->aux_inputs, (GDestroyNotify) gimp_operation_tool_aux_input_free); tool->aux_inputs = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean gimp_operation_tool_initialize (GimpTool *tool, GimpDisplay *display, GError **error) { if (GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error)) { GimpImageMapTool *im_tool = GIMP_IMAGE_MAP_TOOL (tool); GimpOperationTool *op_tool = GIMP_OPERATION_TOOL (tool); GimpImage *image = gimp_display_get_image (display); GimpDrawable *drawable = gimp_image_get_active_drawable (image); if (im_tool->config) gimp_operation_tool_sync_op (op_tool, drawable); return TRUE; } return FALSE; } static void gimp_operation_tool_control (GimpTool *tool, GimpToolAction action, GimpDisplay *display) { GimpOperationTool *op_tool = GIMP_OPERATION_TOOL (tool); switch (action) { case GIMP_TOOL_ACTION_PAUSE: case GIMP_TOOL_ACTION_RESUME: break; case GIMP_TOOL_ACTION_HALT: g_list_foreach (op_tool->aux_inputs, (GFunc) gimp_operation_tool_aux_input_clear, NULL); break; case GIMP_TOOL_ACTION_COMMIT: break; } GIMP_TOOL_CLASS (parent_class)->control (tool, action, display); } static gchar * gimp_operation_tool_get_operation (GimpImageMapTool *im_tool, gchar **title, gchar **description, gchar **undo_desc, gchar **icon_name, gchar **help_id) { GimpOperationTool *tool = GIMP_OPERATION_TOOL (im_tool); *title = g_strdup (tool->title); *description = g_strdup (tool->description); *undo_desc = g_strdup (tool->undo_desc); *icon_name = g_strdup (tool->icon_name); *help_id = g_strdup (tool->help_id); return g_strdup (tool->operation); } static void gimp_operation_tool_dialog (GimpImageMapTool *im_tool) { GimpOperationTool *tool = GIMP_OPERATION_TOOL (im_tool); GtkWidget *main_vbox; GList *list; main_vbox = gimp_image_map_tool_dialog_get_vbox (im_tool); /* The options vbox */ tool->options_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_box_pack_start (GTK_BOX (main_vbox), tool->options_box, FALSE, FALSE, 0); gtk_widget_show (tool->options_box); for (list = tool->aux_inputs; list; list = g_list_next (list)) { AuxInput *input = list->data; gtk_box_pack_start (GTK_BOX (tool->options_box), input->box, FALSE, FALSE, 0); gtk_widget_show (input->box); } if (tool->options_gui) { gtk_box_pack_start (GTK_BOX (tool->options_box), tool->options_gui, FALSE, FALSE, 0); gtk_widget_show (tool->options_gui); } } static void gimp_operation_tool_reset (GimpImageMapTool *im_tool) { GimpOperationTool *tool = GIMP_OPERATION_TOOL (im_tool); GIMP_IMAGE_MAP_TOOL_CLASS (parent_class)->reset (im_tool); if (im_tool->config && GIMP_TOOL (tool)->drawable) gimp_operation_tool_sync_op (tool, GIMP_TOOL (tool)->drawable); } static GtkWidget * gimp_operation_tool_get_settings_ui (GimpImageMapTool *im_tool, GimpContainer *settings, GFile *settings_file, const gchar *import_dialog_title, const gchar *export_dialog_title, const gchar *file_dialog_help_id, GFile *default_folder, GtkWidget **settings_box) { GimpOperationTool *tool = GIMP_OPERATION_TOOL (im_tool); GType type = G_TYPE_FROM_INSTANCE (im_tool->config); GtkWidget *widget; gchar *basename; GFile *file; gchar *import_title; gchar *export_title; settings = gimp_gegl_config_get_container (type); if (! gimp_list_get_sort_func (GIMP_LIST (settings))) gimp_list_set_sort_func (GIMP_LIST (settings), (GCompareFunc) gimp_settings_compare); basename = g_strconcat (G_OBJECT_TYPE_NAME (im_tool->config), ".settings", NULL); file = gimp_directory_file ("filters", basename, NULL); g_free (basename); import_title = g_strdup_printf (_("Import '%s' Settings"), tool->title); export_title = g_strdup_printf (_("Export '%s' Settings"), tool->title); widget = GIMP_IMAGE_MAP_TOOL_CLASS (parent_class)->get_settings_ui (im_tool, settings, file, import_title, export_title, "help-foo", NULL, /* sic */ settings_box); g_free (import_title); g_free (export_title); g_object_unref (file); return widget; } static void gimp_operation_tool_color_picked (GimpImageMapTool *im_tool, gpointer identifier, gdouble x, gdouble y, const Babl *sample_format, const GimpRGB *color) { GimpOperationTool *tool = GIMP_OPERATION_TOOL (im_tool); gchar **pspecs; pspecs = g_strsplit (identifier, ":", 2); if (pspecs[1]) { GimpImageMapOptions *options = GIMP_IMAGE_MAP_TOOL_GET_OPTIONS (tool); GimpDrawable *drawable = GIMP_TOOL (im_tool)->drawable; GObjectClass *object_class = G_OBJECT_GET_CLASS (im_tool->config); GParamSpec *pspec_x; GParamSpec *pspec_y; gint width = 1; gint height = 1; if (drawable) { gint off_x, off_y; gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); x -= off_x; y -= off_y; switch (options->region) { case GIMP_IMAGE_MAP_REGION_SELECTION: if (gimp_item_mask_intersect (GIMP_ITEM (drawable), &off_x, &off_y, &width, &height)) { x -= off_x; y -= off_y; } break; case GIMP_IMAGE_MAP_REGION_DRAWABLE: width = gimp_item_get_width (GIMP_ITEM (drawable)); height = gimp_item_get_height (GIMP_ITEM (drawable)); break; } } pspec_x = g_object_class_find_property (object_class, pspecs[0]); pspec_y = g_object_class_find_property (object_class, pspecs[1]); if (pspec_x && pspec_y && G_PARAM_SPEC_TYPE (pspec_x) == G_PARAM_SPEC_TYPE (pspec_y)) { GValue value_x = G_VALUE_INIT; GValue value_y = G_VALUE_INIT; g_value_init (&value_x, G_PARAM_SPEC_VALUE_TYPE (pspec_x)); g_value_init (&value_y, G_PARAM_SPEC_VALUE_TYPE (pspec_y)); #define HAS_KEY(p,k,v) gimp_gegl_param_spec_has_key (p, k, v) if (HAS_KEY (pspec_x, "unit", "relative-coordinate") && HAS_KEY (pspec_y, "unit", "relative-coordinate")) { x /= (gdouble) width; y /= (gdouble) height; } if (G_IS_PARAM_SPEC_INT (pspec_x)) { g_value_set_int (&value_x, x); g_value_set_int (&value_y, y); g_param_value_validate (pspec_x, &value_x); g_param_value_validate (pspec_y, &value_y); g_object_set (im_tool->config, pspecs[0], g_value_get_int (&value_x), pspecs[1], g_value_get_int (&value_y), NULL); } else if (G_IS_PARAM_SPEC_DOUBLE (pspec_x)) { g_value_set_double (&value_x, x); g_value_set_double (&value_y, y); g_param_value_validate (pspec_x, &value_x); g_param_value_validate (pspec_y, &value_y); g_object_set (im_tool->config, pspecs[0], g_value_get_double (&value_x), pspecs[1], g_value_get_double (&value_y), NULL); } else { g_warning ("%s: unhandled param spec of type %s", G_STRFUNC, G_PARAM_SPEC_TYPE_NAME (pspec_x)); } g_value_unset (&value_x); g_value_unset (&value_y); } } else { g_object_set (im_tool->config, pspecs[0], color, NULL); } g_strfreev (pspecs); } static void gimp_operation_tool_sync_op (GimpOperationTool *op_tool, GimpDrawable *drawable) { GimpImageMapTool *im_tool = GIMP_IMAGE_MAP_TOOL (op_tool); GimpToolOptions *options = GIMP_TOOL_GET_OPTIONS (op_tool); GParamSpec **pspecs; guint n_pspecs; gint bounds_x; gint bounds_y; gint bounds_width; gint bounds_height; gint i; gimp_item_mask_intersect (GIMP_ITEM (drawable), &bounds_x, &bounds_y, &bounds_width, &bounds_height); pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (im_tool->config), &n_pspecs); for (i = 0; i < n_pspecs; i++) { GParamSpec *pspec = pspecs[i]; #define HAS_KEY(p,k,v) gimp_gegl_param_spec_has_key (p, k, v) if (HAS_KEY (pspec, "role", "output-extent")) { if (HAS_KEY (pspec, "unit", "pixel-coordinate") && HAS_KEY (pspec, "axis", "x")) { g_object_set (im_tool->config, pspec->name, 0, NULL); } else if (HAS_KEY (pspec, "unit", "pixel-coordinate") && HAS_KEY (pspec, "axis", "y")) { g_object_set (im_tool->config, pspec->name, 0, NULL); } else if (HAS_KEY (pspec, "unit", "pixel-distance") && HAS_KEY (pspec, "axis", "x")) { g_object_set (im_tool->config, pspec->name, bounds_width, NULL); } else if (HAS_KEY (pspec, "unit", "pixel-distance") && HAS_KEY (pspec, "axis", "y")) { g_object_set (im_tool->config, pspec->name, bounds_height, NULL); } } else if (HAS_KEY (pspec, "role", "color-primary")) { GimpRGB color; gimp_context_get_foreground (GIMP_CONTEXT (options), &color); g_object_set (im_tool->config, pspec->name, &color, NULL); } else if (HAS_KEY (pspec, "role", "color-secondary")) { GimpRGB color; gimp_context_get_background (GIMP_CONTEXT (options), &color); g_object_set (im_tool->config, pspec->name, &color, NULL); } } g_free (pspecs); } /* aux input utility functions */ static void gimp_operation_tool_aux_input_notify (GimpBufferSourceBox *box, const GParamSpec *pspec, GimpOperationTool *tool) { gimp_image_map_tool_preview (GIMP_IMAGE_MAP_TOOL (tool)); } static AuxInput * gimp_operation_tool_aux_input_new (GimpOperationTool *tool, GeglNode *operation, const gchar *input_pad, const gchar *label) { AuxInput *input = g_slice_new (AuxInput); GimpContext *context; input->node = gegl_node_new_child (NULL, "operation", "gegl:buffer-source", NULL); gegl_node_connect_to (input->node, "output", operation, input_pad); context = GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool)); input->box = gimp_buffer_source_box_new (context, input->node, label); g_signal_connect (input->box, "notify::pickable", G_CALLBACK (gimp_operation_tool_aux_input_notify), tool); g_signal_connect (input->box, "notify::enabled", G_CALLBACK (gimp_operation_tool_aux_input_notify), tool); return input; } static void gimp_operation_tool_aux_input_clear (AuxInput *input) { g_object_set (input->box, "pickable", NULL, NULL); } static void gimp_operation_tool_aux_input_free (AuxInput *input) { g_object_unref (input->node); gtk_widget_destroy (input->box); g_slice_free (AuxInput, input); } /* public functions */ void gimp_operation_tool_set_operation (GimpOperationTool *tool, const gchar *operation, const gchar *title, const gchar *description, const gchar *undo_desc, const gchar *icon_name, const gchar *help_id) { GimpImageMapTool *im_tool; GtkSizeGroup *size_group = NULL; gint aux; g_return_if_fail (GIMP_IS_OPERATION_TOOL (tool)); g_return_if_fail (operation != NULL); im_tool = GIMP_IMAGE_MAP_TOOL (tool); if (tool->operation) g_free (tool->operation); if (tool->title) g_free (tool->title); if (tool->description) g_free (tool->description); if (tool->undo_desc) g_free (tool->undo_desc); if (tool->icon_name) g_free (tool->icon_name); if (tool->help_id) g_free (tool->help_id); tool->operation = g_strdup (operation); tool->title = g_strdup (title); tool->description = g_strdup (description); tool->undo_desc = g_strdup (undo_desc); tool->icon_name = g_strdup (icon_name); tool->help_id = g_strdup (help_id); g_list_free_full (tool->aux_inputs, (GDestroyNotify) gimp_operation_tool_aux_input_free); tool->aux_inputs = NULL; gimp_image_map_tool_get_operation (im_tool); if (undo_desc) GIMP_IMAGE_MAP_TOOL_GET_CLASS (tool)->settings_name = "yes"; /* XXX hack */ else GIMP_IMAGE_MAP_TOOL_GET_CLASS (tool)->settings_name = NULL; /* XXX hack */ if (tool->options_gui) { gtk_widget_destroy (tool->options_gui); tool->options_gui = NULL; if (im_tool->active_picker) { im_tool->active_picker = NULL; gimp_color_tool_disable (GIMP_COLOR_TOOL (tool)); } } for (aux = 1; ; aux++) { gchar pad[32]; gchar label[32]; if (aux == 1) { g_snprintf (pad, sizeof (pad), "aux"); g_snprintf (label, sizeof (label), _("Aux Input")); } else { g_snprintf (pad, sizeof (pad), "aux%d", aux); g_snprintf (label, sizeof (label), _("Aux%d Input"), aux); } if (gegl_node_has_pad (im_tool->operation, pad)) { AuxInput *input; GtkWidget *toggle; if (! size_group) size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); input = gimp_operation_tool_aux_input_new (tool, im_tool->operation, pad, label); tool->aux_inputs = g_list_append (tool->aux_inputs, input); toggle = gimp_buffer_source_box_get_toggle (GIMP_BUFFER_SOURCE_BOX (input->box)); gtk_size_group_add_widget (size_group, toggle); if (tool->options_box) { gtk_box_pack_start (GTK_BOX (tool->options_box), input->box, FALSE, FALSE, 0); gtk_widget_show (input->box); } } else { break; } } if (size_group) g_object_unref (size_group); if (im_tool->config) { tool->options_gui = gimp_prop_gui_new (G_OBJECT (im_tool->config), G_TYPE_FROM_INSTANCE (im_tool->config), 0, GIMP_CONTEXT (GIMP_TOOL_GET_OPTIONS (tool)), (GimpCreatePickerFunc) gimp_image_map_tool_add_color_picker, tool); if (tool->options_box) { gtk_box_pack_start (GTK_BOX (tool->options_box), tool->options_gui, FALSE, FALSE, 0); gtk_widget_show (tool->options_gui); } } if (GIMP_TOOL (tool)->drawable) { gimp_operation_tool_sync_op (tool, GIMP_TOOL (tool)->drawable); gimp_image_map_tool_preview (im_tool); } }