File indexing completed on 2024-05-19 04:07:53

0001 /*
0002     SPDX-FileCopyrightText: 2009-2011 Stefan Majewsky <majewsky@gmx.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "components.h"
0008 
0009 #include <QDir>
0010 #include <QFutureSynchronizer>
0011 #include <KConfigGroup>
0012 #include <KDesktopFile>
0013 #include <QTemporaryDir>
0014 
0015 Palapeli::DirectoryStorageComponent::DirectoryStorageComponent()
0016   : m_dir(new QTemporaryDir)
0017 {
0018 }
0019 
0020 Palapeli::DirectoryStorageComponent::~DirectoryStorageComponent()
0021 {
0022     m_dir->remove();
0023     delete m_dir;
0024 }
0025 
0026 QString Palapeli::DirectoryStorageComponent::directory() const
0027 {
0028     return m_dir->path() + QLatin1Char('/');
0029 }
0030 
0031 Palapeli::PuzzleComponent* Palapeli::DirectoryStorageComponent::cast(Palapeli::PuzzleComponent::Type type) const
0032 {
0033     QDir dir(m_dir->path());
0034     //load metadata from directory
0035     if (type == Metadata)
0036     {
0037         Palapeli::PuzzleMetadata metadata;
0038         KDesktopFile manifest(dir.absoluteFilePath(QStringLiteral("pala.desktop")));
0039         //read simple metadata
0040         metadata.name = manifest.readName();
0041         metadata.author = manifest.desktopGroup().readEntry("X-KDE-PluginInfo-Author", QString());
0042         metadata.comment = manifest.readComment();
0043         metadata.modifyProtection = manifest.group(QStringLiteral("Collection")).readEntry("ModifyProtection", false);
0044         //read image
0045         metadata.image.load(dir.absoluteFilePath(QStringLiteral("image.jpg")));
0046         metadata.thumbnail = metadata.image.scaled(Palapeli::PuzzleMetadata::ThumbnailBaseSize, Qt::KeepAspectRatio);
0047         //count pieces
0048         const QStringList keys = manifest.group(QStringLiteral("PieceOffsets")).keyList();
0049         metadata.pieceCount = 0;
0050         while (keys.contains(QString::number(metadata.pieceCount)))
0051             ++metadata.pieceCount;
0052         //done
0053         return new Palapeli::MetadataComponent(metadata);
0054     }
0055     //load contents from directory
0056     else if (type == Contents)
0057     {
0058         Palapeli::PuzzleContents contents;
0059         KDesktopFile manifest(dir.absoluteFilePath(QStringLiteral("pala.desktop")));
0060         contents.imageSize = manifest.group(QStringLiteral("Job")).readEntry("ImageSize", QSize());
0061         //load piece offsets and images
0062         KConfigGroup offsetGroup(&manifest, QStringLiteral("PieceOffsets"));
0063         const QStringList offsetKeys = offsetGroup.keyList();
0064         for (int i = 0; i < offsetKeys.count(); ++i)
0065         {
0066             const QString key = offsetKeys[i];
0067             bool ok = true;
0068             const int pieceIndex = key.toInt(&ok);
0069             if (!ok)
0070                 continue;
0071             contents.pieceOffsets[pieceIndex] = offsetGroup.readEntry(key, QPoint());
0072             contents.pieces[pieceIndex].load(dir.absoluteFilePath(key + QLatin1String(".png")));
0073         }
0074         //load relations
0075         KConfigGroup relationsGroup(&manifest, QStringLiteral("Relations"));
0076         for (int i = 0;; ++i)
0077         {
0078             QList<int> value = relationsGroup.readEntry(QString::number(i), QList<int>());
0079             if (value.size() < 2) //end of relations list
0080                 break;
0081             contents.relations << QPair<int, int>(value[0], value[1]);
0082         }
0083         //done
0084         return new Palapeli::ContentsComponent(contents);
0085     }
0086     //load creation context from directory
0087     else if (type == CreationContext)
0088     {
0089         puzzle()->get(Metadata);
0090         const Palapeli::PuzzleMetadata metadata = puzzle()->component<Palapeli::MetadataComponent>()->metadata;
0091         //initialize creation context from existing metadata
0092         Palapeli::PuzzleCreationContext creationContext;
0093         *((Palapeli::PuzzleMetadata*)&creationContext) = metadata;
0094         //load slicer configuration
0095         KDesktopFile manifest(dir.absoluteFilePath(QStringLiteral("pala.desktop")));
0096         KConfigGroup jobGroup(&manifest, QStringLiteral("Job"));
0097         creationContext.slicer = jobGroup.readEntry("Slicer", QString());
0098         creationContext.slicerMode = jobGroup.readEntry("SlicerMode", QByteArray());
0099         //all the other entries in jobGroup belong into slicerArgs
0100         QMap<QString, QString> args = jobGroup.entryMap();
0101         args.remove(QStringLiteral("Image"));
0102         args.remove(QStringLiteral("ImageSize"));
0103         args.remove(QStringLiteral("Slicer"));
0104         args.remove(QStringLiteral("SlicerMode"));
0105         QMapIterator<QString, QString> iter(args);
0106         while (iter.hasNext())
0107         {
0108             iter.next();
0109             creationContext.slicerArgs.insert(iter.key().toUtf8(), iter.value());
0110         }
0111         return new Palapeli::CreationContextComponent(creationContext);
0112     }
0113     //compress directory into file
0114     else if (type == DirectoryStorage)
0115         return Palapeli::DirectoryStorageComponent::fromData(puzzle());
0116     else
0117         return nullptr;
0118 }
0119 
0120 Palapeli::DirectoryStorageComponent* Palapeli::DirectoryStorageComponent::fromData(Palapeli::Puzzle* puzzle)
0121 {
0122     puzzle->get(Metadata);
0123     puzzle->get(Contents);
0124     puzzle->get(CreationContext);
0125 
0126     //retrieve data (only metadata and contents are totally necessary)
0127     const Palapeli::MetadataComponent* cMetadata = puzzle->component<Palapeli::MetadataComponent>();
0128     const Palapeli::ContentsComponent* cContents = puzzle->component<Palapeli::ContentsComponent>();
0129     if (!cMetadata || !cContents)
0130         return nullptr;
0131     const Palapeli::CreationContextComponent* cCreationContext = puzzle->component<Palapeli::CreationContextComponent>();
0132     const Palapeli::PuzzleMetadata metadata = cMetadata->metadata;
0133     const Palapeli::PuzzleContents contents = cContents->contents;
0134     //create cache
0135     Palapeli::DirectoryStorageComponent* cmp = new Palapeli::DirectoryStorageComponent;
0136     QDir dir(cmp->directory());
0137     //write manifest
0138     KConfig manifest(dir.absoluteFilePath(QStringLiteral("pala.desktop")));
0139     KConfigGroup mainGroup(&manifest, QStringLiteral("Desktop Entry"));
0140     mainGroup.writeEntry("Name", metadata.name);
0141     mainGroup.writeEntry("Comment", metadata.comment);
0142     mainGroup.writeEntry("X-KDE-PluginInfo-Author", metadata.author);
0143     mainGroup.writeEntry("Type", "X-Palapeli-Puzzle");
0144     KConfigGroup collectionGroup(&manifest, QStringLiteral("Collection"));
0145     collectionGroup.writeEntry("ModifyProtection", metadata.modifyProtection);
0146     KConfigGroup jobGroup(&manifest, QStringLiteral("Job"));
0147     jobGroup.writeEntry("ImageSize", contents.imageSize);
0148     if (cCreationContext)
0149     {
0150         const Palapeli::PuzzleCreationContext creationContext = cCreationContext->creationContext;
0151         jobGroup.writeEntry("Image", "kfiledialog:///palapeli/pseudopath"); //just a placeholder, to make sure that an "Image" key is available
0152         jobGroup.writeEntry("Slicer", creationContext.slicer);
0153         jobGroup.writeEntry("SlicerMode", creationContext.slicerMode);
0154         QMapIterator<QByteArray, QVariant> iterSlicerArgs(creationContext.slicerArgs);
0155         while (iterSlicerArgs.hasNext())
0156         {
0157             iterSlicerArgs.next();
0158             jobGroup.writeEntry(QString::fromUtf8(iterSlicerArgs.key()), iterSlicerArgs.value());
0159         }
0160     }
0161     //write pieces to cache
0162     QMapIterator<int, QImage> iterPieces(contents.pieces);
0163     while (iterPieces.hasNext())
0164     {
0165         const QString imagePath = dir.absoluteFilePath(QString::fromLatin1("%1.png").arg(iterPieces.next().key()));
0166         iterPieces.value().save(imagePath);
0167     }
0168     //write thumbnail into tempdir
0169     const QString imagePath = dir.absoluteFilePath(QStringLiteral("image.jpg"));
0170     metadata.image.save(imagePath);
0171     //write piece offsets into target manifest
0172     KConfigGroup offsetGroup(&manifest, QStringLiteral("PieceOffsets"));
0173     QMapIterator<int, QPoint> iterOffsets(contents.pieceOffsets);
0174     while (iterOffsets.hasNext())
0175     {
0176         iterOffsets.next();
0177         offsetGroup.writeEntry(QString::number(iterOffsets.key()), iterOffsets.value());
0178     }
0179     //write piece relations into target manifest
0180     KConfigGroup relationsGroup(&manifest, QStringLiteral("Relations"));
0181     for (int index = 0; index < contents.relations.count(); ++index)
0182     {
0183         const QPair<int, int> relation = contents.relations[index];
0184         relationsGroup.writeEntry(QString::number(index), QList<int>() << relation.first << relation.second);
0185     }
0186     //save manifest; done
0187     manifest.sync();
0188     return cmp;
0189 }