Krita Source Code Documentation
Loading...
Searching...
No Matches
KisPasteActionFactories.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include "kis_config.h"
10#include "kis_image.h"
11#include "KisViewManager.h"
12#include "kis_tool_proxy.h"
13#include "kis_canvas2.h"
15#include "kis_group_layer.h"
16#include "kis_paint_device.h"
17#include "kis_paint_layer.h"
18#include "kis_shape_layer.h"
19#include "kis_import_catcher.h"
20#include "kis_clipboard.h"
21#include "kis_selection.h"
26#include "kis_node_manager.h"
27
28#include <KoDocumentInfo.h>
29#include <KoSvgPaste.h>
30#include <KoShapeController.h>
32#include <KoShapeManager.h>
33#include <KoSelection.h>
35#include "kis_algebra_2d.h"
36#include <KoShapeMoveCommand.h>
38#include "kis_time_span.h"
41#include "kis_painter.h"
42#include <KisPart.h>
43#include <KisDocument.h>
47#include <KoShapeBackground.h>
48#include <KoShapeStroke.h>
49#include <KisMainWindow.h>
50#include <QApplication>
51#include <QClipboard>
52
53namespace {
54QPointF getFittingOffset(QList<KoShape*> shapes,
55 const QPointF &shapesOffset,
56 const QRectF &documentRect,
57 const qreal fitRatio)
58{
59 QPointF accumulatedFitOffset;
60
61 Q_FOREACH (KoShape *shape, shapes) {
62 const QRectF bounds = shape->boundingRect();
63
64 const QPointF center = bounds.center() + shapesOffset;
65
66 const qreal wMargin = (0.5 - fitRatio) * bounds.width();
67 const qreal hMargin = (0.5 - fitRatio) * bounds.height();
68 const QRectF allowedRect = documentRect.adjusted(-wMargin, -hMargin, wMargin, hMargin);
69
70 const QPointF fittedCenter = KisAlgebra2D::clampPoint(center, allowedRect);
71
72 accumulatedFitOffset += fittedCenter - center;
73 }
74
75 return accumulatedFitOffset;
76}
77
78bool tryPasteShapes(KisPasteActionFactory::Flags flags, KisViewManager *view)
79{
80 bool result = false;
81
83
84 if (paste.hasShapes() && view->activeNode()->isEditable()) {
85 KoCanvasBase *canvas = view->canvasBase();
86
87 QSizeF fragmentSize;
88 QList<KoShape*> shapes =
89 paste.fetchShapes(canvas->shapeController()->documentRectInPixels(),
90 canvas->shapeController()->pixelsPerInch(), &fragmentSize);
91
92 if (!shapes.isEmpty()) {
93 KoShapeManager *shapeManager = canvas->shapeManager();
94 shapeManager->selection()->deselectAll();
95
96 // adjust z-index of the shapes so that they would be
97 // pasted on the top of the stack
98 QList<KoShape*> topLevelShapes = shapeManager->topLevelShapes();
99 auto it = std::max_element(topLevelShapes.constBegin(), topLevelShapes.constEnd(), KoShape::compareShapeZIndex);
100 if (it != topLevelShapes.constEnd()) {
101 const int zIndexOffset = (*it)->zIndex();
102
103 std::stable_sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
104
106 std::transform(shapes.constBegin(), shapes.constEnd(),
107 std::back_inserter(indexedShapes),
108 [zIndexOffset] (KoShape *shape) {
109 KoShapeReorderCommand::IndexedShape indexedShape(shape);
110 indexedShape.zIndex += zIndexOffset;
111 return indexedShape;
112 });
113
114 indexedShapes = KoShapeReorderCommand::homogenizeZIndexesLazy(indexedShapes);
115
116 KoShapeReorderCommand cmd(indexedShapes);
117 cmd.redo();
118 }
119
120 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Paste shapes"));
121
122 const bool forceCreateNewLayer = flags.testFlag(KisPasteActionFactory::ForceNewLayer);
123 KoShapeContainer *parentLayer = nullptr;
124
125 if (forceCreateNewLayer) {
126 parentLayer = canvas->shapeController()->documentBase()->createParentForShapes(shapes, true, parentCommand);
127 }
128
129 canvas->shapeController()->addShapesDirect(shapes, parentLayer, parentCommand);
130
131 QPointF finalShapesOffset;
132
133
134 if (flags.testFlag(KisPasteActionFactory::PasteAtCursor)) {
135 QRectF boundingRect = KoShape::boundingRect(shapes);
136 const QPointF cursorPos = canvas->canvasController()->currentCursorPosition();
137 finalShapesOffset = cursorPos - boundingRect.center();
138
139 } else if (!forceCreateNewLayer) {
140 bool foundOverlapping = false;
141
142 QRectF boundingRect = KoShape::boundingRect(shapes);
143 const QPointF offsetStep = 0.1 * QPointF(boundingRect.width(), boundingRect.height());
144
145 QPointF offset;
146
147 Q_FOREACH (KoShape *shape, shapes) {
148 QRectF br1 = shape->boundingRect();
149
150 bool hasOverlappingShape = false;
151
152 do {
153 hasOverlappingShape = false;
154
155 // we cannot use shapesAt() here, because the groups are not
156 // handled in the shape manager's tree
157 QList<KoShape*> conflicts = shapeManager->shapes();
158
159 Q_FOREACH (KoShape *intersectedShape, conflicts) {
160 if (intersectedShape == shape) continue;
161
162 QRectF br2 = intersectedShape->boundingRect();
163
164 const qreal tolerance = 2.0; /* pt */
165 if (KisAlgebra2D::fuzzyCompareRects(br1, br2, tolerance)) {
166 br1.translate(offsetStep.x(), offsetStep.y());
167 offset += offsetStep;
168
169 hasOverlappingShape = true;
170 foundOverlapping = true;
171 break;
172 }
173 }
174 } while (hasOverlappingShape);
175
176 if (foundOverlapping) break;
177 }
178
179 if (foundOverlapping) {
180 finalShapesOffset = offset;
181 }
182 }
183
184 const QRectF documentRect = canvas->shapeController()->documentRect();
185
186 if (!forceCreateNewLayer) {
187 finalShapesOffset += getFittingOffset(shapes, finalShapesOffset, documentRect, 0.1);
188 }
189
190 if (!finalShapesOffset.isNull()) {
191 new KoShapeMoveCommand(shapes, finalShapesOffset, parentCommand);
192 }
193
194 canvas->addCommand(parentCommand);
195
196 Q_FOREACH (KoShape *shape, shapes) {
197 canvas->selectedShapesProxy()->selection()->select(shape);
198 }
199
200 result = true;
201 }
202 }
203
204 return result;
205}
206
207}
208
210{
211 bool pasteAtCursorPosition = flags.testFlag(PasteAtCursor);
212
213 KisImageSP image = view->image();
214 if (!image) return;
215
216 const QPointF docPos = view->canvasBase()->canvasController()->currentCursorPosition();
217
218 // Activate the current tool's paste functionality
219 if (view->canvasBase()->toolProxy()->paste()) {
220 // XXX: "Add saving of XML data for Paste of shapes"
221 return;
222 }
223
224 // Paste shapes
225 if (tryPasteShapes(flags, view)) {
226 return;
227 }
228
229 const QRect fittingBounds =
230 pasteAtCursorPosition ? QRect() : image->bounds();
231
232 // If no shapes, check for layers
233 if (KisClipboard::instance()->hasLayers()) {
234 const QPointF offsetTopLeft = [&]() -> QPointF {
235 KisPaintDeviceSP clip =
237 image->colorSpace());
238
239 if (!clip) {
240 pasteAtCursorPosition = false;
241 return {};
242 }
243
244
245 QPointF imagePos;
246 if (pasteAtCursorPosition) {
247 imagePos =
249 docPos);
250
251 } else if (!clip->exactBounds().intersects(image->bounds())) {
252 // BUG:459111
253 pasteAtCursorPosition = true;
254 imagePos = QPointF(image->bounds().center());
255 } else {
256 return {};
257 }
258 const QPointF offset =
259 (imagePos - QRectF(clip->exactBounds()).center()).toPoint();
260 return offset;
261 }();
262
263
264 if (view->selection()) {
267 KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Add Layer"));
268 KUndo2Command *deselectCmd = new KisDeselectActiveSelectionCommand(view->selection(), image);
269 ap->applyCommand(deselectCmd);
270 view->nodeManager()->pasteLayersFromClipboard(pasteAtCursorPosition,
271 offsetTopLeft,
272 ap);
273 endAction(ap, KisOperationConfiguration(id()).toXML());
274 } else {
275 view->nodeManager()->pasteLayersFromClipboard(pasteAtCursorPosition,
276 offsetTopLeft);
277 }
278
279 return;
280 }
281
282 KisTimeSpan range;
283 KisPaintDeviceSP clip = KisClipboard::instance()->clip(fittingBounds, true, -1, &range);
284
285 if (clip) {
286 if (pasteAtCursorPosition) {
287 const QPointF imagePos = view->canvasBase()->coordinatesConverter()->documentToImage(docPos);
288
289 const QPoint offset =
290 (imagePos - QRectF(clip->exactBounds()).center()).toPoint();
291
292 clip->setX(clip->x() + offset.x());
293 clip->setY(clip->y() + offset.y());
294 }
295
297 bool renamePastedLayers = KisConfig(true).renamePastedLayers();
298 QString pastedLayerName = renamePastedLayers ? image->nextLayerName() + " " + i18n("(pasted)") :
299 image->nextLayerName();
300 KisPaintLayerSP newLayer = new KisPaintLayer(image.data(),
301 pastedLayerName,
303 KisNodeSP aboveNode = view->activeLayer();
304 KisNodeSP parentNode = aboveNode ? aboveNode->parent() : image->root();
305
306 if (range.isValid()) {
307 newLayer->enableAnimation();
309 KisRasterKeyframeChannel *rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(channel);
310 KIS_SAFE_ASSERT_RECOVER_RETURN(rasterChannel);
311 rasterChannel->importFrame(range.start(), clip, nullptr);
312
313 if (!range.isInfinite()) {
314 rasterChannel->addKeyframe(range.end() + 1, 0);
315 }
316 } else {
317 const QRect rc = clip->extent();
318 KisPainter::copyAreaOptimized(rc.topLeft(), clip, newLayer->paintDevice(), rc);
319 }
320
321 KUndo2Command *cmd = new KisImageLayerAddCommand(image, newLayer, parentNode, aboveNode);
322 KisProcessingApplicator *ap = beginAction(view, cmd->text());
324
325 if (view->selection()) {
326 KUndo2Command *deselectCmd = new KisDeselectActiveSelectionCommand(view->selection(), image);
328 }
329
330 if (KisConfig(true).activateTransformToolAfterPaste()) {
331 KUndo2Command *transformToolCmd = new KisTransformToolActivationCommand(view);
333 }
334
335 endAction(ap, KisOperationConfiguration(id()).toXML());
336 }
337}
338
340{
341 if (!viewManager->activeDevice()) return;
342
343 KisImageSP image = viewManager->image();
344 if (!image) return;
345
346 QRect imageBounds = image->bounds();
347
349 KisPaintDeviceSP clip = clipdev ? new KisPaintDevice(*clipdev) : nullptr;
350
351 if (clip)
352 {
353 QRect clipBounds = clip->exactBounds();
354
355 if (!clipBounds.intersects(imageBounds))
356 {
357 QPoint diff = imageBounds.center() - clipBounds.center();
358 clip->setX(diff.x());
359 clip->setY(diff.y());
360 }
361 }
362 else
363 {
364 clip = KisClipboard::instance()->clip(imageBounds, true, -1, nullptr);
365 }
366
367 if (!clip) return;
368
370
371 if (viewManager->selection()) {
372 KUndo2Command *deselectCmd = new KisDeselectActiveSelectionCommand(viewManager->selection(), image);
373 KisProcessingApplicator::runSingleCommandStroke(viewManager->image(), deselectCmd);
374 }
375
376 KisTool* tool = dynamic_cast<KisTool*>(KoToolManager::instance()->toolById(viewManager->canvasBase(), "KisToolTransform"));
377 KIS_ASSERT(tool);
379}
380
382{
383 Q_UNUSED(viewManager);
384
385 KisPaintDeviceSP clip = KisClipboard::instance()->clip(QRect(), true);
386 if (!clip) return;
387
388 QRect rect = clip->exactBounds();
389 if (rect.isEmpty()) return;
390
392 doc->documentInfo()->setAboutInfo("title", i18n("Untitled"));
393 KisImageSP image = new KisImage(doc->createUndoStore(),
394 rect.width(),
395 rect.height(),
396 clip->colorSpace(),
397 i18n("Pasted"));
398 bool renamePastedLayers = KisConfig(true).renamePastedLayers();
399 QString pastedLayerName = renamePastedLayers ? image->nextLayerName() + " " + i18n("(pasted)") :
400 image->nextLayerName();
401 KisPaintLayerSP layer =
402 new KisPaintLayer(image.data(), pastedLayerName,
404
405 KisPainter::copyAreaOptimized(QPoint(), clip, layer->paintDevice(), rect);
406
407 image->addNode(layer.data(), image->rootLayer());
408 doc->setCurrentImage(image);
410
411 KisMainWindow *win = viewManager->mainWindow();
412 win->addViewAndNotifyLoadingCompleted(doc);
413}
414
416{
417 KisCanvas2 *canvasBase = viewManager->canvasBase();
418 if (!canvasBase) return;
419
421 if (!reference) return;
422
423 KisDocument *doc = viewManager->document();
424 canvasBase->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, {reference}));
425
426 KoToolManager::instance()->switchToolRequested("ToolReferenceImages");
427}
428
430{
431 KoSvgPaste paste;
432
433 KisCanvas2 *canvas = view->canvasBase();
434
435 KoShapeManager *shapeManager = canvas->shapeManager();
436 QList<KoShape*> selectedShapes = shapeManager->selection()->selectedEditableShapes();
437
438 if (selectedShapes.isEmpty()) return;
439
440 if (paste.hasShapes()) {
441 KoCanvasBase *canvas = view->canvasBase();
442
443 QSizeF fragmentSize;
444 QList<KoShape*> shapes =
445 paste.fetchShapes(canvas->shapeController()->documentRectInPixels(),
446 canvas->shapeController()->pixelsPerInch(), &fragmentSize);
447
448 if (!shapes.isEmpty()) {
449 KoShape *referenceShape = shapes.first();
450
451
452 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Paste Style"));
453
454 new KoShapeBackgroundCommand(selectedShapes, referenceShape->background(), parentCommand);
455 new KoShapeStrokeCommand(selectedShapes, referenceShape->stroke(), parentCommand);
456
457
458 canvas->addCommand(parentCommand);
459 }
460
461 qDeleteAll(shapes);
462 }
463}
const quint8 OPACITY_OPAQUE_U8
KUndo2MagicString text() const
KisCoordinatesConverter * coordinatesConverter
void addCommand(KUndo2Command *command) override
KoShapeManager shapeManager
KisToolProxy toolProxy
static KisClipboard * instance()
KisPaintDeviceSP clipFromKritaLayers(const KoColorSpace *cs) const
bool renamePastedLayers(bool defaultValue=false) const
_Private::Traits< T >::Result documentToImage(const T &obj) const
KisUndoStore * createUndoStore()
KoDocumentInfo * documentInfo() const
void setCurrentImage(KisImageSP image, bool forceInitialUpdate=true, KisNodeSP preActivatedNode=nullptr)
The command for adding a layer.
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
QRect bounds() const override
static void adaptClipToImageColorSpace(KisPaintDeviceSP dev, KisImageSP image)
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
static const KoID Raster
void addKeyframe(int time, KUndo2Command *parentUndoCmd=nullptr)
Add a new keyframe to the channel at the specified time.
Main window for Krita.
void pasteLayersFromClipboard(bool changeOffset=false, QPointF offset=QPointF(), KisProcessingApplicator *applicator=nullptr)
KisProcessingApplicator * beginAction(KisViewManager *view, const KUndo2MagicString &actionName)
void endAction(KisProcessingApplicator *applicator, const QString &xmlData)
void setX(qint32 x)
QRect exactBounds() const
QRect extent() const
const KoColorSpace * colorSpace() const
void setY(qint32 y)
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
static KisPart * instance()
Definition KisPart.cpp:131
void addDocument(KisDocument *document, bool notify=true)
Definition KisPart.cpp:211
KisDocument * createDocument() const
Definition KisPart.cpp:230
static void runSingleCommandStroke(KisImageSP image, KUndo2Command *cmd, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
runSingleCommandStroke creates a stroke and runs cmd in it. The text() field of cmd is used as a titl...
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
The KisRasterKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisRas...
void importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand)
The KisReferenceImage class represents a single reference image.
static KisReferenceImage * fromClipboard(const KisCoordinatesConverter &converter)
static KUndo2Command * addReferenceImages(KisDocument *document, QList< KoShape * > referenceImages)
int start() const
bool isInfinite() const
int end() const
bool isValid() const
KisMainWindow * mainWindow() const
KisDocument * document() const
KisCanvas2 * canvasBase() const
Return the canvas base class.
KisNodeSP activeNode()
KisSelectionSP selection()
KisLayerSP activeLayer()
Convenience method to get at the active layer.
KisPaintDeviceSP activeDevice()
Convenience method to get at the active paint device.
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
KisImageWSP image() const
Return the image this view is displaying.
QPointer< KoShapeController > shapeController
virtual KoShapeManager * shapeManager() const =0
virtual void addCommand(KUndo2Command *command)=0
KoCanvasController * canvasController() const
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
virtual QPointF currentCursorPosition() const =0
void setAboutInfo(const QString &info, const QString &data)
virtual KoSelection * selection()=0
void deselectAll()
clear the selections list
void select(KoShape *shape)
const QList< KoShape * > selectedEditableShapes() const
The undo / redo command for setting the shape background.
QList< KoShape * > topLevelShapes() const
QList< KoShape * > shapes
KoSelection * selection
The undo / redo command for shape moving.
This command allows you to change the zIndex of a number of shapes.
static QList< KoShapeReorderCommand::IndexedShape > homogenizeZIndexesLazy(QList< IndexedShape > shapes)
The undo / redo command for setting the shape stroke.
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:1067
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:434
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:335
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
KoToolBase * toolById(KoCanvasBase *canvas, const QString &id) const
void switchToolRequested(const QString &id)
static KoToolManager * instance()
Return the toolmanager singleton.
bool paste()
Forwarded to the current KoToolBase.
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define bounds(x, a, b)
KUndo2MagicString kundo2_i18n(const char *text)
QAction * paste(const QObject *recvr, const char *slot, QObject *parent)
bool fuzzyCompareRects(const Rect &r1, const Rect &r2, Difference tolerance)
Point clampPoint(Point pt, const Rect &bounds)
bool isEditable(bool checkVisibility=true) const
KisKeyframeChannel * getKeyframeChannel(const QString &id, bool create)
void enableAnimation()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
KisNodeWSP parent
Definition kis_node.cpp:86
KisPaintDeviceSP paintDevice
void run(Flags flags, KisViewManager *view)
void run(KisViewManager *view) override
void run(KisViewManager *view) override
void run(KisViewManager *view) override
void run(KisViewManager *view) override
virtual void newActivationWithExternalSource(KisPaintDeviceSP externalSource)
newActivationWithExternalSource Makes sure that the tool is active and starts a new stroke,...
Definition kis_tool.cc:573