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 }