File indexing completed on 2024-12-22 04:16:49

0001 /*
0002  * KDE. Krita Project.
0003  *
0004  * SPDX-FileCopyrightText: 2022 Deif Lou <ginoba@gmail.com>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include <kis_debug.h>
0010 #include <klocalizedstring.h>
0011 
0012 #include <QLabel>
0013 #include <QCheckBox>
0014 #include <QPushButton>
0015 
0016 #include <KisOptionButtonStrip.h>
0017 #include <KisOptionCollectionWidget.h>
0018 #include <KoGroupButton.h>
0019 
0020 #include <ksharedconfig.h>
0021 
0022 #include <KoCanvasBase.h>
0023 #include <KoPointerEvent.h>
0024 
0025 #include <kis_layer.h>
0026 #include <kis_painter.h>
0027 #include <resources/KoPattern.h>
0028 #include <kis_selection.h>
0029 
0030 #include <KisViewManager.h>
0031 #include <canvas/kis_canvas2.h>
0032 #include <widgets/kis_cmb_composite.h>
0033 #include <kis_slider_spin_box.h>
0034 #include <kis_cursor.h>
0035 #include "kis_resources_snapshot.h"
0036 #include <kis_color_filter_combo.h>
0037 #include <KisAngleSelector.h>
0038 #include <KoGroupButton.h>
0039 #include <kis_color_button.h>
0040 #include <kis_color_label_selector_widget.h>
0041 #include <kis_cmb_composite.h>
0042 #include <kis_image_animation_interface.h>
0043 
0044 #include <kis_processing_applicator.h>
0045 #include <kis_command_utils.h>
0046 #include <functional>
0047 #include <kis_group_layer.h>
0048 #include <kis_layer_utils.h>
0049 
0050 #include <KisSpinBoxI18nHelper.h>
0051 #include <KisPart.h>
0052 #include <KisDocument.h>
0053 #include <kis_dummies_facade.h>
0054 #include <KoShapeControllerBase.h>
0055 #include <kis_shape_controller.h>
0056 #include <kis_canvas_resource_provider.h>
0057 
0058 #include <KoCompositeOpRegistry.h>
0059 
0060 #include <processing/KisEncloseAndFillProcessingVisitor.h>
0061 
0062 #include "KisToolEncloseAndFill.h"
0063 #include "subtools/KisRectangleEnclosingProducer.h"
0064 #include "subtools/KisEllipseEnclosingProducer.h"
0065 #include "subtools/KisPathEnclosingProducer.h"
0066 #include "subtools/KisLassoEnclosingProducer.h"
0067 #include "subtools/KisBrushEnclosingProducer.h"
0068 
0069 KisToolEncloseAndFill::KisToolEncloseAndFill(KoCanvasBase * canvas)
0070     : KisDynamicDelegatedTool<KisToolShape>(canvas, QCursor())
0071 {
0072     setObjectName("tool_enclose_and_fill");
0073 }
0074 
0075 KisToolEncloseAndFill::~KisToolEncloseAndFill()
0076 {}
0077 
0078 void KisToolEncloseAndFill::resetCursorStyle()
0079 {
0080     KisDynamicDelegatedTool::resetCursorStyle();
0081     overrideCursorIfNotEditable();
0082 }
0083 
0084 void KisToolEncloseAndFill::activate(const QSet<KoShape*> &shapes)
0085 {
0086     KisDynamicDelegatedTool::activate(shapes);
0087     m_configGroup = KSharedConfig::openConfig()->group(toolId());
0088     KisCanvas2 *kisCanvas = static_cast<KisCanvas2*>(canvas());
0089     KisCanvasResourceProvider *resourceProvider = kisCanvas->viewManager()->canvasResourceProvider();
0090     if (resourceProvider) {
0091         connect(resourceProvider,
0092                 SIGNAL(sigNodeChanged(const KisNodeSP)),
0093                 this,
0094                 SLOT(slot_currentNodeChanged(const KisNodeSP)));
0095         slot_currentNodeChanged(currentNode());
0096     }
0097 }
0098 
0099 void KisToolEncloseAndFill::deactivate()
0100 {
0101     m_referencePaintDevice = nullptr;
0102     m_referenceNodeList = nullptr;
0103     KisCanvas2 *kisCanvas = static_cast<KisCanvas2*>(canvas());
0104     KisCanvasResourceProvider *resourceProvider = kisCanvas->viewManager()->canvasResourceProvider();
0105     if (resourceProvider) {
0106         disconnect(resourceProvider,
0107                    SIGNAL(sigNodeChanged(const KisNodeSP)),
0108                    this,
0109                    SLOT(slot_currentNodeChanged(const KisNodeSP)));
0110     }
0111     slot_currentNodeChanged(nullptr);
0112     KisDynamicDelegatedTool::deactivate();
0113 }
0114 
0115 void KisToolEncloseAndFill::setupEnclosingSubtool()
0116 {
0117     if (delegateTool()) {
0118         delegateTool()->deactivate();
0119     }
0120 
0121     if (m_enclosingMethod == Ellipse) {
0122         KisEllipseEnclosingProducer *newDelegateTool = new KisEllipseEnclosingProducer(canvas());
0123         setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool));
0124         setCursor(newDelegateTool->cursor());
0125     } else if (m_enclosingMethod == Path) {
0126         KisPathEnclosingProducer *newDelegateTool = new KisPathEnclosingProducer(canvas());
0127         setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool));
0128         setCursor(newDelegateTool->cursor());
0129     } else if (m_enclosingMethod == Lasso) {
0130         KisLassoEnclosingProducer *newDelegateTool = new KisLassoEnclosingProducer(canvas());
0131         setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool));
0132         setCursor(newDelegateTool->cursor());
0133     } else if (m_enclosingMethod == Brush) {
0134         KisBrushEnclosingProducer *newDelegateTool = new KisBrushEnclosingProducer(canvas());
0135         setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool));
0136         setCursor(newDelegateTool->cursor());
0137     } else {
0138         KisRectangleEnclosingProducer *newDelegateTool = new KisRectangleEnclosingProducer(canvas());
0139         setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool));
0140         setCursor(newDelegateTool->cursor());
0141     }
0142 
0143     connect(delegateTool(), SIGNAL(enclosingMaskProduced(KisPixelSelectionSP)), SLOT(slot_delegateTool_enclosingMaskProduced(KisPixelSelectionSP)));
0144 
0145     if (isActivated()) {
0146         delegateTool()->activate(QSet<KoShape*>());
0147     }
0148 }
0149 
0150 bool KisToolEncloseAndFill::subtoolHasUserInteractionRunning() const
0151 {
0152     if (!delegateTool()) {
0153         return false;
0154     }
0155     
0156     if (m_enclosingMethod == Rectangle) {
0157         return reinterpret_cast<KisRectangleEnclosingProducer*>(delegateTool())->hasUserInteractionRunning();
0158     } else if (m_enclosingMethod == Ellipse) {
0159         return reinterpret_cast<KisEllipseEnclosingProducer*>(delegateTool())->hasUserInteractionRunning();
0160     } else if (m_enclosingMethod == Path) {
0161         return reinterpret_cast<KisPathEnclosingProducer*>(delegateTool())->hasUserInteractionRunning();
0162     } else if (m_enclosingMethod == Lasso) {
0163         return reinterpret_cast<KisLassoEnclosingProducer*>(delegateTool())->hasUserInteractionRunning();
0164     } else if (m_enclosingMethod == Brush) {
0165         return reinterpret_cast<KisBrushEnclosingProducer*>(delegateTool())->hasUserInteractionRunning();
0166     }
0167     return false;
0168 }
0169 
0170 void KisToolEncloseAndFill::beginPrimaryAction(KoPointerEvent *event)
0171 {
0172     // cannot use enclose and fill tool on non-painting layers.
0173     // this logic triggers with multiple layer types like vector layer, clone layer, file layer, group layer
0174     if (currentNode().isNull() || currentNode()->inherits("KisShapeLayer") || nodePaintAbility() != NodePaintAbility::PAINT) {
0175         KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas());
0176         kiscanvas->viewManager()->
0177                 showFloatingMessage(
0178                     i18n("You cannot use this tool with the selected layer type"),
0179                     QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter);
0180         event->ignore();
0181         return;
0182     }
0183 
0184     if (!nodeEditable()) {
0185         event->ignore();
0186         return;
0187     }
0188 
0189     KisDynamicDelegatedTool::beginPrimaryAction(event);
0190 }
0191 
0192 void KisToolEncloseAndFill::activateAlternateAction(AlternateAction action)
0193 {
0194     if (subtoolHasUserInteractionRunning()) {
0195         if (delegateTool()) {
0196             delegateTool()->activatePrimaryAction();
0197         }
0198         return;
0199     }
0200     KisDynamicDelegatedTool::activateAlternateAction(action);
0201 }
0202 
0203 void KisToolEncloseAndFill::deactivateAlternateAction(AlternateAction action)
0204 {
0205     if (subtoolHasUserInteractionRunning()) {
0206         return;
0207     }
0208     KisDynamicDelegatedTool::deactivateAlternateAction(action);
0209 }
0210 
0211 void KisToolEncloseAndFill::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
0212 {
0213     if (subtoolHasUserInteractionRunning()) {
0214         if (delegateTool()) {
0215             delegateTool()->beginPrimaryAction(event);
0216         }
0217         return;
0218     }
0219     KisDynamicDelegatedTool::beginAlternateAction(event, action);
0220     m_alternateActionStarted = true;
0221 }
0222 
0223 void KisToolEncloseAndFill::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
0224 {
0225     if (subtoolHasUserInteractionRunning()) {
0226         if (delegateTool()) {
0227             delegateTool()->continuePrimaryAction(event);
0228         }
0229         return;
0230     }
0231     if (!m_alternateActionStarted) {
0232         return;
0233     }
0234     KisDynamicDelegatedTool::continueAlternateAction(event, action);
0235 }
0236 
0237 void KisToolEncloseAndFill::endAlternateAction(KoPointerEvent *event, AlternateAction action)
0238 {
0239     if (subtoolHasUserInteractionRunning()) {
0240         if (delegateTool()) {
0241             delegateTool()->endPrimaryAction(event);
0242         }
0243         return;
0244     }
0245     if (!m_alternateActionStarted) {
0246         return;
0247     }
0248     KisDynamicDelegatedTool::endAlternateAction(event, action);
0249     m_alternateActionStarted = false;
0250 }
0251 
0252 void KisToolEncloseAndFill::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action)
0253 {
0254     if (subtoolHasUserInteractionRunning()) {
0255         if (delegateTool()) {
0256             delegateTool()->beginPrimaryDoubleClickAction(event);
0257         }
0258         return;
0259     }
0260     KisDynamicDelegatedTool::beginAlternateDoubleClickAction(event, action);
0261 }
0262 
0263 void KisToolEncloseAndFill::slot_delegateTool_enclosingMaskProduced(KisPixelSelectionSP enclosingMask)
0264 {
0265     KisProcessingApplicator applicator(currentImage(), currentNode(),
0266                                        KisProcessingApplicator::SUPPORTS_WRAPAROUND_MODE,
0267                                        KisImageSignalVector(),
0268                                        kundo2_i18n("Enclose and Fill"));
0269 
0270     KisResourcesSnapshotSP resources =
0271         new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
0272 
0273     if (m_reference == CurrentLayer) {
0274         m_referencePaintDevice = currentNode()->paintDevice();
0275     } else if (m_reference == AllLayers) {
0276         m_referencePaintDevice = currentImage()->projection();
0277     } else if (m_reference == ColorLabeledLayers) {
0278         if (!m_referenceNodeList) {
0279             m_referencePaintDevice = KisMergeLabeledLayersCommand::createRefPaintDevice(image(), "Enclose and Fill Tool Reference Result Paint Device");
0280             m_referenceNodeList.reset(new KisMergeLabeledLayersCommand::ReferenceNodeInfoList);
0281         }
0282         KisPaintDeviceSP newReferencePaintDevice = KisMergeLabeledLayersCommand::createRefPaintDevice(image(), "Enclose and Fill Tool Reference Result Paint Device");
0283         KisMergeLabeledLayersCommand::ReferenceNodeInfoListSP newReferenceNodeList(new KisMergeLabeledLayersCommand::ReferenceNodeInfoList);
0284         const int currentTime = image()->animationInterface()->currentTime();
0285         applicator.applyCommand(
0286             new KisMergeLabeledLayersCommand(
0287                 image(),
0288                 m_referenceNodeList,
0289                 newReferenceNodeList,
0290                 m_referencePaintDevice,
0291                 newReferencePaintDevice,
0292                 m_selectedColorLabels,
0293                 KisMergeLabeledLayersCommand::GroupSelectionPolicy_SelectIfColorLabeled,
0294                 m_previousTime != currentTime
0295             ),
0296             KisStrokeJobData::SEQUENTIAL,
0297             KisStrokeJobData::EXCLUSIVE
0298         );
0299         m_referencePaintDevice = newReferencePaintDevice;
0300         m_referenceNodeList = newReferenceNodeList;
0301         m_previousTime = currentTime;
0302     }
0303 
0304     if (m_reference != ColorLabeledLayers) {
0305         // Reset this so that the device from color labeled layers gets
0306         // regenerated when that mode is selected again
0307         m_referenceNodeList.reset();
0308     }
0309 
0310     QTransform transform;
0311     transform.rotate(m_patternRotation);
0312     const qreal normalizedScale = m_patternScale * 0.01;
0313     transform.scale(normalizedScale, normalizedScale);
0314     resources->setFillTransform(transform);
0315 
0316     KisProcessingVisitorSP visitor =
0317         new KisEncloseAndFillProcessingVisitor(m_referencePaintDevice,
0318                                                enclosingMask,
0319                                                resources->activeSelection(),
0320                                                resources,
0321                                                m_regionSelectionMethod,
0322                                                m_regionSelectionColor,
0323                                                m_regionSelectionInvert,
0324                                                m_regionSelectionIncludeContourRegions,
0325                                                false,
0326                                                m_fillThreshold,
0327                                                m_fillOpacitySpread,
0328                                                m_antiAlias,
0329                                                m_expand,
0330                                                m_stopGrowingAtDarkestPixel,
0331                                                m_feather,
0332                                                m_useSelectionAsBoundary,
0333                                                m_fillType == FillWithPattern,
0334                                                false,
0335                                                m_fillType == FillWithBackgroundColor,
0336                                                m_useCustomBlendingOptions,
0337                                                m_customOpacity * OPACITY_OPAQUE_U8 / 100,
0338                                                m_customCompositeOp);
0339 
0340     applicator.applyVisitor(visitor,
0341                             KisStrokeJobData::SEQUENTIAL,
0342                             KisStrokeJobData::EXCLUSIVE);
0343 
0344     applicator.end();
0345 }
0346 
0347 int KisToolEncloseAndFill::flags() const
0348 {
0349     return KisDynamicDelegatedTool::flags() | KisTool::FLAG_USES_CUSTOM_SIZE | KisTool::FLAG_USES_CUSTOM_PRESET;
0350 }
0351 
0352 QWidget* KisToolEncloseAndFill::createOptionWidget()
0353 {
0354     loadConfiguration();
0355 
0356     // Create widgets
0357     KisOptionButtonStrip *optionButtonStripEnclosingMethod =
0358         new KisOptionButtonStrip;
0359     m_buttonEnclosingMethodRectangle =
0360         optionButtonStripEnclosingMethod->addButton(
0361             KisIconUtils::loadIcon("tool_rect_selection"));
0362     m_buttonEnclosingMethodEllipse =
0363         optionButtonStripEnclosingMethod->addButton(
0364             KisIconUtils::loadIcon("tool_elliptical_selection"));
0365     m_buttonEnclosingMethodPath = optionButtonStripEnclosingMethod->addButton(
0366         KisIconUtils::loadIcon("tool_path_selection"));
0367     m_buttonEnclosingMethodLasso = optionButtonStripEnclosingMethod->addButton(
0368         KisIconUtils::loadIcon("tool_outline_selection"));
0369     m_buttonEnclosingMethodBrush = optionButtonStripEnclosingMethod->addButton(
0370         KisIconUtils::loadIcon("krita_tool_freehand"));
0371     m_buttonEnclosingMethodLasso->setChecked(true);
0372 
0373     m_comboBoxRegionSelectionMethod = new QComboBox;
0374     m_comboBoxRegionSelectionMethod->addItem(
0375         regionSelectionMethodToUserString(RegionSelectionMethod::SelectAllRegions),
0376         static_cast<int>(RegionSelectionMethod::SelectAllRegions)
0377     );
0378     m_comboBoxRegionSelectionMethod->addItem(
0379         regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsFilledWithSpecificColor),
0380         static_cast<int>(RegionSelectionMethod::SelectRegionsFilledWithSpecificColor)
0381     );
0382     m_comboBoxRegionSelectionMethod->addItem(
0383         regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsFilledWithTransparent),
0384         static_cast<int>(RegionSelectionMethod::SelectRegionsFilledWithTransparent)
0385     );
0386     m_comboBoxRegionSelectionMethod->addItem(
0387         regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent),
0388         static_cast<int>(RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent)
0389     );
0390     m_comboBoxRegionSelectionMethod->addItem(
0391         regionSelectionMethodToUserString(RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor),
0392         static_cast<int>(RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor)
0393     );
0394     m_comboBoxRegionSelectionMethod->addItem(
0395         regionSelectionMethodToUserString(RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent),
0396         static_cast<int>(RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent)
0397     );
0398     m_comboBoxRegionSelectionMethod->addItem(
0399         regionSelectionMethodToUserString(
0400             RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent
0401         ),
0402         static_cast<int>(RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent)
0403     );
0404     m_comboBoxRegionSelectionMethod->addItem(
0405         regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor),
0406         static_cast<int>(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor)
0407     );
0408     m_comboBoxRegionSelectionMethod->addItem(
0409         regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsSurroundedByTransparent),
0410         static_cast<int>(RegionSelectionMethod::SelectRegionsSurroundedByTransparent)
0411     );
0412     m_comboBoxRegionSelectionMethod->addItem(
0413         regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent),
0414         static_cast<int>(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent)
0415     );
0416     m_comboBoxRegionSelectionMethod->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
0417     m_comboBoxRegionSelectionMethod->setMinimumContentsLength(15);
0418     m_comboBoxRegionSelectionMethod->view()->setMinimumWidth(
0419         m_comboBoxRegionSelectionMethod->view()->sizeHintForColumn(0)
0420     );
0421     m_buttonRegionSelectionColor = new KisColorButton;
0422     m_checkBoxRegionSelectionInvert =
0423         new QCheckBox(
0424             i18nc("The 'invert' checkbox in enclose and fill tool",
0425                   "Invert")
0426         );
0427     m_checkBoxRegionSelectionIncludeContourRegions =
0428         new QCheckBox(
0429             i18nc("The 'include contour regions' checkbox in enclose and fill tool",
0430                   "Include contour regions")
0431         );
0432     m_checkBoxRegionSelectionIncludeContourRegions->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
0433 
0434     KisOptionButtonStrip *optionButtonStripFillWith = new KisOptionButtonStrip;
0435     m_buttonFillWithFG = optionButtonStripFillWith->addButton(
0436         KisIconUtils::loadIcon("object-order-lower-calligra"));
0437     m_buttonFillWithBG = optionButtonStripFillWith->addButton(
0438         KisIconUtils::loadIcon("object-order-raise-calligra"));
0439     m_buttonFillWithPattern =
0440         optionButtonStripFillWith->addButton(KisIconUtils::loadIcon("pattern"));
0441     m_buttonFillWithFG->setChecked(true);
0442     m_sliderPatternScale = new KisDoubleSliderSpinBox;
0443     m_sliderPatternScale->setRange(0, 10000, 2);
0444     m_sliderPatternScale->setSoftMaximum(500);
0445     KisSpinBoxI18nHelper::setText(m_sliderPatternScale,
0446                                   i18nc("The pattern 'scale' spinbox in enclose and fill tool options; {n} is the "
0447                                         "number value, % is the percent sign",
0448                                         "Scale: {n}%"));
0449     m_angleSelectorPatternRotation = new KisAngleSelector;
0450     m_angleSelectorPatternRotation->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_ContextMenu);
0451     m_angleSelectorPatternRotation->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
0452     m_checkBoxCustomBlendingOptions = new QCheckBox(i18n("Use custom blending options"));
0453     m_sliderCustomOpacity = new KisSliderSpinBox;
0454     m_sliderCustomOpacity->setRange(0, 100);
0455     KisSpinBoxI18nHelper::setText(m_sliderCustomOpacity,
0456                                   i18nc("{n} is the number value, % is the percent sign", "Opacity: {n}%"));
0457     m_comboBoxCustomCompositeOp = new KisCompositeOpComboBox;
0458 
0459     m_sliderFillThreshold = new KisSliderSpinBox;
0460     m_sliderFillThreshold->setPrefix(i18nc("The 'threshold' spinbox prefix in enclose and fill tool options", "Threshold: "));
0461     m_sliderFillThreshold->setRange(1, 100);
0462     m_sliderFillOpacitySpread = new KisSliderSpinBox;
0463     KisSpinBoxI18nHelper::setText(
0464         m_sliderFillOpacitySpread,
0465         i18nc("The 'spread' spinbox in enclose and fill tool options; {n} is the number value, % is the percent sign",
0466               "Spread: {n}%"));
0467     m_sliderFillOpacitySpread->setRange(0, 100);
0468     m_checkBoxSelectionAsBoundary =
0469         new QCheckBox(
0470             i18nc("The 'use selection as boundary' checkbox in enclose and fill tool to use selection borders as boundary when filling",
0471                   "Use selection as boundary")
0472         );
0473     m_checkBoxSelectionAsBoundary->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
0474 
0475     m_checkBoxAntiAlias = new QCheckBox(i18nc("The anti-alias checkbox in enclose and fill tool options", "Anti-aliasing"));
0476     KisOptionCollectionWidget *containerGrow = new KisOptionCollectionWidget;
0477     m_sliderExpand = new KisSliderSpinBox;
0478     m_sliderExpand->setPrefix(i18nc("The 'grow/shrink' spinbox prefix in enclose and fill tool options", "Grow: "));
0479     m_sliderExpand->setRange(-400, 400);
0480     m_sliderExpand->setSoftRange(-40, 40);
0481     m_sliderExpand->setSuffix(i18n(" px"));
0482     m_buttonStopGrowingAtDarkestPixel = new QToolButton;
0483     m_buttonStopGrowingAtDarkestPixel->setAutoRaise(true);
0484     m_buttonStopGrowingAtDarkestPixel->setCheckable(true);
0485     m_buttonStopGrowingAtDarkestPixel->setIcon(KisIconUtils::loadIcon("stop-at-boundary"));
0486     containerGrow->appendWidget("sliderExpand", m_sliderExpand);
0487     containerGrow->appendWidget("buttonStopGrowingAtDarkestPixel", m_buttonStopGrowingAtDarkestPixel);
0488     containerGrow->setOrientation(Qt::Horizontal);
0489     m_sliderFeather = new KisSliderSpinBox;
0490     m_sliderFeather->setPrefix(i18nc("The 'feather' spinbox prefix in enclose and fill tool options", "Feather: "));
0491     m_sliderFeather->setRange(0, 400);
0492     m_sliderFeather->setSoftRange(0, 40);
0493     m_sliderFeather->setSuffix(i18n(" px"));
0494 
0495     KisOptionButtonStrip *optionButtonStripReference = new KisOptionButtonStrip;
0496     m_buttonReferenceCurrent = optionButtonStripReference->addButton(
0497         KisIconUtils::loadIcon("current-layer"));
0498     m_buttonReferenceAll = optionButtonStripReference->addButton(
0499         KisIconUtils::loadIcon("all-layers"));
0500     m_buttonReferenceLabeled =
0501         optionButtonStripReference->addButton(KisIconUtils::loadIcon("tag"));
0502     m_buttonReferenceCurrent->setChecked(true);
0503     m_widgetLabels = new KisColorLabelSelectorWidget;
0504     m_widgetLabels->setExclusive(false);
0505     m_widgetLabels->setButtonSize(20);
0506     m_widgetLabels->setButtonWrapEnabled(true);
0507     m_widgetLabels->setMouseDragEnabled(true);
0508 
0509     QPushButton *buttonReset = new QPushButton(i18nc("The 'reset' button in enclose and fill tool options", "Reset"));
0510 
0511     // Set the tooltips
0512     m_buttonEnclosingMethodRectangle->setToolTip(i18n("Rectangle"));
0513     m_buttonEnclosingMethodEllipse->setToolTip(i18n("Ellipse"));
0514     m_buttonEnclosingMethodPath->setToolTip(i18n("Bezier Curve"));
0515     m_buttonEnclosingMethodLasso->setToolTip(i18n("Lasso"));
0516     m_buttonEnclosingMethodBrush->setToolTip(i18n("Brush"));
0517     m_comboBoxRegionSelectionMethod->setToolTip(regionSelectionMethodToUserString(m_regionSelectionMethod));
0518     m_checkBoxRegionSelectionInvert->setToolTip(i18n("Enable to fill opposite regions instead"));
0519     m_checkBoxRegionSelectionIncludeContourRegions->setToolTip(i18n("Enable to also fill shapes that touch the contour of the enclosing region"));
0520     m_buttonFillWithFG->setToolTip(i18n("Foreground color"));
0521     m_buttonFillWithBG->setToolTip(i18n("Background color"));
0522     m_buttonFillWithPattern->setToolTip(i18n("Pattern"));
0523     m_sliderPatternScale->setToolTip(i18n("Set the scale of the pattern"));
0524     m_angleSelectorPatternRotation->setToolTip(i18n("Set the rotation of the pattern"));
0525     m_checkBoxCustomBlendingOptions->setToolTip(i18n("Set custom blending options instead of using the brush ones"));
0526     m_sliderCustomOpacity->setToolTip(i18n("Set a custom opacity for the fill"));
0527     m_comboBoxCustomCompositeOp->setToolTip(i18n("Set a custom blend mode for the fill"));
0528 
0529     m_sliderFillThreshold->setToolTip(i18n("Set the color similarity tolerance of the fill. Increasing threshold increases the range of similar colors to be filled."));
0530     m_sliderFillOpacitySpread->setToolTip(i18n("Set the extent of the opaque portion of the fill. Decreasing spread decreases opacity of fill areas depending on color similarity."));
0531     m_checkBoxSelectionAsBoundary->setToolTip(i18n("Set if the contour of the active selection should be treated as a boundary when filling the region"));
0532 
0533     m_checkBoxAntiAlias->setToolTip(i18n("Smooths the edges of the fill"));
0534     m_sliderExpand->setToolTip(i18n("Grow or shrink the fill by the set amount"));
0535     m_buttonStopGrowingAtDarkestPixel->setToolTip(i18n("Stop growing at the darkest and/or most opaque pixels"));
0536     m_sliderFeather->setToolTip(i18n("Blur the fill by the set amount"));
0537 
0538     m_buttonReferenceCurrent->setToolTip(i18n("Fill regions found from the active layer"));
0539     m_buttonReferenceAll->setToolTip(i18n("Fill regions found from the merging of all layers"));
0540     m_buttonReferenceLabeled->setToolTip(i18n("Fill regions found from the merging of layers with specific color labels"));
0541 
0542     buttonReset->setToolTip(i18n("Reset the options to their default values"));
0543 
0544     // Construct the option widget
0545     m_optionWidget = new KisOptionCollectionWidget;
0546     m_optionWidget->setContentsMargins(0, 10, 0, 10);
0547     m_optionWidget->setSeparatorsVisible(true);
0548 
0549     KisOptionCollectionWidgetWithHeader *sectionEnclosingMethod =
0550         new KisOptionCollectionWidgetWithHeader(
0551             i18nc("The 'enclosing method' section label in enclose and fill tool options", "Enclosing method")
0552         );
0553     sectionEnclosingMethod->setPrimaryWidget(optionButtonStripEnclosingMethod);
0554     m_optionWidget->appendWidget("sectionEnclosingMethod", sectionEnclosingMethod);
0555 
0556     KisOptionCollectionWidgetWithHeader *sectionReference =
0557         new KisOptionCollectionWidgetWithHeader(
0558             i18nc("The 'reference' section label in enclose and fill tool options", "Reference")
0559         );
0560     sectionReference->setPrimaryWidget(optionButtonStripReference);
0561     sectionReference->appendWidget("widgetLabels", m_widgetLabels);
0562     sectionReference->setWidgetVisible("widgetLabels", false);
0563     m_optionWidget->appendWidget("sectionReference", sectionReference);
0564 
0565 
0566     KisOptionCollectionWidgetWithHeader *sectionWhatToFill =
0567         new KisOptionCollectionWidgetWithHeader(
0568             i18nc("The 'target regions' section label in enclose and fill tool options", "Target regions")
0569         );
0570     sectionWhatToFill->setPrimaryWidget(m_comboBoxRegionSelectionMethod);
0571     sectionWhatToFill->appendWidget("buttonRegionSelectionColor", m_buttonRegionSelectionColor);
0572     sectionWhatToFill->appendWidget("checkBoxRegionSelectionInvert", m_checkBoxRegionSelectionInvert);
0573     sectionWhatToFill->appendWidget("checkBoxRegionSelectionIncludeContourRegions", m_checkBoxRegionSelectionIncludeContourRegions);
0574     m_optionWidget->appendWidget("sectionWhatToFill", sectionWhatToFill);
0575 
0576     KisOptionCollectionWidgetWithHeader *sectionFillWith =
0577         new KisOptionCollectionWidgetWithHeader(
0578             i18nc("The 'fill source' section label in enclose and fill tool options", "Fill source")
0579         );
0580     sectionFillWith->setPrimaryWidget(optionButtonStripFillWith);
0581     sectionFillWith->appendWidget("sliderPatternScale", m_sliderPatternScale);
0582     sectionFillWith->appendWidget("angleSelectorPatternRotation", m_angleSelectorPatternRotation);
0583     sectionFillWith->appendWidget("checkBoxCustomBlendingOptions", m_checkBoxCustomBlendingOptions);
0584     sectionFillWith->appendWidget("sliderCustomOpacity", m_sliderCustomOpacity);
0585     sectionFillWith->appendWidget("comboBoxCustomCompositeOp", m_comboBoxCustomCompositeOp);
0586     sectionFillWith->setWidgetVisible("sliderPatternScale", false);
0587     sectionFillWith->setWidgetVisible("angleSelectorPatternRotation", false);
0588     m_optionWidget->appendWidget("sectionFillWith", sectionFillWith);
0589 
0590     KisOptionCollectionWidgetWithHeader *sectionRegionExtent =
0591         new KisOptionCollectionWidgetWithHeader(
0592             i18nc("The 'fill extent' section label in enclose and fill tool options", "Fill extent")
0593         );
0594     sectionRegionExtent->appendWidget("sliderThreshold", m_sliderFillThreshold);
0595     sectionRegionExtent->appendWidget("sliderSpread", m_sliderFillOpacitySpread);
0596     sectionRegionExtent->appendWidget("checkBoxSelectionAsBoundary", m_checkBoxSelectionAsBoundary);
0597     m_optionWidget->appendWidget("sectionRegionExtent", sectionRegionExtent);
0598 
0599     KisOptionCollectionWidgetWithHeader *sectionAdjustments =
0600         new KisOptionCollectionWidgetWithHeader(
0601             i18nc("The 'adjustments' section label in enclose and fill tool options", "Adjustments")
0602         );
0603     sectionAdjustments->appendWidget("checkBoxAntiAlias", m_checkBoxAntiAlias);
0604     sectionAdjustments->appendWidget("containerGrow", containerGrow);
0605     sectionAdjustments->appendWidget("sliderFeather", m_sliderFeather);
0606     m_optionWidget->appendWidget("sectionAdjustments", sectionAdjustments);
0607 
0608     m_optionWidget->appendWidget("buttonReset", buttonReset);
0609 
0610     // Initialize widgets
0611     if (m_enclosingMethod == Rectangle) {
0612         m_buttonEnclosingMethodRectangle->setChecked(true);
0613     } else if (m_enclosingMethod == Ellipse) {
0614         m_buttonEnclosingMethodEllipse->setChecked(true);
0615     } else if (m_enclosingMethod == Path) {
0616         m_buttonEnclosingMethodPath->setChecked(true);
0617     } else if (m_enclosingMethod == Lasso) {
0618         m_buttonEnclosingMethodLasso->setChecked(true);
0619     } else {
0620         m_buttonEnclosingMethodBrush->setChecked(true);
0621     }
0622     m_comboBoxRegionSelectionMethod->setCurrentIndex(m_comboBoxRegionSelectionMethod->findData(static_cast<int>(m_regionSelectionMethod)));
0623     m_buttonRegionSelectionColor->setColor(m_regionSelectionColor);
0624     sectionWhatToFill->setWidgetVisible(
0625         "buttonRegionSelectionColor",
0626         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor ||
0627         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent ||
0628         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor ||
0629         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent ||
0630         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor ||
0631         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent
0632     );
0633     m_checkBoxRegionSelectionInvert->setChecked(m_regionSelectionInvert);
0634     m_checkBoxRegionSelectionIncludeContourRegions->setChecked(m_regionSelectionIncludeContourRegions);
0635     sectionWhatToFill->setWidgetVisible(
0636         "checkBoxRegionSelectionIncludeContourRegions",
0637         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor ||
0638         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent ||
0639         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent ||
0640         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor ||
0641         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent ||
0642         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent
0643     );
0644     if (m_fillType == FillWithBackgroundColor) {
0645         m_buttonFillWithBG->setChecked(true);
0646     } else if (m_fillType == FillWithPattern) {
0647         m_buttonFillWithPattern->setChecked(true);
0648         sectionFillWith->setWidgetVisible("sliderPatternScale", true);
0649         sectionFillWith->setWidgetVisible("angleSelectorPatternRotation", true);
0650     }
0651     m_sliderPatternScale->setValue(m_patternScale);
0652     m_angleSelectorPatternRotation->setAngle(m_patternRotation);
0653     m_checkBoxCustomBlendingOptions->setChecked(m_useCustomBlendingOptions);
0654     m_sliderCustomOpacity->setValue(m_customOpacity);
0655     slot_colorSpaceChanged(currentNode() && currentNode()->paintDevice()
0656                            ? currentNode()->paintDevice()->colorSpace()
0657                            : nullptr);
0658     m_comboBoxCustomCompositeOp->selectCompositeOp(KoID(m_customCompositeOp));
0659     if (!m_useCustomBlendingOptions) {
0660         sectionFillWith->setWidgetVisible("sliderCustomOpacity", false);
0661         sectionFillWith->setWidgetVisible("comboBoxCustomCompositeOp", false);
0662     }
0663     m_sliderFillThreshold->setValue(m_fillThreshold);
0664     m_sliderFillOpacitySpread->setValue(m_fillOpacitySpread);
0665     m_checkBoxSelectionAsBoundary->setChecked(m_useSelectionAsBoundary);
0666     m_checkBoxAntiAlias->setChecked(m_antiAlias);
0667     m_sliderExpand->setValue(m_expand);
0668     m_buttonStopGrowingAtDarkestPixel->setChecked(m_stopGrowingAtDarkestPixel);
0669     m_sliderFeather->setValue(m_feather);
0670     if (m_reference == AllLayers) {
0671         m_buttonReferenceAll->setChecked(true);
0672     } else if (m_reference == ColorLabeledLayers) {
0673         m_buttonReferenceLabeled->setChecked(true);
0674         sectionReference->setWidgetVisible("widgetLabels", true);
0675     }
0676     m_widgetLabels->setSelection(m_selectedColorLabels);
0677 
0678     // Make connections
0679     connect(optionButtonStripEnclosingMethod,
0680             SIGNAL(buttonToggled(KoGroupButton *, bool)),
0681             SLOT(slot_optionButtonStripEnclosingMethod_buttonToggled(
0682                 KoGroupButton *,
0683                 bool)));
0684     connect(m_comboBoxRegionSelectionMethod,
0685             SIGNAL(currentIndexChanged(int)),
0686             SLOT(slot_comboBoxRegionSelectionMethod_currentIndexChanged(int)));
0687     connect(m_buttonRegionSelectionColor,
0688             SIGNAL(changed(const KoColor&)),
0689             SLOT(slot_buttonRegionSelectionColor_changed(const KoColor&)));
0690     connect(m_checkBoxRegionSelectionInvert,
0691             SIGNAL(toggled(bool)),
0692             SLOT(slot_checkBoxRegionSelectionInvert_toggled(bool)));
0693     connect(m_checkBoxRegionSelectionIncludeContourRegions,
0694             SIGNAL(toggled(bool)),
0695             SLOT(slot_checkBoxRegionSelectionIncludeContourRegions_toggled(bool)));
0696     connect(optionButtonStripFillWith,
0697             SIGNAL(buttonToggled(KoGroupButton *, bool)),
0698             SLOT(slot_optionButtonStripFillWith_buttonToggled(KoGroupButton *, bool)));
0699     connect(m_sliderPatternScale,
0700             SIGNAL(valueChanged(double)),
0701             SLOT(slot_sliderPatternScale_valueChanged(double)));
0702     connect(m_angleSelectorPatternRotation,
0703             SIGNAL(angleChanged(double)),
0704             SLOT(slot_angleSelectorPatternRotation_angleChanged(double)));
0705     connect(m_checkBoxCustomBlendingOptions,
0706             SIGNAL(toggled(bool)),
0707             SLOT(slot_checkBoxUseCustomBlendingOptions_toggled(bool)));
0708     connect(m_sliderCustomOpacity,
0709             SIGNAL(valueChanged(int)),
0710             SLOT(slot_sliderCustomOpacity_valueChanged(int)));
0711     connect(m_comboBoxCustomCompositeOp,
0712             SIGNAL(currentIndexChanged(int)),
0713             SLOT(slot_comboBoxCustomCompositeOp_currentIndexChanged(int)));
0714     connect(m_sliderFillThreshold,
0715             SIGNAL(valueChanged(int)),
0716             SLOT(slot_sliderFillThreshold_valueChanged(int)));
0717     connect(m_sliderFillOpacitySpread,
0718             SIGNAL(valueChanged(int)),
0719             SLOT(slot_sliderFillOpacitySpread_valueChanged(int)));
0720     connect(m_checkBoxSelectionAsBoundary,
0721             SIGNAL(toggled(bool)),
0722             SLOT(slot_checkBoxSelectionAsBoundary_toggled(bool)));
0723     connect(m_checkBoxAntiAlias,
0724             SIGNAL(toggled(bool)),
0725             SLOT(slot_checkBoxAntiAlias_toggled(bool)));
0726     connect(m_sliderExpand,
0727             SIGNAL(valueChanged(int)),
0728             SLOT(slot_sliderExpand_valueChanged(int)));
0729     connect(m_buttonStopGrowingAtDarkestPixel,
0730             SIGNAL(toggled(bool)),
0731             SLOT(slot_buttonStopGrowingAtDarkestPixel_toggled(bool)));
0732     connect(m_sliderFeather,
0733             SIGNAL(valueChanged(int)),
0734             SLOT(slot_sliderFeather_valueChanged(int)));
0735     connect(optionButtonStripReference,
0736             SIGNAL(buttonToggled(KoGroupButton *, bool)),
0737             SLOT(slot_optionButtonStripReference_buttonToggled(KoGroupButton *, bool)));
0738     connect(m_widgetLabels,
0739             SIGNAL(selectionChanged()),
0740             SLOT(slot_widgetLabels_selectionChanged()));
0741     connect(buttonReset,
0742             SIGNAL(clicked()),
0743             SLOT(slot_buttonReset_clicked()));
0744     
0745     return m_optionWidget;
0746 }
0747 
0748 void KisToolEncloseAndFill::loadConfiguration()
0749 {
0750     m_enclosingMethod = loadEnclosingMethodFromConfig();
0751     m_regionSelectionMethod = loadRegionSelectionMethodFromConfig();
0752     m_regionSelectionColor = loadRegionSelectionColorFromConfig();
0753     m_regionSelectionInvert = m_configGroup.readEntry<bool>("regionSelectionInvert", false);
0754     m_regionSelectionIncludeContourRegions = m_configGroup.readEntry<bool>("regionSelectionIncludeContourRegions", false);
0755     {
0756         const QString fillTypeStr = m_configGroup.readEntry<QString>("fillWith", "");
0757         if (fillTypeStr == "foregroundColor") {
0758             m_fillType = FillWithForegroundColor;
0759         } else if (fillTypeStr == "backgroundColor") {
0760             m_fillType = FillWithBackgroundColor;
0761         } else if (fillTypeStr == "pattern") {
0762             m_fillType = FillWithPattern;
0763         } else {
0764             if (m_configGroup.readEntry<bool>("usePattern", false)) {
0765                 m_fillType = FillWithPattern;
0766             } else {
0767                 m_fillType = FillWithForegroundColor;
0768             }
0769         }
0770     }
0771     m_patternScale = m_configGroup.readEntry<qreal>("patternScale", 100.0);
0772     m_patternRotation = m_configGroup.readEntry<qreal>("patternRotate", 0.0);
0773     m_useCustomBlendingOptions = m_configGroup.readEntry<bool>("useCustomBlendingOptions", false);
0774     m_customOpacity = qBound(0, m_configGroup.readEntry<int>("customOpacity", 100), 100);
0775     m_customCompositeOp = m_configGroup.readEntry<QString>("customCompositeOp", COMPOSITE_OVER);
0776     if (KoCompositeOpRegistry::instance().getKoID(m_customCompositeOp).id().isNull()) {
0777         m_customCompositeOp = COMPOSITE_OVER;
0778     }
0779     m_fillThreshold = m_configGroup.readEntry<int>("fillThreshold", 8);
0780     m_fillOpacitySpread = m_configGroup.readEntry<int>("fillOpacitySpread", 100);
0781     m_useSelectionAsBoundary = m_configGroup.readEntry<bool>("useSelectionAsBoundary", true);
0782     m_antiAlias = m_configGroup.readEntry<bool>("antiAlias", false);
0783     m_expand = m_configGroup.readEntry<int>("expand", 0);
0784     m_stopGrowingAtDarkestPixel = m_configGroup.readEntry<bool>("stopGrowingAtDarkestPixel", false);
0785     m_feather = m_configGroup.readEntry<int>("feather", 0);
0786     {
0787         const QString sampleLayersModeStr = m_configGroup.readEntry<QString>("reference", "currentLayer");
0788         if (sampleLayersModeStr == "allLayers") {
0789             m_reference = AllLayers;
0790         } else if (sampleLayersModeStr == "colorLabeledLayers") {
0791             m_reference = ColorLabeledLayers;
0792         } else {
0793             m_reference = CurrentLayer;
0794         }
0795     }
0796     {
0797 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
0798         const QStringList colorLabelsStr = m_configGroup.readEntry<QString>("colorLabels", "").split(',', Qt::SkipEmptyParts);
0799 #else
0800         const QStringList colorLabelsStr = m_configGroup.readEntry<QString>("colorLabels", "").split(',', QString::SkipEmptyParts);
0801 #endif
0802 
0803         m_selectedColorLabels.clear();
0804         for (const QString &colorLabelStr : colorLabelsStr) {
0805             bool ok;
0806             const int colorLabel = colorLabelStr.toInt(&ok);
0807             if (ok) {
0808                 m_selectedColorLabels << colorLabel;
0809             }
0810         }
0811     }
0812 
0813     setupEnclosingSubtool();
0814 }
0815 
0816 KisToolEncloseAndFill::EnclosingMethod KisToolEncloseAndFill::loadEnclosingMethodFromConfig() const
0817 {
0818     return configStringToEnclosingMethod(m_configGroup.readEntry("enclosingMethod", enclosingMethodToConfigString(defaultEnclosingMethod())));
0819 }
0820 
0821 void KisToolEncloseAndFill::saveEnclosingMethodToConfig(EnclosingMethod enclosingMethod)
0822 {
0823     m_configGroup.writeEntry("enclosingMethod", enclosingMethodToConfigString(enclosingMethod));
0824 }
0825 
0826 QString KisToolEncloseAndFill::enclosingMethodToConfigString(EnclosingMethod enclosingMethod) const
0827 {
0828     switch (enclosingMethod) {
0829         case Rectangle: return "rectangle";
0830         case Ellipse: return "ellipse";
0831         case Path: return "path";
0832         case Brush: return "brush";
0833         default: return "lasso";
0834     }
0835 }
0836 
0837 KisToolEncloseAndFill::EnclosingMethod KisToolEncloseAndFill::configStringToEnclosingMethod(const QString &configString) const
0838 {
0839     if (configString == "rectangle") {
0840         return Rectangle;
0841     } else if (configString == "ellipse") {
0842         return Ellipse;
0843     } else if (configString == "path") {
0844         return Path;
0845     } else if (configString == "brush") {
0846         return Brush;
0847     }
0848     return Lasso;
0849 }
0850 
0851 QString KisToolEncloseAndFill::regionSelectionMethodToUserString(RegionSelectionMethod regionSelectionMethod) const
0852 {
0853     if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegions) {
0854         return i18nc("Region selection method in enclose and fill tool",
0855                      "All");
0856     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor) {
0857         return i18nc("Region selection method in enclose and fill tool",
0858                      "Specific color");
0859     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent) {
0860         return i18nc("Region selection method in enclose and fill tool",
0861                      "Transparency");
0862     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent) {
0863         return i18nc("Region selection method in enclose and fill tool",
0864                      "Specific color or transparency");
0865     } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor) {
0866         return i18nc("Region selection method in enclose and fill tool",
0867                      "All, excluding a specific color");
0868     } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent) {
0869         return i18nc("Region selection method in enclose and fill tool",
0870                      "All, excluding transparency");
0871     } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent) {
0872         return i18nc("Region selection method in enclose and fill tool",
0873                      "All, excluding a specific color or transparency");
0874     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor) {
0875         return i18nc("Region selection method in enclose and fill tool",
0876                      "Any surrounded by a specific color");
0877     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedByTransparent) {
0878         return i18nc("Region selection method in enclose and fill tool",
0879                      "Any surrounded by transparency");
0880     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent) {
0881         return i18nc("Region selection method in enclose and fill tool",
0882                      "Any surrounded by a specific color or transparency");
0883     }
0884     return QString();
0885 }
0886 
0887 KisToolEncloseAndFill::RegionSelectionMethod KisToolEncloseAndFill::loadRegionSelectionMethodFromConfig() const
0888 {
0889     return configStringToRegionSelectionMethod(m_configGroup.readEntry("regionSelectionMethod", regionSelectionMethodToConfigString(defaultRegionSelectionMethod())));
0890 }
0891 
0892 void KisToolEncloseAndFill::saveRegionSelectionMethodToConfig(RegionSelectionMethod regionSelectionMethod)
0893 {
0894     m_configGroup.writeEntry("regionSelectionMethod", regionSelectionMethodToConfigString(regionSelectionMethod));
0895 }
0896 
0897 QString KisToolEncloseAndFill::regionSelectionMethodToConfigString(RegionSelectionMethod regionSelectionMethod) const
0898 {
0899     if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegions) {
0900         return "allRegions";
0901     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor) {
0902         return "regionsFilledWithSpecificColor";
0903     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent) {
0904         return "regionsFilledWithTransparent";
0905     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent) {
0906         return "regionsFilledWithSpecificColorOrTransparent";
0907     } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor) {
0908         return "allRegionsExceptFilledWithSpecificColor";
0909     } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent) {
0910         return "allRegionsExceptFilledWithTransparent";
0911     } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent) {
0912         return "allRegionsExceptFilledWithSpecificColorOrTransparent";
0913     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor) {
0914         return "regionsSurroundedBySpecificColor";
0915     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedByTransparent) {
0916         return "regionsSurroundedByTransparent";
0917     } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent) {
0918         return "regionsSurroundedBySpecificColorOrTransparent";
0919     }
0920     return QString();
0921 }
0922 
0923 KisToolEncloseAndFill::RegionSelectionMethod KisToolEncloseAndFill::configStringToRegionSelectionMethod(const QString &configString) const
0924 {
0925     if (configString == "regionsFilledWithSpecificColor") {
0926         return RegionSelectionMethod::SelectRegionsFilledWithSpecificColor;
0927     } else if (configString == "regionsFilledWithTransparent") {
0928         return RegionSelectionMethod::SelectRegionsFilledWithTransparent;
0929     } else if (configString == "regionsFilledWithSpecificColorOrTransparent") {
0930         return RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent;
0931     } else if (configString == "allRegionsExceptFilledWithSpecificColor") {
0932         return RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor;
0933     } else if (configString == "allRegionsExceptFilledWithTransparent") {
0934         return RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent;
0935     } else if (configString == "allRegionsExceptFilledWithSpecificColorOrTransparent") {
0936         return RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent;
0937     } else if (configString == "regionsSurroundedBySpecificColor") {
0938         return RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor;
0939     } else if (configString == "regionsSurroundedByTransparent") {
0940         return RegionSelectionMethod::SelectRegionsSurroundedByTransparent;
0941     } else if (configString == "regionsSurroundedBySpecificColorOrTransparent") {
0942         return RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent;
0943     }
0944     return RegionSelectionMethod::SelectAllRegions;
0945 }
0946 
0947 KoColor KisToolEncloseAndFill::loadRegionSelectionColorFromConfig()
0948 {
0949     const QString xmlColor = m_configGroup.readEntry("regionSelectionColor", QString());
0950     QDomDocument doc;
0951     if (doc.setContent(xmlColor)) {
0952         QDomElement e = doc.documentElement().firstChild().toElement();
0953         QString channelDepthID = doc.documentElement().attribute("channeldepth", Integer16BitsColorDepthID.id());
0954         bool ok;
0955         if (e.hasAttribute("space") || e.tagName().toLower() == "srgb") {
0956             return KoColor::fromXML(e, channelDepthID, &ok);
0957         } else if (doc.documentElement().hasAttribute("space") || doc.documentElement().tagName().toLower() == "srgb"){
0958             return KoColor::fromXML(doc.documentElement(), channelDepthID, &ok);
0959         }
0960     }
0961     return KoColor();
0962 }
0963 
0964 KisToolEncloseAndFill::Reference KisToolEncloseAndFill::loadReferenceFromConfig() const
0965 {
0966     if (m_configGroup.hasKey("reference")) {
0967         return configStringToReference(m_configGroup.readEntry("reference", referenceToConfigString(defaultReference())));
0968     } else {
0969         bool sampleMerged = m_configGroup.readEntry("sampleMerged", false);
0970         return sampleMerged ? AllLayers : CurrentLayer;
0971     }
0972     return CurrentLayer;
0973 }
0974 
0975 void KisToolEncloseAndFill::saveReferenceToConfig(Reference reference)
0976 {
0977     m_configGroup.writeEntry("reference", referenceToConfigString(reference));
0978 }
0979 
0980 QString KisToolEncloseAndFill::referenceToConfigString(Reference reference) const
0981 {
0982     if (reference == AllLayers) {
0983         return "allLayers";
0984     } else if (reference == ColorLabeledLayers) {
0985         return "colorLabeledLayers";
0986     }
0987     return "currentLayer";
0988 }
0989 
0990 KisToolEncloseAndFill::Reference KisToolEncloseAndFill::configStringToReference(const QString &configString) const
0991 {
0992     if (configString == "allLayers") {
0993         return AllLayers;
0994     } else if (configString == "colorLabeledLayers") {
0995         return ColorLabeledLayers;
0996     }
0997     return CurrentLayer;
0998 }
0999 
1000 void KisToolEncloseAndFill::slot_optionButtonStripEnclosingMethod_buttonToggled(
1001     KoGroupButton *button,
1002     bool checked)
1003 {
1004     if (!checked) {
1005         return;
1006     }
1007 
1008     if (button == m_buttonEnclosingMethodRectangle) {
1009         m_enclosingMethod = Rectangle;
1010     } else if (button == m_buttonEnclosingMethodEllipse) {
1011         m_enclosingMethod = Ellipse;
1012     } else if (button == m_buttonEnclosingMethodPath) {
1013         m_enclosingMethod = Path;
1014     } else if (button == m_buttonEnclosingMethodLasso) {
1015         m_enclosingMethod = Lasso;
1016     } else {
1017         m_enclosingMethod = Brush;
1018     }
1019 
1020     saveEnclosingMethodToConfig(m_enclosingMethod);
1021     setupEnclosingSubtool();
1022 }
1023 
1024 void KisToolEncloseAndFill::slot_comboBoxRegionSelectionMethod_currentIndexChanged(int)
1025 {
1026     m_regionSelectionMethod = static_cast<RegionSelectionMethod>(m_comboBoxRegionSelectionMethod->currentData().toInt());
1027 
1028     KisOptionCollectionWidgetWithHeader *sectionWhatToFill =
1029         m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionWhatToFill");
1030     sectionWhatToFill->setWidgetVisible("buttonRegionSelectionColor",
1031         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor ||
1032         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent ||
1033         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor ||
1034         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent ||
1035         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor ||
1036         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent
1037     );
1038     sectionWhatToFill->setWidgetVisible(
1039         "checkBoxRegionSelectionIncludeContourRegions",
1040         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor ||
1041         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent ||
1042         m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent ||
1043         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor ||
1044         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent ||
1045         m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent
1046     );
1047 
1048     m_comboBoxRegionSelectionMethod->setToolTip(m_comboBoxRegionSelectionMethod->currentText());
1049 
1050     saveRegionSelectionMethodToConfig(m_regionSelectionMethod);
1051 }
1052 
1053 void KisToolEncloseAndFill::slot_buttonRegionSelectionColor_changed(const KoColor &color)
1054 {
1055     if (color == m_regionSelectionColor) {
1056         return;
1057     }
1058     m_regionSelectionColor = color;
1059     m_configGroup.writeEntry("regionSelectionColor", color.toXML());
1060 }
1061 
1062 void KisToolEncloseAndFill::slot_checkBoxRegionSelectionInvert_toggled(bool checked)
1063 {
1064     if (checked == m_regionSelectionInvert) {
1065         return;
1066     }
1067     m_regionSelectionInvert = checked;
1068     m_configGroup.writeEntry("regionSelectionInvert", checked);
1069 }
1070 
1071 void KisToolEncloseAndFill::slot_checkBoxRegionSelectionIncludeContourRegions_toggled(bool checked)
1072 {
1073     if (checked == m_regionSelectionIncludeContourRegions) {
1074         return;
1075     }
1076     m_regionSelectionIncludeContourRegions = checked;
1077     m_configGroup.writeEntry("regionSelectionIncludeContourRegions", checked);
1078 }
1079 
1080 void KisToolEncloseAndFill::slot_optionButtonStripFillWith_buttonToggled(
1081     KoGroupButton *button,
1082     bool checked)
1083 {
1084     if (!checked) {
1085         return;
1086     }
1087     const bool visible = button == m_buttonFillWithPattern;
1088     KisOptionCollectionWidgetWithHeader *sectionFillWith =
1089         m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionFillWith");
1090     sectionFillWith->setWidgetVisible("sliderPatternScale", visible);
1091     sectionFillWith->setWidgetVisible("angleSelectorPatternRotation", visible);
1092     
1093     m_fillType = button == m_buttonFillWithFG ? FillWithForegroundColor
1094                                               : (button == m_buttonFillWithBG ? FillWithBackgroundColor : FillWithPattern);
1095 
1096     m_configGroup.writeEntry(
1097         "fillWith",
1098         button == m_buttonFillWithFG ? "foregroundColor" : (button == m_buttonFillWithBG ? "backgroundColor" : "pattern")
1099     );
1100 }
1101 
1102 void KisToolEncloseAndFill::slot_sliderPatternScale_valueChanged(double value)
1103 {
1104     if (value == m_patternScale) {
1105         return;
1106     }
1107     m_patternScale = value;
1108     m_configGroup.writeEntry("patternScale", value);
1109 }
1110 
1111 void KisToolEncloseAndFill::slot_angleSelectorPatternRotation_angleChanged(double value)
1112 {
1113     if (value == m_patternRotation) {
1114         return;
1115     }
1116     m_patternRotation = value;
1117     m_configGroup.writeEntry("patternRotate", value);
1118 }
1119 
1120 void KisToolEncloseAndFill::slot_checkBoxUseCustomBlendingOptions_toggled(bool checked)
1121 {
1122     KisOptionCollectionWidgetWithHeader *sectionFillWith =
1123         m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionFillWith");
1124     sectionFillWith->setWidgetVisible("sliderCustomOpacity", checked);
1125     sectionFillWith->setWidgetVisible("comboBoxCustomCompositeOp", checked);
1126     m_useCustomBlendingOptions = checked;
1127     m_configGroup.writeEntry("useCustomBlendingOptions", checked);
1128 }
1129 
1130 void KisToolEncloseAndFill::slot_sliderCustomOpacity_valueChanged(int value)
1131 {
1132     if (value == m_customOpacity) {
1133         return;
1134     }
1135     m_customOpacity = value;
1136     m_configGroup.writeEntry("customOpacity", value);
1137 }
1138 
1139 void KisToolEncloseAndFill::slot_comboBoxCustomCompositeOp_currentIndexChanged(int index)
1140 {
1141     Q_UNUSED(index);
1142     const QString compositeOpId = m_comboBoxCustomCompositeOp->selectedCompositeOp().id();
1143     if (compositeOpId == m_customCompositeOp) {
1144         return;
1145     }
1146     m_customCompositeOp = compositeOpId;
1147     m_configGroup.writeEntry("customCompositeOp", compositeOpId);
1148 }
1149 
1150 void KisToolEncloseAndFill::slot_sliderFillThreshold_valueChanged(int value)
1151 {
1152     if (value == m_fillThreshold) {
1153         return;
1154     }
1155     m_fillThreshold = value;
1156     m_configGroup.writeEntry("fillThreshold", value);
1157 }
1158 
1159 void KisToolEncloseAndFill::slot_sliderFillOpacitySpread_valueChanged(int value)
1160 {
1161     if (value == m_fillOpacitySpread) {
1162         return;
1163     }
1164     m_fillOpacitySpread = value;
1165     m_configGroup.writeEntry("fillOpacitySpread", value);
1166 }
1167 
1168 void KisToolEncloseAndFill::slot_checkBoxSelectionAsBoundary_toggled(bool checked)
1169 {
1170     if (checked == m_useSelectionAsBoundary) {
1171         return;
1172     }
1173     m_useSelectionAsBoundary = checked;
1174     m_configGroup.writeEntry("useSelectionAsBoundary", checked);
1175 }
1176 
1177 void KisToolEncloseAndFill::slot_checkBoxAntiAlias_toggled(bool checked)
1178 {
1179     if (checked == m_antiAlias) {
1180         return;
1181     }
1182     m_antiAlias = checked;
1183     m_configGroup.writeEntry("antiAlias", checked);
1184 }
1185 
1186 void KisToolEncloseAndFill::slot_sliderExpand_valueChanged(int value)
1187 {
1188     if (value == m_expand) {
1189         return;
1190     }
1191     m_expand = value;
1192     m_configGroup.writeEntry("expand", value);
1193 }
1194 
1195 void KisToolEncloseAndFill::slot_buttonStopGrowingAtDarkestPixel_toggled(bool enabled)
1196 {
1197     if (enabled == m_stopGrowingAtDarkestPixel) {
1198         return;
1199     }
1200     m_stopGrowingAtDarkestPixel = enabled;
1201     m_configGroup.writeEntry("stopGrowingAtDarkestPixel", enabled);
1202 }
1203 
1204 void KisToolEncloseAndFill::slot_sliderFeather_valueChanged(int value)
1205 {
1206     if (value == m_feather) {
1207         return;
1208     }
1209     m_feather = value;
1210     m_configGroup.writeEntry("feather", value);
1211 }
1212 
1213 void KisToolEncloseAndFill::slot_optionButtonStripReference_buttonToggled(
1214     KoGroupButton *button,
1215     bool checked)
1216 {
1217     if (!checked) {
1218         return;
1219     }
1220     KisOptionCollectionWidgetWithHeader *sectionReference =
1221         m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionReference");
1222     sectionReference->setWidgetVisible("widgetLabels", button == m_buttonReferenceLabeled);
1223     
1224     m_reference = button == m_buttonReferenceCurrent ? CurrentLayer
1225                                                      : (button == m_buttonReferenceAll ? AllLayers : ColorLabeledLayers);
1226 
1227     m_configGroup.writeEntry(
1228         "reference",
1229         button == m_buttonReferenceCurrent ? "currentLayer" : (button == m_buttonReferenceAll ? "allLayers" : "colorLabeledLayers")
1230     );
1231 }
1232 
1233 void KisToolEncloseAndFill::slot_widgetLabels_selectionChanged()
1234 {
1235     QList<int> labels = m_widgetLabels->selection();
1236     if (labels == m_selectedColorLabels) {
1237         return;
1238     }
1239     m_selectedColorLabels = labels;
1240     if (labels.isEmpty()) {
1241         return;
1242     }
1243     QString colorLabels = QString::number(labels.first());
1244     for (int i = 1; i < labels.size(); ++i) {
1245         colorLabels += "," + QString::number(labels[i]);
1246     }
1247     m_configGroup.writeEntry("colorLabels", colorLabels);
1248 }
1249 
1250 void KisToolEncloseAndFill::slot_buttonReset_clicked()
1251 {
1252     m_buttonEnclosingMethodLasso->setChecked(true);
1253     m_comboBoxRegionSelectionMethod->setCurrentIndex(
1254         m_comboBoxRegionSelectionMethod->findData(static_cast<int>(RegionSelectionMethod::SelectAllRegions))
1255     );
1256     m_buttonRegionSelectionColor->setColor(KoColor());
1257     m_checkBoxRegionSelectionInvert->setChecked(false);
1258     m_checkBoxRegionSelectionIncludeContourRegions->setChecked(false);
1259     m_buttonFillWithFG->setChecked(true);
1260     m_sliderPatternScale->setValue(100.0);
1261     m_angleSelectorPatternRotation->setAngle(0.0);
1262     m_checkBoxCustomBlendingOptions->setChecked(false);
1263     m_sliderCustomOpacity->setValue(100);
1264     m_comboBoxCustomCompositeOp->selectCompositeOp(KoID(COMPOSITE_OVER));
1265     m_sliderFillThreshold->setValue(8);
1266     m_sliderFillOpacitySpread->setValue(100);
1267     m_checkBoxSelectionAsBoundary->setChecked(true);
1268     m_checkBoxAntiAlias->setChecked(false);
1269     m_sliderExpand->setValue(0);
1270     m_buttonStopGrowingAtDarkestPixel->setChecked(false);
1271     m_sliderFeather->setValue(0);
1272     m_buttonReferenceCurrent->setChecked(true);
1273     m_widgetLabels->setSelection({});
1274 }
1275 
1276 void KisToolEncloseAndFill::slot_currentNodeChanged(const KisNodeSP node)
1277 {
1278     if (m_previousNode && m_previousNode->paintDevice()) {
1279         disconnect(m_previousNode->paintDevice().data(),
1280                    SIGNAL(colorSpaceChanged(const KoColorSpace*)),
1281                    this,
1282                    SLOT(slot_colorSpaceChanged(const KoColorSpace*)));
1283     }
1284     if (node && node->paintDevice()) {
1285         connect(node->paintDevice().data(),
1286                 SIGNAL(colorSpaceChanged(const KoColorSpace*)),
1287                 this,
1288                 SLOT(slot_colorSpaceChanged(const KoColorSpace*)));
1289         slot_colorSpaceChanged(node->paintDevice()->colorSpace());
1290     }
1291     m_previousNode = node;
1292 }
1293 
1294 void KisToolEncloseAndFill::slot_colorSpaceChanged(const KoColorSpace *colorSpace)
1295 {
1296     if (!m_comboBoxCustomCompositeOp) {
1297         return;
1298     }
1299     const KoColorSpace *compositionSpace = colorSpace;
1300     if (currentNode() && currentNode()->paintDevice()) {
1301         // Currently, composition source is enough to determine the available blending mode,
1302         // because either destination is the same (paint layers), or composition happens
1303         // in source space (masks).
1304         compositionSpace = currentNode()->paintDevice()->compositionSourceColorSpace();
1305     }
1306     m_comboBoxCustomCompositeOp->validate(compositionSpace);
1307 }