app: globally block editing vector and link layers.

Instead of blocking various drawable-editing tools to work on vector or
link layers, per tool (as in commits 38c379cd92 or 36330a271a), let's
have a more generic logic in GimpTool. We will now block the tool
initialization early when it's destructive and tries to work on link or
vector layers.

By default, all tools are set as being destructive, but we can override
this per-class by setting the class' is_destructive flag. For instance,
text and path tools, or the Zoom or Color picker tools, etc. are
non-destructive.
Filter tools also are non-destructive (they may be destructive, but have
their internal code to disable this path on these types of layers).

Source tools are special-cased because we may allow them to be
initialized on a link/vector layer as sources. For this special-case, I
make a second check on gimp_tool_button_press().
This commit is contained in:
Jehan 2025-10-08 23:00:54 +02:00
parent 759a886e4b
commit 5ddb853964
14 changed files with 133 additions and 73 deletions

View file

@ -155,6 +155,7 @@ gimp_align_tool_class_init (GimpAlignToolClass *klass)
tool_class->cursor_update = gimp_align_tool_cursor_update;
tool_class->can_undo = gimp_align_tool_can_undo;
tool_class->undo = gimp_align_tool_undo;
tool_class->is_destructive = FALSE;
draw_tool_class->draw = gimp_align_tool_draw;
}

View file

@ -582,7 +582,6 @@ gimp_bucket_fill_tool_button_press (GimpTool *tool,
GimpBucketFillOptions *options = GIMP_BUCKET_FILL_TOOL_GET_OPTIONS (tool);
GimpGuiConfig *config = GIMP_GUI_CONFIG (display->gimp->config);
GimpImage *image = gimp_display_get_image (display);
GimpItem *locked_item = NULL;
GList *drawables = gimp_image_get_selected_drawables (image);
GimpDrawable *drawable;
@ -623,28 +622,6 @@ gimp_bucket_fill_tool_button_press (GimpTool *tool,
return;
}
if (gimp_item_is_link_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Link layers must be rasterized "
"before they can be painted on."));
return;
}
else if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Vector layers must be rasterized "
"before they can be painted on."));
return;
}
else if (gimp_item_is_content_locked (GIMP_ITEM (drawable), &locked_item))
{
gimp_tool_message_literal (tool, display,
_("The selected layer's pixels are locked."));
gimp_tools_blink_lock_box (display->gimp, locked_item);
return;
}
if (options->fill_area == GIMP_BUCKET_FILL_LINE_ART &&
! gimp_line_art_get_input (bucket_tool->priv->line_art))
{

View file

@ -120,14 +120,15 @@ gimp_color_picker_tool_class_init (GimpColorPickerToolClass *klass)
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
GimpColorToolClass *color_tool_class = GIMP_COLOR_TOOL_CLASS (klass);
object_class->constructed = gimp_color_picker_tool_constructed;
object_class->dispose = gimp_color_picker_tool_dispose;
object_class->constructed = gimp_color_picker_tool_constructed;
object_class->dispose = gimp_color_picker_tool_dispose;
tool_class->control = gimp_color_picker_tool_control;
tool_class->modifier_key = gimp_color_picker_tool_modifier_key;
tool_class->oper_update = gimp_color_picker_tool_oper_update;
tool_class->control = gimp_color_picker_tool_control;
tool_class->modifier_key = gimp_color_picker_tool_modifier_key;
tool_class->oper_update = gimp_color_picker_tool_oper_update;
tool_class->is_destructive = FALSE;
color_tool_class->picked = gimp_color_picker_tool_picked;
color_tool_class->picked = gimp_color_picker_tool_picked;
}
static void

View file

@ -231,6 +231,7 @@ gimp_filter_tool_class_init (GimpFilterToolClass *klass)
tool_class->oper_update = gimp_filter_tool_oper_update;
tool_class->cursor_update = gimp_filter_tool_cursor_update;
tool_class->options_notify = gimp_filter_tool_options_notify;
tool_class->is_destructive = FALSE;
color_tool_class->can_pick = gimp_filter_tool_can_pick_color;
color_tool_class->pick = gimp_filter_tool_pick_color;

View file

@ -109,6 +109,7 @@ gimp_magnify_tool_class_init (GimpMagnifyToolClass *klass)
tool_class->motion = gimp_magnify_tool_motion;
tool_class->modifier_key = gimp_magnify_tool_modifier_key;
tool_class->cursor_update = gimp_magnify_tool_cursor_update;
tool_class->is_destructive = FALSE;
draw_tool_class->draw = gimp_magnify_tool_draw;
}

