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"
69#include "kis_filter_manager.h"
70#include "kis_node_visitor.h"
71#include "kis_paint_layer.h"
78#include "kis_statusbar.h"
79#include "KisViewManager.h"
80#include "kis_zoom_manager.h"
81#include "canvas/kis_canvas2.h"
84#include "kis_progress_widget.h"
86#include "kis_node_manager.h"
87#include "kis_action.h"
88#include "kis_action_manager.h"
94#include "kis_selection_mask.h"
95#include "kis_layer_utils.h"
98#include "kis_projection_leaf.h"
100
101#include "KisSaveGroupVisitor.h"
103
104
106 : m_view(view)
107 , m_commandsAdapter(new KisNodeCommandsAdapter(m_view))
108
109{
110}
111
116
121
123{
124 if (m_imageView) {
125 return m_imageView->currentLayer();
126 }
127 return 0;
128}
129
131{
132 if (activeLayer()) {
133 return activeLayer()->paintDevice();
134 }
135 return 0;
136}
137
139{
140 if (m_imageView) {
142 if (layer) {
144 }
145 }
146}
147
148
150{
151 m_imageFlatten = actionManager->createAction("flatten_image");
152 connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage()));
153
154 m_imageMergeLayer = actionManager->createAction("merge_layer");
155 connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer()));
156
157 m_flattenLayer = actionManager->createAction("flatten_layer");
158 connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer()));
159
160 m_groupLayersSave = actionManager->createAction("save_groups_as_images");
161 connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers()));
162
163 m_convertGroupAnimated = actionManager->createAction("convert_group_to_animated");
164 connect(m_convertGroupAnimated, SIGNAL(triggered()), this, SLOT(convertGroupToAnimated()));
165
166 m_imageResizeToLayer = actionManager->createAction("resizeimagetolayer");
167 connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer()));
168
169 KisAction *action = actionManager->createAction("trim_to_image");
170 connect(action, SIGNAL(triggered()), this, SLOT(trimToImage()));
171
172 m_layerStyle = actionManager->createAction("layer_style");
173 connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle()));
174
175 m_copyLayerStyle = actionManager->createAction("copy_layer_style");
176 connect(m_copyLayerStyle, SIGNAL(triggered()), this, SLOT(copyLayerStyle()));
177
178 m_pasteLayerStyle = actionManager->createAction("paste_layer_style");
179 connect(m_pasteLayerStyle, SIGNAL(triggered()), this, SLOT(pasteLayerStyle()));
180}
181
183{
184 KisImageSP image = m_view->image();
185 KisLayerSP layer = activeLayer();
186
187 const bool isGroupLayer = layer && layer->inherits("KisGroupLayer");
188
189 QString mergeLayerText;
190 if (m_view->nodeManager()->selectedNodes().size() > 1) {
191 mergeLayerText = i18nc("@action:inmenu", "Merge Selected Layers");
192 } else if (isGroupLayer) {
193 mergeLayerText = i18nc("@action:inmenu", "Merge Group");
194 } else {
195 mergeLayerText = i18nc("@action:inmenu", "Merge with Layer Below");
196 }
197 m_imageMergeLayer->setText(mergeLayerText);
198
199 m_flattenLayer->setVisible(!isGroupLayer);
200
201 if (m_view->statusBar())
202 m_view->statusBar()->setProfile(image);
203}
204
206{
207 KisLayerSP layer;
208 KisImageWSP image = m_view->image();
209
210 if (image && (layer = activeLayer())) {
211 QRect cropRect = layer->projection()->nonDefaultPixelArea();
212 if (!cropRect.isEmpty()) {
213 image->cropImage(cropRect);
214 } else {
216 i18nc("floating message in layer manager",
217 "Layer is empty "),
218 QIcon(), 2000, KisFloatingMessage::Low);
219 }
220 }
221}
222
224{
225 KisImageWSP image = m_view->image();
226 if (image) {
227 image->cropImage(image->bounds());
228 }
229}
230
232{
233 if (!m_view) return;
234 if (!m_view->document()) return;
235
236 // For non-modal dialogs, only allow one instance of the dialog to exist.
237 // Modal dialogs won't set this.
239
240 KisLayerSP layer = activeLayer();
241 if (!layer) return;
242
243 const QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
244 const bool multipleLayersSelected = selectedNodes.size() > 1;
245
246 if (!m_view->nodeManager()->canModifyLayers(selectedNodes)) return;
247
248 KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
249 KisGeneratorLayerSP generatorLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
250 KisFileLayerSP fileLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
251
252 if (adjustmentLayer && !multipleLayersSelected) {
253
254 KisPaintDeviceSP dev = adjustmentLayer->projection();
255
256 KisDlgAdjLayerProps dlg(adjustmentLayer, adjustmentLayer.data(), dev, m_view, adjustmentLayer->filter().data(), adjustmentLayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops");
257 dlg.resize(dlg.minimumSizeHint());
258
259
260 KisFilterConfigurationSP configBefore(adjustmentLayer->filter());
261 KIS_ASSERT_RECOVER_RETURN(configBefore);
262 QString xmlBefore = configBefore->toXML();
263
264
265 if (dlg.exec() == QDialog::Accepted) {
266
267 adjustmentLayer->setName(dlg.layerName());
268
270 Q_ASSERT(configAfter);
271 QString xmlAfter = configAfter->toXML();
272
273 if(xmlBefore != xmlAfter) {
275 = new KisChangeFilterCmd(adjustmentLayer,
276 configBefore->cloneWithResourcesSnapshot(),
277 configAfter->cloneWithResourcesSnapshot());
278 // FIXME: check whether is needed
279 cmd->redo();
281 m_view->document()->setModified(true);
282 }
283 }
284 else {
286 Q_ASSERT(configAfter);
287 QString xmlAfter = configAfter->toXML();
288
289 if(xmlBefore != xmlAfter) {
290 adjustmentLayer->setFilter(configBefore->cloneWithResourcesSnapshot());
291 adjustmentLayer->setDirty();
292 }
293 }
294 }
295 else if (fileLayer && !multipleLayersSelected){
296 QString basePath = QFileInfo(m_view->document()->path()).absolutePath();
297 QString fileNameOld = fileLayer->fileName();
298 KisFileLayer::ScalingMethod scalingMethodOld = fileLayer->scalingMethod();
299 QString scalingFilterOld = fileLayer->scalingFilter();
300 KisDlgFileLayer dlg(basePath, fileLayer->name(), m_view->mainWindow());
301 dlg.setCaption(i18n("File Layer Properties"));
302 dlg.setFileName(fileNameOld);
303 dlg.setScalingMethod(scalingMethodOld);
304 dlg.setScalingFilter(scalingFilterOld);
305
306 if (dlg.exec() == QDialog::Accepted) {
307 const QString fileNameNew = dlg.fileName();
309 QString scalingFilterNew = dlg.scalingFilter();
310
311 if(fileNameNew.isEmpty()){
312 QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
313 return;
314 }
315 fileLayer->setName(dlg.layerName());
316
317 if (fileNameOld!= fileNameNew || scalingMethodOld != scalingMethodNew || scalingFilterOld != scalingFilterNew) {
319 = new KisChangeFileLayerCmd(fileLayer,
320 basePath,
321 fileNameOld,
322 scalingMethodOld,
323 scalingFilterOld,
324 basePath,
325 fileNameNew,
326 scalingMethodNew,
327 scalingFilterNew);
329 }
330 }
331 } else {
332 QDialog* dlg = nullptr;
333
334 if (generatorLayer && !multipleLayersSelected) {
335 KisFilterConfigurationSP configBefore(generatorLayer->filter());
336 Q_ASSERT(configBefore);
337
338 auto* genDlg = new KisDlgGeneratorLayer(generatorLayer->name(), m_view, m_view->canvas(), generatorLayer, configBefore, KisStrokeId());
339 genDlg->setWindowTitle(i18n("Fill Layer Properties"));
340 genDlg->setConfiguration(configBefore.data());
341
342 dlg = genDlg;
343 }
344
345 // Handle these layer(s) as normal painting layer, vector layer, or group layer
346 if (!dlg) {
347 dlg = new KisDlgLayerProperties(selectedNodes, m_view, m_view->canvas());
348 }
349
350 dlg->resize(dlg->minimumSizeHint());
351 dlg->setAttribute(Qt::WA_DeleteOnClose);
352
353 const Qt::WindowFlags flags = dlg->windowFlags();
354#ifdef Q_OS_ANDROID
355 // a Qt::Tool window seems incapable of receiving keyboard focus
356 dlg->setWindowFlags(flags | Qt::Dialog);
357#else
358 dlg->setWindowFlags(flags | Qt::Tool | Qt::Dialog);
359#endif
360 connect(dlg, SIGNAL(destroyed()), this, SLOT(layerPropertiesDialogClosed()));
362
363 dlg->show();
364 dlg->activateWindow();
365 }
366}
367
372
374{
375 QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
376 if (selectedNodes.isEmpty()) {
377 return;
378 }
379
380 QList<KisCloneLayerSP> cloneLayers;
381 KisNodeSP node;
382 Q_FOREACH (node, selectedNodes) {
383 KisCloneLayerSP cloneLayer(qobject_cast<KisCloneLayer *>(node.data()));
384 if (cloneLayer) {
385 cloneLayers << cloneLayer;
386 }
387 }
388
389 if (cloneLayers.isEmpty()) {
390 return;
391 }
392
393 if (!m_view->nodeManager()->canModifyLayers(implicitCastList<KisNodeSP>(cloneLayers))) return;
394
395 KisDlgChangeCloneSource *dialog = new KisDlgChangeCloneSource(cloneLayers, m_view);
396 dialog->setCaption(i18n("Change Clone Layer"));
397 dialog->resize(dialog->minimumSizeHint());
398 dialog->setAttribute(Qt::WA_DeleteOnClose);
399 Qt::WindowFlags flags = dialog->windowFlags();
400 dialog->setWindowFlags(flags | Qt::Tool | Qt::Dialog);
401 dialog->show();
402 dialog->activateWindow();
403}
404
406{
407 KisImageSP image = m_view->image();
408 if (!image) return;
409
410 KisLayerSP layer = activeLayer();
411 if (!layer) return;
412
414 if (!layerStyle) return;
415
418 QString psdxml = serializer.formPsdXmlDocument().toString();
419
420 if (!psdxml.isEmpty()) {
421 QMimeData *mimeData = new QMimeData;
422
423 mimeData->setText(psdxml);
424 mimeData->setData("application/x-krita-layer-style", psdxml.toUtf8());
425
426 QGuiApplication::clipboard()->setMimeData(mimeData);
427 }
428}
429
431{
432 KisImageSP image = m_view->image();
433 if (!image) return;
434
435 KisLayerSP layer = activeLayer();
436 if (!layer) return;
437
438 QString aslXml;
439
440 if (KisClipboard::instance()->hasLayerStyles()) {
441 aslXml = QString::fromUtf8(QGuiApplication::clipboard()->mimeData()->data("application/x-krita-layer-style"));
442 } else {
443 aslXml = QGuiApplication::clipboard()->text();
444 }
445
446 if (aslXml.isEmpty()) return;
447
448 QDomDocument aslDoc;
449 if (!aslDoc.setContent(aslXml)) return;
450
452 serializer.registerPSDPattern(aslDoc);
453 serializer.readFromPSDXML(aslDoc);
454
455 if (serializer.styles().size() != 1) return;
456
457 KisPSDLayerStyleSP newStyle = serializer.styles().first()->cloneWithResourcesSnapshot(
459 m_view->canvasBase()->resourceManager()->canvasResourcesInterface());
460 KUndo2Command *cmd = new KisSetLayerStyleCommand(layer, layer->layerStyle(), newStyle);
461
463 image->waitForDone();
464}
465
467{
468 KisImageWSP image = m_view->image();
469 if (!image) return;
470
471 // this precondition must be checked at higher level
472 KIS_SAFE_ASSERT_RECOVER_RETURN(source->isEditable(false));
473
474 KisLayer *srcLayer = qobject_cast<KisLayer*>(source.data());
475 if (srcLayer && (srcLayer->inherits("KisGroupLayer") || srcLayer->layerStyle() || srcLayer->childCount() > 0)) {
476 image->flattenLayer(srcLayer);
477 return;
478 }
479
481}
482
484{
485 KisGroupLayerSP targetGroup = dynamic_cast<KisGroupLayer*>(activeLayer().data());
486 if (targetGroup.isNull()) {
487 // Try containing group, if it exists...
488 KisNodeSP parent = activeLayer()->parent();
489 if (!parent->parent()) return; // watch out for the root node!
490 targetGroup = dynamic_cast<KisGroupLayer*>(parent.data());
491 }
492
493 if (targetGroup.isNull()) return;
494 if (!m_view->nodeManager()->canModifyLayer(targetGroup)) return;
495
496 KisPaintLayerSP animatedLayer = new KisPaintLayer(m_view->image(), targetGroup->name(), OPACITY_OPAQUE_U8);
497 animatedLayer->enableAnimation();
498 KisRasterKeyframeChannel *keyframeChannel = dynamic_cast<KisRasterKeyframeChannel*>(
499 animatedLayer->getKeyframeChannel(KisKeyframeChannel::Raster.id(), true));
500 KIS_ASSERT_RECOVER_RETURN(keyframeChannel);
501
502 KisNodeSP childNode = targetGroup->firstChild();
503 int time = 0;
504 while (childNode) {
505 keyframeChannel->importFrame(time, childNode->projection(), NULL);
506 time++;
507
508 childNode = childNode->nextSibling();
509 }
510
511 m_commandsAdapter->beginMacro(kundo2_i18n("Convert Group to Animated Layer"));
512 m_commandsAdapter->addNode(animatedLayer, targetGroup->parent(), targetGroup);
513 m_commandsAdapter->removeNode(targetGroup);
515}
516
518{
519 KisImageSP image = m_view->image();
520 if (!image) return;
521
522 // this precondition must be checked at higher level
523 KIS_SAFE_ASSERT_RECOVER_RETURN(source->isEditable(false));
524
526
527 KoDialog dlg;
528 QWidget *page = new QWidget(&dlg);
529 dlg.setMainWidget(page);
530 QBoxLayout *layout = new QVBoxLayout(page);
531 dlg.setWindowTitle(i18n("Save layers to..."));
532 QLabel *lbl = new QLabel(i18n("Choose the location where the layer will be saved to. The new file layer will then reference this location."));
533 lbl->setWordWrap(true);
534 layout->addWidget(lbl);
535 KisFileNameRequester *urlRequester = new KisFileNameRequester(page);
536 urlRequester->setMode(KoFileDialog::SaveFile);
537 urlRequester->setMimeTypeFilters(listMimeFilter);
538 urlRequester->setFileName(m_view->document()->path());
539 if (!m_view->document()->path().isEmpty()) {
540 QFileInfo location = QFileInfo(m_view->document()->path());
541 location.setFile(location.dir(), location.completeBaseName() + "_" + source->name() + ".png");
542 urlRequester->setFileName(location.absoluteFilePath());
543 }
544 else {
545 const QFileInfo location = QFileInfo(QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
546 const QString proposedFileName = QDir(location.absoluteFilePath()).absoluteFilePath(source->name() + ".png");
547 urlRequester->setFileName(proposedFileName);
548 }
549
550 layout->addWidget(urlRequester);
551 if (!dlg.exec()) return;
552
553 QString path = urlRequester->fileName();
554
555 if (path.isEmpty()) return;
556
557 QFileInfo f(path);
558
559 QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName());
560 if (mimeType.isEmpty()) {
561 mimeType = "image/png";
562 }
563 QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
564
565 QRect bounds = source->exactBounds();
566 if (bounds.isEmpty()) {
567 bounds = image->bounds();
568 }
569 KisImageSP dst = new KisImage(doc->createUndoStore(),
570 image->width(),
571 image->height(),
573 source->name());
574 dst->setResolution(image->xRes(), image->yRes());
575 doc->setFileBatchMode(false);
576 doc->setCurrentImage(dst);
577 KisNodeSP node = source->clone();
578 dst->addNode(node);
579 dst->initialRefreshGraph();
580 dst->cropImage(bounds);
581 dst->waitForDone();
582
583 bool r = doc->exportDocumentSync(path, mimeType.toLatin1());
584 if (!r) {
585
586 qWarning() << "Converting layer to file layer. path:"<< path << "gave errors" << doc->errorMessage();
587 } else {
588 QString basePath = QFileInfo(m_view->document()->path()).absolutePath();
589 QString relativePath = QDir(basePath).relativeFilePath(path);
590 KisFileLayer *fileLayer = new KisFileLayer(image, basePath, relativePath, KisFileLayer::None, "Bicubic", source->name(), OPACITY_OPAQUE_U8);
591 fileLayer->setX(bounds.x());
592 fileLayer->setY(bounds.y());
593 KisNodeSP dstParent = source->parent();
594 KisNodeSP dstAboveThis = source->prevSibling();
595 m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a file layer"));
597 m_commandsAdapter->addNode(fileLayer, dstParent, dstAboveThis);
599 }
600 doc->closePath(false);
601}
602
604{
605 Q_ASSERT(activeNode);
606
607 parent = activeNode;
608 above = parent->lastChild();
609
610 if (parent->inherits("KisGroupLayer") && parent->collapsed()) {
611 above = parent;
612 parent = parent->parent();
613 return;
614 }
615
616 while (parent &&
617 (!parent->allowAsChild(node) || !parent->isEditable(false))) {
618
619 above = parent;
620 parent = parent->parent();
621 }
622
623 if (!parent) {
624 warnKrita << "KisLayerManager::adjustLayerPosition:"
625 << "No node accepted newly created node";
626
627 parent = m_view->image()->root();
628 above = parent->lastChild();
629 }
630}
631
632void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisNodeSP layer, bool updateImage, KisProcessingApplicator *applicator)
633{
634 KisNodeSP parent;
635 KisNodeSP above;
636 adjustLayerPosition(layer, activeNode, parent, above);
637
638 KisGroupLayer *group = dynamic_cast<KisGroupLayer*>(parent.data());
639
640 if (layer->inherits("KisGroupLayer") || layer->inherits("KisPaintLayer")) {
641 const KoColorSpace *colorSpace = group->colorSpace();
642
643 if (layer->inherits("KisGroupLayer")) {
644 KisGroupLayer *newLayer = dynamic_cast<KisGroupLayer*>(layer.data());
645 newLayer->resetCache(colorSpace);
646 } else {
647 KisPaintLayer *newLayer = qobject_cast<KisPaintLayer *>(layer.data());
648 newLayer->paintDevice()->convertTo(colorSpace);
649 }
650 }
651
652 const bool parentForceUpdate = group && !group->projectionIsValid();
653 updateImage |= parentForceUpdate;
654
655 m_commandsAdapter->addNodeAsync(layer, parent, above, updateImage, updateImage, applicator);
656}
657
659{
660 KisImageWSP image = m_view->image();
661 KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName( i18n("Paint Layer") ), OPACITY_OPAQUE_U8, image->colorSpace());
662
663 KisConfig cfg(true);
665
666 addLayerCommon(activeNode, layer, false, 0);
667
668 return layer;
669}
670
672{
673 KisImageWSP image = m_view->image();
674 KisGroupLayerSP group = new KisGroupLayer(image.data(), image->nextLayerName( i18nc("A group of layers", "Group") ), OPACITY_OPAQUE_U8);
675 addLayerCommon(activeNode, group, false, 0);
676 return group;
677}
678
680{
681 KisImageWSP image = m_view->image();
682
684 if (filteredNodes.isEmpty()) return KisNodeSP();
685
686 KisNodeSP newAbove = filteredNodes.last();
687
688 KisNodeSP node, lastClonedNode;
689 Q_FOREACH (node, filteredNodes) {
690 lastClonedNode = new KisCloneLayer(qobject_cast<KisLayer*>(node.data()), image.data(), image->nextLayerName( i18n("Clone Layer") ), OPACITY_OPAQUE_U8);
691 addLayerCommon(newAbove, lastClonedNode, true, 0 );
692 }
693
694 return lastClonedNode;
695}
696
698{
699 if (!m_view) return 0;
700 if (!m_view->document()) return 0;
701
702 KisImageWSP image = m_view->image();
703 KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(i18n("Vector Layer")), OPACITY_OPAQUE_U8);
704
705 addLayerCommon(activeNode, layer, false, 0);
706
707 return layer;
708}
709
711{
712 KisImageWSP image = m_view->image();
713
714 KisSelectionSP selection = m_view->selection();
715
718 kundo2_i18n("Add Layer"));
719
720
721 KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection, &applicator);
722
723 KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original());
724
725 KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(i18n("Filter Layer")), i18n("New Filter Layer"), m_view, qApp->activeWindow());
726 dlg.resize(dlg.minimumSizeHint());
727
728 // ensure that the device may be free'd by the dialog
729 // when it is not needed anymore
730 previewDevice = 0;
731
732 if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) {
733 // XXX: add messagebox warning if there's no filter set!
734 applicator.cancel();
735 } else {
736 applicator.applyCommand(new KisNodeRenameCommand(adjl, adjl->name(), dlg.layerName()));
737 applicator.end();
738 }
739
740 return adjl;
741}
742
745 KisSelectionSP selection,
746 KisProcessingApplicator *applicator)
747{
748 KisImageWSP image = m_view->image();
749 KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter ? filter->cloneWithResourcesSnapshot() : 0, selection);
750 addLayerCommon(activeNode, layer, true, applicator);
751
752 return layer;
753}
754
756{
757 KisImageWSP image = m_view->image();
758 auto layer = new KisGeneratorLayer(image, name, filter, selection);
759 addLayerCommon(activeNode, layer, true, applicator);
760
761 return layer;
762}
763
765{
766 KisImageWSP image = m_view->image();
767 KisSelectionSP selection = m_view->selection();
768 QColor currentForeground = m_view->canvasResourceProvider()->fgColor().toQColor();
769
771
772 KisGeneratorLayerSP node = addGeneratorLayer(activeNode, QString(), nullptr, selection, &applicator);
773
774 KisDlgGeneratorLayer dlg(image->nextLayerName(i18n("Fill Layer")), m_view, m_view->mainWindow(), node, nullptr, applicator.getStroke());
775 KisFilterConfigurationSP defaultConfig = dlg.configuration();
776 defaultConfig->setProperty("color", currentForeground);
777 dlg.setConfiguration(defaultConfig);
778
779 if (dlg.exec() == QDialog::Accepted) {
780 applicator.applyCommand(new KisNodeRenameCommand(node, node->name(), dlg.layerName()));
781 applicator.end();
782 return node;
783 }
784 else {
785 applicator.cancel();
786 return nullptr;
787 }
788}
789
791{
792 KisImageSP image = m_view->image();
793
794 if (!m_view->blockUntilOperationsFinished(image)) return;
795
796 if (image) {
797 bool doIt = true;
798
799 if (image->nHiddenLayers() > 0) {
800 int answer = QMessageBox::warning(m_view->mainWindow(),
801 i18nc("@title:window", "Flatten Image"),
802 i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"),
803 QMessageBox::Yes | QMessageBox::No,
804 QMessageBox::No);
805
806 if (answer != QMessageBox::Yes) {
807 doIt = false;
808 }
809 }
810
811 if (doIt) {
812 image->flatten(m_view->activeNode());
813 }
814 }
815}
816
817inline bool isSelectionMask(KisNodeSP node) {
818 return dynamic_cast<KisSelectionMask*>(node.data());
819}
820
822{
823 bool result = false;
824
825 KisNodeSP prevNode = currentNode->prevSibling();
826 if (isSelectionMask(currentNode) &&
827 prevNode && isSelectionMask(prevNode)) {
828
829 QList<KisNodeSP> mergedNodes;
830 mergedNodes.append(currentNode);
831 mergedNodes.append(prevNode);
832
833 image->mergeMultipleLayers(mergedNodes, currentNode);
834
835 result = true;
836 }
837
838 return result;
839}
840
842{
843 bool result = false;
844
845 if (currentNode->inherits("KisGroupLayer")) {
846 KisGroupLayer *layer = qobject_cast<KisGroupLayer*>(currentNode.data());
848
849 image->flattenLayer(layer);
850 result = true;
851 }
852
853 return result;
854}
855
857{
858 KisImageSP image = m_view->image();
859 if (!image) return;
860
861 KisLayerSP layer = activeLayer();
862 if (!layer) return;
863
864 if (!m_view->blockUntilOperationsFinished(image)) return;
865
866 QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
867
868 // check if all the layers are a part of a locked group
869 bool hasEditableLayer = false;
870 Q_FOREACH (KisNodeSP node, selectedNodes) {
871 if (node->isEditable(false)) {
872 hasEditableLayer = true;
873 break;
874 }
875 }
876
877 if (!hasEditableLayer) {
879 i18ncp("floating message in layer manager",
880 "Layer is locked", "Layers are locked", selectedNodes.size()),
881 QIcon(), 2000, KisFloatingMessage::Low);
882 return;
883 }
884
885 if (selectedNodes.size() > 1) {
886 image->mergeMultipleLayers(selectedNodes, m_view->activeNode());
887 }
888
889 else if (tryMergeSelectionMasks(m_view->activeNode(), image)) {
890 // already done!
891 } else if (tryFlattenGroupLayer(m_view->activeNode(), image)) {
892 // already done!
893 } else {
894
895 if (!layer->prevSibling()) return;
896 KisLayer *prevLayer = qobject_cast<KisLayer*>(layer->prevSibling().data());
897 if (!prevLayer) return;
898
899 if (prevLayer->userLocked()) {
901 i18nc("floating message in layer manager when previous layer is locked",
902 "Layer is locked"),
903 QIcon(), 2000, KisFloatingMessage::Low);
904 } else {
905 const KisMetaData::MergeStrategy* strategy = nullptr;
906
907 if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) {
909 }
910 else {
912 }
913
914 if (!strategy) return;
915
916 if (!layer->isAnimated() && prevLayer->isAnimated()) {
918 i18nc("floating message in layer manager when trying to merge a non-animated layer into an animated one",
919 "Non-animated layer is merged into the current frame. To merge it into the whole clip, create at least one frame"),
920 QIcon(), 5000, KisFloatingMessage::Medium);
921 }
922 image->mergeDown(layer, strategy);
923 }
924 }
925
926 m_view->updateGUI();
927}
928
930{
931 KisImageSP image = m_view->image();
932 if (!image) return;
933
934 KisLayerSP layer = activeLayer();
935 if (!layer) return;
936
937 if (!m_view->blockUntilOperationsFinished(image)) return;
938 if (!m_view->nodeManager()->canModifyLayer(layer)) return;
939
941 m_view->updateGUI();
942}
943
945{
946 KisLayerSP layer = activeLayer();
947 if (!layer) return;
948
949 m_view->updateGUI();
950}
951
953{
955
956 KoDialog dlg;
957 QWidget *page = new QWidget(&dlg);
958 dlg.setMainWidget(page);
959 QBoxLayout *layout = new QVBoxLayout(page);
960
961 KisFileNameRequester *urlRequester = new KisFileNameRequester(page);
962 urlRequester->setMode(KoFileDialog::SaveFile);
963 urlRequester->setStartDir(QFileInfo(m_view->document()->path()).absolutePath());
964 urlRequester->setMimeTypeFilters(listMimeFilter);
965 urlRequester->setFileName(m_view->document()->path());
966 layout->addWidget(urlRequester);
967
968 QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page);
969 chkInvisible->setChecked(false);
970 layout->addWidget(chkInvisible);
971 QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page);
972 chkDepth->setChecked(true);
973 layout->addWidget(chkDepth);
974
975 if (!dlg.exec()) return;
976
977 QString path = urlRequester->fileName();
978
979 if (path.isEmpty()) return;
980
981 QFileInfo f(path);
982
983 QString mimeType= KisMimeDatabase::mimeTypeForFile(f.fileName(), false);
984 if (mimeType.isEmpty()) {
985 mimeType = "image/png";
986 }
987 QString extension = KisMimeDatabase::suffixesForMimeType(mimeType).first();
988 QString basename = f.completeBaseName();
989
990 KisImageSP image = m_view->image();
991 if (!image) return;
992
993 KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), f.absolutePath(), basename, extension, mimeType);
994 image->rootLayer()->accept(v);
995
996}
997
999{
1000 return (activeLayer()->selection() != 0);
1001}
1002
1004{
1005 QString basePath;
1006 QString path = m_view->document()->path();
1007 basePath = QFileInfo(path).absolutePath();
1008 KisImageWSP image = m_view->image();
1009
1010 KisDlgFileLayer dlg(basePath, image->nextLayerName(i18n("File Layer")), m_view->mainWindow());
1011 dlg.resize(dlg.minimumSizeHint());
1012
1013 if (dlg.exec() == QDialog::Accepted) {
1014 QString name = dlg.layerName();
1015 QString fileName = dlg.fileName();
1016
1017 if(fileName.isEmpty()){
1018 QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
1019 return 0;
1020 }
1021
1023 QString scalingFilter = dlg.scalingFilter();
1024 KisNodeSP node = new KisFileLayer(image, basePath, fileName, scalingMethod, scalingFilter, name, OPACITY_OPAQUE_U8);
1025 addLayerCommon(activeNode, node, true, 0);
1026 return node;
1027 }
1028 return 0;
1029}
1030
1032{
1034 dlg->style()->cloneWithResourcesSnapshot(
1036 canvasResourcesInterface));
1037}
1038
1040{
1041 KisImageWSP image = m_view->image();
1042 if (!image) return;
1043
1044 KisLayerSP layer = activeLayer();
1045 if (!layer) return;
1046
1047 if (!m_view->blockUntilOperationsFinished(image)) return;
1048 if (!m_view->nodeManager()->canModifyLayer(layer)) return;
1049
1050 KoCanvasResourcesInterfaceSP canvasResourcesInterface = m_view->canvasBase()->resourceManager()->canvasResourcesInterface();
1051
1052 KisPSDLayerStyleSP oldStyle;
1053 if (layer->layerStyle()) {
1054 oldStyle = layer->layerStyle()->clone().dynamicCast<KisPSDLayerStyle>();
1055
1056 } else {
1058 ->cloneWithResourcesSnapshot(KisGlobalResourcesInterface::instance(),
1059 canvasResourcesInterface);
1060 }
1061
1062 KisPSDLayerStyleSP newStyle = oldStyle->clone().dynamicCast<KisPSDLayerStyle>();
1063 // We want to also change the UUID, else it might be considered the same style after save and won't load correctly
1064 newStyle->setUuid(QUuid::createUuid());
1065 newStyle->setResourcesInterface(KisGlobalResourcesInterface::instance());
1066
1068
1069 std::function<void ()> updateCall(std::bind(updateLayerStyles, layer, &dlg, canvasResourcesInterface));
1070 SignalToFunctionProxy proxy(updateCall);
1071 connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start()));
1072
1073 if (dlg.exec() == QDialog::Accepted) {
1074 KisPSDLayerStyleSP newStyle =
1075 dlg.style()->cloneWithResourcesSnapshot(KisGlobalResourcesInterface::instance(),
1076 canvasResourcesInterface);
1077
1078 KUndo2CommandSP command = toQShared(
1079 new KisSetLayerStyleCommand(layer, oldStyle, newStyle));
1080
1081 image->postExecutionUndoAdapter()->addCommand(command);
1082 }
1083}
1084
qreal v
QVector< KisImageSignalType > KisImageSignalVector
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
const quint8 OPACITY_OPAQUE_U8
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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
void setModified(bool _mod)
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
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
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:870
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:131
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:354
virtual void setCaption(const QString &caption)
Definition KoDialog.cpp:498
QSize minimumSizeHint() const override
Definition KoDialog.cpp:391
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:87
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)
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)
void setName(const QString &name)
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:820
KisMetaData::Store * metaData()
KisPSDLayerStyleSP layerStyle
Definition kis_layer.cc:171
void setX(qint32 x) override
Definition kis_layer.cc:983
void setY(qint32 y) override
Definition kis_layer.cc:989
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