Which replaces all the deparate functions to turn layers, channels, layer masks and vectors into selections. Use the new virtual function all over the place instead of calling the functions in gimpchannel-select.c manually.
1212 lines
35 KiB
C
1212 lines
35 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "actions-types.h"
|
|
|
|
#include "config/gimpcoreconfig.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimpcontext.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/gimplayer-floating-sel.h"
|
|
#include "core/gimplayermask.h"
|
|
#include "core/gimppickable.h"
|
|
#include "core/gimptoolinfo.h"
|
|
#include "core/gimpundostack.h"
|
|
#include "core/gimpprogress.h"
|
|
|
|
#include "text/gimptext.h"
|
|
#include "text/gimptext-vectors.h"
|
|
#include "text/gimptextlayer.h"
|
|
|
|
#include "vectors/gimpvectors-warp.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/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 "layers-commands.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
static const GimpLayerModeEffects layer_modes[] =
|
|
{
|
|
GIMP_NORMAL_MODE,
|
|
GIMP_DISSOLVE_MODE,
|
|
GIMP_MULTIPLY_MODE,
|
|
GIMP_DIVIDE_MODE,
|
|
GIMP_SCREEN_MODE,
|
|
GIMP_OVERLAY_MODE,
|
|
GIMP_DODGE_MODE,
|
|
GIMP_BURN_MODE,
|
|
GIMP_HARDLIGHT_MODE,
|
|
GIMP_SOFTLIGHT_MODE,
|
|
GIMP_GRAIN_EXTRACT_MODE,
|
|
GIMP_GRAIN_MERGE_MODE,
|
|
GIMP_DIFFERENCE_MODE,
|
|
GIMP_ADDITION_MODE,
|
|
GIMP_SUBTRACT_MODE,
|
|
GIMP_DARKEN_ONLY_MODE,
|
|
GIMP_LIGHTEN_ONLY_MODE,
|
|
GIMP_HUE_MODE,
|
|
GIMP_SATURATION_MODE,
|
|
GIMP_COLOR_MODE,
|
|
GIMP_VALUE_MODE
|
|
};
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
static void layers_new_layer_response (GtkWidget *widget,
|
|
gint response_id,
|
|
LayerOptionsDialog *dialog);
|
|
static void layers_edit_layer_response (GtkWidget *widget,
|
|
gint response_id,
|
|
LayerOptionsDialog *dialog);
|
|
static void layers_add_mask_response (GtkWidget *widget,
|
|
gint response_id,
|
|
LayerAddMaskDialog *dialog);
|
|
|
|
static void layers_scale_layer_callback (GtkWidget *dialog,
|
|
GimpViewable *viewable,
|
|
gint width,
|
|
gint height,
|
|
GimpUnit unit,
|
|
GimpInterpolationType interpolation,
|
|
gdouble xresolution,
|
|
gdouble yresolution,
|
|
GimpUnit resolution_unit,
|
|
gpointer data);
|
|
static void layers_resize_layer_callback (GtkWidget *dialog,
|
|
GimpViewable *viewable,
|
|
gint width,
|
|
gint height,
|
|
GimpUnit unit,
|
|
gint offset_x,
|
|
gint offset_y,
|
|
GimpItemSet unused,
|
|
gpointer data);
|
|
|
|
static gint layers_mode_index (GimpLayerModeEffects layer_mode);
|
|
|
|
|
|
/* private variables */
|
|
|
|
static GimpFillType layer_fill_type = GIMP_TRANSPARENT_FILL;
|
|
static gchar *layer_name = NULL;
|
|
static GimpUnit layer_resize_unit = GIMP_UNIT_PIXEL;
|
|
static GimpUnit layer_scale_unit = GIMP_UNIT_PIXEL;
|
|
static GimpInterpolationType layer_scale_interp = -1;
|
|
static GimpAddMaskType layer_add_mask_type = GIMP_ADD_WHITE_MASK;
|
|
static gboolean layer_mask_invert = FALSE;
|
|
|
|
|
|
/* public functions */
|
|
|
|
void
|
|
layers_text_tool_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GtkWidget *widget;
|
|
GimpTool *active_tool;
|
|
return_if_no_layer (image, layer, data);
|
|
return_if_no_widget (widget, data);
|
|
|
|
if (! gimp_drawable_is_text_layer (GIMP_DRAWABLE (layer)))
|
|
{
|
|
layers_edit_attributes_cmd_callback (action, data);
|
|
return;
|
|
}
|
|
|
|
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))
|
|
gimp_text_tool_set_layer (GIMP_TEXT_TOOL (active_tool), layer);
|
|
}
|
|
|
|
void
|
|
layers_edit_attributes_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
LayerOptionsDialog *dialog;
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GtkWidget *widget;
|
|
return_if_no_layer (image, layer, data);
|
|
return_if_no_widget (widget, data);
|
|
|
|
dialog = layer_options_dialog_new (gimp_item_get_image (GIMP_ITEM (layer)),
|
|
layer,
|
|
action_data_get_context (data),
|
|
widget,
|
|
gimp_object_get_name (layer),
|
|
layer_fill_type,
|
|
_("Layer Attributes"),
|
|
"gimp-layer-edit",
|
|
GTK_STOCK_EDIT,
|
|
_("Edit Layer Attributes"),
|
|
GIMP_HELP_LAYER_EDIT);
|
|
|
|
g_signal_connect (dialog->dialog, "response",
|
|
G_CALLBACK (layers_edit_layer_response),
|
|
dialog);
|
|
|
|
gtk_widget_show (dialog->dialog);
|
|
}
|
|
|
|
void
|
|
layers_new_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
LayerOptionsDialog *dialog;
|
|
GimpImage *image;
|
|
GtkWidget *widget;
|
|
GimpLayer *floating_sel;
|
|
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;
|
|
}
|
|
|
|
dialog = layer_options_dialog_new (image, NULL,
|
|
action_data_get_context (data),
|
|
widget,
|
|
layer_name ? layer_name : _("Layer"),
|
|
layer_fill_type,
|
|
_("New Layer"),
|
|
"gimp-layer-new",
|
|
GIMP_STOCK_LAYER,
|
|
_("Create a New Layer"),
|
|
GIMP_HELP_LAYER_NEW);
|
|
|
|
g_signal_connect (dialog->dialog, "response",
|
|
G_CALLBACK (layers_new_layer_response),
|
|
dialog);
|
|
|
|
gtk_widget_show (dialog->dialog);
|
|
}
|
|
|
|
void
|
|
layers_new_last_vals_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GtkWidget *widget;
|
|
GimpLayer *floating_sel;
|
|
GimpLayer *new_layer;
|
|
gint width, height;
|
|
gint off_x, off_y;
|
|
gdouble opacity;
|
|
GimpLayerModeEffects mode;
|
|
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;
|
|
}
|
|
|
|
if (GIMP_IS_LAYER (GIMP_ACTION (action)->viewable))
|
|
{
|
|
GimpLayer *template = GIMP_LAYER (GIMP_ACTION (action)->viewable);
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (template), &off_x, &off_y);
|
|
width = gimp_item_get_width (GIMP_ITEM (template));
|
|
height = gimp_item_get_height (GIMP_ITEM (template));
|
|
opacity = gimp_layer_get_opacity (template);
|
|
mode = gimp_layer_get_mode (template);
|
|
}
|
|
else
|
|
{
|
|
width = gimp_image_get_width (image);
|
|
height = gimp_image_get_height (image);
|
|
off_x = 0;
|
|
off_y = 0;
|
|
opacity = 1.0;
|
|
mode = GIMP_NORMAL_MODE;
|
|
}
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_EDIT_PASTE,
|
|
_("New Layer"));
|
|
|
|
new_layer = gimp_layer_new (image, width, height,
|
|
gimp_image_base_type_with_alpha (image),
|
|
layer_name,
|
|
opacity, mode);
|
|
|
|
gimp_drawable_fill_by_type (GIMP_DRAWABLE (new_layer),
|
|
action_data_get_context (data),
|
|
layer_fill_type);
|
|
gimp_item_translate (GIMP_ITEM (new_layer), off_x, off_y, FALSE);
|
|
|
|
gimp_image_add_layer (image, new_layer,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_new_from_visible_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpPickable *pickable;
|
|
return_if_no_image (image, data);
|
|
|
|
pickable = GIMP_PICKABLE (gimp_image_get_projection (image));
|
|
|
|
gimp_pickable_flush (pickable);
|
|
|
|
layer = gimp_layer_new_from_tiles (gimp_pickable_get_tiles (pickable),
|
|
image,
|
|
gimp_image_base_type_with_alpha (image),
|
|
_("Visible"),
|
|
GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
|
|
|
|
gimp_image_add_layer (image, layer,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_new_group_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_image (image, data);
|
|
|
|
layer = gimp_group_layer_new (image);
|
|
|
|
gimp_image_add_layer (image, layer,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_select_cmd_callback (GtkAction *action,
|
|
gint value,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpContainer *container;
|
|
GimpLayer *new_layer;
|
|
return_if_no_image (image, data);
|
|
|
|
layer = gimp_image_get_active_layer (image);
|
|
|
|
if (layer)
|
|
container = gimp_item_get_container (GIMP_ITEM (layer));
|
|
else
|
|
container = gimp_image_get_layers (image);
|
|
|
|
new_layer = (GimpLayer *) action_select_object ((GimpActionSelectType) value,
|
|
container,
|
|
(GimpObject *) layer);
|
|
|
|
if (new_layer && new_layer != layer)
|
|
{
|
|
gimp_image_set_active_layer (image, new_layer);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_raise_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_image_raise_item (image, GIMP_ITEM (layer), NULL);
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_raise_to_top_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_image_raise_item_to_top (image, GIMP_ITEM (layer));
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_lower_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_image_lower_item (image, GIMP_ITEM (layer), NULL);
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_lower_to_bottom_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_image_lower_item_to_bottom (image, GIMP_ITEM (layer));
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_duplicate_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpLayer *new_layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
new_layer = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (layer),
|
|
G_TYPE_FROM_INSTANCE (layer)));
|
|
|
|
/* 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 (layer), -1,
|
|
TRUE);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_anchor_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
if (gimp_layer_is_floating_sel (layer))
|
|
{
|
|
floating_sel_anchor (layer);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_merge_down_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_image_merge_down (image, layer, action_data_get_context (data),
|
|
GIMP_EXPAND_AS_NECESSARY, NULL);
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_merge_group_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_image_merge_group_layer (image, GIMP_GROUP_LAYER (layer));
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_delete_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_image_remove_layer (image, layer, TRUE, NULL);
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_text_discard_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
if (GIMP_IS_TEXT_LAYER (layer))
|
|
gimp_text_layer_discard (GIMP_TEXT_LAYER (layer));
|
|
}
|
|
|
|
void
|
|
layers_text_to_vectors_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
if (GIMP_IS_TEXT_LAYER (layer))
|
|
{
|
|
GimpVectors *vectors;
|
|
gint x, y;
|
|
|
|
vectors = gimp_text_vectors_new (image, GIMP_TEXT_LAYER (layer)->text);
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (layer), &x, &y);
|
|
gimp_item_translate (GIMP_ITEM (vectors), x, y, FALSE);
|
|
|
|
gimp_image_add_vectors (image, vectors,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_text_along_vectors_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpVectors *vectors;
|
|
return_if_no_layer (image, layer, data);
|
|
return_if_no_vectors (image, vectors, data);
|
|
|
|
if (GIMP_IS_TEXT_LAYER (layer))
|
|
{
|
|
GimpVectors *new_vectors;
|
|
|
|
new_vectors = gimp_text_vectors_new (image, GIMP_TEXT_LAYER (layer)->text);
|
|
|
|
gimp_vectors_warp_vectors (vectors, new_vectors,
|
|
0.5 * gimp_item_get_height (GIMP_ITEM (layer)));
|
|
|
|
gimp_item_set_visible (GIMP_ITEM (new_vectors), TRUE, FALSE);
|
|
|
|
gimp_image_add_vectors (image, new_vectors,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_resize_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpDisplay *display = NULL;
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GtkWidget *widget;
|
|
GtkWidget *dialog;
|
|
return_if_no_layer (image, layer, data);
|
|
return_if_no_widget (widget, data);
|
|
|
|
if (GIMP_IS_IMAGE_WINDOW (data))
|
|
display = action_data_get_display (data);
|
|
|
|
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,
|
|
layers_resize_layer_callback,
|
|
action_data_get_context (data));
|
|
|
|
gtk_widget_show (dialog);
|
|
}
|
|
|
|
void
|
|
layers_resize_to_image_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_layer_resize_to_image (layer, action_data_get_context (data));
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_scale_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpDisplay *display = NULL;
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GtkWidget *widget;
|
|
GtkWidget *dialog;
|
|
return_if_no_layer (image, layer, data);
|
|
return_if_no_widget (widget, data);
|
|
|
|
if (GIMP_IS_IMAGE_WINDOW (data))
|
|
display = action_data_get_display (data);
|
|
|
|
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_layer_callback,
|
|
display);
|
|
|
|
gtk_widget_show (dialog);
|
|
}
|
|
|
|
void
|
|
layers_crop_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GtkWidget *widget;
|
|
gint x1, y1, x2, y2;
|
|
gint off_x, off_y;
|
|
return_if_no_layer (image, layer, data);
|
|
return_if_no_widget (widget, data);
|
|
|
|
if (! gimp_channel_bounds (gimp_image_get_mask (image),
|
|
&x1, &y1, &x2, &y2))
|
|
{
|
|
gimp_message_literal (image->gimp,
|
|
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
|
|
_("Cannot crop because the current selection is empty."));
|
|
return;
|
|
}
|
|
|
|
gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
|
|
|
|
off_x -= x1;
|
|
off_y -= y1;
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE,
|
|
_("Crop Layer"));
|
|
|
|
gimp_item_resize (GIMP_ITEM (layer), action_data_get_context (data),
|
|
x2 - x1, y2 - y1, off_x, off_y);
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_mask_add_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
LayerAddMaskDialog *dialog;
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GtkWidget *widget;
|
|
return_if_no_layer (image, layer, data);
|
|
return_if_no_widget (widget, data);
|
|
|
|
dialog = layer_add_mask_dialog_new (layer, action_data_get_context (data),
|
|
widget,
|
|
layer_add_mask_type, layer_mask_invert);
|
|
|
|
g_signal_connect (dialog->dialog, "response",
|
|
G_CALLBACK (layers_add_mask_response),
|
|
dialog);
|
|
|
|
gtk_widget_show (dialog->dialog);
|
|
}
|
|
|
|
void
|
|
layers_mask_apply_cmd_callback (GtkAction *action,
|
|
gint value,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpMaskApplyMode mode;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
mode = (GimpMaskApplyMode) value;
|
|
|
|
if (gimp_layer_get_mask (layer))
|
|
{
|
|
gimp_layer_apply_mask (layer, mode, TRUE);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_mask_edit_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpLayerMask *mask;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
mask = gimp_layer_get_mask (layer);
|
|
|
|
if (mask)
|
|
{
|
|
gboolean active;
|
|
|
|
active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
|
|
|
|
gimp_layer_mask_set_edit (mask, active);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_mask_show_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpLayerMask *mask;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
mask = gimp_layer_get_mask (layer);
|
|
|
|
if (mask)
|
|
{
|
|
gboolean active;
|
|
|
|
active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
|
|
|
|
gimp_layer_mask_set_show (mask, active, TRUE);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_mask_disable_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpLayerMask *mask;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
mask = gimp_layer_get_mask (layer);
|
|
|
|
if (mask)
|
|
{
|
|
gboolean active;
|
|
|
|
active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
|
|
|
|
gimp_layer_mask_set_apply (mask, ! active, TRUE);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_mask_to_selection_cmd_callback (GtkAction *action,
|
|
gint value,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpLayerMask *mask;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
mask = gimp_layer_get_mask (layer);
|
|
|
|
if (mask)
|
|
{
|
|
gimp_item_to_selection (GIMP_ITEM (mask),
|
|
(GimpChannelOps) value,
|
|
TRUE, FALSE, 0.0, 0.0);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_alpha_add_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
if (! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
|
|
{
|
|
gimp_layer_add_alpha (layer);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_alpha_remove_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
|
|
{
|
|
gimp_layer_flatten (layer, action_data_get_context (data));
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
void
|
|
layers_alpha_to_selection_cmd_callback (GtkAction *action,
|
|
gint value,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
gimp_item_to_selection (GIMP_ITEM (layer),
|
|
(GimpChannelOps) value,
|
|
TRUE, FALSE, 0.0, 0.0);
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_opacity_cmd_callback (GtkAction *action,
|
|
gint value,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
gdouble opacity;
|
|
GimpUndo *undo;
|
|
gboolean push_undo = TRUE;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
|
|
GIMP_UNDO_LAYER_OPACITY);
|
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer))
|
|
push_undo = FALSE;
|
|
|
|
opacity = action_select_value ((GimpActionSelectType) value,
|
|
gimp_layer_get_opacity (layer),
|
|
0.0, 1.0, 1.0,
|
|
1.0 / 255.0, 0.01, 0.1, 0.0, FALSE);
|
|
gimp_layer_set_opacity (layer, opacity, push_undo);
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_mode_cmd_callback (GtkAction *action,
|
|
gint value,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
GimpLayerModeEffects layer_mode;
|
|
gint index;
|
|
GimpUndo *undo;
|
|
gboolean push_undo = TRUE;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
|
|
GIMP_UNDO_LAYER_MODE);
|
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer))
|
|
push_undo = FALSE;
|
|
|
|
layer_mode = gimp_layer_get_mode (layer);
|
|
|
|
index = action_select_value ((GimpActionSelectType) value,
|
|
layers_mode_index (layer_mode),
|
|
0, G_N_ELEMENTS (layer_modes) - 1, 0,
|
|
0.0, 1.0, 1.0, 0.0, FALSE);
|
|
gimp_layer_set_mode (layer, layer_modes[index], push_undo);
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
void
|
|
layers_lock_alpha_cmd_callback (GtkAction *action,
|
|
gpointer data)
|
|
{
|
|
GimpImage *image;
|
|
GimpLayer *layer;
|
|
gboolean lock_alpha;
|
|
return_if_no_layer (image, layer, data);
|
|
|
|
lock_alpha = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
|
|
|
|
if (lock_alpha != gimp_layer_get_lock_alpha (layer))
|
|
{
|
|
GimpUndo *undo;
|
|
gboolean push_undo = TRUE;
|
|
|
|
undo = gimp_image_undo_can_compress (image, GIMP_TYPE_ITEM_UNDO,
|
|
GIMP_UNDO_LAYER_LOCK_ALPHA);
|
|
|
|
if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer))
|
|
push_undo = FALSE;
|
|
|
|
gimp_layer_set_lock_alpha (layer, lock_alpha, push_undo);
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
|
|
|
|
/* private functions */
|
|
|
|
static void
|
|
layers_new_layer_response (GtkWidget *widget,
|
|
gint response_id,
|
|
LayerOptionsDialog *dialog)
|
|
{
|
|
if (response_id == GTK_RESPONSE_OK)
|
|
{
|
|
GimpLayer *layer;
|
|
|
|
if (layer_name)
|
|
g_free (layer_name);
|
|
|
|
layer_name =
|
|
g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->name_entry)));
|
|
|
|
layer_fill_type = dialog->fill_type;
|
|
|
|
dialog->xsize =
|
|
RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (dialog->size_se),
|
|
0));
|
|
dialog->ysize =
|
|
RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (dialog->size_se),
|
|
1));
|
|
|
|
layer = gimp_layer_new (dialog->image,
|
|
dialog->xsize,
|
|
dialog->ysize,
|
|
gimp_image_base_type_with_alpha (dialog->image),
|
|
layer_name,
|
|
GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE);
|
|
|
|
if (layer)
|
|
{
|
|
gimp_drawable_fill_by_type (GIMP_DRAWABLE (layer),
|
|
dialog->context,
|
|
layer_fill_type);
|
|
|
|
gimp_image_add_layer (dialog->image, layer,
|
|
GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
|
|
|
|
gimp_image_flush (dialog->image);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("%s: could not allocate new layer", G_STRFUNC);
|
|
}
|
|
}
|
|
|
|
gtk_widget_destroy (dialog->dialog);
|
|
}
|
|
|
|
static void
|
|
layers_edit_layer_response (GtkWidget *widget,
|
|
gint response_id,
|
|
LayerOptionsDialog *dialog)
|
|
{
|
|
if (response_id == GTK_RESPONSE_OK)
|
|
{
|
|
GimpLayer *layer = dialog->layer;
|
|
const gchar *new_name;
|
|
|
|
new_name = gtk_entry_get_text (GTK_ENTRY (dialog->name_entry));
|
|
|
|
if (strcmp (new_name, gimp_object_get_name (layer)))
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (gimp_item_rename (GIMP_ITEM (layer), new_name, &error))
|
|
{
|
|
gimp_image_flush (dialog->image);
|
|
}
|
|
else
|
|
{
|
|
gimp_message_literal (dialog->image->gimp,
|
|
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
|
|
error->message);
|
|
g_clear_error (&error);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (dialog->rename_toggle &&
|
|
gimp_drawable_is_text_layer (GIMP_DRAWABLE (layer)))
|
|
{
|
|
g_object_set (layer,
|
|
"auto-rename",
|
|
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->rename_toggle)),
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
gtk_widget_destroy (dialog->dialog);
|
|
}
|
|
|
|
static void
|
|
layers_add_mask_response (GtkWidget *widget,
|
|
gint response_id,
|
|
LayerAddMaskDialog *dialog)
|
|
{
|
|
if (response_id == GTK_RESPONSE_OK)
|
|
{
|
|
GimpLayer *layer = dialog->layer;
|
|
GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
|
|
GimpLayerMask *mask;
|
|
|
|
if (dialog->add_mask_type == GIMP_ADD_CHANNEL_MASK &&
|
|
! dialog->channel)
|
|
{
|
|
gimp_message_literal (image->gimp,
|
|
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
|
|
_("Please select a channel first"));
|
|
return;
|
|
}
|
|
|
|
layer_add_mask_type = dialog->add_mask_type;
|
|
layer_mask_invert = dialog->invert;
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_LAYER_ADD_MASK,
|
|
_("Add Layer Mask"));
|
|
|
|
mask = gimp_layer_create_mask (layer, layer_add_mask_type,
|
|
dialog->channel);
|
|
|
|
if (layer_mask_invert)
|
|
gimp_channel_invert (GIMP_CHANNEL (mask), FALSE);
|
|
|
|
gimp_layer_add_mask (layer, mask, TRUE, NULL);
|
|
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
|
|
gtk_widget_destroy (dialog->dialog);
|
|
}
|
|
|
|
static void
|
|
layers_scale_layer_callback (GtkWidget *dialog,
|
|
GimpViewable *viewable,
|
|
gint width,
|
|
gint height,
|
|
GimpUnit unit,
|
|
GimpInterpolationType interpolation,
|
|
gdouble xresolution, /* unused */
|
|
gdouble yresolution, /* unused */
|
|
GimpUnit resolution_unit,/* unused */
|
|
gpointer data)
|
|
{
|
|
GimpDisplay *display = GIMP_DISPLAY (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, _("Scaling"), FALSE);
|
|
|
|
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_layer_callback (GtkWidget *dialog,
|
|
GimpViewable *viewable,
|
|
gint width,
|
|
gint height,
|
|
GimpUnit unit,
|
|
gint offset_x,
|
|
gint offset_y,
|
|
GimpItemSet unused,
|
|
gpointer data)
|
|
{
|
|
GimpContext *context = GIMP_CONTEXT (data);
|
|
|
|
layer_resize_unit = unit;
|
|
|
|
if (width > 0 && height > 0)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (viewable);
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
if (width == gimp_item_get_width (item) &&
|
|
height == gimp_item_get_height (item))
|
|
return;
|
|
|
|
gimp_item_resize (item, context,
|
|
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 (GimpLayerModeEffects layer_mode)
|
|
{
|
|
gint i = 0;
|
|
|
|
while (i < (G_N_ELEMENTS (layer_modes) - 1) && layer_modes[i] != layer_mode)
|
|
i++;
|
|
|
|
return i;
|
|
}
|