View file

@ -146,6 +146,7 @@ gimp_measure_tool_class_init (GimpMeasureToolClass *klass)
tool_class->button_press = gimp_measure_tool_button_press;
tool_class->button_release = gimp_measure_tool_button_release;
tool_class->motion = gimp_measure_tool_motion;
tool_class->is_destructive = FALSE;
tr_class->recalc_matrix = gimp_measure_tool_recalc_matrix;
tr_class->get_undo_desc = gimp_measure_tool_get_undo_desc;

View file

@ -136,6 +136,7 @@ gimp_move_tool_class_init (GimpMoveToolClass *klass)
tool_class->modifier_key = gimp_move_tool_modifier_key;
tool_class->oper_update = gimp_move_tool_oper_update;
tool_class->cursor_update = gimp_move_tool_cursor_update;
tool_class->is_destructive = FALSE;
draw_tool_class->draw = gimp_move_tool_draw;
}

View file

@ -315,8 +315,7 @@ gimp_paint_tool_button_press (GimpTool *tool,
for (iter = drawables; iter; iter = iter->next)
{
GimpDrawable *drawable = iter->data;
GimpItem *locked_item = NULL;
GimpDrawable *drawable = iter->data;
if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
{
@ -327,41 +326,6 @@ gimp_paint_tool_button_press (GimpTool *tool,
return;
}
if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)) ||
gimp_item_is_link_layer (GIMP_ITEM (drawable)) ||
gimp_item_is_content_locked (GIMP_ITEM (drawable), &locked_item))
{
gboolean constrain_only;
/* Allow vector/link or pixel-locaked layers to be set as sources */
constrain_only = (state & gimp_get_constrain_behavior_mask () &&
! (state & gimp_get_extend_selection_mask ()));
if (! (GIMP_IS_SOURCE_TOOL (tool) && constrain_only))
{
if (gimp_item_is_link_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Link layers must be rasterized "
"before they can be painted on."));
}
else if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Vector layers must be rasterized "
"before they can be painted on."));
}
else
{
gimp_tool_message_literal (tool, display,
_("The selected item's pixels are locked."));
gimp_tools_blink_lock_box (display->gimp, locked_item);
}
g_list_free (drawables);
return;
}
}
if (! gimp_paint_tool_check_alpha (paint_tool, drawable, display, &error))
{
GtkWidget *options_gui;

View file

@ -187,6 +187,7 @@ gimp_path_tool_class_init (GimpPathToolClass *klass)
tool_class->motion = gimp_path_tool_motion;
tool_class->modifier_key = gimp_path_tool_modifier_key;
tool_class->cursor_update = gimp_path_tool_cursor_update;
tool_class->is_destructive = FALSE;
}
static void

View file

@ -89,13 +89,14 @@ gimp_selection_tool_class_init (GimpSelectionToolClass *klass)
{
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
tool_class->control = gimp_selection_tool_control;
tool_class->modifier_key = gimp_selection_tool_modifier_key;
tool_class->key_press = gimp_edit_selection_tool_key_press;
tool_class->oper_update = gimp_selection_tool_oper_update;
tool_class->cursor_update = gimp_selection_tool_cursor_update;
tool_class->control = gimp_selection_tool_control;
tool_class->modifier_key = gimp_selection_tool_modifier_key;
tool_class->key_press = gimp_edit_selection_tool_key_press;
tool_class->oper_update = gimp_selection_tool_oper_update;
tool_class->cursor_update = gimp_selection_tool_cursor_update;
tool_class->is_destructive = FALSE;
klass->have_selection = gimp_selection_tool_real_have_selection;
klass->have_selection = gimp_selection_tool_real_have_selection;
}
static void

View file

@ -242,6 +242,7 @@ gimp_text_tool_class_init (GimpTextToolClass *klass)
tool_class->oper_update = gimp_text_tool_oper_update;
tool_class->cursor_update = gimp_text_tool_cursor_update;
tool_class->get_popup = gimp_text_tool_get_popup;
tool_class->is_destructive = FALSE;
draw_tool_class->draw = gimp_text_tool_draw;
}

View file

