File indexing completed on 2024-05-19 04:28:54

0001 /*
0002  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_clipboard.h"
0009 
0010 #include <QApplication>
0011 #include <QBuffer>
0012 #include <QClipboard>
0013 #include <QDesktopWidget>
0014 #include <QFileInfo>
0015 #include <QMessageBox>
0016 #include <QMimeData>
0017 #include <QScopedPointer>
0018 #include <QTemporaryFile>
0019 
0020 // kritaglobal
0021 #include <algorithm>
0022 #include <kis_assert.h>
0023 #include <kis_debug.h>
0024 
0025 // kritastore
0026 #include <KoStore.h>
0027 
0028 // kritaimage
0029 #include <kis_annotation.h>
0030 #include <kis_layer_utils.h>
0031 #include <kis_paint_device.h>
0032 #include <kis_time_span.h>
0033 
0034 // local
0035 #include "KisDocument.h"
0036 #include "KisImportExportManager.h"
0037 #include "KisMainWindow.h"
0038 #include "KisMimeDatabase.h"
0039 #include "KisPart.h"
0040 #include "KisRemoteFileFetcher.h"
0041 #include "dialogs/kis_dlg_missing_color_profile.h"
0042 #include "dialogs/kis_dlg_paste_format.h"
0043 #include "kis_mimedata.h"
0044 #include "kis_store_paintdevice_writer.h"
0045 
0046 Q_GLOBAL_STATIC(KisClipboard, s_instance)
0047 
0048 struct ClipboardImageFormat {
0049     QSet<QString> mimeTypes;
0050     QString format;
0051 };
0052 
0053 class Q_DECL_HIDDEN KisClipboardPrivate
0054 {
0055 public:
0056     KisClipboardPrivate()
0057         : clipboard(QApplication::clipboard())
0058     {
0059     }
0060 
0061     bool hasClip{};
0062     bool pushedClipboard{};
0063     QClipboard *clipboard;
0064 };
0065 
0066 KisClipboard::KisClipboard()
0067     : d(new KisClipboardPrivate)
0068 {
0069     // Check that we don't already have a clip ready
0070     clipboardDataChanged();
0071 
0072     // Make sure we are notified when clipboard changes
0073     connect(d->clipboard, &QClipboard::dataChanged, this, &KisClipboard::clipboardDataChanged, Qt::UniqueConnection);
0074 }
0075 
0076 KisClipboard::~KisClipboard()
0077 {
0078     dbgRegistry << "deleting KisClipBoard";
0079     delete d;
0080 }
0081 
0082 KisClipboard *KisClipboard::instance()
0083 {
0084     return s_instance;
0085 }
0086 
0087 void KisClipboard::setClip(KisPaintDeviceSP dev, const QPoint &topLeft, const KisTimeSpan &range)
0088 {
0089     if (!dev)
0090         return;
0091 
0092     d->hasClip = true;
0093 
0094     // We'll create a store (ZIP format) in memory
0095     QBuffer buffer;
0096     const auto mimeType = QByteArrayLiteral("application/x-krita-selection");
0097     QScopedPointer<KoStore> store(KoStore::createStore(&buffer, KoStore::Write, mimeType));
0098     KisStorePaintDeviceWriter writer(store.data());
0099     Q_ASSERT(store);
0100     Q_ASSERT(!store->bad());
0101 
0102     // Layer data
0103     if (store->open("layerdata")) {
0104         if (!dev->write(writer)) {
0105             dev->disconnect();
0106             store->close();
0107             return;
0108         }
0109         store->close();
0110     }
0111 
0112     // copied frame time limits
0113     if (range.isValid() && store->open("timeRange")) {
0114         store->write(QString("%1 %2").arg(range.start()).arg(range.end()).toLatin1());
0115         store->close();
0116     }
0117 
0118     // Coordinates
0119     if (store->open("topLeft")) {
0120         store->write(QString("%1 %2").arg(topLeft.x()).arg(topLeft.y()).toLatin1());
0121         store->close();
0122     }
0123     // ColorSpace id of layer data
0124     if (store->open("colormodel")) {
0125         QString csName = dev->colorSpace()->colorModelId().id();
0126         store->write(csName.toLatin1());
0127         store->close();
0128     }
0129     if (store->open("colordepth")) {
0130         QString csName = dev->colorSpace()->colorDepthId().id();
0131         store->write(csName.toLatin1());
0132         store->close();
0133     }
0134 
0135     if (dev->colorSpace()->profile()) {
0136         const KoColorProfile *profile = dev->colorSpace()->profile();
0137         KisAnnotationSP annotation;
0138 
0139         if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) {
0140             annotation = new KisAnnotation("icc", profile->name(), profile->rawData());
0141 
0142             if (annotation) {
0143                 // save layer profile
0144                 if (store->open("profile.icc")) {
0145                     store->write(annotation->annotation());
0146                     store->close();
0147                 }
0148             }
0149         }
0150     }
0151 
0152     QMimeData *mimeData = new QMimeData;
0153     Q_CHECK_PTR(mimeData);
0154 
0155     if (mimeData) {
0156         mimeData->setData(mimeType, buffer.buffer());
0157     }
0158 
0159     // We also create a QImage so we can interchange with other applications
0160     QImage qimage;
0161     KisConfig cfg(true);
0162     const KoColorProfile *monitorProfile =
0163             cfg.displayProfile(QApplication::desktop()->screenNumber(QApplication::activeWindow()));
0164     qimage = dev->convertToQImage(monitorProfile,
0165                                   KoColorConversionTransformation::internalRenderingIntent(),
0166                                   KoColorConversionTransformation::internalConversionFlags());
0167     if (!qimage.isNull() && mimeData) {
0168         mimeData->setImageData(qimage);
0169     }
0170 
0171     if (mimeData) {
0172         d->pushedClipboard = true;
0173         d->clipboard->setMimeData(mimeData);
0174     }
0175 }
0176 
0177 void KisClipboard::setClip(KisPaintDeviceSP dev, const QPoint &topLeft)
0178 {
0179     setClip(dev, topLeft, KisTimeSpan());
0180 }
0181 
0182 KisPaintDeviceSP KisClipboard::clip(const QRect &imageBounds, bool showPopup, int overridePasteBehaviour, KisTimeSpan *clipRange) const
0183 {
0184     const QMimeData *cbData = d->clipboard->mimeData();
0185 
0186     if (!cbData) {
0187         return nullptr;
0188     }
0189 
0190     dbgUI << Q_FUNC_INFO;
0191     dbgUI << "\tFormats: " << cbData->formats();
0192     dbgUI << "\tUrls: " << cbData->urls();
0193     dbgUI << "\tHas images: " << cbData->hasImage();
0194 
0195     return clipFromMimeData(cbData, imageBounds, showPopup, overridePasteBehaviour, clipRange, true);
0196 }
0197 
0198 KisPaintDeviceSP KisClipboard::clipFromMimeData(const QMimeData *cbData,
0199                                                 const QRect &imageBounds,
0200                                                 bool showPopup,
0201                                                 int overridePasteBehaviour,
0202                                                 KisTimeSpan *clipRange,
0203                                                 bool useClipboardFallback) const
0204 {
0205     if (clipRange) {
0206         *clipRange = KisTimeSpan();
0207     }
0208 
0209     KisPaintDeviceSP clip = clipFromKritaSelection(cbData, imageBounds, clipRange);
0210 
0211     if (!clip) {
0212         clip = clipFromBoardContents(cbData, imageBounds, showPopup, overridePasteBehaviour, useClipboardFallback);
0213     }
0214 
0215     return clip;
0216 }
0217 
0218 KisPaintDeviceSP KisClipboard::clipFromKritaSelection(const QMimeData *cbData, const QRect &imageBounds, KisTimeSpan *clipRange) const
0219 {
0220     const QByteArray mimeType = QByteArrayLiteral("application/x-krita-selection");
0221 
0222     KisPaintDeviceSP clip;
0223 
0224     if (!cbData) {
0225         return nullptr;
0226     }
0227 
0228     if (cbData->hasFormat(mimeType)) {
0229         QByteArray encodedData = cbData->data(mimeType);
0230         QBuffer buffer(&encodedData);
0231         QScopedPointer<KoStore> store(KoStore::createStore(&buffer, KoStore::Read, mimeType));
0232 
0233         const KoColorProfile *profile = 0;
0234 
0235         QString csDepth, csModel;
0236 
0237         // ColorSpace id of layer data
0238         if (store->hasFile("colormodel")) {
0239             store->open("colormodel");
0240             csModel = QString(store->read(store->size()));
0241             store->close();
0242         }
0243 
0244         if (store->hasFile("colordepth")) {
0245             store->open("colordepth");
0246             csDepth = QString(store->read(store->size()));
0247             store->close();
0248         }
0249 
0250         if (store->hasFile("profile.icc")) {
0251             QByteArray data;
0252             store->open("profile.icc");
0253             data = store->read(store->size());
0254             store->close();
0255             profile = KoColorSpaceRegistry::instance()->createColorProfile(csModel, csDepth, data);
0256         }
0257 
0258         const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(csModel, csDepth, profile);
0259         if (cs) {
0260             clip = new KisPaintDevice(cs);
0261 
0262             if (store->hasFile("layerdata")) {
0263                 store->open("layerdata");
0264                 if (!clip->read(store->device())) {
0265                     clip = 0;
0266                 }
0267                 store->close();
0268             }
0269 
0270             if (clip && !imageBounds.isEmpty()) {
0271                 // load topLeft
0272                 if (store->hasFile("topLeft")) {
0273                     store->open("topLeft");
0274                     QString str = store->read(store->size());
0275                     store->close();
0276                     QStringList list = str.split(' ');
0277                     if (list.size() == 2) {
0278                         QPoint topLeft(list[0].toInt(), list[1].toInt());
0279                         clip->setX(topLeft.x());
0280                         clip->setY(topLeft.y());
0281                     }
0282                 }
0283 
0284                 QRect clipBounds = clip->exactBounds();
0285 
0286                 if (!imageBounds.contains(clipBounds) && !imageBounds.intersects(clipBounds)) {
0287                     QPoint diff = imageBounds.center() - clipBounds.center();
0288                     clip->setX(clip->x() + diff.x());
0289                     clip->setY(clip->y() + diff.y());
0290                 }
0291 
0292                 if (store->hasFile("timeRange") && clipRange) {
0293                     store->open("timeRange");
0294                     QString str = store->read(store->size());
0295                     store->close();
0296                     QStringList list = str.split(' ');
0297                     if (list.size() == 2) {
0298                         KisTimeSpan range = KisTimeSpan::fromTimeToTime(list[0].toInt(), list[1].toInt());
0299                         *clipRange = range;
0300                         dbgUI << "Pasted time range" << range;
0301                     }
0302                 }
0303             }
0304         }
0305     }
0306 
0307     return clip;
0308 }
0309 
0310 KisPaintDeviceSP KisClipboard::clipFromKritaLayers(const QRect &imageBounds,
0311                                                    const KoColorSpace *cs) const
0312 {
0313     const QMimeData *data = KisClipboard::instance()->layersMimeData();
0314 
0315     if (!data) {
0316         return nullptr;
0317     }
0318 
0319     const auto *mimedata = qobject_cast<const KisMimeData *>(data);
0320     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(mimedata, nullptr);
0321 
0322     KisNodeList nodes = mimedata->nodes();
0323 
0324     if (nodes.size() > 1) {
0325         KisImageSP tempImage = new KisImage(nullptr,
0326                                             imageBounds.width(),
0327                                             imageBounds.height(),
0328                                             cs,
0329                                             "ClipImage");
0330         for (KisNodeSP node : nodes) {
0331             tempImage->addNode(node, tempImage->root());
0332         }
0333         tempImage->refreshGraphAsync();
0334         tempImage->waitForDone();
0335 
0336         return tempImage->projection();
0337     } else if (!nodes.isEmpty()) {
0338         return nodes.first()->projection();
0339     }
0340 
0341     return nullptr;
0342 }
0343 
0344 QPair<bool, KisClipboard::PasteFormatBehaviour>
0345 KisClipboard::askUserForSource(const QMimeData *cbData,
0346                                bool useClipboardFallback) const
0347 {
0348     if (!cbData) {
0349         return {false, PASTE_FORMAT_ASK};
0350     }
0351 
0352     KisConfig cfg(true);
0353 
0354     bool saveSourceSetting = false;
0355 
0356     auto choice = (PasteFormatBehaviour)cfg.pasteFormat(false);
0357 
0358     const QImage qimage = [&]() {
0359         QImage qimage = getImageFromMimeData(cbData);
0360 
0361         if (qimage.isNull() && useClipboardFallback) {
0362             qimage = d->clipboard->image();
0363         }
0364 
0365         return qimage;
0366     }();
0367 
0368     if (!qimage.isNull() || cbData->hasUrls()) {
0369         const auto &urls = cbData->urls();
0370 
0371         bool local = false;
0372         bool remote = false;
0373         bool isURI = false;
0374 
0375         std::for_each(urls.constBegin(), urls.constEnd(), [&](const QUrl &url) {
0376             local |= url.isLocalFile();
0377             remote |= !url.isLocalFile();
0378             isURI |= url.scheme() == "data";
0379         });
0380 
0381         const bool hasMultipleFormatsAvailable = (remote && local)
0382                 || (remote && !qimage.isNull()) || (local && !qimage.isNull());
0383 
0384         const bool defaultOptionUnavailable =
0385                 (!remote && choice == PASTE_FORMAT_DOWNLOAD)
0386                 || (!local && choice == PASTE_FORMAT_LOCAL)
0387                 || (qimage.isNull() && choice == PASTE_FORMAT_CLIP);
0388 
0389         dbgUI << "Incoming paste event:";
0390         dbgUI << "\tHas attached bitmap:" << cbData->hasImage();
0391         dbgUI << "\tHas local images:" << local;
0392         dbgUI << "\tHas remote images:" << remote;
0393         dbgUI << "\tHas multiple formats:" << hasMultipleFormatsAvailable;
0394         dbgUI << "\tDefault source preference" << choice;
0395         dbgUI << "\tDefault source available:" << !defaultOptionUnavailable;
0396         dbgUI << "\tIs data URI:" << isURI;
0397 
0398         if (hasMultipleFormatsAvailable && choice == PASTE_FORMAT_ASK && !isURI) {
0399             KisDlgPasteFormat dlg(qApp->activeWindow());
0400 
0401             dlg.setSourceAvailable(PASTE_FORMAT_DOWNLOAD, remote);
0402             dlg.setSourceAvailable(PASTE_FORMAT_LOCAL, local);
0403             dlg.setSourceAvailable(PASTE_FORMAT_CLIP, !qimage.isNull());
0404 
0405             if (dlg.exec() != KoDialog::Accepted) {
0406                 return {false, PASTE_FORMAT_ASK};
0407             };
0408 
0409             choice = dlg.source();
0410 
0411             saveSourceSetting = dlg.remember();
0412         } else if (defaultOptionUnavailable || choice == PASTE_FORMAT_ASK) {
0413             if (remote) {
0414                 choice = PASTE_FORMAT_DOWNLOAD;
0415             } else if (local) {
0416                 choice = PASTE_FORMAT_LOCAL;
0417             } else if (!qimage.isNull()) {
0418                 choice = PASTE_FORMAT_CLIP;
0419             } else {
0420                 return {false, PASTE_FORMAT_ASK};
0421             }
0422         } else if (isURI) {
0423             choice = PASTE_FORMAT_DOWNLOAD;
0424         }
0425     }
0426 
0427     if (saveSourceSetting) {
0428         cfg.setPasteFormat(choice);
0429     }
0430 
0431     dbgUI << "Selected source for the paste:" << choice;
0432 
0433     return {true, choice};
0434 }
0435 
0436 KisPaintDeviceSP KisClipboard::clipFromBoardContents(const QMimeData *cbData,
0437                                                      const QRect &imageBounds,
0438                                                      bool showPopup,
0439                                                      int pasteBehaviourOverride,
0440                                                      bool useClipboardFallback,
0441                                                      QPair<bool, PasteFormatBehaviour> source) const
0442 {
0443     if (!cbData) {
0444         return nullptr;
0445     }
0446 
0447     KisPaintDeviceSP clip;
0448 
0449     PasteFormatBehaviour choice = PASTE_FORMAT_ASK;
0450 
0451     if (!source.first) {
0452         choice = askUserForSource(cbData).second;
0453     } else {
0454         choice = source.second;
0455     }
0456 
0457     if (choice == PASTE_FORMAT_CLIP) {
0458         const QImage qimage = [&]() {
0459             QImage qimage = getImageFromMimeData(cbData);
0460 
0461             if (qimage.isNull() && useClipboardFallback) {
0462                 qimage = d->clipboard->image();
0463             }
0464 
0465             return qimage;
0466         }();
0467 
0468         KIS_SAFE_ASSERT_RECOVER(!qimage.isNull())
0469         {
0470             warnKrita << "Clipboard was cleared before loading image";
0471             return nullptr;
0472         }
0473 
0474         int behaviour = pasteBehaviourOverride;
0475         bool saveColorSetting = false;
0476 
0477         KisConfig cfg(true);
0478 
0479         if (pasteBehaviourOverride == -1) {
0480             behaviour = cfg.pasteBehaviour();
0481         }
0482 
0483         if (behaviour == PASTE_ASK && showPopup) {
0484             // Ask user each time.
0485             KisDlgMissingColorProfile dlg(qApp->activeWindow());
0486 
0487             if (dlg.exec() != QDialog::Accepted) {
0488                 return nullptr;
0489             }
0490 
0491             behaviour = dlg.source();
0492 
0493             saveColorSetting = dlg.remember(); // should we save this option to the config for next time?
0494         }
0495 
0496         const KoColorSpace *cs = nullptr;
0497         const KoColorProfile *profile = nullptr;
0498         if (!profile && behaviour == PASTE_ASSUME_MONITOR)
0499             profile = cfg.displayProfile(QApplication::desktop()->screenNumber(QApplication::activeWindow()));
0500 
0501         cs = KoColorSpaceRegistry::instance()->rgb8(profile);
0502         if (!cs) {
0503             cs = KoColorSpaceRegistry::instance()->rgb8();
0504             profile = cs->profile();
0505         }
0506 
0507         clip = new KisPaintDevice(cs);
0508         Q_CHECK_PTR(clip);
0509         clip->convertFromQImage(qimage, profile);
0510 
0511         // save the user's selection to the configuration if the option is checked
0512         if (saveColorSetting) {
0513             cfg.setPasteBehaviour(behaviour);
0514         }
0515     } else {
0516         const auto &urls = cbData->urls();
0517         const auto url = std::find_if(urls.constBegin(), urls.constEnd(), [&](const QUrl &url) {
0518             if (choice == PASTE_FORMAT_DOWNLOAD) {
0519                 return !url.isLocalFile();
0520             } else if (choice == PASTE_FORMAT_LOCAL) {
0521                 return url.isLocalFile();
0522             } else {
0523                 return false;
0524             }
0525         });
0526 
0527         if (url != urls.constEnd()) {
0528             clip = fetchImageByURL(*url);
0529         }
0530     }
0531 
0532     if (clip && !imageBounds.isEmpty()) {
0533         QRect clipBounds = clip->exactBounds();
0534         QPoint diff = imageBounds.center() - clipBounds.center();
0535         clip->setX(diff.x());
0536         clip->setY(diff.y());
0537     }
0538 
0539     return clip;
0540 }
0541 
0542 void KisClipboard::clipboardDataChanged()
0543 {
0544     if (!d->pushedClipboard) {
0545         const QMimeData *cbData = d->clipboard->mimeData();
0546         d->hasClip = d->clipboard->mimeData()->hasImage()
0547                 || (cbData && cbData->hasFormat("application/x-krita-selection"));
0548     }
0549     d->pushedClipboard = false;
0550     emit clipChanged();
0551 }
0552 
0553 bool KisClipboard::hasClip() const
0554 {
0555     return d->hasClip;
0556 }
0557 
0558 QSize KisClipboard::clipSize() const
0559 {
0560     const auto mimeType = QByteArrayLiteral("application/x-krita-selection");
0561     const QMimeData *cbData = d->clipboard->mimeData();
0562 
0563     KisPaintDeviceSP clip;
0564 
0565     if (cbData && cbData->hasFormat(mimeType)) {
0566         QByteArray encodedData = cbData->data(mimeType);
0567         QBuffer buffer(&encodedData);
0568         QScopedPointer<KoStore> store(KoStore::createStore(&buffer, KoStore::Read, mimeType));
0569         const KoColorProfile *profile = 0;
0570         QString csDepth, csModel;
0571 
0572         // ColorSpace id of layer data
0573         if (store->hasFile("colormodel")) {
0574             store->open("colormodel");
0575             csModel = QString(store->read(store->size()));
0576             store->close();
0577         }
0578 
0579         if (store->hasFile("colordepth")) {
0580             store->open("colordepth");
0581             csDepth = QString(store->read(store->size()));
0582             store->close();
0583         }
0584 
0585         if (store->hasFile("profile.icc")) {
0586             QByteArray data;
0587             store->open("profile.icc");
0588             data = store->read(store->size());
0589             store->close();
0590             profile = KoColorSpaceRegistry::instance()->createColorProfile(csModel, csDepth, data);
0591         }
0592 
0593         const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(csModel, csDepth, profile);
0594         if (!cs) {
0595             cs = KoColorSpaceRegistry::instance()->rgb8();
0596         }
0597         clip = new KisPaintDevice(cs);
0598 
0599         if (store->hasFile("layerdata")) {
0600             store->open("layerdata");
0601             clip->read(store->device());
0602             store->close();
0603         }
0604 
0605         return clip->exactBounds().size();
0606     } else {
0607         if (d->clipboard->mimeData()->hasImage()) {
0608             QImage qimage = d->clipboard->image();
0609             return qimage.size();
0610         }
0611     }
0612     return QSize();
0613 }
0614 
0615 void KisClipboard::setLayers(KisNodeList nodes, KisImageSP image, bool forceCopy)
0616 {
0617     /**
0618      * See a comment in KisMimeData::deepCopyNodes()
0619      */
0620     QMimeData *data = KisMimeData::mimeForLayersDeepCopy(nodes, image, forceCopy);
0621     if (!data)
0622         return;
0623 
0624     d->clipboard->setMimeData(data);
0625 }
0626 
0627 bool KisClipboard::hasLayers() const
0628 {
0629     const QByteArray mimeType = QByteArrayLiteral("application/x-krita-node-internal-pointer");
0630     return d->clipboard->mimeData()->hasFormat(mimeType);
0631 }
0632 
0633 bool KisClipboard::hasLayerStyles() const
0634 {
0635     // NOTE: please don't disable the paste action based on the
0636     //       result of this function, because we allow pasting
0637     //       of the layer styles as 'text/plain'
0638 
0639     return d->clipboard->mimeData()->hasFormat("application/x-krita-layer-style");
0640 }
0641 
0642 const QMimeData *KisClipboard::layersMimeData() const
0643 {
0644     const QMimeData *cbData = d->clipboard->mimeData();
0645     return cbData->hasFormat("application/x-krita-node-internal-pointer") ? cbData : 0;
0646 }
0647 
0648 bool KisClipboard::hasUrls() const
0649 {
0650     return d->clipboard->mimeData()->hasUrls();
0651 }
0652 
0653 QImage KisClipboard::getImageFromMimeData(const QMimeData *cbData) const
0654 {
0655     static const QList<ClipboardImageFormat> supportedFormats = {
0656         {{"image/png"}, "PNG"},
0657         {{"image/tiff"}, "TIFF"},
0658         {{"image/bmp", "image/x-bmp", "image/x-MS-bmp", "image/x-win-bitmap"}, "BMP"}};
0659 
0660     QImage image;
0661     QSet<QString> clipboardMimeTypes;
0662 
0663     Q_FOREACH (const QString &format, cbData->formats()) {
0664         clipboardMimeTypes << format;
0665     }
0666 
0667     Q_FOREACH (const ClipboardImageFormat &item, supportedFormats) {
0668         const QSet<QString> &intersection = item.mimeTypes & clipboardMimeTypes;
0669         if (intersection.isEmpty()) {
0670             continue;
0671         }
0672 
0673         const QString &format = *intersection.constBegin();
0674         const QByteArray &imageData = cbData->data(format);
0675         if (imageData.isEmpty()) {
0676             continue;
0677         }
0678 
0679         if (image.loadFromData(imageData, item.format.toLatin1())) {
0680             break;
0681         }
0682     }
0683 
0684     if (image.isNull() && cbData->hasImage()) {
0685         image = qvariant_cast<QImage>(cbData->imageData());
0686     }
0687 
0688     return image;
0689 }
0690 
0691 KisPaintDeviceSP KisClipboard::fetchImageByURL(const QUrl &originalUrl) const
0692 {
0693     KisPaintDeviceSP result;
0694     QUrl url(originalUrl);
0695     QScopedPointer<QTemporaryFile> tmp;
0696 
0697     if (!originalUrl.isLocalFile()) {
0698         tmp.reset(new QTemporaryFile());
0699         tmp->setAutoRemove(true);
0700 
0701         // download the file and substitute the url
0702         KisRemoteFileFetcher fetcher;
0703 
0704         if (!fetcher.fetchFile(originalUrl, tmp.data())) {
0705             qWarning() << "Fetching" << originalUrl << "failed";
0706             return result;
0707         }
0708         url = QUrl::fromLocalFile(tmp->fileName());
0709     }
0710 
0711     if (url.isLocalFile()) {
0712         QFileInfo fileInfo(url.toLocalFile());
0713 
0714         QString type = KisMimeDatabase::mimeTypeForFile(url.toLocalFile());
0715         QStringList mimes = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import);
0716 
0717         if (!mimes.contains(type)) {
0718             QString msg = KisImportExportErrorCode(ImportExportCodes::FileFormatNotSupported).errorMessage();
0719             QMessageBox::warning(KisPart::instance()->currentMainwindow(),
0720                                  i18nc("@title:window", "Krita"),
0721                                  i18n("Could not open %2.\nReason: %1.", msg, url.toDisplayString()));
0722             return result;
0723         }
0724 
0725         QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
0726 
0727         if (doc->openPath(url.toLocalFile(), KisDocument::DontAddToRecent)) {
0728             // Wait for required updates, if any. BUG: 448256
0729             KisLayerUtils::forceAllDelayedNodesUpdate(doc->image()->root());
0730             doc->image()->waitForDone();
0731             result = new KisPaintDevice(*doc->image()->projection());
0732         } else {
0733             qWarning() << "Failed to import file" << url.toLocalFile();
0734         }
0735     }
0736 
0737     return result;
0738 }