File indexing completed on 2024-04-28 04:18:52

0001 // vim: set tabstop=4 shiftwidth=4 expandtab
0002 /*
0003 Gwenview - A simple image viewer for KDE
0004 Copyright 2006 Aurelien Gateau <agateau@kde.org>
0005 
0006 This program is free software; you can redistribute it and/or
0007 modify it under the terms of the GNU General Public License
0008 as published by the Free Software Foundation; either version 2
0009 of the License, or (at your option) any later version.
0010 
0011 This program is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU General Public License for more details.
0015 
0016 You should have received a copy of the GNU General Public License
0017 along with this program; if not, write to the Free Software
0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0019 
0020 */
0021 #include "mimetypeutils.h"
0022 #include "mimetypeutils_p.h"
0023 
0024 // Qt
0025 #include <QFileInfo>
0026 #include <QImageReader>
0027 #include <QMimeData>
0028 #include <QMimeDatabase>
0029 #include <QUrl>
0030 
0031 // KF
0032 #include <KFileItem>
0033 #include <KIO/TransferJob>
0034 
0035 // Local
0036 #include "gwenview_lib_debug.h"
0037 #include <archiveutils.h>
0038 #include <gvdebug.h>
0039 #include <lib/document/documentfactory.h>
0040 
0041 namespace Gwenview
0042 {
0043 namespace MimeTypeUtils
0044 {
0045 static inline QString resolveAlias(const QString &name)
0046 {
0047     QMimeDatabase db;
0048     return db.mimeTypeForName(name).name();
0049 }
0050 
0051 static void resolveAliasInList(QStringList *list)
0052 {
0053     QStringList::Iterator it = list->begin(), end = list->end();
0054     for (; it != end; ++it) {
0055         *it = resolveAlias(*it);
0056     }
0057 }
0058 
0059 static inline QStringList rawMimeTypes()
0060 {
0061     // need to invent more intelligent way to whitelist raws
0062     return {QStringLiteral("image/x-nikon-nef"),
0063             QStringLiteral("image/x-nikon-nrw"),
0064             QStringLiteral("image/x-canon-cr2"),
0065             QStringLiteral("image/x-canon-crw"),
0066             QStringLiteral("image/x-pentax-pef"),
0067             QStringLiteral("image/x-adobe-dng"),
0068             QStringLiteral("image/x-sony-arw"),
0069             QStringLiteral("image/x-minolta-mrw"),
0070             QStringLiteral("image/x-panasonic-raw"),
0071             QStringLiteral("image/x-panasonic-raw2"),
0072             QStringLiteral("image/x-panasonic-rw"),
0073             QStringLiteral("image/x-panasonic-rw2"),
0074             QStringLiteral("image/x-samsung-srw"),
0075             QStringLiteral("image/x-olympus-orf"),
0076             QStringLiteral("image/x-fuji-raf"),
0077             QStringLiteral("image/x-kodak-dcr"),
0078             QStringLiteral("image/x-sigma-x3f")};
0079 }
0080 
0081 const QStringList &rasterImageMimeTypes()
0082 {
0083     static QStringList list;
0084     if (list.isEmpty()) {
0085         const auto supported = QImageReader::supportedMimeTypes();
0086         for (const auto &mime : supported) {
0087             const auto resolved = resolveAlias(QString::fromUtf8(mime));
0088             if (resolved.isEmpty()) {
0089                 qCWarning(GWENVIEW_LIB_LOG) << "Unresolved mime type " << mime;
0090             } else {
0091                 list << resolved;
0092             }
0093         }
0094         // We don't want svg images to be considered as raster images
0095         const QStringList svgImageMimeTypesList = svgImageMimeTypes();
0096         for (const QString &mimeType : svgImageMimeTypesList) {
0097             list.removeOne(mimeType);
0098         }
0099         for (const QString &rawMimetype : rawMimeTypes()) {
0100             const auto resolved = resolveAlias(rawMimetype);
0101             if (resolved.isEmpty()) {
0102                 qCWarning(GWENVIEW_LIB_LOG) << "Unresolved raw mime type " << rawMimetype;
0103             } else {
0104                 list << resolved;
0105             }
0106         }
0107     }
0108     return list;
0109 }
0110 
0111 const QStringList &svgImageMimeTypes()
0112 {
0113     static QStringList list;
0114     if (list.isEmpty()) {
0115         list << QStringLiteral("image/svg+xml") << QStringLiteral("image/svg+xml-compressed");
0116         resolveAliasInList(&list);
0117     }
0118     return list;
0119 }
0120 
0121 const QStringList &imageMimeTypes()
0122 {
0123     static QStringList list;
0124     if (list.isEmpty()) {
0125         list = rasterImageMimeTypes();
0126         list += svgImageMimeTypes();
0127     }
0128 
0129     return list;
0130 }
0131 
0132 QString urlMimeType(const QUrl &url)
0133 {
0134     if (url.isEmpty()) {
0135         return QStringLiteral("unknown");
0136     }
0137 
0138     QMimeDatabase db;
0139     return db.mimeTypeForUrl(url).name();
0140 }
0141 
0142 Kind mimeTypeKind(const QString &mimeType)
0143 {
0144     if (rasterImageMimeTypes().contains(mimeType)) {
0145         return KIND_RASTER_IMAGE;
0146     }
0147     if (svgImageMimeTypes().contains(mimeType)) {
0148         return KIND_SVG_IMAGE;
0149     }
0150     if (mimeType.startsWith(QLatin1String("video/"))) {
0151         return KIND_VIDEO;
0152     }
0153     if (mimeType.startsWith(QLatin1String("inode/directory"))) {
0154         return KIND_DIR;
0155     }
0156     if (!ArchiveUtils::protocolForMimeType(mimeType).isEmpty()) {
0157         return KIND_ARCHIVE;
0158     }
0159 
0160     return KIND_FILE;
0161 }
0162 
0163 Kind fileItemKind(const KFileItem &item)
0164 {
0165     GV_RETURN_VALUE_IF_FAIL(!item.isNull(), KIND_UNKNOWN);
0166     return mimeTypeKind(item.mimetype());
0167 }
0168 
0169 Kind urlKind(const QUrl &url)
0170 {
0171     return mimeTypeKind(urlMimeType(url));
0172 }
0173 
0174 QMimeData *selectionMimeData(const KFileItemList &selectedFiles, MimeTarget mimeTarget)
0175 {
0176     auto mimeData = new QMimeData;
0177 
0178     if (selectedFiles.count() == 1) {
0179         // When a single file is selected, there are a couple of cases:
0180         // - Pasting unmodified images: Set both image data and URL
0181         //   (since some apps only support either image data or URL)
0182         // - Dragging unmodified images: Only set URL
0183         //   (otherwise dragging to Chromium or the desktop fails, see https://phabricator.kde.org/D13249#300894)
0184         // - Dragging or pasting modified images: Only set image data
0185         //   (otherwise some apps prefer the URL, which would only contain the unmodified image)
0186 
0187         const QUrl url = selectedFiles.first().url();
0188         const MimeTypeUtils::Kind mimeKind = MimeTypeUtils::urlKind(url);
0189         bool documentIsModified = false;
0190 
0191         if (mimeKind == MimeTypeUtils::KIND_RASTER_IMAGE || mimeKind == MimeTypeUtils::KIND_SVG_IMAGE) {
0192             const Document::Ptr doc = DocumentFactory::instance()->load(url);
0193             doc->waitUntilLoaded();
0194             documentIsModified = doc->isModified();
0195 
0196             if (mimeTarget == ClipboardTarget || (mimeTarget == DropTarget && documentIsModified)) {
0197                 QString suggestedFileName;
0198 
0199                 if (mimeKind == MimeTypeUtils::KIND_RASTER_IMAGE) {
0200                     mimeData->setImageData(doc->image());
0201 
0202                     // Set the filename extension to PNG, as it is the first
0203                     // entry in the combobox when pasting to Dolphin
0204                     suggestedFileName = QFileInfo(url.fileName()).completeBaseName() + QStringLiteral(".png");
0205                 } else {
0206                     mimeData->setData(MimeTypeUtils::urlMimeType(url), doc->rawData());
0207                     suggestedFileName = url.fileName();
0208                 }
0209 
0210                 mimeData->setData(QStringLiteral("application/x-kde-suggestedfilename"), QFile::encodeName(suggestedFileName));
0211             }
0212         }
0213 
0214         if (!documentIsModified) {
0215             mimeData->setUrls({url});
0216         }
0217     } else {
0218         mimeData->setUrls(selectedFiles.urlList());
0219     }
0220 
0221     return mimeData;
0222 }
0223 
0224 DataAccumulator::DataAccumulator(KIO::TransferJob *job)
0225     : QObject()
0226     , mFinished(false)
0227 {
0228     connect(job, &KIO::TransferJob::data, this, &DataAccumulator::slotDataReceived);
0229     connect(job, &KJob::result, this, &DataAccumulator::slotFinished);
0230 }
0231 
0232 void DataAccumulator::slotDataReceived(KIO::Job *, const QByteArray &data)
0233 {
0234     mData += data;
0235 }
0236 
0237 void DataAccumulator::slotFinished()
0238 {
0239     mFinished = true;
0240 }
0241 
0242 } // namespace MimeTypeUtils
0243 } // namespace Gwenview
0244 
0245 #include "moc_mimetypeutils_p.cpp"