@ -28,6 +28,7 @@
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpimage.h"
#include "core/gimplinklayer.h"
#include "core/gimpprogress.h"
#include "core/gimptoolinfo.h"
@ -36,9 +37,15 @@
#include "display/gimpdisplayshell-cursor.h"
#include "display/gimpstatusbar.h"
#include "path/gimpvectorlayer.h"
#include "widgets/gimpwidgets-utils.h"
#include "gimpsourcetool.h"
#include "gimptool.h"
#include "gimptool-progress.h"
#include "gimptoolcontrol.h"
#include "gimptools-utils.h"
#include "gimp-log.h"
#include "gimp-intl.h"
@ -188,6 +195,8 @@ gimp_tool_class_init (GimpToolClass *klass)
GIMP_TYPE_TOOL_INFO,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
klass->is_destructive = TRUE;
}
static void
@ -632,6 +641,54 @@ gimp_tool_initialize (GimpTool *tool,
g_return_val_if_fail (GIMP_IS_TOOL (tool), FALSE);
g_return_val_if_fail (GIMP_IS_DISPLAY (display), FALSE);
/* Source tools are special-cased as we want to allow vector/link or
* pixel-locked layers to be set as sources. So we still let these be
* initialized but will further verify the button state in
* gimp_tool_button_press().
*/
if (GIMP_TOOL_GET_CLASS (tool)->is_destructive && !
GIMP_IS_SOURCE_TOOL (tool))
{
GimpImage *image;
GList *drawables;
image = gimp_display_get_image (display);
drawables = gimp_image_get_selected_drawables (image);
for (GList *iter = drawables; iter; iter = iter->next)
{
GimpDrawable *drawable = iter->data;
GimpItem *locked_item = NULL;
if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)) ||
gimp_item_is_link_layer (GIMP_ITEM (drawable)) ||
gimp_item_is_content_locked (GIMP_ITEM (drawable), &locked_item))
{
if (gimp_item_is_link_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Link layers must be rasterized "
"before they can be painted on."));
}
else if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Vector layers must be rasterized "
"before they can be painted on."));
}
else
{
gimp_tool_message_literal (tool, display,
_("The selected item's pixels are locked."));
gimp_tools_blink_lock_box (display->gimp, locked_item);
}
g_list_free (drawables);
return FALSE;
}
}
g_list_free (drawables);
}
if (! GIMP_TOOL_GET_CLASS (tool)->initialize (tool, display, &error))
{
if (error)
@ -719,6 +776,56 @@ gimp_tool_button_press (GimpTool *tool,
g_return_if_fail (coords != NULL);
g_return_if_fail (GIMP_IS_DISPLAY (display));
if (GIMP_IS_SOURCE_TOOL (tool))
{
GimpImage *image;
GList *drawables;
image = gimp_display_get_image (display);
drawables = gimp_image_get_selected_drawables (image);
for (GList *iter = drawables; iter; iter = iter->next)
{
GimpDrawable *drawable = iter->data;
GimpItem *locked_item = NULL;
if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)) ||
gimp_item_is_link_layer (GIMP_ITEM (drawable)) ||
gimp_item_is_content_locked (GIMP_ITEM (drawable), &locked_item))
{
gboolean constrain_only;
/* Allow vector/link or pixel-locked layers to be set as sources */
constrain_only = (state & gimp_get_constrain_behavior_mask () &&
! (state & gimp_get_extend_selection_mask ()));
if (! constrain_only)
{
if (gimp_item_is_link_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Link layers must be rasterized "
"before they can be painted on."));
}
else if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Vector layers must be rasterized "
"before they can be painted on."));
}
else
{
gimp_tool_message_literal (tool, display,
_("The selected item's pixels are locked."));
gimp_tools_blink_lock_box (display->gimp, locked_item);
}
g_list_free (drawables);
return;
}
}
}
g_list_free (drawables);
}
GIMP_TOOL_GET_CLASS (tool)->button_press (tool, coords, time, state,
press_type, display);

View file

@ -163,6 +163,8 @@ struct _GimpToolClass
void (* options_notify) (GimpTool *tool,
GimpToolOptions *options,
const GParamSpec *pspec);
gboolean is_destructive;
};

View file

@ -98,7 +98,8 @@ gimp_transform_tool_class_init (GimpTransformToolClass *klass)
{
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
tool_class->control = gimp_transform_tool_control;
tool_class->control = gimp_transform_tool_control;
tool_class->is_destructive = FALSE;
klass->recalc_matrix = NULL;
klass->get_undo_desc = gimp_transform_tool_real_get_undo_desc;