From 5ddb853964cd12dbc0cd750102471494635dc212 Mon Sep 17 00:00:00 2001 From: Jehan Date: Wed, 8 Oct 2025 23:00:54 +0200 Subject: [PATCH] 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(). --- app/tools/gimpaligntool.c | 1 + app/tools/gimpbucketfilltool.c | 23 ------- app/tools/gimpcolorpickertool.c | 13 ++-- app/tools/gimpfiltertool.c | 1 + app/tools/gimpmagnifytool.c | 1 + app/tools/gimpmeasuretool.c | 1 + app/tools/gimpmovetool.c | 1 + app/tools/gimppainttool.c | 38 +----------- app/tools/gimppathtool.c | 1 + app/tools/gimpselectiontool.c | 13 ++-- app/tools/gimptexttool.c | 1 + app/tools/gimptool.c | 107 ++++++++++++++++++++++++++++++++ app/tools/gimptool.h | 2 + app/tools/gimptransformtool.c | 3 +- 14 files changed, 133 insertions(+), 73 deletions(-) diff --git a/app/tools/gimpaligntool.c b/app/tools/gimpaligntool.c index fced3c55b0..6b13519d74 100644 --- a/app/tools/gimpaligntool.c +++ b/app/tools/gimpaligntool.c @@ -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; } diff --git a/app/tools/gimpbucketfilltool.c b/app/tools/gimpbucketfilltool.c index 7248c8fc69..813908f065 100644 --- a/app/tools/gimpbucketfilltool.c +++ b/app/tools/gimpbucketfilltool.c @@ -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)) { diff --git a/app/tools/gimpcolorpickertool.c b/app/tools/gimpcolorpickertool.c index ccdd5dc578..40d4224f39 100644 --- a/app/tools/gimpcolorpickertool.c +++ b/app/tools/gimpcolorpickertool.c @@ -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 diff --git a/app/tools/gimpfiltertool.c b/app/tools/gimpfiltertool.c index 8ce1f0061e..a574863ec8 100644 --- a/app/tools/gimpfiltertool.c +++ b/app/tools/gimpfiltertool.c @@ -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; diff --git a/app/tools/gimpmagnifytool.c b/app/tools/gimpmagnifytool.c index 8c5f5d3983..e0ddac6423 100644 --- a/app/tools/gimpmagnifytool.c +++ b/app/tools/gimpmagnifytool.c @@ -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; } diff --git a/app/tools/gimpmeasuretool.c b/app/tools/gimpmeasuretool.c index 4ab820a2a9..f232f767ce 100644 --- a/app/tools/gimpmeasuretool.c +++ b/app/tools/gimpmeasuretool.c @@ -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; diff --git a/app/tools/gimpmovetool.c b/app/tools/gimpmovetool.c index 70c315a0a0..9f8407d4e7 100644 --- a/app/tools/gimpmovetool.c +++ b/app/tools/gimpmovetool.c @@ -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; } diff --git a/app/tools/gimppainttool.c b/app/tools/gimppainttool.c index 6faa9a9c91..6db16017e0 100644 --- a/app/tools/gimppainttool.c +++ b/app/tools/gimppainttool.c @@ -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; diff --git a/app/tools/gimppathtool.c b/app/tools/gimppathtool.c index 9f4944be14..c78eeeec21 100644 --- a/app/tools/gimppathtool.c +++ b/app/tools/gimppathtool.c @@ -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 diff --git a/app/tools/gimpselectiontool.c b/app/tools/gimpselectiontool.c index 4f35bbfd9f..3c422f2e5b 100644 --- a/app/tools/gimpselectiontool.c +++ b/app/tools/gimpselectiontool.c @@ -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 diff --git a/app/tools/gimptexttool.c b/app/tools/gimptexttool.c index ac92aafcd5..c0f97b1108 100644 --- a/app/tools/gimptexttool.c +++ b/app/tools/gimptexttool.c @@ -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; } diff --git a/app/tools/gimptool.c b/app/tools/gimptool.c index 94b4843539..803e10a450 100644 --- a/app/tools/gimptool.c +++ b/app/tools/gimptool.c @@ -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); diff --git a/app/tools/gimptool.h b/app/tools/gimptool.h index bfea9dd2d0..bde5f5e272 100644 --- a/app/tools/gimptool.h +++ b/app/tools/gimptool.h @@ -163,6 +163,8 @@ struct _GimpToolClass void (* options_notify) (GimpTool *tool, GimpToolOptions *options, const GParamSpec *pspec); + + gboolean is_destructive; }; diff --git a/app/tools/gimptransformtool.c b/app/tools/gimptransformtool.c index 429c79642a..97b4c6dffa 100644 --- a/app/tools/gimptransformtool.c +++ b/app/tools/gimptransformtool.c @@ -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;