Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_layer_manager.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "kis_layer_manager.h"
9
10#include <QRect>
11#include <QApplication>
12#include <QCursor>
13#include <QString>
14#include <QDialog>
15#include <QVBoxLayout>
16#include <QFileInfo>
17#include <QStandardPaths>
18#include <QClipboard>
19#include <QMimeData>
20
21#include <kactioncollection.h>
22#include <klocalizedstring.h>
23#include <QMessageBox>
24#include <QUrl>
25
27#include <kis_icon.h>
29#include <KisDocument.h>
30#include <KoColorSpace.h>
32#include <KoPointerEvent.h>
33#include <KoColorProfile.h>
34#include <KoSelection.h>
35#include <KisPart.h>
36#include <KisMainWindow.h>
37
39#include <filter/kis_filter.h>
40#include <kis_filter_strategy.h>
42#include <kis_file_layer.h>
44#include <kis_mask.h>
45#include <kis_clone_layer.h>
46#include <kis_group_layer.h>
47#include <kis_image.h>
48#include <kis_layer.h>
49#include <kis_paint_device.h>
50#include <kis_selection.h>
52#include <kis_undo_adapter.h>
53#include <kis_painter.h>
54#include <kis_meta_data_store.h>
56#include <kis_psd_layer_style.h>
57#include <KisMimeDatabase.h>
58#include <kis_clipboard.h>
59
60#include "kis_config.h"
61#include "kis_cursor.h"
68#include "kis_filter_manager.h"
69#include "kis_node_visitor.h"
70#include "kis_paint_layer.h"
77#include "kis_statusbar.h"
78#include "KisViewManager.h"
79#include "kis_zoom_manager.h"
80#include "canvas/kis_canvas2.h"
83#include "kis_progress_widget.h"
85#include "kis_node_manager.h"
86#include "kis_action.h"
87#include "kis_action_manager.h"
93#include "kis_selection_mask.h"
94#include "kis_layer_utils.h"
97#include "kis_projection_leaf.h"
99
100#include "KisSaveGroupVisitor.h"
102
103
105 : m_view(view)
106 , m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
107
108{
109}
110
115
120
122{
123 if (m_imageView) {
124 return m_imageView->currentLayer();
125 }
126 return 0;
127}
128
130{
131 if (activeLayer()) {
132 return activeLayer()->paintDevice();
133 }
134 return 0;
135}
136
138{
139 if (m_imageView) {
141 if (layer) {
143 }
144 }
145}
146
147
149{
150 m_imageFlatten = actionManager->createAction("flatten_image");
151 connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage()));
152
153 m_imageMergeLayer = actionManager->createAction("merge_layer");
154 connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer()));
155
156 m_flattenLayer = actionManager->createAction("flatten_layer");
157 connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer()));
158
159 m_groupLayersSave = actionManager->createAction("save_groups_as_images");
160 connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers()));
161
162 m_convertGroupAnimated = actionManager->createAction("convert_group_to_animated");
163 connect(m_convertGroupAnimated, SIGNAL(triggered()), this, SLOT(convertGroupToAnimated()));
164
165 m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer");
166 connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer()));
167
168 KisAction *action = actionManager->createAction("trim_to_image");
169 connect(action, SIGNAL(triggered()), this, SLOT(trimToImage()));
170
171 action = actionManager->createAction("purge_unused_image_data");
172 connect(action, SIGNAL(triggered()), this, SLOT(purgeUnusedImageData()));
173
174 m_layerStyle = actionManager->createAction("layer_style");
175 connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle()));
176
177 m_copyLayerStyle = actionManager->createAction("copy_layer_style");
178 connect(m_copyLayerStyle, SIGNAL(triggered()), this, SLOT(copyLayerStyle()));
179
180 m_pasteLayerStyle = actionManager->createAction("paste_layer_style");
181 connect(m_pasteLayerStyle, SIGNAL(triggered()), this, SLOT(pasteLayerStyle()));
182}
183
185{
186 KisImageSP image = m_view->image();
187 KisLayerSP layer = activeLayer();
188
189 const bool isGroupLayer = layer && layer->inherits("KisGroupLayer");
190
191 QString mergeLayerText;
192 if (m_view->nodeManager()->selectedNodes().size() > 1) {
193 mergeLayerText = i18nc("@action:inmenu", "Merge Selected Layers");
194 } else if (isGroupLayer) {
195 mergeLayerText = i18nc("@action:inmenu", "Merge Group");
196 } else {
197 mergeLayerText = i18nc("@action:inmenu", "Merge with Layer Below");
198 }
199 m_imageMergeLayer->setText(mergeLayerText);
200
201 m_flattenLayer->setVisible(!isGroupLayer);
202
203 if (m_view->statusBar())
204 m_view->statusBar()->setProfile(image);
205}
206
208{
209 KisLayerSP layer;
210 KisImageWSP image = m_view->image();
211
212 if (image && (layer = activeLayer())) {
213 QRect cropRect = layer->projection()->nonDefaultPixelArea();
214 if (!cropRect.isEmpty()) {
215 image->cropImage(cropRect);
216 } else {
218 i18nc("floating message in layer manager",
219 "Layer is empty "),
220 QIcon(), 2000, KisFloatingMessage::Low);
221 }
222 }
223}
224
226{
227 KisImageWSP image = m_view->image();
228 if (image) {
229 image->cropImage(image->bounds());
230 }
231}
232
234{
235 KisImageWSP image = m_view->image();
236 if (image) {
237 image->purgeUnusedData(false);
238 }
239}
240
242{
243 if (!m_view) return;
244 if (!m_view->document()) return;
245
246 // For non-modal dialogs, only allow one instance of the dialog to exist.
247 // Modal dialogs won't set this.
249
250 KisLayerSP layer = activeLayer();
251 if (!layer) return;
252
253 const QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
254 const bool multipleLayersSelected = selectedNodes.size() > 1;
255
256 if (!m_view->nodeManager()->canModifyLayers(selectedNodes)) return;
257
258 KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
259 KisGeneratorLayerSP generatorLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
260 KisFileLayerSP fileLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
261
262 if (adjustmentLayer && !multipleLayersSelected) {
263 KisPaintDeviceSP dev = adjustmentLayer->projection();
264
265 KisDlgAdjustmentLayer dlg(adjustmentLayer, adjustmentLayer.data(), dev,
266 adjustmentLayer->name(), i18n("Filter Layer Properties"),
267 m_view, m_view->mainWindow(), adjustmentLayer->filter());
268 dlg.resize(dlg.minimumSizeHint());
269
270 KisFilterConfigurationSP configBefore(adjustmentLayer->filter());
271 KIS_ASSERT_RECOVER_RETURN(configBefore);
272 QString xmlBefore = configBefore->toXML();
273
274 if (dlg.exec() == QDialog::Accepted) {
275 KUndo2Command *cmd = nullptr;
276
278 Q_ASSERT(configAfter);
279 QString xmlAfter = configAfter->toXML();
280
281 if(xmlBefore != xmlAfter) {
283 new KisChangeFilterCmd(adjustmentLayer,
284 configBefore->cloneWithResourcesSnapshot(),
285 configAfter->cloneWithResourcesSnapshot()));
286 }
287
288 if (adjustmentLayer->name() != dlg.layerName()) {
290 new KisNodeRenameCommand(adjustmentLayer,
291 adjustmentLayer->name(),
292 dlg.layerName()));
293 }
294
295 if (cmd) {
297 }
298 }
299 else {
301 Q_ASSERT(configAfter);
302 QString xmlAfter = configAfter->toXML();
303
304 if(xmlBefore != xmlAfter) {
305 adjustmentLayer->setFilter(configBefore->cloneWithResourcesSnapshot());
306 adjustmentLayer->setDirty();
307 }
308 }
309 }
310 else if (fileLayer && !multipleLayersSelected){
311 QString basePath = QFileInfo(m_view->document()->path()).absolutePath();
312 QString fileNameOld = fileLayer->fileName();
313 KisFileLayer::ScalingMethod scalingMethodOld = fileLayer->scalingMethod();
314 QString scalingFilterOld = fileLayer->scalingFilter();
315 KisDlgFileLayer dlg(basePath, fileLayer->name(), m_view->mainWindow());
316 dlg.setCaption(i18n("File Layer Properties"));
317 dlg.setFileName(fileNameOld);
318 dlg.setScalingMethod(scalingMethodOld);
319 dlg.setScalingFilter(scalingFilterOld);
320
321 if (dlg.exec() == QDialog::Accepted) {
322 const QString fileNameNew = dlg.fileName();
324 QString scalingFilterNew = dlg.scalingFilter();
325
326 if(fileNameNew.isEmpty()){
327 QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
328 return;
329 }
330
331 KUndo2Command *cmd = nullptr;
332
333 if (fileNameOld != fileNameNew || scalingMethodOld != scalingMethodNew
334 || scalingFilterOld != scalingFilterNew) {
335
337 new KisChangeFileLayerCmd(fileLayer,
338 basePath,
339 fileNameOld,
340 scalingMethodOld,
341 scalingFilterOld,
342 basePath,
343 fileNameNew,
344 scalingMethodNew,
345 scalingFilterNew));
346 }
347
348 if (fileLayer->name() != dlg.layerName()) {
350 new KisNodeRenameCommand(fileLayer, fileLayer->name(), dlg.layerName()));
351 }
352
353 if (cmd) {
355 }
356 }
357 } else {
358 QDialog* dlg = nullptr;
359
360 if (generatorLayer && !multipleLayersSelected) {
361 KisFilterConfigurationSP configBefore(generatorLayer->filter());
362 Q_ASSERT(configBefore);
363
364 auto* genDlg = new KisDlgGeneratorLayer(generatorLayer->name(), m_view, m_view->canvas(), generatorLayer, configBefore, KisStrokeId());
365 genDlg->setWindowTitle(i18n("Fill Layer Properties"));
366 genDlg->setConfiguration(configBefore.data());
367
368 dlg = genDlg;
369 }
370
371 // Handle these layer(s) as normal painting layer, vector layer, or group layer
372 if (!dlg) {
373 dlg = new KisDlgLayerProperties(selectedNodes, m_view, m_view->canvas());
374 }
375
376 dlg->resize(dlg->minimumSizeHint());
377 dlg->setAttribute(Qt::WA_DeleteOnClose);
378
379 const Qt::WindowFlags flags = dlg->windowFlags();
380#ifdef Q_OS_ANDROID
381 // a Qt::Tool window seems incapable of receiving keyboard focus
382 dlg->setWindowFlags(flags | Qt::Dialog);
383#else
384 dlg->setWindowFlags(flags | Qt::Tool | Qt::Dialog);
385#endif
386 connect(dlg, SIGNAL(destroyed()), this, SLOT(layerPropertiesDialogClosed()));
388
389 dlg->show();
390 dlg->activateWindow();
391 }
392}
393
398
400{
401 QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
402 if (selectedNodes.isEmpty()) {
403 return;
404 }
405
406 QList<KisCloneLayerSP> cloneLayers;
407 KisNodeSP node;
408 Q_FOREACH (node, selectedNodes) {
409 KisCloneLayerSP cloneLayer(qobject_cast<KisCloneLayer *>(node.data()));
410 if (cloneLayer) {
411 cloneLayers << cloneLayer;
412 }
413 }
414
415 if (cloneLayers.isEmpty()) {
416 return;
417 }
418
419 if (!m_view->nodeManager()->canModifyLayers(implicitCastList<KisNodeSP>(cloneLayers))) return;
420
421 KisDlgChangeCloneSource *dialog = new KisDlgChangeCloneSource(cloneLayers, m_view);
422 dialog->setCaption(i18n("Change Clone Layer"));
423 dialog->resize(dialog->minimumSizeHint());
424 dialog->setAttribute(Qt::WA_DeleteOnClose);
425 Qt::WindowFlags flags = dialog->windowFlags();
426 dialog->setWindowFlags(flags | Qt::Tool | Qt::Dialog);
427 dialog->show();
428 dialog->activateWindow();
429}
430
432{
433 KisImageSP image = m_view->image();
434 if (!image) return;
435
436 KisLayerSP layer = activeLayer();
437 if (!layer) return;
438
440 if (!layerStyle) return;
441
444 QString psdxml = serializer.formPsdXmlDocument().toString();
445
446 if (!psdxml.isEmpty()) {
447 QMimeData *mimeData = new QMimeData;
448
449 mimeData->setText(psdxml);
450 mimeData->setData("application/x-krita-layer-style", psdxml.toUtf8());
451
452 QGuiApplication::clipboard()->setMimeData(mimeData);
453 }
454}
455
457{
458 KisImageSP image = m_view->image();
459 if (!image) return;
460
461 KisLayerSP layer = activeLayer();
462 if (!layer) return;
463
464 QString aslXml;
465
466 if (KisClipboard::instance()->hasLayerStyles()) {
467 aslXml = QString::fromUtf8(QGuiApplication::clipboard()->mimeData()->data("application/x-krita-layer-style"));
468 } else {
469 aslXml = QGuiApplication::clipboard()->text();
470 }
471
472 if (aslXml.isEmpty()) return;
473
474 QDomDocument aslDoc;
475 if (!aslDoc.setContent(aslXml)) return;
476
478 serializer.registerPSDPattern(aslDoc);
479 serializer.readFromPSDXML(aslDoc);
480
481 if (serializer.styles().size() != 1) return;
482
483 KisPSDLayerStyleSP newStyle = serializer.styles().first()->cloneWithResourcesSnapshot(
485 m_view->canvasBase()->resourceManager()->canvasResourcesInterface());
486 KUndo2Command *cmd = new KisSetLayerStyleCommand(layer, layer->layerStyle(), newStyle);
487
489 image->waitForDone();
490}
491
493{
494 KisImageWSP image = m_view->image();
495 if (!image) return;
496
497 // this precondition must be checked at higher level
498 KIS_SAFE_ASSERT_RECOVER_RETURN(source->isEditable(false));
499
500 KisLayer *srcLayer = qobject_cast<KisLayer*>(source.data());
501 if (srcLayer && (srcLayer->inherits("KisGroupLayer") || srcLayer->layerStyle() || srcLayer->childCount() > 0)) {
502 image->flattenLayer(srcLayer);
503 return;
504 }
505
507}
508
510{
511 KisGroupLayerSP targetGroup = dynamic_cast<KisGroupLayer*>(activeLayer().data());
512 if (targetGroup.isNull()) {
513 // Try containing group, if it exists...
514 KisNodeSP parent = activeLayer()->parent();
515 if (!parent->parent()) return; // watch out for the root node!
516 targetGroup = dynamic_cast<KisGroupLayer*>(parent.data());
517 }
518
519 if (targetGroup.isNull()) return;
520 if (!m_view->nodeManager()->canModifyLayer(targetGroup)) return;
521
522 KisPaintLayerSP animatedLayer = new KisPaintLayer(m_view->image(), targetGroup->name(), OPACITY_OPAQUE_U8);
523 animatedLayer->enableAnimation();
524 KisRasterKeyframeChannel *keyframeChannel = dynamic_cast<KisRasterKeyframeChannel*>(
525 animatedLayer->getKeyframeChannel(KisKeyframeChannel::Raster.id(), true));
526 KIS_ASSERT_RECOVER_RETURN(keyframeChannel);
527
528 KisNodeSP childNode = targetGroup->firstChild();
529 int time = 0;
530 while (childNode) {
531 keyframeChannel->importFrame(time, childNode->projection(), NULL);
532 time++;
533
534 childNode = childNode->nextSibling();
535 }
536
537 m_commandsAdapter->beginMacro(kundo2_i18n("Convert Group to Animated Layer"));
538 m_commandsAdapter->addNode(animatedLayer, targetGroup->parent(), targetGroup);
539 m_commandsAdapter->removeNode(targetGroup);
541}
542
544{
545 KisImageSP image = m_view->image();
546 if (!image) return;
547
548 // this precondition must be checked at higher level
549 KIS_SAFE_ASSERT_RECOVER_RETURN(source->isEditable(false));
550
552
553 KoDialog dlg;
554 QWidget *page = new QWidget(&dlg);
555 dlg.setMainWidget(page);
556 QBoxLayout *layout = new QVBoxLayout(page);
557 dlg.setWindowTitle(i18n("Save layers to..."));
558 QLabel *lbl = new QLabel(i18n("Choose the location where the layer will be saved to. The new file layer will then reference this location."));
559 lbl->setWordWrap(true);
560 layout->addWidget(lbl);
561 KisFileNameRequester *urlRequester = new KisFileNameRequester(page);
562 urlRequester->setMode(KoFileDialog::SaveFile);
563 urlRequester->setMimeTypeFilters(listMimeFilter);
564 urlRequester->setFileName(m_view->document()->path());
565 if (!m_view->document()->path().isEmpty()) {
566 QFileInfo location = QFileInfo(m_view->document()->path());
567 location.setFile(location.dir(), location.completeBaseName() + "_" + source->name() + ".png");
568 urlRequester->setFileName(location.absoluteFilePath());
569 }
570 else {
571 const QFileInfo location = QFileInfo(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
572 const QString proposedFileName = QDir(location.absoluteFilePath()).absoluteFilePath(source->name() + ".png");
573 urlRequester->setFileName(proposedFileName);
574 }
575
576 layout->addWidget(urlRequester);
577 if (!dlg.exec()) return;
578
579 QString path = urlRequester->fileName();
580
581 if (path.isEmpty()) return;
582
583 QFileInfo f(path);
584
585 QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName());
586 if (mimeType.isEmpty()) {
587 mimeType = "image/png";
588 }
589 QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
590
591 QRect bounds = source->exactBounds();
592 if (bounds.isEmpty()) {
593 bounds = image->bounds();
594 }
595 KisImageSP dst = new KisImage(doc->createUndoStore(),
596 image->width(),
597 image->height(),
599 source->name());
600 dst->setResolution(image->xRes(), image->yRes());
601 doc->setFileBatchMode(false);
602 doc->setCurrentImage(dst);
603 KisNodeSP node = source->clone();
604 dst->addNode(node);
605 dst->initialRefreshGraph();
606 dst->cropImage(bounds);
607 dst->waitForDone();
608
609 bool r = doc->exportDocumentSync(path, mimeType.toLatin1());
610 if (!r) {
611
612 qWarning() << "Converting layer to file layer. path:"<< path << "gave errors" << doc->errorMessage();
613 } else {
614 QString basePath = QFileInfo(m_view->document()->path()).absolutePath();
615 QString relativePath = QDir(basePath).relativeFilePath(path);
616 KisFileLayer *fileLayer = new KisFileLayer(image, basePath, relativePath, KisFileLayer::None, "Bicubic", source->name(), OPACITY_OPAQUE_U8);
617 fileLayer->setX(bounds.x());
618 fileLayer->setY(bounds.y());
619 KisNodeSP dstParent = source->parent();
620 KisNodeSP dstAboveThis = source->prevSibling();
621 m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a file layer"));
623 m_commandsAdapter->addNode(fileLayer, dstParent, dstAboveThis);
625 }
626 doc->closePath(false);
627}
628
630{
631 Q_ASSERT(activeNode);
632
633 parent = activeNode;
634 above = parent->lastChild();
635
636 if (parent->inherits("KisGroupLayer") && parent->collapsed()) {
637 above = parent;
638 parent = parent->parent();
639 return;
640 }
641
642 while (parent &&
643 (!parent->allowAsChild(node) || !parent->isEditable(false))) {
644
645 above = parent;
646 parent = parent->parent();
647 }
648
649 if (!parent) {
650 warnKrita << "KisLayerManager::adjustLayerPosition:"
651 << "No node accepted newly created node";
652
653 parent = m_view->image()->root();
654 above = parent->lastChild();
655 }
656}
657
658void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisNodeSP layer, bool updateImage, KisProcessingApplicator *applicator)
659{
660 KisNodeSP parent;
661 KisNodeSP above;
662 adjustLayerPosition(layer, activeNode, parent, above);
663
664 KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(parent.data());
665
666 if (layer->inherits("KisGroupLayer") || layer->inherits("KisPaintLayer")) {
667 const KoColorSpace *colorSpace = group->colorSpace();
668
669 if (layer->inherits("KisGroupLayer")) {
670 KisGroupLayer *newLayer = dynamic_cast<KisGroupLayer*>(layer.data());
671 newLayer->resetCache(colorSpace);
672 } else {
673 KisPaintLayer *newLayer = qobject_cast<KisPaintLayer *>(layer.data());
674 newLayer->paintDevice()->convertTo(colorSpace);
675 }
676 }
677
678 const bool parentForceUpdate = group && !group->projectionIsValid();
679 updateImage |= parentForceUpdate;
680
681 m_commandsAdapter->addNodeAsync(layer, parent, above, updateImage, updateImage, applicator);
682}
683
685{
686 KisImageWSP image = m_view->image();
687 KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName( i18n("Paint Layer") ), OPACITY_OPAQUE_U8, image->colorSpace());
688
689 KisConfig cfg(true);
691
692 addLayerCommon(activeNode, layer, false, 0);
693
694 return layer;
695}
696
698{
699 KisImageWSP image = m_view->image();
700 KisGroupLayerSP group = new KisGroupLayer(image.data(), image->nextLayerName( i18nc("A group of layers", "Group") ), OPACITY_OPAQUE_U8);
701 addLayerCommon(activeNode, group, false, 0);
702 return group;
703}
704
706{
707 KisImageWSP image = m_view->image();
708
710 if (filteredNodes.isEmpty()) return KisNodeSP();
711
712 KisNodeSP newAbove = filteredNodes.last();
713
714 KisNodeSP node, lastClonedNode;
715 Q_FOREACH (node, filteredNodes) {
716 lastClonedNode = new KisCloneLayer(qobject_cast<KisLayer*>(node.data()), image.data(), image->nextLayerName( i18n("Clone Layer") ), OPACITY_OPAQUE_U8);
717 addLayerCommon(newAbove, lastClonedNode, true, 0 );
718 }
719
720 return lastClonedNode;
721}
722
724{
725 if (!m_view) return 0;
726 if (!m_view->document()) return 0;
727
728 KisImageWSP image = m_view->image();
729 KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(i18n("Vector Layer")), OPACITY_OPAQUE_U8);
730
731 addLayerCommon(activeNode, layer, false, 0);
732
733 return layer;
734}
735
737{
738 KisImageWSP image = m_view->image();
739
740 KisSelectionSP selection = m_view->selection();
741
744 kundo2_i18n("Add Layer"));
745
746
747 KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection, &applicator);
748 applicator.end();
749 image->waitForDone(); // So that previewDevice doesn't end up empty and result in empty histograms
750
751 KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original());
752
753 KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(i18n("Filter Layer")), i18n("New Filter Layer"), m_view, qApp->activeWindow());
754 dlg.resize(dlg.minimumSizeHint());
755
756 // ensure that the device may be free'd by the dialog
757 // when it is not needed anymore
758 previewDevice = 0;
759
760 if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) {
761 // XXX: add messagebox warning if there's no filter set!
763 } else {
764 adjl->setName(dlg.layerName());
765 }
766
767 return adjl;
768}
769
772 KisSelectionSP selection,
773 KisProcessingApplicator *applicator)
774{
775 KisImageWSP image = m_view->image();
776 KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter ? filter->cloneWithResourcesSnapshot() : 0, selection);
777 addLayerCommon(activeNode, layer, true, applicator);
778
779 return layer;
780}
781
783{
784 KisImageWSP image = m_view->image();
785 auto layer = new KisGeneratorLayer(image, name, filter, selection);
786 addLayerCommon(activeNode, layer, true, applicator);
787
788 return layer;
789}
790
792{
793 KisImageWSP image = m_view->image();
794 KisSelectionSP selection = m_view->selection();
795 QColor currentForeground = m_view->canvasResourceProvider()->fgColor().toQColor();
796
798
799 KisGeneratorLayerSP node = addGeneratorLayer(activeNode, QString(), nullptr, selection, &applicator);
800
801 KisDlgGeneratorLayer dlg(image->nextLayerName(i18n("Fill Layer")), m_view, m_view->mainWindow(), node, nullptr, applicator.getStroke());
802 KisFilterConfigurationSP defaultConfig = dlg.configuration();
803 defaultConfig->setProperty("color", currentForeground);
804 dlg.setConfiguration(defaultConfig);
805
806 if (dlg.exec() == QDialog::Accepted) {
807 applicator.applyCommand(new KisNodeRenameCommand(node, node->name(), dlg.layerName()));
808 applicator.end();
809 return node;
810 }
811 else {
812 applicator.cancel();
813 return nullptr;
814 }
815}
816
818{
819 KisImageSP image = m_view->image();
820
821 if (!m_view->blockUntilOperationsFinished(image)) return;
822
823 if (image) {
824 bool doIt = true;
825
826 if (image->nHiddenLayers() > 0) {
827 int answer = QMessageBox::warning(m_view->mainWindow(),
828 i18nc("@title:window", "Flatten Image"),
829 i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"),
830 QMessageBox::Yes | QMessageBox::No,
831 QMessageBox::No);
832
833 if (answer != QMessageBox::Yes) {
834 doIt = false;
835 }
836 }
837
838 if (doIt) {
839 image->flatten(m_view->activeNode());
840 }
841 }
842}
843
844inline bool isSelectionMask(KisNodeSP node) {
845 return dynamic_cast<KisSelectionMask*>(node.data());
846}
847
849{
850 bool result = false;
851
852 KisNodeSP prevNode = currentNode->prevSibling();
853 if (isSelectionMask(currentNode) &&
854 prevNode && isSelectionMask(prevNode)) {
855
856 QList<KisNodeSP> mergedNodes;
857 mergedNodes.append(currentNode);
858 mergedNodes.append(prevNode);
859
860 image->mergeMultipleLayers(mergedNodes, currentNode);
861
862 result = true;
863 }
864
865 return result;
866}
867
869{
870 bool result = false;
871
872 if (currentNode->inherits("KisGroupLayer")) {
873 KisGroupLayer *layer = qobject_cast<KisGroupLayer*>(currentNode.data());
875
876 image->flattenLayer(layer);
877 result = true;
878 }
879
880 return result;
881}
882
884{
885 KisImageSP image = m_view->image();
886 if (!image) return;
887
888 KisLayerSP layer = activeLayer();
889 if (!layer) return;
890
891 if (!m_view->blockUntilOperationsFinished(image)) return;
892
893 QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
894
895 // check if all the layers are a part of a locked group
896 bool hasEditableLayer = false;
897 Q_FOREACH (KisNodeSP node, selectedNodes) {
898 if (node->isEditable(false)) {
899 hasEditableLayer = true;
900 break;
901 }
902 }
903
904 if (!hasEditableLayer) {
906 i18ncp("floating message in layer manager",
907 "Layer is locked", "Layers are locked", selectedNodes.size()),
908 QIcon(), 2000, KisFloatingMessage::Low);
909 return;
910 }
911
912 if (selectedNodes.size() > 1) {
913 image->mergeMultipleLayers(selectedNodes, m_view->activeNode());
914 }
915
916 else if (tryMergeSelectionMasks(m_view->activeNode(), image)) {
917 // already done!
918 } else if (tryFlattenGroupLayer(m_view->activeNode(), image)) {
919 // already done!
920 } else {
921
922 if (!layer->prevSibling()) return;
923 KisLayer *prevLayer = qobject_cast<KisLayer*>(layer->prevSibling().data());
924 if (!prevLayer) return;
925
926 if (prevLayer->userLocked()) {
928 i18nc("floating message in layer manager when previous layer is locked",
929 "Layer is locked"),
930 QIcon(), 2000, KisFloatingMessage::Low);
931 } else {
932 const KisMetaData::MergeStrategy* strategy = nullptr;
933
934 if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) {
936 }
937 else {
939 }
940
941 if (!strategy) return;
942
943 if (!layer->isAnimated() && prevLayer->isAnimated()) {
945 i18nc("floating message in layer manager when trying to merge a non-animated layer into an animated one",
946 "Non-animated layer is merged into the current frame. To merge it into the whole clip, create at least one frame"),
947 QIcon(), 5000, KisFloatingMessage::Medium);
948 }
949 image->mergeDown(layer, strategy);
950 }
951 }
952
953 m_view->updateGUI();
954}
955
957{
958 KisImageSP image = m_view->image();
959 if (!image) return;
960
961 KisLayerSP layer = activeLayer();
962 if (!layer) return;
963
964 if (!m_view->blockUntilOperationsFinished(image)) return;
965 if (!m_view->nodeManager()->canModifyLayer(layer)) return;
966
968 m_view->updateGUI();
969}
970
972{
973 KisLayerSP layer = activeLayer();
974 if (!layer) return;
975
976 m_view->updateGUI();
977}
978
980{
982
983 KoDialog dlg;
984 QWidget *page = new QWidget(&dlg);
985 dlg.setMainWidget(page);
986 QBoxLayout *layout = new QVBoxLayout(page);
987
988 KisFileNameRequester *urlRequester = new KisFileNameRequester(page);
989 urlRequester->setMode(KoFileDialog::SaveFile);
990 urlRequester->setStartDir(QFileInfo(m_view->document()->path()).absolutePath());
991 urlRequester->setMimeTypeFilters(listMimeFilter);
992 urlRequester->setFileName(m_view->document()->path());
993 layout->addWidget(urlRequester);
994
995 QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page);
996 chkInvisible->setChecked(false);
997 layout->addWidget(chkInvisible);
998 QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page);
999 chkDepth->setChecked(true);
1000 layout->addWidget(chkDepth);
1001
1002 if (!dlg.exec()) return;
1003
1004 QString path = urlRequester->fileName();
1005
1006 if (path.isEmpty()) return;
1007
1008 QFileInfo f(path);
1009
1010 QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName(), false);
1011 if (mimeType.isEmpty()) {
1012 mimeType = "image/png";
1013 }
1014 QString extension = KisMimeDatabase::suffixesForMimeType(mimeType).first();
1015 QString basename = f.completeBaseName();
1016
1017 KisImageSP image = m_view->image();
1018 if (!image) return;
1019
1020 KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), f.absolutePath(), basename, extension, mimeType);
1021 image->rootLayer()->accept(v);
1022
1023}
1024
1026{
1027 return (activeLayer()->selection() != 0);
1028}
1029
1031{
1032 QString basePath;
1033 QString path = m_view->document()->path();
1034 basePath = QFileInfo(path).absolutePath();
1035 KisImageWSP image = m_view->image();
1036
1037 KisDlgFileLayer dlg(basePath, image->nextLayerName(i18n("File Layer")), m_view->mainWindow());
1038 dlg.resize(dlg.minimumSizeHint());
1039
1040 if (dlg.exec() == QDialog::Accepted) {
1041 QString name = dlg.layerName();
1042 QString fileName = dlg.fileName();
1043
1044 if(fileName.isEmpty()){
1045 QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
1046 return 0;
1047 }
1048
1050 QString scalingFilter = dlg.scalingFilter();
1051 KisNodeSP node = new KisFileLayer(image, basePath, fileName, scalingMethod, scalingFilter, name, OPACITY_OPAQUE_U8);
1052 addLayerCommon(activeNode, node, true, 0);
1053 return node;
1054 }
1055 return 0;
1056}
1057
1059{
1061 dlg->style()->cloneWithResourcesSnapshot(
1063 canvasResourcesInterface));
1064}
1065
1067{
1068 KisImageWSP image = m_view->image();
1069 if (!image) return;
1070
1071 KisLayerSP layer = activeLayer();
1072 if (!layer) return;
1073
1074 if (!m_view->blockUntilOperationsFinished(image)) return;
1075 if (!m_view->nodeManager()->canModifyLayer(layer)) return;
1076
1077 KoCanvasResourcesInterfaceSP canvasResourcesInterface = m_view->canvasBase()->resourceManager()->canvasResourcesInterface();
1078
1079 KisPSDLayerStyleSP oldStyle;
1080 if (layer->layerStyle()) {
1081 oldStyle = layer->layerStyle()->clone().dynamicCast<KisPSDLayerStyle>();
1082
1083 } else {
1085 ->cloneWithResourcesSnapshot(KisGlobalResourcesInterface::instance(),
1086 canvasResourcesInterface);
1087 }
1088
1089 KisPSDLayerStyleSP newStyle = oldStyle->clone().dynamicCast<KisPSDLayerStyle>();
1090 // We want to also change the UUID, else it might be considered the same style after save and won't load correctly
1091 newStyle->setUuid(QUuid::createUuid());
1092
1094
1095 std::function<void ()> updateCall(std::bind(updateLayerStyles, layer, &dlg, canvasResourcesInterface));
1096 SignalToFunctionProxy proxy(updateCall);
1097 connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start()));
1098
1099 if (dlg.exec() == QDialog::Accepted) {
1100 KisPSDLayerStyleSP newStyle =
1101 dlg.style()->cloneWithResourcesSnapshot(KisGlobalResourcesInterface::instance(),
1102 canvasResourcesInterface);
1103
1104 KUndo2CommandSP command = toQShared(
1105 new KisSetLayerStyleCommand(layer, oldStyle, newStyle));
1106
1107 image->postExecutionUndoAdapter()->addCommand(command);
1108 }
1109}
qreal v
QVector< KisImageSignalType > KisImageSignalVector
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
const quint8 OPACITY_OPAQUE_U8
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
KisAction * createAction(const QString &name)
QVector< KisPSDLayerStyleSP > styles() const
void readFromPSDXML(const QDomDocument &doc)
void registerPSDPattern(const QDomDocument &doc)
void setStyles(const QVector< KisPSDLayerStyleSP > &styles)
void slotNodeActivated(const KisNodeSP node)
static KisClipboard * instance()
bool autoPinLayersToTimeline(bool defaultValue=false) const
KisFilterConfigurationSP filterConfiguration() const
void setScalingFilter(QString filter)
QString layerName() const
void setFileName(QString fileName)
KisFileLayer::ScalingMethod scaleToImageResolution() const
QString scalingFilter() const
void setScalingMethod(KisFileLayer::ScalingMethod method)
QString fileName() const
KisFilterConfigurationSP configuration() const
void setConfiguration(const KisFilterConfigurationSP config)
KisPSDLayerStyleSP style() const
KisShapeController * shapeController
QString path() const
The KisFileLayer class loads a particular file as a layer into the layer stack.
ScalingMethod scalingMethod() const
QString fileName() const
QString scalingFilter() const
void setStartDir(const QString &path)
void setMimeTypeFilters(const QStringList &filterList, QString defaultFilter=QString())
void setMode(KoFileDialog::DialogType mode)
void setFileName(const QString &path)
static KisResourcesInterfaceSP instance()
void waitForDone()
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
void purgeUnusedData(bool isCancellable)
purge all pixels that have default pixel to free up memory
Definition kis_image.cc:901
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:741
void flattenLayer(KisLayerSP layer)
void flatten(KisNodeSP activeNode)
KisPaintDeviceSP projection() const
qint32 width() const
KisPostExecutionUndoAdapter * postExecutionUndoAdapter() const override
qint32 nHiddenLayers() const
void cropImage(const QRect &newRect)
start asynchronous operation on cropping the image
Definition kis_image.cc:896
void initialRefreshGraph()
double xRes() const
double yRes() const
qint32 height() const
void mergeMultipleLayers(QList< KisNodeSP > mergedLayers, KisNodeSP putAfter)
void mergeDown(KisLayerSP l, const KisMetaData::MergeStrategy *strategy)
QRect bounds() const override
void setResolution(double xres, double yres)
static QStringList supportedMimeTypes(Direction direction)
static const KoID Raster
void addLayerCommon(KisNodeSP activeNode, KisNodeSP layer, bool updateImage=true, KisProcessingApplicator *applicator=0)
KisNodeSP addShapeLayer(KisNodeSP activeNode)
KisNodeSP addGeneratorLayer(KisNodeSP activeNode)
void setView(QPointer< KisView >view)
KisNodeSP addFileLayer(KisNodeSP activeNode)
KisAction * m_imageResizeToLayer
void activateLayer(KisLayerSP layer)
KisLayerManager(KisViewManager *view)
void adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above)
KisPaintDeviceSP activeDevice()
KisAction * m_copyLayerStyle
KisLayerSP activeLayer()
KisAction * m_layerStyle
void setup(KisActionManager *actionManager)
void convertLayerToFileLayer(KisNodeSP source)
KisLayerSP addPaintLayer(KisNodeSP activeNode)
KisAction * m_flattenLayer
KisAction * m_convertGroupAnimated
KisNodeSP addGroupLayer(KisNodeSP activeNode)
KisNodeSP addCloneLayer(KisNodeList nodes)
KisAction * m_imageFlatten
QPointer< KisView > m_imageView
void convertNodeToPaintLayer(KisNodeSP source)
KisAction * m_pasteLayerStyle
KisViewManager * m_view
KisAction * m_imageMergeLayer
KisAction * m_groupLayersSave
~KisLayerManager() override
KisNodeSP addAdjustmentLayer(KisNodeSP activeNode)
KisNodeCommandsAdapter * m_commandsAdapter
static const KisMetaData::MergeStrategy * showDialog(QWidget *parent)
static QStringList suffixesForMimeType(const QString &mimeType)
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
void addNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis, KisImageLayerAddCommand::Flags flags=KisImageLayerAddCommand::DoRedoUpdates|KisImageLayerAddCommand::DoUndoUpdates)
void addNodeAsync(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis, bool doRedoUpdates=true, bool doUndoUpdates=true, KisProcessingApplicator *applicator=0)
void beginMacro(const KUndo2MagicString &macroName)
virtual KisFilterConfigurationSP filter() const
KisNodeList selectedNodes()
bool canModifyLayers(KisNodeList nodes, bool showWarning=true)
bool canModifyLayer(KisNodeSP node, bool showWarning=true)
The command for setting the node's name.
QRect nonDefaultPixelArea() const
virtual const KoColorSpace * compositionSourceColorSpace() const
void convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KUndo2Command *parentCommand=nullptr, KoUpdater *progressUpdater=nullptr)
static KisPart * instance()
Definition KisPart.cpp:130
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...
const KisStrokeId getStroke() const
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 KisSaveGroupVisitor class saves the groups in a Krita image to separate images.
static void updateLayerStyle(KisLayerSP layer, KisPSDLayerStyleSP style)
bool isNull() const
void setProfile(KisImageWSP image)
virtual void addCommand(KUndo2Command *cmd)=0
bool blockUntilOperationsFinished(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
KisMainWindow * mainWindow() const
KisDocument * document() const
KisCanvas2 * canvasBase() const
Return the canvas base class.
KisNodeSP activeNode()
KisUndoAdapter * undoAdapter()
The undo adapter is used to add commands to the undo stack.
KisSelectionSP selection()
QWidget * canvas() const
Return the actual widget that is displaying the current image.
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
KisImageWSP image() const
Return the image this view is displaying.
KisCanvasResourceProvider * canvasResourceProvider()
KisStatusBar * statusBar() const
Return the wrapper class around the statusbar.
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
QPointer< KoCanvasResourceProvider > resourceManager
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
A dialog base class with standard buttons and predefined layouts.
Definition KoDialog.h:116
void setMainWidget(QWidget *widget)
Definition KoDialog.cpp:345
virtual void setCaption(const QString &caption)
Definition KoDialog.cpp:489
QSize minimumSizeHint() const override
Definition KoDialog.cpp:382
T get(const QString &id) const
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define bounds(x, a, b)
#define warnKrita
Definition kis_debug.h:94
bool isSelectionMask(KisNodeSP node)
void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg, KoCanvasResourcesInterfaceSP canvasResourcesInterface)
bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image)
bool tryFlattenGroupLayer(KisNodeSP currentNode, KisImageSP image)
KisDocument * createDocument(QList< KisNodeSP > nodes, KisImageSP srcImage, const QRect &copiedBounds)
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
QSharedPointer< T > toQShared(T *ptr)
KisSharedPtr< KisFileLayer > KisFileLayerSP
Definition kis_types.h:145
KisSharedPtr< KisAdjustmentLayer > KisAdjustmentLayerSP
Definition kis_types.h:131
KisSharedPtr< KisGeneratorLayer > KisGeneratorLayerSP
Definition kis_types.h:134
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
KUndo2MagicString kundo2_i18n(const char *text)
KUndo2Command * composeCommands(KUndo2Command *parent, KUndo2Command *cmd)
KisNodeList sortAndFilterMergeableInternalNodes(KisNodeList nodes, bool allowMasks)
std::future< KisNodeSP > convertToPaintLayer(KisImageSP image, KisNodeSP src)
void setPinnedToTimeline(bool pinned)
virtual KisPaintDeviceSP projection() const =0
bool isEditable(bool checkVisibility=true) const
KisKeyframeChannel * getKeyframeChannel(const QString &id, bool create)
bool userLocked() const
bool isAnimated() const
virtual KisPaintDeviceSP paintDevice() const =0
QString name() const
void enableAnimation()
bool projectionIsValid() const
const KoColorSpace * colorSpace() const override
bool accept(KisNodeVisitor &v) override
void resetCache(const KoColorSpace *colorSpace)
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:826
KisMetaData::Store * metaData()
KisPSDLayerStyleSP layerStyle
Definition kis_layer.cc:171
void setX(qint32 x) override
Definition kis_layer.cc:986
void setY(qint32 y) override
Definition kis_layer.cc:992
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
KisNodeSP prevSibling() const
Definition kis_node.cpp:402
KisNodeSP firstChild() const
Definition kis_node.cpp:361
quint32 childCount() const
Definition kis_node.cpp:414
KisNodeWSP parent
Definition kis_node.cpp:86
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
The KisPSDLayerStyle class implements loading, saving and applying the PSD layer effects.
KisPaintDeviceSP paintDevice