/* 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 "libgimpmath/gimpmath.h" #include "libgimpbase/gimpbase.h" #include "libgimpcolor/gimpcolor.h" #include "libgimpwidgets/gimpwidgets.h" #include "actions-types.h" #include "config/gimpdialogconfig.h" #include "operations/layer-modes/gimp-layer-modes.h" #include "core/gimp.h" #include "core/gimpchannel.h" #include "core/gimpchannel-combine.h" #include "core/gimpcontainer.h" #include "core/gimpcontext.h" #include "core/gimpdrawable-fill.h" #include "core/gimpdrawable-filters.h" #include "core/gimpdrawablefilter.h" #include "core/gimpgrouplayer.h" #include "core/gimpimage.h" #include "core/gimpimage-merge.h" #include "core/gimpimage-undo.h" #include "core/gimpimage-undo-push.h" #include "core/gimpitemundo.h" #include "core/gimplayerpropundo.h" #include "core/gimplayer-floating-selection.h" #include "core/gimplayer-new.h" #include "core/gimplist.h" #include "core/gimppickable.h" #include "core/gimppickable-auto-shrink.h" #include "core/gimptoolinfo.h" #include "core/gimpundostack.h" #include "core/gimpprogress.h" #include "text/gimptext.h" #include "text/gimptext-path.h" #include "text/gimptextlayer.h" #include "vectors/gimppath.h" #include "vectors/gimppath-warp.h" #include "vectors/gimpstroke.h" #include "widgets/gimpaction.h" #include "widgets/gimpdock.h" #include "widgets/gimphelp-ids.h" #include "widgets/gimpprogressdialog.h" #include "display/gimpdisplay.h" #include "display/gimpdisplayshell.h" #include "display/gimpimagewindow.h" #include "tools/gimptexttool.h" #include "tools/tool_manager.h" #include "dialogs/dialogs.h" #include "dialogs/layer-add-mask-dialog.h" #include "dialogs/layer-options-dialog.h" #include "dialogs/resize-dialog.h" #include "dialogs/scale-dialog.h" #include "actions.h" #include "items-commands.h" #include "layers-commands.h" #include "gimp-intl.h" /* local function prototypes */ static void layers_new_callback (GtkWidget *dialog, GimpImage *image, GimpLayer *layer, GimpContext *context, const gchar *layer_name, GimpLayerMode layer_mode, GimpLayerColorSpace layer_blend_space, GimpLayerColorSpace layer_composite_space, GimpLayerCompositeMode layer_composite_mode, gdouble layer_opacity, GimpFillType layer_fill_type, gint layer_width, gint layer_height, gint layer_offset_x, gint layer_offset_y, gboolean layer_visible, GimpColorTag layer_color_tag, gboolean layer_lock_pixels, gboolean layer_lock_position, gboolean layer_lock_visibility, gboolean layer_lock_alpha, gboolean rename_text_layer, gpointer user_data); static void layers_edit_attributes_callback (GtkWidget *dialog, GimpImage *image, GimpLayer *layer, GimpContext *context, const gchar *layer_name, GimpLayerMode layer_mode, GimpLayerColorSpace layer_blend_space, GimpLayerColorSpace layer_composite_space, GimpLayerCompositeMode layer_composite_mode, gdouble layer_opacity, GimpFillType layer_fill_type, gint layer_width, gint layer_height, gint layer_offset_x, gint layer_offset_y, gboolean layer_visible, GimpColorTag layer_color_tag, gboolean layer_lock_pixels, gboolean layer_lock_position, gboolean layer_lock_visibility, gboolean layer_lock_alpha, gboolean rename_text_layer, gpointer user_data); static void layers_add_mask_callback (GtkWidget *dialog, GList *layers, GimpAddMaskType add_mask_type, GimpChannel *channel, gboolean invert, gpointer user_data); static void layers_scale_callback (GtkWidget *dialog, GimpViewable *viewable, gint width, gint height, GimpUnit *unit, GimpInterpolationType interpolation, gdouble xresolution, gdouble yresolution, GimpUnit *resolution_unit, gpointer user_data); static void layers_resize_callback (GtkWidget *dialog, GimpViewable *viewable, GimpContext *context, gint width, gint height, GimpUnit *unit, gint offset_x, gint offset_y, gdouble unused0, gdouble unused1, GimpUnit *unused2, GimpFillType fill_type, GimpItemSet unused3, gboolean unused4, gpointer data); static gint layers_mode_index (GimpLayerMode layer_mode, const GimpLayerMode *modes, gint n_modes); /* private variables */ static GimpUnit *layer_resize_unit = NULL; static GimpUnit *layer_scale_unit = NULL; static GimpInterpolationType layer_scale_interp = -1; /* public functions */ void layers_edit_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GtkWidget *widget; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); if (g_list_length (layers) != 1) return; if (gimp_item_is_text_layer (GIMP_ITEM (layers->data))) { layers_edit_text_cmd_callback (action, value, data); } else { layers_edit_attributes_cmd_callback (action, value, data); } } void layers_edit_text_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GimpLayer *layer; GList *layers; GtkWidget *widget; GimpTool *active_tool; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); if (g_list_length (layers) != 1) return; layer = layers->data; g_return_if_fail (gimp_item_is_text_layer (GIMP_ITEM (layer))); active_tool = tool_manager_get_active (image->gimp); if (! GIMP_IS_TEXT_TOOL (active_tool)) { GimpToolInfo *tool_info = gimp_get_tool_info (image->gimp, "gimp-text-tool"); if (GIMP_IS_TOOL_INFO (tool_info)) { gimp_context_set_tool (action_data_get_context (data), tool_info); active_tool = tool_manager_get_active (image->gimp); } } if (GIMP_IS_TEXT_TOOL (active_tool)) { if (gimp_text_tool_set_layer (GIMP_TEXT_TOOL (active_tool), layer)) { GimpDisplayShell *shell; shell = gimp_display_get_shell (active_tool->display); gtk_widget_grab_focus (shell->canvas); } } } void layers_edit_attributes_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GimpLayer *layer; GList *layers; GtkWidget *widget; GtkWidget *dialog; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); if (g_list_length (layers) != 1) return; layer = layers->data; if (gimp_layer_is_floating_sel (layer)) return; #define EDIT_DIALOG_KEY "gimp-layer-edit-attributes-dialog" dialog = dialogs_get_dialog (G_OBJECT (layer), EDIT_DIALOG_KEY); if (! dialog) { GimpItem *item = GIMP_ITEM (layer); dialog = layer_options_dialog_new (gimp_item_get_image (GIMP_ITEM (layer)), layer, action_data_get_context (data), widget, _("Layer Attributes"), "gimp-layer-edit", GIMP_ICON_EDIT, _("Edit Layer Attributes"), GIMP_HELP_LAYER_EDIT, gimp_object_get_name (layer), gimp_layer_get_mode (layer), gimp_layer_get_blend_space (layer), gimp_layer_get_composite_space (layer), gimp_layer_get_composite_mode (layer), gimp_layer_get_opacity (layer), 0 /* unused */, gimp_item_get_visible (item), gimp_item_get_color_tag (item), gimp_item_get_lock_content (item), gimp_item_get_lock_position (item), gimp_item_get_lock_visibility (item), gimp_layer_get_lock_alpha (layer), layers_edit_attributes_callback, NULL); dialogs_attach_dialog (G_OBJECT (layer), EDIT_DIALOG_KEY, dialog); } gtk_window_present (GTK_WINDOW (dialog)); } void layers_new_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GtkWidget *widget; GimpLayer *floating_sel; GtkWidget *dialog; return_if_no_image (image, data); return_if_no_widget (widget, data); /* If there is a floating selection, the new command transforms * the current fs into a new layer */ if ((floating_sel = gimp_image_get_floating_selection (image))) { GError *error = NULL; if (! floating_sel_to_layer (floating_sel, &error)) { gimp_message_literal (image->gimp, G_OBJECT (widget), GIMP_MESSAGE_WARNING, error->message); g_clear_error (&error); return; } gimp_image_flush (image); return; } #define NEW_DIALOG_KEY "gimp-layer-new-dialog" dialog = dialogs_get_dialog (G_OBJECT (image), NEW_DIALOG_KEY); if (! dialog) { GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); const gchar *title; gchar *desc; gint n_layers; GimpLayerMode layer_mode = config->layer_new_mode; n_layers = g_list_length (gimp_image_get_selected_layers (image)); title = ngettext ("New Layer", "New Layers", n_layers > 0 ? n_layers : 1); desc = ngettext ("Create a New Layer", "Create %d New Layers", n_layers > 0 ? n_layers : 1); desc = g_strdup_printf (desc, n_layers > 0 ? n_layers : 1); if (layer_mode == GIMP_LAYER_MODE_NORMAL || layer_mode == GIMP_LAYER_MODE_NORMAL_LEGACY) { layer_mode = gimp_image_get_default_new_layer_mode (image); } dialog = layer_options_dialog_new (image, NULL, action_data_get_context (data), widget, title, "gimp-layer-new", GIMP_ICON_LAYER, desc, GIMP_HELP_LAYER_NEW, config->layer_new_name, layer_mode, config->layer_new_blend_space, config->layer_new_composite_space, config->layer_new_composite_mode, config->layer_new_opacity, config->layer_new_fill_type, TRUE, GIMP_COLOR_TAG_NONE, FALSE, FALSE, FALSE, FALSE, layers_new_callback, NULL); g_free (desc); dialogs_attach_dialog (G_OBJECT (image), NEW_DIALOG_KEY, dialog); } gtk_window_present (GTK_WINDOW (dialog)); } void layers_new_last_vals_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GtkWidget *widget; GimpLayer *layer; GimpDialogConfig *config; GList *layers; GList *new_layers = NULL; GList *iter; GimpLayerMode layer_mode; gint n_layers; gboolean run_once; return_if_no_image (image, data); return_if_no_widget (widget, data); config = GIMP_DIALOG_CONFIG (image->gimp->config); /* If there is a floating selection, the new command transforms * the current fs into a new layer */ if (gimp_image_get_floating_selection (image)) { layers_new_cmd_callback (action, value, data); return; } layer_mode = config->layer_new_mode; if (layer_mode == GIMP_LAYER_MODE_NORMAL || layer_mode == GIMP_LAYER_MODE_NORMAL_LEGACY) { layer_mode = gimp_image_get_default_new_layer_mode (image); } layers = gimp_image_get_selected_layers (image); layers = g_list_copy (layers); n_layers = g_list_length (layers); run_once = (n_layers == 0); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, ngettext ("New layer", "New layers", n_layers > 0 ? n_layers : 1)); for (iter = layers; iter || run_once ; iter = iter ? iter->next : NULL) { GimpLayer *parent; gint position; run_once = FALSE; if (iter) { if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data))) { parent = iter->data; position = 0; } else { parent = GIMP_LAYER (gimp_item_get_parent (iter->data)); position = gimp_item_get_index (iter->data); } } else /* run_once */ { parent = NULL; position = -1; } layer = gimp_layer_new (image, gimp_image_get_width (image), gimp_image_get_height (image), gimp_image_get_layer_format (image, TRUE), config->layer_new_name, config->layer_new_opacity, layer_mode); gimp_drawable_fill (GIMP_DRAWABLE (layer), action_data_get_context (data), config->layer_new_fill_type); gimp_layer_set_blend_space (layer, config->layer_new_blend_space, FALSE); gimp_layer_set_composite_space (layer, config->layer_new_composite_space, FALSE); gimp_layer_set_composite_mode (layer, config->layer_new_composite_mode, FALSE); gimp_image_add_layer (image, layer, parent, position, TRUE); new_layers = g_list_prepend (new_layers, layer); } gimp_image_set_selected_layers (image, new_layers); gimp_image_undo_group_end (image); g_list_free (layers); g_list_free (new_layers); gimp_image_flush (image); } void layers_new_from_visible_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GimpDisplayShell *shell; GimpLayer *layer; GimpPickable *pickable; GimpColorProfile *profile; return_if_no_image (image, data); return_if_no_shell (shell, data); pickable = gimp_display_shell_get_canvas_pickable (shell); gimp_pickable_flush (pickable); profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image)); layer = gimp_layer_new_from_gegl_buffer (gimp_pickable_get_buffer (pickable), image, gimp_image_get_layer_format (image, TRUE), _("Visible"), GIMP_OPACITY_OPAQUE, gimp_image_get_default_new_layer_mode (image), profile); gimp_image_add_layer (image, layer, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); gimp_image_flush (image); } void layers_new_group_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *new_layers = NULL; GList *layers; GList *iter; gint n_layers; gboolean run_once; return_if_no_image (image, data); layers = gimp_image_get_selected_layers (image); layers = g_list_copy (layers); n_layers = g_list_length (layers); run_once = (n_layers == 0); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, ngettext ("New layer group", "New layer groups", n_layers > 0 ? n_layers : 1)); for (iter = layers; iter || run_once ; iter = iter ? iter->next : NULL) { GimpLayer *layer; GimpLayer *parent; gint position; run_once = FALSE; if (iter) { if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data))) { parent = iter->data; position = 0; } else { parent = GIMP_LAYER (gimp_item_get_parent (iter->data)); position = gimp_item_get_index (iter->data); } } else /* run_once */ { parent = NULL; position = -1; } layer = gimp_group_layer_new (image); gimp_image_add_layer (image, layer, parent, position, TRUE); new_layers = g_list_prepend (new_layers, layer); } gimp_image_set_selected_layers (image, new_layers); gimp_image_undo_group_end (image); gimp_image_flush (image); g_list_free (layers); g_list_free (new_layers); } void layers_select_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *new_layers = NULL; GList *layers; GList *iter; GimpActionSelectType select_type; gboolean run_once; return_if_no_image (image, data); select_type = (GimpActionSelectType) g_variant_get_int32 (value); layers = gimp_image_get_selected_layers (image); run_once = (g_list_length (layers) == 0); for (iter = layers; iter || run_once; iter = iter ? iter->next : NULL) { GimpLayer *new_layer; GimpContainer *container; if (iter) { container = gimp_item_get_container (GIMP_ITEM (iter->data)); } else /* run_once */ { container = gimp_image_get_layers (image); run_once = FALSE; } new_layer = (GimpLayer *) action_select_object (select_type, container, iter ? iter->data : NULL); if (new_layer) new_layers = g_list_prepend (new_layers, new_layer); } if (new_layers) { gimp_image_set_selected_layers (image, new_layers); gimp_image_flush (image); } g_list_free (new_layers); } void layers_raise_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GList *raised_layers = NULL; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { gint index; index = gimp_item_get_index (iter->data); if (index > 0) { raised_layers = g_list_prepend (raised_layers, iter->data); } else { gimp_image_flush (image); g_list_free (raised_layers); return; } } gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE, ngettext ("Raise Layer", "Raise Layers", g_list_length (raised_layers))); raised_layers = g_list_reverse (raised_layers); for (iter = raised_layers; iter; iter = iter->next) gimp_image_raise_item (image, iter->data, NULL); gimp_image_flush (image); gimp_image_undo_group_end (image); g_list_free (raised_layers); } void layers_raise_to_top_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GList *raised_layers = NULL; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { gint index; index = gimp_item_get_index (iter->data); if (index > 0) raised_layers = g_list_prepend (raised_layers, iter->data); } gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE, ngettext ("Raise Layer to Top", "Raise Layers to Top", g_list_length (raised_layers))); for (iter = raised_layers; iter; iter = iter->next) gimp_image_raise_item_to_top (image, iter->data); gimp_image_flush (image); gimp_image_undo_group_end (image); g_list_free (raised_layers); } void layers_lower_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GList *lowered_layers = NULL; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { GList *layer_list; gint index; layer_list = gimp_item_get_container_iter (GIMP_ITEM (iter->data)); index = gimp_item_get_index (iter->data); if (index < g_list_length (layer_list) - 1) { lowered_layers = g_list_prepend (lowered_layers, iter->data); } else { gimp_image_flush (image); g_list_free (lowered_layers); return; } } gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE, ngettext ("Lower Layer", "Lower Layers", g_list_length (lowered_layers))); for (iter = lowered_layers; iter; iter = iter->next) gimp_image_lower_item (image, iter->data, NULL); gimp_image_flush (image); gimp_image_undo_group_end (image); g_list_free (lowered_layers); } void layers_lower_to_bottom_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GList *lowered_layers = NULL; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { GList *layer_list; gint index; layer_list = gimp_item_get_container_iter (GIMP_ITEM (iter->data)); index = gimp_item_get_index (iter->data); if (index < g_list_length (layer_list) - 1) lowered_layers = g_list_prepend (lowered_layers, iter->data); } gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE, ngettext ("Lower Layer to Bottom", "Lower Layers to Bottom", g_list_length (lowered_layers))); for (iter = lowered_layers; iter; iter = iter->next) gimp_image_lower_item_to_bottom (image, iter->data); gimp_image_flush (image); gimp_image_undo_group_end (image); g_list_free (lowered_layers); } void layers_duplicate_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *new_layers = NULL; GList *iter; return_if_no_layers (image, layers, data); layers = g_list_copy (layers); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, _("Duplicate layers")); for (iter = layers; iter; iter = iter->next) { GimpLayer *new_layer; GimpContainer *filters; new_layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (iter->data), G_TYPE_FROM_INSTANCE (iter->data))); /* use the actual parent here, not GIMP_IMAGE_ACTIVE_PARENT because * the latter would add a duplicated group inside itself instead of * above it */ gimp_image_add_layer (image, new_layer, gimp_layer_get_parent (iter->data), gimp_item_get_index (iter->data), TRUE); gimp_drawable_enable_resize_undo (GIMP_DRAWABLE (new_layer)); new_layers = g_list_prepend (new_layers, new_layer); /* Import any attached layer effects */ filters = gimp_drawable_get_filters (GIMP_DRAWABLE (iter->data)); if (gimp_container_get_n_children (filters) > 0) { GList *filter_list; GimpContainer *filters; filters = gimp_drawable_get_filters (GIMP_DRAWABLE (iter->data)); for (filter_list = GIMP_LIST (filters)->queue->tail; filter_list; filter_list = g_list_previous (filter_list)) { if (GIMP_IS_DRAWABLE_FILTER (filter_list->data)) { GimpDrawableFilter *old_filter = filter_list->data; GimpDrawableFilter *filter; filter = gimp_drawable_filter_duplicate (GIMP_DRAWABLE (new_layer), old_filter); if (filter != NULL) { gimp_drawable_filter_apply (filter, NULL); gimp_drawable_filter_commit (filter, TRUE, NULL, FALSE); gimp_drawable_filter_layer_mask_freeze (filter); g_object_unref (filter); } } } } } gimp_image_set_selected_layers (image, new_layers); g_list_free (layers); g_list_free (new_layers); gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_anchor_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; return_if_no_layers (image, layers, data); if (g_list_length (layers) == 1 && gimp_layer_is_floating_sel (layers->data)) { floating_sel_anchor (layers->data); gimp_image_flush (image); } } void layers_merge_down_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GimpDisplay *display; GError *error = NULL; return_if_no_layers (image, layers, data); return_if_no_display (display, data); layers = gimp_image_merge_down (image, layers, action_data_get_context (data), GIMP_EXPAND_AS_NECESSARY, GIMP_PROGRESS (display), &error); if (error) { gimp_message_literal (image->gimp, G_OBJECT (display), GIMP_MESSAGE_WARNING, error->message); g_clear_error (&error); return; } gimp_image_set_selected_layers (image, layers); g_list_free (layers); gimp_image_flush (image); } void layers_merge_group_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *merge_layers = NULL; GList *iter; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data))) { GList *iter2; for (iter2 = layers; iter2; iter2 = iter2->next) { if (iter->data == iter2->data) continue; /* Do not merge a layer when we already merge one of its * ancestors. */ if (gimp_viewable_is_ancestor (iter2->data, iter->data)) break; /* Do not merge a layer which has a little sister (same * parent and smaller index) or a little cousin (one of * its ancestors is a little sister) of a pass-through * group layer. * These will be rendered and merged through the * pass-through by definition. */ if (gimp_viewable_get_children (GIMP_VIEWABLE (iter2->data)) && gimp_layer_get_mode (iter2->data) == GIMP_LAYER_MODE_PASS_THROUGH) { GimpLayer *pass_through_parent = gimp_layer_get_parent (iter2->data); GimpLayer *cousin = iter->data; gboolean ignore = FALSE; do { GimpLayer *cousin_parent = gimp_layer_get_parent (cousin); if (pass_through_parent == cousin_parent && gimp_item_get_index (GIMP_ITEM (iter2->data)) < gimp_item_get_index (GIMP_ITEM (cousin))) { ignore = TRUE; break; } cousin = cousin_parent; } while (cousin != NULL); if (ignore) break; } } if (iter2 == NULL) merge_layers = g_list_prepend (merge_layers, iter->data); } } if (g_list_length (merge_layers) > 1) { gchar *undo_name; undo_name = g_strdup_printf (C_("undo-type", "Merge %d Layer Groups"), g_list_length (merge_layers)); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, undo_name); g_free (undo_name); } for (iter = merge_layers; iter; iter = iter->next) gimp_image_merge_group_layer (image, GIMP_GROUP_LAYER (iter->data)); if (g_list_length (merge_layers) > 1) gimp_image_undo_group_end (image); g_list_free (merge_layers); gimp_image_flush (image); } void layers_delete_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *removed_layers; GList *iter; GList *iter2; return_if_no_image (image, data); layers = gimp_image_get_selected_layers (image); /*TODO: we should have a failsafe to determine when we are going to * delete all layers (i.e. all layers of first level at least) and * forbid it. */ /* Copy of the original selection. */ removed_layers = g_list_copy (layers); /* Removing children layers (they will be removed anyway by removing * the parent). */ for (iter = removed_layers; iter; iter = iter->next) { for (iter2 = removed_layers; iter2; iter2 = iter2->next) { if (iter->data != iter2->data && gimp_viewable_is_ancestor (iter2->data, iter->data)) { removed_layers = g_list_delete_link (removed_layers, iter); iter = removed_layers; break; } } } if (g_list_length (removed_layers) > 1) { gchar *undo_name; undo_name = g_strdup_printf (C_("undo-type", "Remove %d Layers"), g_list_length (removed_layers)); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_ITEM_REMOVE, undo_name); } for (iter = removed_layers; iter; iter = iter->next) gimp_image_remove_layer (image, iter->data, TRUE, NULL); if (g_list_length (removed_layers) > 1) gimp_image_undo_group_end (image); g_list_free (removed_layers); gimp_image_flush (image); } void layers_text_discard_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; return_if_no_layers (image, layers, data); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, _("Discard Text Information")); for (iter = layers; iter; iter = iter->next) if (GIMP_IS_TEXT_LAYER (iter->data)) gimp_text_layer_discard (GIMP_TEXT_LAYER (iter->data)); gimp_image_undo_group_end (image); } void layers_text_to_vectors_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; return_if_no_layers (image, layers, data); /* TODO: have the proper undo group. */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_PATHS_IMPORT, _("Add Paths")); for (iter = layers; iter; iter = iter->next) { GimpLayer *layer = iter->data; if (GIMP_IS_TEXT_LAYER (layer)) { GimpPath *path; gint x, y; path = gimp_text_path_new (image, GIMP_TEXT_LAYER (layer)->text); gimp_item_get_offset (GIMP_ITEM (layer), &x, &y); gimp_item_translate (GIMP_ITEM (path), x, y, FALSE); gimp_image_add_path (image, path, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); gimp_image_flush (image); } } gimp_image_undo_group_end (image); } void layers_text_along_vectors_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *paths; GimpLayer *layer; GimpPath *path; return_if_no_layers (image, layers, data); return_if_no_paths (image, paths, data); if (g_list_length (layers) != 1 || g_list_length (paths) != 1) return; layer = layers->data; path = paths->data; if (GIMP_IS_TEXT_LAYER (layer)) { gdouble box_width; gdouble box_height; GimpPath *new_path; gdouble offset; box_width = gimp_item_get_width (GIMP_ITEM (layer)); box_height = gimp_item_get_height (GIMP_ITEM (layer)); new_path = gimp_text_path_new (image, GIMP_TEXT_LAYER (layer)->text); offset = 0; switch (GIMP_TEXT_LAYER (layer)->text->base_dir) { case GIMP_TEXT_DIRECTION_LTR: case GIMP_TEXT_DIRECTION_RTL: offset = 0.5 * box_height; break; case GIMP_TEXT_DIRECTION_TTB_RTL: case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: case GIMP_TEXT_DIRECTION_TTB_LTR: case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: { GimpStroke *stroke = NULL; while ((stroke = gimp_path_stroke_get_next (new_path, stroke))) { gimp_stroke_rotate (stroke, 0, 0, 270); gimp_stroke_translate (stroke, 0, box_width); } } offset = 0.5 * box_width; break; } gimp_path_warp_path (path, new_path, offset); gimp_item_set_visible (GIMP_ITEM (new_path), TRUE, FALSE); gimp_image_add_path (image, new_path, GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE); gimp_image_flush (image); } } void layers_resize_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GimpLayer *layer; GList *layers; GtkWidget *widget; GtkWidget *dialog; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); #define RESIZE_DIALOG_KEY "gimp-resize-dialog" g_return_if_fail (g_list_length (layers) == 1); layer = layers->data; dialog = dialogs_get_dialog (G_OBJECT (layer), RESIZE_DIALOG_KEY); if (! dialog) { GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); GimpDisplay *display = NULL; if (GIMP_IS_IMAGE_WINDOW (data)) display = action_data_get_display (data); if (layer_resize_unit == NULL) layer_resize_unit = gimp_unit_pixel (); if (layer_resize_unit != gimp_unit_percent () && display) layer_resize_unit = gimp_display_get_shell (display)->unit; dialog = resize_dialog_new (GIMP_VIEWABLE (layer), action_data_get_context (data), _("Set Layer Boundary Size"), "gimp-layer-resize", widget, gimp_standard_help_func, GIMP_HELP_LAYER_RESIZE, layer_resize_unit, config->layer_resize_fill_type, GIMP_ITEM_SET_NONE, FALSE, layers_resize_callback, NULL); dialogs_attach_dialog (G_OBJECT (layer), RESIZE_DIALOG_KEY, dialog); } gtk_window_present (GTK_WINDOW (dialog)); } void layers_resize_to_image_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; return_if_no_layers (image, layers, data); if (g_list_length (layers) > 1) gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, _("Layers to Image Size")); for (iter = layers; iter; iter = iter->next) gimp_layer_resize_to_image (iter->data, action_data_get_context (data), GIMP_FILL_TRANSPARENT); if (g_list_length (layers) > 1) gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_scale_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GimpLayer *layer; GtkWidget *widget; GtkWidget *dialog; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); #define SCALE_DIALOG_KEY "gimp-scale-dialog" g_return_if_fail (g_list_length (layers) == 1); layer = layers->data; dialog = dialogs_get_dialog (G_OBJECT (layer), SCALE_DIALOG_KEY); if (! dialog) { GimpDisplay *display = NULL; if (GIMP_IS_IMAGE_WINDOW (data)) display = action_data_get_display (data); if (layer_scale_unit == NULL) layer_scale_unit = gimp_unit_pixel ();; if (layer_scale_unit != gimp_unit_percent () && display) layer_scale_unit = gimp_display_get_shell (display)->unit; if (layer_scale_interp == -1) layer_scale_interp = image->gimp->config->interpolation_type; dialog = scale_dialog_new (GIMP_VIEWABLE (layer), action_data_get_context (data), _("Scale Layer"), "gimp-layer-scale", widget, gimp_standard_help_func, GIMP_HELP_LAYER_SCALE, layer_scale_unit, layer_scale_interp, layers_scale_callback, display); dialogs_attach_dialog (G_OBJECT (layer), SCALE_DIALOG_KEY, dialog); } gtk_window_present (GTK_WINDOW (dialog)); } void layers_crop_to_selection_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GtkWidget *widget; gchar *desc; gint x, y; gint width, height; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); if (! gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), &x, &y, &width, &height)) { gimp_message_literal (image->gimp, G_OBJECT (widget), GIMP_MESSAGE_WARNING, _("Cannot crop because the current selection " "is empty.")); return; } desc = g_strdup_printf (ngettext ("Crop Layer to Selection", "Crop %d Layers to Selection", g_list_length (layers)), g_list_length (layers)); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, desc); g_free (desc); for (iter = layers; iter; iter = iter->next) { gint off_x, off_y; gimp_item_get_offset (GIMP_ITEM (iter->data), &off_x, &off_y); off_x -= x; off_y -= y; gimp_item_resize (GIMP_ITEM (iter->data), action_data_get_context (data), GIMP_FILL_TRANSPARENT, width, height, off_x, off_y); } gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_crop_to_content_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GtkWidget *widget; gchar *desc; gint x, y; gint width, height; gint n_croppable = 0; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); for (iter = layers; iter; iter = iter->next) { switch (gimp_pickable_auto_shrink (GIMP_PICKABLE (iter->data), 0, 0, gimp_item_get_width (iter->data), gimp_item_get_height (iter->data), &x, &y, &width, &height)) { case GIMP_AUTO_SHRINK_SHRINK: n_croppable++; break; case GIMP_AUTO_SHRINK_EMPTY: /* Cannot crop because the layer has no content. */ case GIMP_AUTO_SHRINK_UNSHRINKABLE: /* Cannot crop because the active layer is already cropped to * its content. */ break; } } if (n_croppable == 0) { gimp_message_literal (image->gimp, G_OBJECT (widget), GIMP_MESSAGE_INFO, _("Cannot crop because none of the selected" " layers have content or they are already" " cropped to their content.")); return; } desc = g_strdup_printf (ngettext ("Crop Layer to Content", "Crop %d Layers to Content", n_croppable), n_croppable); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE, desc); g_free (desc); for (iter = layers; iter; iter = iter->next) { switch (gimp_pickable_auto_shrink (GIMP_PICKABLE (iter->data), 0, 0, gimp_item_get_width (iter->data), gimp_item_get_height (iter->data), &x, &y, &width, &height)) { case GIMP_AUTO_SHRINK_SHRINK: gimp_item_resize (iter->data, action_data_get_context (data), GIMP_FILL_TRANSPARENT, width, height, -x, -y); break; default: break; } } gimp_image_flush (image); gimp_image_undo_group_end (image); } void layers_mask_add_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GtkWidget *widget; GtkWidget *dialog; GList *update_layers = NULL; gint n_channels = 0; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); for (iter = layers; iter; iter = iter->next) { g_return_if_fail (GIMP_IS_LAYER (iter->data)); if (! gimp_layer_get_mask (iter->data)) { update_layers = g_list_prepend (update_layers, iter->data); n_channels++; } } if (n_channels == 0) /* No layers or they all have masks already. */ return; #define ADD_MASK_DIALOG_KEY "gimp-add-mask-dialog" for (iter = update_layers; iter; iter = iter->next) { dialog = dialogs_get_dialog (G_OBJECT (iter->data), ADD_MASK_DIALOG_KEY); if (dialog) break; } if (! dialog) { GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); dialog = layer_add_mask_dialog_new (update_layers, action_data_get_context (data), widget, config->layer_add_mask_type, config->layer_add_mask_invert, layers_add_mask_callback, NULL); for (iter = update_layers; iter; iter = iter->next) dialogs_attach_dialog (G_OBJECT (iter->data), ADD_MASK_DIALOG_KEY, dialog); } gtk_window_present (GTK_WINDOW (dialog)); } void layers_mask_add_last_vals_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GtkWidget *widget; GimpDialogConfig *config; GimpChannel *channel = NULL; GimpLayerMask *mask; return_if_no_layers (image, layers, data); return_if_no_widget (widget, data); config = GIMP_DIALOG_CONFIG (image->gimp->config); if (config->layer_add_mask_type == GIMP_ADD_MASK_CHANNEL) { GList *selected_channels; selected_channels = gimp_image_get_selected_channels (image); if (selected_channels) { channel = selected_channels->data; } else { GimpContainer *channels = gimp_image_get_channels (image); channel = GIMP_CHANNEL (gimp_container_get_first_child (channels)); } if (! channel) { layers_mask_add_cmd_callback (action, value, data); return; } } for (iter = layers; iter; iter = iter->next) { if (! gimp_layer_get_mask (iter->data)) break; } if (iter == NULL) /* No layers or they all have masks already. */ return; gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, _("Add Layer Masks")); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data)) continue; mask = gimp_layer_create_mask (iter->data, config->layer_add_mask_type, channel); if (config->layer_add_mask_invert) gimp_channel_invert (GIMP_CHANNEL (mask), FALSE); gimp_layer_add_mask (iter->data, mask, TRUE, NULL); } gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_mask_apply_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpMaskApplyMode mode; GimpImage *image; GList *layers; GList *iter; gchar *undo_text = NULL; GimpUndoType undo_type = GIMP_UNDO_GROUP_NONE; return_if_no_layers (image, layers, data); mode = (GimpMaskApplyMode) g_variant_get_int32 (value); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data) && (mode != GIMP_MASK_APPLY || (! gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)) && ! gimp_item_is_content_locked (GIMP_ITEM (iter->data), NULL)))) break; } if (iter == NULL) /* No layers or none have applicable masks. */ return; switch (mode) { case GIMP_MASK_APPLY: undo_type = GIMP_UNDO_GROUP_MASK; undo_text = _("Apply Layer Masks"); break; case GIMP_MASK_DISCARD: undo_type = GIMP_UNDO_GROUP_MASK; undo_text = _("Delete Layer Masks"); break; default: g_warning ("%s: unhandled GimpMaskApplyMode %d\n", G_STRFUNC, mode); break; } if (undo_type != GIMP_UNDO_GROUP_NONE) gimp_image_undo_group_start (image, undo_type, undo_text); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data)) { if (mode == GIMP_MASK_APPLY && (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data)) || gimp_item_is_content_locked (GIMP_ITEM (iter->data), NULL))) /* Layer groups cannot apply masks. Neither can * content-locked layers. */ continue; gimp_layer_apply_mask (iter->data, mode, TRUE); } } if (undo_type != GIMP_UNDO_GROUP_NONE) gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_mask_edit_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; gboolean active = g_variant_get_boolean (value); return_if_no_layers (image, layers, data); /* Multiple-layer selection cannot edit masks. */ active = active && (g_list_length (layers) == 1); for (iter = layers; iter; iter = iter->next) if (gimp_layer_get_mask (iter->data)) gimp_layer_set_edit_mask (iter->data, active); gimp_image_flush (image); } void layers_mask_show_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; gboolean active = g_variant_get_boolean (value); gboolean have_masks = FALSE; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data)) { have_masks = TRUE; /* A bit of tricky to handle multiple and diverse layers with * a toggle action (with only binary state). * In non-active state, we will consider sets of both shown * and hidden masks as ok and exits. This allows us to switch * the action "active" state without actually changing * individual masks state without explicit user request. */ if (! active && ! gimp_layer_get_show_mask (iter->data)) return; } } if (! have_masks) return; gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, _("Show Layer Masks")); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data)) { gimp_layer_set_show_mask (iter->data, active, TRUE); } } gimp_image_flush (image); gimp_image_undo_group_end (image); } void layers_mask_disable_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; gboolean active = g_variant_get_boolean (value); gboolean have_masks = FALSE; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data)) { have_masks = TRUE; /* A bit of tricky to handle multiple and diverse layers with * a toggle action (with only binary state). * In non-active state, we will consider sets of both enabled * and disabled masks as ok and exits. This allows us to * switch the action "active" state without actually changing * individual masks state without explicit user request. */ if (! active && gimp_layer_get_apply_mask (iter->data)) return; } } if (! have_masks) return; gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, _("Disable Layer Masks")); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data)) { gimp_layer_set_apply_mask (iter->data, ! active, TRUE); } } gimp_image_flush (image); gimp_image_undo_group_end (image); } void layers_mask_to_selection_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GList *masks = NULL; return_if_no_layers (image, layers, data); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_get_mask (iter->data)) masks = g_list_prepend (masks, gimp_layer_get_mask (iter->data)); } if (masks) { GimpChannelOps operation = (GimpChannelOps) g_variant_get_int32 (value); switch (operation) { case GIMP_CHANNEL_OP_REPLACE: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Masks to Selection")); break; case GIMP_CHANNEL_OP_ADD: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Add Masks to Selection")); break; case GIMP_CHANNEL_OP_SUBTRACT: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Subtract Masks from Selection")); break; case GIMP_CHANNEL_OP_INTERSECT: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Intersect Masks with Selection")); break; } gimp_channel_combine_items (gimp_image_get_mask (image), masks, operation); gimp_image_flush (image); g_list_free (masks); } } void layers_alpha_add_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; return_if_no_layers (image, layers, data); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD_ALPHA, _("Add Alpha Channel")); for (iter = layers; iter; iter = iter->next) if (! gimp_drawable_has_alpha (iter->data)) gimp_layer_add_alpha (iter->data); gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_alpha_remove_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; return_if_no_layers (image, layers, data); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD_ALPHA, _("Remove Alpha Channel")); for (iter = layers; iter; iter = iter->next) if (gimp_drawable_has_alpha (iter->data)) gimp_layer_remove_alpha (iter->data, action_data_get_context (data)); gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_alpha_to_selection_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GimpDisplay *display; GList *layers; GimpChannelOps operation; return_if_no_layers (image, layers, data); return_if_no_display (display, data); operation = (GimpChannelOps) g_variant_get_int32 (value); switch (operation) { case GIMP_CHANNEL_OP_REPLACE: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Alpha to Selection")); break; case GIMP_CHANNEL_OP_ADD: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Add Alpha to Selection")); break; case GIMP_CHANNEL_OP_SUBTRACT: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Subtract Alpha from Selection")); break; case GIMP_CHANNEL_OP_INTERSECT: gimp_channel_push_undo (gimp_image_get_mask (image), C_("undo-type", "Intersect Alpha with Selection")); break; } gimp_channel_combine_items (gimp_image_get_mask (image), layers, operation); gimp_image_flush (image); if (gimp_channel_is_empty (gimp_image_get_mask (image))) { gimp_message_literal (image->gimp, G_OBJECT (display), GIMP_MESSAGE_WARNING, _("Empty Selection")); } } void layers_opacity_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; gdouble opacity; GimpUndo *undo; GimpActionSelectType select_type; gboolean push_undo = TRUE; return_if_no_layers (image, layers, data); select_type = (GimpActionSelectType) g_variant_get_int32 (value); if (g_list_length (layers) == 1) { undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, GIMP_UNDO_LAYER_OPACITY); if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layers->data)) push_undo = FALSE; } if (g_list_length (layers) > 1) gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_OPACITY, _("Set layers opacity")); for (iter = layers; iter; iter = iter->next) { opacity = action_select_value (select_type, gimp_layer_get_opacity (iter->data), 0.0, 1.0, 1.0, 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); gimp_layer_set_opacity (iter->data, opacity, push_undo); } if (g_list_length (layers) > 1) gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_mode_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; GimpActionSelectType select_type; gboolean push_undo = TRUE; return_if_no_layers (image, layers, data); select_type = (GimpActionSelectType) g_variant_get_int32 (value); if (g_list_length (layers) == 1) { GimpUndo *undo; undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO, GIMP_UNDO_LAYER_MODE); if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layers->data)) push_undo = FALSE; } if (g_list_length (layers) > 1) gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_OPACITY, _("Set layers opacity")); for (iter = layers; iter; iter = iter->next) { GimpLayerMode *modes; gint n_modes; GimpLayerMode layer_mode; gint index; layer_mode = gimp_layer_get_mode (iter->data); modes = gimp_layer_mode_get_context_array (layer_mode, GIMP_LAYER_MODE_CONTEXT_LAYER, &n_modes); index = layers_mode_index (layer_mode, modes, n_modes); index = action_select_value (select_type, index, 0, n_modes - 1, 0, 0.0, 1.0, 1.0, 0.0, FALSE); layer_mode = modes[index]; g_free (modes); gimp_layer_set_mode (iter->data, layer_mode, push_undo); } if (g_list_length (layers) > 1) gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_blend_space_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *update_layers = NULL; GList *iter; GimpLayerColorSpace blend_space; gboolean push_undo = TRUE; return_if_no_layers (image, layers, data); blend_space = (GimpLayerColorSpace) g_variant_get_int32 (value); for (iter = layers; iter; iter = iter->next) { GimpLayerMode mode; mode = gimp_layer_get_mode (iter->data); if (gimp_layer_mode_is_blend_space_mutable (mode) && blend_space != gimp_layer_get_blend_space (iter->data)) update_layers = g_list_prepend (update_layers, iter->data); } if (g_list_length (update_layers) == 1) { GimpUndo *undo; undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO, GIMP_UNDO_LAYER_MODE); if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (update_layers->data)) push_undo = FALSE; } if (update_layers) { if (g_list_length (update_layers) > 1) gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_MODE, _("Set layers' blend space")); for (iter = update_layers; iter; iter = iter->next) gimp_layer_set_blend_space (iter->data, blend_space, push_undo); if (g_list_length (update_layers) > 1) gimp_image_undo_group_end (image); g_list_free (update_layers); gimp_image_flush (image); } } void layers_composite_space_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *update_layers = NULL; GList *iter; GimpLayerColorSpace composite_space; gboolean push_undo = TRUE; return_if_no_layers (image, layers, data); composite_space = (GimpLayerColorSpace) g_variant_get_int32 (value); for (iter = layers; iter; iter = iter->next) { GimpLayerMode mode; mode = gimp_layer_get_mode (iter->data); if (gimp_layer_mode_is_composite_space_mutable (mode) && composite_space != gimp_layer_get_composite_space (iter->data)) update_layers = g_list_prepend (update_layers, iter->data); } if (g_list_length (update_layers) == 1) { GimpUndo *undo; undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO, GIMP_UNDO_LAYER_MODE); if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (update_layers->data)) push_undo = FALSE; } if (update_layers) { if (g_list_length (update_layers) > 1) gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_MODE, _("Set layers' composite space")); for (iter = update_layers; iter; iter = iter->next) gimp_layer_set_composite_space (iter->data, composite_space, push_undo); if (g_list_length (update_layers) > 1) gimp_image_undo_group_end (image); g_list_free (update_layers); gimp_image_flush (image); } } void layers_composite_mode_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *update_layers = NULL; GList *iter; GimpLayerCompositeMode composite_mode; gboolean push_undo = TRUE; return_if_no_layers (image, layers, data); composite_mode = (GimpLayerCompositeMode) g_variant_get_int32 (value); for (iter = layers; iter; iter = iter->next) { GimpLayerMode mode; mode = gimp_layer_get_mode (iter->data); if (gimp_layer_mode_is_composite_mode_mutable (mode) && composite_mode != gimp_layer_get_composite_mode (iter->data)) update_layers = g_list_prepend (update_layers, iter->data); } if (g_list_length (update_layers) == 1) { GimpUndo *undo; undo = gimp_image_undo_can_compress (image, GIMP_TYPE_LAYER_PROP_UNDO, GIMP_UNDO_LAYER_MODE); if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (update_layers->data)) push_undo = FALSE; } if (update_layers) { if (g_list_length (update_layers) > 1) gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_MODE, _("Set layers' composite mode")); for (iter = update_layers; iter; iter = iter->next) gimp_layer_set_composite_mode (iter->data, composite_mode, push_undo); if (g_list_length (update_layers) > 1) gimp_image_undo_group_end (image); g_list_free (update_layers); gimp_image_flush (image); } } void layers_visible_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; return_if_no_layers (image, layers, data); items_visible_cmd_callback (action, value, image, layers); } void layers_lock_content_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; return_if_no_layers (image, layers, data); items_lock_content_cmd_callback (action, value, image, layers); } void layers_lock_position_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; return_if_no_layers (image, layers, data); items_lock_position_cmd_callback (action, value, image, layers); } void layers_lock_alpha_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GList *iter; gboolean lock_alpha; gboolean lock_change = FALSE; return_if_no_layers (image, layers, data); lock_alpha = g_variant_get_boolean (value); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_can_lock_alpha (iter->data)) { /* Similar trick as in layers_mask_show_cmd_callback(). * When unlocking, we expect all selected layers to be locked, * otherwise SET_ACTIVE() calls in layers-actions.c will * trigger lock updates. */ if (! lock_alpha && ! gimp_layer_get_lock_alpha (iter->data)) return; if (lock_alpha != gimp_layer_get_lock_alpha (iter->data)) lock_change = TRUE; } } if (! lock_change) /* No layer locks would be changed. */ return; gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_LOCK_ALPHA, lock_alpha ? _("Lock alpha channels") : _("Unlock alpha channels")); for (iter = layers; iter; iter = iter->next) { if (gimp_layer_can_lock_alpha (iter->data)) { if (lock_alpha != gimp_layer_get_lock_alpha (iter->data)) gimp_layer_set_lock_alpha (iter->data, lock_alpha, TRUE); } } gimp_image_undo_group_end (image); gimp_image_flush (image); } void layers_color_tag_cmd_callback (GimpAction *action, GVariant *value, gpointer data) { GimpImage *image; GList *layers; GimpColorTag color_tag; return_if_no_layers (image, layers, data); color_tag = (GimpColorTag) g_variant_get_int32 (value); items_color_tag_cmd_callback (action, image, layers, color_tag); } /* private functions */ static void layers_new_callback (GtkWidget *dialog, GimpImage *image, GimpLayer *layer, GimpContext *context, const gchar *layer_name, GimpLayerMode layer_mode, GimpLayerColorSpace layer_blend_space, GimpLayerColorSpace layer_composite_space, GimpLayerCompositeMode layer_composite_mode, gdouble layer_opacity, GimpFillType layer_fill_type, gint layer_width, gint layer_height, gint layer_offset_x, gint layer_offset_y, gboolean layer_visible, GimpColorTag layer_color_tag, gboolean layer_lock_pixels, gboolean layer_lock_position, gboolean layer_lock_visibility, gboolean layer_lock_alpha, gboolean rename_text_layer, /* unused */ gpointer user_data) { GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); GList *layers = gimp_image_get_selected_layers (image); GList *new_layers = NULL; GList *iter; gint n_layers = g_list_length (layers); gboolean run_once = (n_layers == 0); g_object_set (config, "layer-new-name", layer_name, "layer-new-mode", layer_mode, "layer-new-blend-space", layer_blend_space, "layer-new-composite-space", layer_composite_space, "layer-new-composite-mode", layer_composite_mode, "layer-new-opacity", layer_opacity, "layer-new-fill-type", layer_fill_type, NULL); layers = g_list_copy (layers); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, ngettext ("New layer", "New layers", n_layers > 0 ? n_layers : 1)); for (iter = layers; iter || run_once; iter = iter ? iter->next : NULL) { GimpLayer *parent; gint position; run_once = FALSE; if (iter) { if (gimp_viewable_get_children (GIMP_VIEWABLE (iter->data))) { parent = iter->data; position = 0; } else { parent = GIMP_LAYER (gimp_item_get_parent (iter->data)); position = gimp_item_get_index (iter->data); } } else /* run_once */ { parent = NULL; position = 0; } layer = gimp_layer_new (image, layer_width, layer_height, gimp_image_get_layer_format (image, TRUE), config->layer_new_name, config->layer_new_opacity, config->layer_new_mode); if (layer) { gimp_item_set_offset (GIMP_ITEM (layer), layer_offset_x, layer_offset_y); gimp_drawable_fill (GIMP_DRAWABLE (layer), context, config->layer_new_fill_type); gimp_item_set_visible (GIMP_ITEM (layer), layer_visible, FALSE); gimp_item_set_color_tag (GIMP_ITEM (layer), layer_color_tag, FALSE); gimp_item_set_lock_content (GIMP_ITEM (layer), layer_lock_pixels, FALSE); gimp_item_set_lock_position (GIMP_ITEM (layer), layer_lock_position, FALSE); gimp_item_set_lock_visibility (GIMP_ITEM (layer), layer_lock_visibility, FALSE); gimp_layer_set_lock_alpha (layer, layer_lock_alpha, FALSE); gimp_layer_set_blend_space (layer, layer_blend_space, FALSE); gimp_layer_set_composite_space (layer, layer_composite_space, FALSE); gimp_layer_set_composite_mode (layer, layer_composite_mode, FALSE); gimp_image_add_layer (image, layer, parent, position, TRUE); gimp_image_flush (image); new_layers = g_list_prepend (new_layers, layer); } else { g_warning ("%s: could not allocate new layer", G_STRFUNC); } } gimp_image_undo_group_end (image); gimp_image_set_selected_layers (image, new_layers); g_list_free (layers); g_list_free (new_layers); gtk_widget_destroy (dialog); } static void layers_edit_attributes_callback (GtkWidget *dialog, GimpImage *image, GimpLayer *layer, GimpContext *context, const gchar *layer_name, GimpLayerMode layer_mode, GimpLayerColorSpace layer_blend_space, GimpLayerColorSpace layer_composite_space, GimpLayerCompositeMode layer_composite_mode, gdouble layer_opacity, GimpFillType unused1, gint unused2, gint unused3, gint layer_offset_x, gint layer_offset_y, gboolean layer_visible, GimpColorTag layer_color_tag, gboolean layer_lock_pixels, gboolean layer_lock_position, gboolean layer_lock_visibility, gboolean layer_lock_alpha, gboolean rename_text_layer, gpointer user_data) { GimpItem *item = GIMP_ITEM (layer); if (strcmp (layer_name, gimp_object_get_name (layer)) || layer_mode != gimp_layer_get_mode (layer) || layer_blend_space != gimp_layer_get_blend_space (layer) || layer_composite_space != gimp_layer_get_composite_space (layer) || layer_composite_mode != gimp_layer_get_composite_mode (layer) || layer_opacity != gimp_layer_get_opacity (layer) || layer_offset_x != gimp_item_get_offset_x (item) || layer_offset_y != gimp_item_get_offset_y (item) || layer_visible != gimp_item_get_visible (item) || layer_color_tag != gimp_item_get_color_tag (item) || layer_lock_pixels != gimp_item_get_lock_content (item) || layer_lock_position != gimp_item_get_lock_position (item) || layer_lock_visibility != gimp_item_get_lock_visibility (item) || layer_lock_alpha != gimp_layer_get_lock_alpha (layer)) { gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_PROPERTIES, _("Layer Attributes")); if (strcmp (layer_name, gimp_object_get_name (layer))) { GError *error = NULL; if (! gimp_item_rename (GIMP_ITEM (layer), layer_name, &error)) { gimp_message_literal (image->gimp, G_OBJECT (dialog), GIMP_MESSAGE_WARNING, error->message); g_clear_error (&error); } } if (layer_mode != gimp_layer_get_mode (layer)) gimp_layer_set_mode (layer, layer_mode, TRUE); if (layer_blend_space != gimp_layer_get_blend_space (layer)) gimp_layer_set_blend_space (layer, layer_blend_space, TRUE); if (layer_composite_space != gimp_layer_get_composite_space (layer)) gimp_layer_set_composite_space (layer, layer_composite_space, TRUE); if (layer_composite_mode != gimp_layer_get_composite_mode (layer)) gimp_layer_set_composite_mode (layer, layer_composite_mode, TRUE); if (layer_opacity != gimp_layer_get_opacity (layer)) gimp_layer_set_opacity (layer, layer_opacity, TRUE); if (layer_offset_x != gimp_item_get_offset_x (item) || layer_offset_y != gimp_item_get_offset_y (item)) { gimp_item_translate (item, layer_offset_x - gimp_item_get_offset_x (item), layer_offset_y - gimp_item_get_offset_y (item), TRUE); } if (layer_visible != gimp_item_get_visible (item)) gimp_item_set_visible (item, layer_visible, TRUE); if (layer_color_tag != gimp_item_get_color_tag (item)) gimp_item_set_color_tag (item, layer_color_tag, TRUE); if (layer_lock_pixels != gimp_item_get_lock_content (item)) gimp_item_set_lock_content (item, layer_lock_pixels, TRUE); if (layer_lock_position != gimp_item_get_lock_position (item)) gimp_item_set_lock_position (item, layer_lock_position, TRUE); if (layer_lock_visibility != gimp_item_get_lock_visibility (item)) gimp_item_set_lock_visibility (item, layer_lock_visibility, TRUE); if (layer_lock_alpha != gimp_layer_get_lock_alpha (layer)) gimp_layer_set_lock_alpha (layer, layer_lock_alpha, TRUE); gimp_image_undo_group_end (image); gimp_image_flush (image); } if (gimp_item_is_text_layer (GIMP_ITEM (layer))) { g_object_set (layer, "auto-rename", rename_text_layer, NULL); } gtk_widget_destroy (dialog); } static void layers_add_mask_callback (GtkWidget *dialog, GList *layers, GimpAddMaskType add_mask_type, GimpChannel *channel, gboolean invert, gpointer user_data) { GimpImage *image = gimp_item_get_image (GIMP_ITEM (layers->data)); GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); GimpLayerMask *mask; GList *iter; GError *error = NULL; g_object_set (config, "layer-add-mask-type", add_mask_type, "layer-add-mask-invert", invert, NULL); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD, _("Add Layer Masks")); for (iter = layers; iter; iter = iter->next) { mask = gimp_layer_create_mask (iter->data, config->layer_add_mask_type, channel); if (config->layer_add_mask_invert) gimp_channel_invert (GIMP_CHANNEL (mask), FALSE); if (! gimp_layer_add_mask (iter->data, mask, TRUE, &error)) { gimp_message_literal (image->gimp, G_OBJECT (dialog), GIMP_MESSAGE_WARNING, error->message); g_object_unref (mask); g_clear_error (&error); return; } } gimp_image_undo_group_end (image); gimp_image_flush (image); gtk_widget_destroy (dialog); } static void layers_scale_callback (GtkWidget *dialog, GimpViewable *viewable, gint width, gint height, GimpUnit *unit, GimpInterpolationType interpolation, gdouble xresolution, /* unused */ gdouble yresolution, /* unused */ GimpUnit *resolution_unit,/* unused */ gpointer user_data) { GimpDisplay *display = GIMP_DISPLAY (user_data); layer_scale_unit = unit; layer_scale_interp = interpolation; if (width > 0 && height > 0) { GimpItem *item = GIMP_ITEM (viewable); GimpProgress *progress; GtkWidget *progress_dialog = NULL; gtk_widget_destroy (dialog); if (width == gimp_item_get_width (item) && height == gimp_item_get_height (item)) return; if (display) { progress = GIMP_PROGRESS (display); } else { progress_dialog = gimp_progress_dialog_new (); progress = GIMP_PROGRESS (progress_dialog); } progress = gimp_progress_start (progress, FALSE, _("Scaling")); gimp_item_scale_by_origin (item, width, height, interpolation, progress, TRUE); if (progress) gimp_progress_end (progress); if (progress_dialog) gtk_widget_destroy (progress_dialog); gimp_image_flush (gimp_item_get_image (item)); } else { g_warning ("Scale Error: " "Both width and height must be greater than zero."); } } static void layers_resize_callback (GtkWidget *dialog, GimpViewable *viewable, GimpContext *context, gint width, gint height, GimpUnit *unit, gint offset_x, gint offset_y, gdouble unused0, gdouble unused1, GimpUnit *unused2, GimpFillType fill_type, GimpItemSet unused3, gboolean unused4, gpointer user_data) { layer_resize_unit = unit; if (width > 0 && height > 0) { GimpItem *item = GIMP_ITEM (viewable); GimpImage *image = gimp_item_get_image (item); GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); g_object_set (config, "layer-resize-fill-type", fill_type, NULL); gtk_widget_destroy (dialog); if (width == gimp_item_get_width (item) && height == gimp_item_get_height (item)) return; gimp_item_resize (item, context, fill_type, width, height, offset_x, offset_y); gimp_image_flush (gimp_item_get_image (item)); } else { g_warning ("Resize Error: " "Both width and height must be greater than zero."); } } static gint layers_mode_index (GimpLayerMode layer_mode, const GimpLayerMode *modes, gint n_modes) { gint i = 0; while (i < (n_modes - 1) && modes[i] != layer_mode) i++; return i; }