File indexing completed on 2024-12-22 04:12:14

0001 /*
0002  *  SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_selection_action_factories.h"
0008 
0009 #include <QMimeData>
0010 
0011 #include <klocalizedstring.h>
0012 #include <kundo2command.h>
0013 
0014 #include <KisMainWindow.h>
0015 #include <KisDocument.h>
0016 #include <KisPart.h>
0017 #include <KoPathShape.h>
0018 #include <KoShapeController.h>
0019 #include <KoShapeRegistry.h>
0020 #include <KoCompositeOpRegistry.h>
0021 #include <KoShapeManager.h>
0022 #include <KoSelection.h>
0023 #include <KoDocumentResourceManager.h>
0024 #include <KoShapeStroke.h>
0025 #include <KoDocumentInfo.h>
0026 #include <KoCanvasBase.h>
0027 
0028 #include "KisViewManager.h"
0029 #include "kis_canvas_resource_provider.h"
0030 #include "kis_clipboard.h"
0031 #include "kis_pixel_selection.h"
0032 #include "kis_paint_layer.h"
0033 #include "kis_image.h"
0034 #include "KisImageBarrierLock.h"
0035 #include "kis_fill_painter.h"
0036 #include "kis_transaction.h"
0037 #include "kis_iterator_ng.h"
0038 #include "kis_processing_applicator.h"
0039 #include "kis_group_layer.h"
0040 #include "commands/kis_selection_commands.h"
0041 #include "commands/kis_image_layer_add_command.h"
0042 #include "kis_tool_proxy.h"
0043 #include "kis_canvas2.h"
0044 #include "kis_canvas_controller.h"
0045 #include "kis_selection_manager.h"
0046 #include "commands_new/kis_transaction_based_command.h"
0047 #include "kis_selection_filters.h"
0048 #include "kis_shape_selection.h"
0049 #include "kis_shape_layer.h"
0050 #include <kis_shape_controller.h>
0051 #include "kis_image_animation_interface.h"
0052 #include "kis_time_span.h"
0053 #include "kis_keyframe_channel.h"
0054 #include "kis_node_manager.h"
0055 #include "kis_layer_utils.h"
0056 #include <kis_selection_mask.h>
0057 #include <KisReferenceImagesLayer.h>
0058 
0059 #include <processing/fill_processing_visitor.h>
0060 #include <kis_selection_tool_helper.h>
0061 
0062 #include "kis_figure_painting_tool_helper.h"
0063 #include "kis_update_outline_job.h"
0064 
0065 namespace ActionHelper {
0066 
0067     void trimDevice(KisViewManager *view,
0068                         KisPaintDeviceSP device,
0069                         bool makeSharpClip = false,
0070                         const KisTimeSpan &range = KisTimeSpan())
0071     {
0072         Q_UNUSED(range); // TODO: Allow multiple frame operation across a timespan.
0073 
0074         KisImageWSP image = view->image();
0075         if (!image) return;
0076 
0077         KisSelectionSP selection = view->selection();
0078 
0079         QRect rc = (selection) ? selection->selectedExactRect() : image->bounds();
0080 
0081         const KoColorSpace *cs = device->colorSpace();
0082 
0083         // We need to allow for trimming from non-transparent defaultPixel layers.
0084         // Default color should be phased out of use when the area in question is not aligned with image bounds.
0085         // Otherwise, we can maintain default pixel.
0086         const bool hasNonTransparentDefaultPixel = device->defaultPixel() != KoColor::createTransparent(device->colorSpace());
0087         const bool needsTransparentPixel = selection && rc != image->bounds() && hasNonTransparentDefaultPixel;
0088 
0089         if (selection) {
0090             // Apply selection mask.
0091             KisPaintDeviceSP selectionProjection = selection->projection();
0092             const KoColorSpace *selCs = selection->projection()->colorSpace();
0093 
0094             KisSequentialIterator layerIt(device, rc);
0095             KisSequentialConstIterator selectionIt(selectionProjection, rc);
0096 
0097             while (layerIt.nextPixel() && selectionIt.nextPixel()) {
0098 
0099                 /**
0100                  * Sharp method is an exact reverse of COMPOSITE_OVER
0101                  * so if you cover the cut/copied piece over its source
0102                  * you get an exactly the same image without any seams
0103                  */
0104                 if (makeSharpClip) {
0105                     qreal dstAlpha = cs->opacityF(layerIt.rawData());
0106                     qreal sel = selCs->opacityF(selectionIt.oldRawData());
0107                     qreal newAlpha = sel * dstAlpha / (1.0 - dstAlpha + sel * dstAlpha);
0108                     float mask = newAlpha / dstAlpha;
0109 
0110                     cs->applyAlphaNormedFloatMask(layerIt.rawData(), &mask, 1);
0111                 } else {
0112                     cs->applyAlphaU8Mask(layerIt.rawData(), selectionIt.oldRawData(), 1);
0113                 }
0114             }
0115         }
0116 
0117 
0118 
0119         if ( needsTransparentPixel ) {
0120             device->setDefaultPixel(KoColor::createTransparent(device->colorSpace()));
0121             device->purgeDefaultPixels();
0122         }
0123 
0124         device->crop(rc);
0125     }
0126 
0127     KisImageSP makeImage(KisViewManager *view, KisNodeList nodes)
0128     {
0129         KisImageWSP image = view->image();
0130 
0131         KisImageSP clipImage = new KisImage(0, image->width(), image->height(), image->colorSpace(), "ClipImage");
0132         clipImage->setResolution(image->xRes(), image->yRes());
0133         Q_FOREACH (KisNodeSP node, nodes) {
0134             clipImage->addNode(node, clipImage->root());
0135         }
0136 
0137         clipImage->refreshGraphAsync();
0138         clipImage->waitForDone();
0139 
0140         return clipImage;
0141     }
0142 }
0143 
0144 void KisSelectAllActionFactory::run(KisViewManager *view)
0145 {
0146     KisImageWSP image = view->image();
0147     if (!image) return;
0148 
0149     if (view->canvasBase()->toolProxy()->selectAll()) {
0150         return;
0151     }
0152 
0153     KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Select All"));
0154 
0155     ap->applyCommand(new KisCommandUtils::LambdaCommand(
0156         [image] () {
0157             return !image->globalSelection() ?
0158                 new KisSetEmptyGlobalSelectionCommand(image) : 0;
0159         }),
0160         KisStrokeJobData::SEQUENTIAL,
0161         KisStrokeJobData::EXCLUSIVE);
0162 
0163     struct SelectAll : public KisTransactionBasedCommand {
0164         SelectAll(KisImageSP image) : m_image(image) {}
0165         KisImageSP m_image;
0166         KUndo2Command* paint() override {
0167             KisSelectionSP selection = m_image->globalSelection();
0168             KisSelectionTransaction transaction(selection->pixelSelection());
0169             selection->pixelSelection()->clear();
0170             selection->pixelSelection()->select(m_image->bounds());
0171             return transaction.endAndTake();
0172         }
0173     };
0174 
0175     ap->applyCommand(new SelectAll(image),
0176                      KisStrokeJobData::SEQUENTIAL,
0177                      KisStrokeJobData::EXCLUSIVE);
0178 
0179     endAction(ap, KisOperationConfiguration(id()).toXML());
0180 }
0181 
0182 void KisDeselectActionFactory::run(KisViewManager *view)
0183 {
0184     KisImageWSP image = view->image();
0185     if (!image) return;
0186 
0187     if (view->canvasBase()->toolProxy()->hasSelection()) {
0188         // see KisCutCopyActionFactory::run
0189         KisImageBarrierLock lock(image, std::try_to_lock);
0190         if (!lock.owns_lock()) return;
0191 
0192         view->canvasBase()->toolProxy()->deselect();
0193         return;
0194     }
0195 
0196     KUndo2Command *cmd = new KisDeselectActiveSelectionCommand(view->selection(), image);
0197 
0198     KisProcessingApplicator *ap = beginAction(view, cmd->text());
0199     ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
0200     endAction(ap, KisOperationConfiguration(id()).toXML());
0201 }
0202 
0203 void KisReselectActionFactory::run(KisViewManager *view)
0204 {
0205     KisImageWSP image = view->image();
0206     if (!image) return;
0207 
0208     KUndo2Command *cmd = new KisReselectActiveSelectionCommand(view->activeNode(), image);
0209 
0210     KisProcessingApplicator *ap = beginAction(view, cmd->text());
0211     ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
0212     endAction(ap, KisOperationConfiguration(id()).toXML());
0213 }
0214 
0215 void KisFillActionFactory::run(const QString &fillSource, KisViewManager *view)
0216 {
0217     KisNodeSP node = view->activeNode();
0218     if (!node || !node->hasEditablePaintDevice()) return;
0219 
0220     KisSelectionSP selection = view->selection();
0221     QRect selectedRect = selection ?
0222                          selection->selectedRect() : view->image()->bounds();
0223     Q_UNUSED(selectedRect);
0224     KisPaintDeviceSP filled = node->paintDevice()->createCompositionSourceDevice();
0225     Q_UNUSED(filled);
0226     bool usePattern = false;
0227     bool useBgColor = false;
0228 
0229     if (fillSource.contains("pattern")) {
0230         usePattern = true;
0231     } else if (fillSource.contains("bg")) {
0232         useBgColor = true;
0233     }
0234 
0235     KisProcessingApplicator applicator(view->image(), node,
0236                                        KisProcessingApplicator::NONE,
0237                                        KisImageSignalVector(),
0238                                        kundo2_i18n("Flood Fill Layer"));
0239 
0240     KisResourcesSnapshotSP resources =
0241         new KisResourcesSnapshot(view->image(), node, view->canvasResourceProvider()->resourceManager());
0242     if (!fillSource.contains("opacity")) {
0243         resources->setOpacity(1.0);
0244     }
0245 
0246     FillProcessingVisitor *visitor =
0247         new FillProcessingVisitor(resources->image()->projection(),
0248                                   selection,
0249                                   resources);
0250 
0251     visitor->setSeedPoint(QPoint(0, 0));
0252     visitor->setUsePattern(usePattern);
0253     visitor->setSelectionOnly(true);
0254     visitor->setUseBgColor(useBgColor);
0255 
0256     applicator.applyVisitor(visitor,
0257                             KisStrokeJobData::SEQUENTIAL,
0258                             KisStrokeJobData::EXCLUSIVE);
0259 
0260     applicator.end();
0261 
0262     view->canvasResourceProvider()->slotPainting();
0263 }
0264 
0265 void KisClearActionFactory::run(KisViewManager *view)
0266 {
0267     // XXX: "Add saving of XML data for Clear action"
0268 
0269     view->canvasBase()->toolProxy()->deleteSelection();
0270 }
0271 
0272 void KisImageResizeToSelectionActionFactory::run(KisViewManager *view)
0273 {
0274     // XXX: "Add saving of XML data for Image Resize To Selection action"
0275 
0276     KisSelectionSP selection = view->selection();
0277     if (!selection) return;
0278 
0279     view->image()->cropImage(selection->selectedExactRect());
0280 }
0281 
0282 void KisCutCopyActionFactory::run(bool willCut, bool makeSharpClip, KisViewManager *view)
0283 {
0284     KisImageSP image = view->image();
0285     if (!image) return;
0286 
0287     if (!view->blockUntilOperationsFinished(image)) return;
0288 
0289     // Reference layers is a fake node, so it isn't added to the layer stack, this results in KisSelectedShapesProxy not
0290     // being aware of the active shapeManager and its selected shapes.
0291     const auto currentToolHasSelection =
0292         view->canvasBase()->toolProxy()->hasSelection();
0293 
0294     const bool haveShapesSelected =
0295         view->selectionManager()->haveShapesSelected();
0296 
0297     KisNodeSP node = view->activeNode();
0298     KisSelectionSP selection = view->selection();
0299 
0300     if (!makeSharpClip && (haveShapesSelected || currentToolHasSelection)) {
0301         /**
0302          * Make sure that we use tryBarrierLock() here becasue it does **not**
0303          * cause requestStrokeEnd() to be called in the tools, hence does not
0304          * prevent disruptions in the text tool.
0305          */
0306         KisImageBarrierLock lock(image, std::try_to_lock);
0307         if (!lock.owns_lock()) return;
0308 
0309         // XXX: "Add saving of XML data for Cut/Copy of shapes"
0310         if (willCut) {
0311             view->canvasBase()->toolProxy()->cut();
0312         } else {
0313             view->canvasBase()->toolProxy()->copy();
0314         }
0315     } else if (selection) {
0316         KisNodeList selectedNodes = view->nodeManager()->selectedNodes();
0317 
0318         KisNodeList masks;
0319         Q_FOREACH (KisNodeSP node, selectedNodes) {
0320             if (node->inherits("KisMask")) {
0321                 masks.append(node);
0322             }
0323         }
0324 
0325         selectedNodes = KisLayerUtils::sortAndFilterMergeableInternalNodes(selectedNodes);
0326 
0327         KisNodeList nodes;
0328         Q_FOREACH (KisNodeSP node, selectedNodes) {
0329             KisNodeSP dupNode;
0330             if (node->inherits("KisShapeLayer")) {
0331                 KisPaintDeviceSP dev = new KisPaintDevice(*node->projection());
0332                 // might have to change node's name (vector to paint layer)
0333                 dupNode = new KisPaintLayer(image, node->name(), node->opacity(), dev);
0334             } else {
0335                 dupNode = node->clone();
0336             }
0337             nodes.append(dupNode);
0338         }
0339 
0340         Q_FOREACH (KisNodeSP node, nodes) {
0341             KisLayerUtils::recursiveApplyNodes(node, [image, view, makeSharpClip] (KisNodeSP node) {
0342                 if (node && node->paintDevice()) {
0343                     node->paintDevice()->burnKeyframe();
0344                 }
0345 
0346                 KisTimeSpan range;
0347 
0348                 KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Raster.id());
0349                 if (channel) {
0350                     const int currentTime = image->animationInterface()->currentTime();
0351                     range = channel->affectedFrames(currentTime);
0352                 }
0353 
0354                 if (node && node->paintDevice() && !node->inherits("KisMask")) {
0355                     ActionHelper::trimDevice(view, node->paintDevice(), makeSharpClip, range);
0356                 }
0357             });
0358         }
0359 
0360         KisImageSP tempImage = ActionHelper::makeImage(view, nodes);
0361         KisClipboard::instance()->setLayers(nodes, tempImage);
0362 
0363 
0364         KUndo2MagicString actionName = willCut ?
0365                     kundo2_i18n("Cut") :
0366                     kundo2_i18n("Copy");
0367         KisProcessingApplicator *ap = beginAction(view, actionName);
0368 
0369         if (willCut) {
0370             selectedNodes.append(masks);
0371             Q_FOREACH (KisNodeSP node, selectedNodes) {
0372                 KisLayerUtils::recursiveApplyNodes(node, [selection, masks, ap] (KisNodeSP node){
0373 
0374                     if (!node->hasEditablePaintDevice()) {
0375                         return;
0376                     }
0377 
0378                     // applied on masks if selected explicitly (when CTRL-X(cut) is used for deletion)
0379                     if (node->inherits("KisMask") && !masks.contains(node)) {
0380                         return;
0381                     }
0382 
0383                     struct ClearSelection : public KisTransactionBasedCommand {
0384                         ClearSelection(KisNodeSP node, KisSelectionSP sel)
0385                             : m_node(node), m_sel(sel) {}
0386                         KisNodeSP m_node;
0387                         KisSelectionSP m_sel;
0388 
0389                         KUndo2Command* paint() override {
0390                             KisSelectionSP cutSelection = m_sel;
0391                             // Shrinking the cutting area was previously used
0392                             // for getting seamless cut-paste. Now we use makeSharpClip
0393                             // instead.
0394                             // QRect originalRect = cutSelection->selectedExactRect();
0395                             // static const int preciseSelectionThreshold = 16;
0396                             //
0397                             // if (originalRect.width() > preciseSelectionThreshold ||
0398                             //     originalRect.height() > preciseSelectionThreshold) {
0399                             //     cutSelection = new KisSelection(*m_sel);
0400                             //     delete cutSelection->flatten();
0401                             //
0402                             //     KisSelectionFilter* filter = new KisShrinkSelectionFilter(1, 1, false);
0403                             //
0404                             //     QRect processingRect = filter->changeRect(originalRect);
0405                             //     filter->process(cutSelection->pixelSelection(), processingRect);
0406                             // }
0407 
0408                             KisTransaction transaction(m_node->paintDevice());
0409                             m_node->paintDevice()->clearSelection(cutSelection);
0410                             m_node->setDirty(cutSelection->selectedRect());
0411                             return transaction.endAndTake();
0412                         }
0413                     };
0414 
0415                     KUndo2Command *command = new ClearSelection(node, selection);
0416                     ap->applyCommand(command, KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL);
0417 
0418                 });
0419             }
0420         }
0421 
0422         KisOperationConfiguration config(id());
0423         config.setProperty("will-cut", willCut);
0424         endAction(ap, config.toXML());
0425     } else if (!makeSharpClip) {
0426         if (willCut) {
0427             view->nodeManager()->cutLayersToClipboard();
0428         } else {
0429             view->nodeManager()->copyLayersToClipboard();
0430         }
0431     }
0432 }
0433 
0434 void KisCopyMergedActionFactory::run(KisViewManager *view)
0435 {
0436     KisImageWSP image = view->image();
0437     if (!image) return;
0438     if (!view->blockUntilOperationsFinished(image)) return;
0439 
0440     image->barrierLock();
0441     KisPaintDeviceSP dev = new KisPaintDevice(*image->root()->projection());
0442     ActionHelper::trimDevice(view, dev);
0443 
0444     KisNodeSP node = new KisPaintLayer(image, "Projection", OPACITY_OPAQUE_U8, dev);
0445     KisNodeList nodes{node};
0446 
0447     KisImageSP tempImage = ActionHelper::makeImage(view, nodes);
0448     KisClipboard::instance()->setLayers(nodes, tempImage);
0449     image->unlock();
0450 
0451     KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Copy Merged"));
0452     endAction(ap, KisOperationConfiguration(id()).toXML());
0453 }
0454 
0455 void KisInvertSelectionOperation::runFromXML(KisViewManager* view, const KisOperationConfiguration& config)
0456 {
0457     KisSelectionFilter* filter = new KisInvertSelectionFilter();
0458 
0459     runFilter(filter, view, config);
0460 }
0461 
0462 void KisSelectionToVectorActionFactory::run(KisViewManager *view)
0463 {
0464     KisSelectionSP selection = view->selection();
0465 
0466     if (selection->hasShapeSelection()) {
0467         view->showFloatingMessage(i18nc("floating message",
0468                                         "Selection is already in a vector format "),
0469                                   QIcon(), 2000, KisFloatingMessage::Low);
0470         return;
0471     }
0472 
0473     if (!selection->outlineCacheValid()) {
0474         view->image()->addSpontaneousJob(new KisUpdateOutlineJob(selection, false, Qt::transparent));
0475         if (!view->blockUntilOperationsFinished(view->image())) {
0476             return;
0477         }
0478     }
0479 
0480     QPainterPath selectionOutline = selection->outlineCache();
0481     QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
0482 
0483     KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline));
0484     shape->setShapeId(KoPathShapeId);
0485 
0486     /**
0487      * Mark a shape that it belongs to a shape selection
0488      */
0489     if(!shape->userData()) {
0490         shape->setUserData(new KisShapeSelectionMarker);
0491     }
0492 
0493     KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Convert to Vector Selection"));
0494 
0495     ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape, 0),
0496                      KisStrokeJobData::SEQUENTIAL,
0497                      KisStrokeJobData::EXCLUSIVE);
0498 
0499     endAction(ap, KisOperationConfiguration(id()).toXML());
0500 }
0501 
0502 void KisSelectionToRasterActionFactory::run(KisViewManager *view)
0503 {
0504     KisSelectionSP selection = view->selection();
0505 
0506     if (!selection->hasShapeSelection()) {
0507         view->showFloatingMessage(i18nc("floating message",
0508                                         "Selection is already in a raster format "),
0509                                   QIcon(), 2000, KisFloatingMessage::Low);
0510         return;
0511     }
0512 
0513     KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Convert to Vector Selection"));
0514 
0515     struct RasterizeSelection : public KisTransactionBasedCommand {
0516         RasterizeSelection(KisSelectionSP sel)
0517             : m_sel(sel) {}
0518         KisSelectionSP m_sel;
0519 
0520         KUndo2Command* paint() override {
0521             // just create an empty transaction: it will rasterize the
0522             // selection and emit the necessary signals
0523 
0524             KisTransaction transaction(m_sel->pixelSelection());
0525             return transaction.endAndTake();
0526         }
0527     };
0528 
0529     ap->applyCommand(new RasterizeSelection(selection),
0530                      KisStrokeJobData::SEQUENTIAL,
0531                      KisStrokeJobData::EXCLUSIVE);
0532 
0533     endAction(ap, KisOperationConfiguration(id()).toXML());
0534 }
0535 
0536 void KisShapesToVectorSelectionActionFactory::run(KisViewManager* view)
0537 {
0538     const QList<KoShape*> originalShapes = view->canvasBase()->shapeManager()->selection()->selectedShapes();
0539 
0540     bool hasSelectionShapes = false;
0541     QList<KoShape*> clonedShapes;
0542 
0543     Q_FOREACH (KoShape *shape, originalShapes) {
0544         if (dynamic_cast<KisShapeSelectionMarker*>(shape->userData())) {
0545             hasSelectionShapes = true;
0546             continue;
0547         }
0548 
0549         clonedShapes << shape->cloneShapeAndBakeAbsoluteTransform();
0550     }
0551 
0552     if (clonedShapes.isEmpty()) {
0553         if (hasSelectionShapes) {
0554             view->showFloatingMessage(i18nc("floating message",
0555                                             "The shape already belongs to a selection"),
0556                                       QIcon(), 2000, KisFloatingMessage::Low);
0557         }
0558         return;
0559     }
0560 
0561     KisSelectionToolHelper helper(view->canvasBase(), kundo2_i18n("Convert shapes to vector selection"));
0562     helper.addSelectionShapes(clonedShapes);
0563 }
0564 
0565 void KisSelectionToShapeActionFactory::run(KisViewManager *view)
0566 {
0567     KisSelectionSP selection = view->selection();
0568     if (!selection->outlineCacheValid()) {
0569         return;
0570     }
0571 
0572     QPainterPath selectionOutline = selection->outlineCache();
0573     QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
0574 
0575     KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline));
0576     shape->setShapeId(KoPathShapeId);
0577 
0578     KoColor fgColor = view->canvasBase()->resourceManager()->resource(KoCanvasResource::ForegroundColor).value<KoColor>();
0579     KoShapeStrokeSP border(new KoShapeStroke(1.0, fgColor.toQColor()));
0580     shape->setStroke(border);
0581 
0582     KUndo2Command *cmd = view->canvasBase()->shapeController()->addShapeDirect(shape, 0);
0583     KisProcessingApplicator::runSingleCommandStroke(view->image(), cmd);
0584 }
0585 
0586 void KisStrokeSelectionActionFactory::run(KisViewManager *view, const StrokeSelectionOptions& params)
0587 {
0588     KisImageWSP image = view->image();
0589     if (!image) {
0590         return;
0591     }
0592 
0593     KisSelectionSP selection = view->selection();
0594     if (!selection) {
0595         return;
0596     }
0597 
0598     int size = params.lineSize;
0599 
0600     KisPixelSelectionSP pixelSelection = selection->projection();
0601     if (!pixelSelection->outlineCacheValid()) {
0602         pixelSelection->recalculateOutlineCache();
0603     }
0604 
0605     QPainterPath outline = pixelSelection->outlineCache();
0606     QColor color = params.color.toQColor();
0607 
0608     KisNodeSP currentNode = view->canvasResourceProvider()->resourceManager()->resource(KoCanvasResource::CurrentKritaNode).value<KisNodeWSP>();
0609     if (!currentNode->inherits("KisShapeLayer") && currentNode->paintDevice()) {
0610         KoCanvasResourceProvider * rManager = view->canvasResourceProvider()->resourceManager();
0611         KisToolShapeUtils::StrokeStyle strokeStyle =  KisToolShapeUtils::StrokeStyleForeground;
0612         KisToolShapeUtils::FillStyle fillStyle = params.fillStyle();
0613 
0614         KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"),
0615                                        image,
0616                                        currentNode,
0617                                        rManager ,
0618                                        strokeStyle,
0619                                        fillStyle);
0620         helper.setFGColorOverride(params.color);
0621         helper.setSelectionOverride(0);
0622         QPen pen(Qt::red, size);
0623         pen.setJoinStyle(Qt::RoundJoin);
0624 
0625         if (fillStyle != KisToolShapeUtils::FillStyleNone) {
0626             helper.paintPainterPathQPenFill(outline, pen, params.fillColor);
0627         }
0628         else {
0629             helper.paintPainterPathQPen(outline, pen, params.fillColor);
0630         }
0631     }
0632     else if (currentNode->inherits("KisShapeLayer")) {
0633 
0634         QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform();
0635 
0636         KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(outline));
0637         shape->setShapeId(KoPathShapeId);
0638 
0639         KoShapeStrokeSP border(new KoShapeStroke(size, color));
0640         shape->setStroke(border);
0641 
0642         KUndo2Command *cmd = view->canvasBase()->shapeController()->addShapeDirect(shape, 0);
0643         KisProcessingApplicator::runSingleCommandStroke(view->image(), cmd);
0644     }
0645 }
0646 
0647 void KisStrokeBrushSelectionActionFactory::run(KisViewManager *view, const StrokeSelectionOptions& params)
0648 {
0649     KisImageWSP image = view->image();
0650     if (!image) {
0651         return;
0652     }
0653 
0654     KisSelectionSP selection = view->selection();
0655     if (!selection) {
0656         return;
0657     }
0658 
0659     KisPixelSelectionSP pixelSelection = selection->projection();
0660     if (!pixelSelection->outlineCacheValid()) {
0661         pixelSelection->recalculateOutlineCache();
0662     }
0663 
0664     KisNodeSP currentNode = view->canvasResourceProvider()->resourceManager()->resource(KoCanvasResource::CurrentKritaNode).value<KisNodeWSP>();
0665     if (!currentNode->inherits("KisShapeLayer") && currentNode->paintDevice())
0666     {
0667         KoCanvasResourceProvider * rManager = view->canvasResourceProvider()->resourceManager();
0668         QPainterPath outline = pixelSelection->outlineCache();
0669         KisToolShapeUtils::StrokeStyle strokeStyle =  KisToolShapeUtils::StrokeStyleForeground;
0670         KisToolShapeUtils::FillStyle fillStyle =  KisToolShapeUtils::FillStyleNone;
0671         KoColor color = params.color;
0672 
0673         KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"),
0674                                        image,
0675                                        currentNode,
0676                                        rManager,
0677                                        strokeStyle,
0678                                        fillStyle);
0679         helper.setFGColorOverride(color);
0680         helper.setSelectionOverride(0);
0681         helper.paintPainterPath(outline);
0682     }
0683 }