Permalink
Browse files

Allow opening both map and tileset files

The FileFormat::supportsFile of the TMX and JSON formats was extended a
bit, to allow using it on .xml or .json files that may be either a map
or a tileset.
  • Loading branch information...
bjorn committed Mar 6, 2016
1 parent 4371068 commit 137949b9478314d98c1c5ec9998dea99bc33545d
View
@@ -101,6 +101,8 @@ class TILEDSHARED_EXPORT FileFormat : public QObject
*/
class TILEDSHARED_EXPORT MapFormat : public FileFormat
{
Q_OBJECT
public:
explicit MapFormat(QObject *parent = nullptr)
: FileFormat(parent)
@@ -49,6 +49,7 @@ QVariant MapToVariantConverter::toVariant(const Map *map, const QDir &mapDir)
QVariantMap mapVariant;
mapVariant[QLatin1String("type")] = QLatin1String("map");
mapVariant[QLatin1String("version")] = 1.0;
mapVariant[QLatin1String("orientation")] = orientationToString(map->orientation());
mapVariant[QLatin1String("renderorder")] = renderOrderToString(map->renderOrder());
@@ -127,6 +128,10 @@ QVariant MapToVariantConverter::toVariant(const Tileset *tileset,
return tilesetVariant;
}
// Include a 'type' property if we are writing the tileset to its own file
if (firstGid == 0)
tilesetVariant[QLatin1String("type")] = QLatin1String("tileset");
tilesetVariant[QLatin1String("name")] = tileset->name();
tilesetVariant[QLatin1String("tilewidth")] = tileset->tileWidth();
tilesetVariant[QLatin1String("tileheight")] = tileset->tileHeight();
@@ -41,6 +41,8 @@ namespace Tiled {
*/
class TILEDSHARED_EXPORT TilesetFormat : public FileFormat
{
Q_OBJECT
public:
explicit TilesetFormat(QObject *parent = nullptr)
: FileFormat(parent)
@@ -28,6 +28,8 @@
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSaveFile>
#include <QTextStream>
@@ -147,10 +149,41 @@ QString JsonMapFormat::nameFilter() const
bool JsonMapFormat::supportsFile(const QString &fileName) const
{
if (mSubFormat == Json)
return fileName.endsWith(QLatin1String(".json"), Qt::CaseInsensitive);
else
return fileName.endsWith(QLatin1String(".js"), Qt::CaseInsensitive);
if (mSubFormat == Json) {
if (!fileName.endsWith(QLatin1String(".json"), Qt::CaseInsensitive))
return false;
} else {
if (!fileName.endsWith(QLatin1String(".js"), Qt::CaseInsensitive))
return false;
}
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QByteArray contents = file.readAll();
if (mSubFormat == JavaScript && contents.size() > 0 && contents[0] != '{') {
// Scan past JSONP prefix; look for an open curly at the start of the line
int i = contents.indexOf(QLatin1String("\n{"));
if (i > 0) {
contents.remove(0, i);
contents = contents.trimmed(); // potential trailing whitespace
if (contents.endsWith(';')) contents.chop(1);
if (contents.endsWith(')')) contents.chop(1);
}
}
const QJsonObject object = QJsonDocument::fromJson(contents).object();
// This is a good indication, but not present in older map files
if (object.value(QLatin1String("type")).toString() == QLatin1String("map"))
return true;
// Guess based on expected property
if (object.contains(QLatin1String("orientation")))
return true;
}
return false;
}
QString JsonMapFormat::errorString() const
@@ -199,7 +232,25 @@ Tiled::SharedTileset JsonTilesetFormat::read(const QString &fileName)
bool JsonTilesetFormat::supportsFile(const QString &fileName) const
{
return fileName.endsWith(QLatin1String(".json"), Qt::CaseInsensitive);
if (fileName.endsWith(QLatin1String(".json"), Qt::CaseInsensitive)) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
const QJsonObject object = QJsonDocument::fromJson(file.readAll()).object();
// This is a good indication, but not present in older external tilesets
if (object.value(QLatin1String("type")).toString() == QLatin1String("tileset"))
return true;
// Guess based on some expected properties
if (object.contains(QLatin1String("name")) &&
object.contains(QLatin1String("tilewidth")) &&
object.contains(QLatin1String("tileheight")))
return true;
}
}
return false;
}
bool JsonTilesetFormat::write(const Tiled::Tileset &tileset,
View
@@ -104,6 +104,8 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags)
, mObjectTypesEditor(new ObjectTypesEditor(this))
, mAutomappingManager(new AutomappingManager(this))
, mDocumentManager(DocumentManager::instance())
, mTmxMapFormat(new TmxMapFormat(this))
, mTsxTilesetFormat(new TsxTilesetFormat(this))
{
mUi->setupUi(this);
@@ -112,6 +114,9 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags)
setCentralWidget(mDocumentManager->widget());
PluginManager::addObject(mTmxMapFormat);
PluginManager::addObject(mTsxTilesetFormat);
#ifdef Q_OS_MAC
MacSupport::addFullscreen(this);
#endif
@@ -469,6 +474,9 @@ MainWindow::~MainWindow()
// delete mTileStampManager;
// mTileStampManager = nullptr;
PluginManager::removeObject(mTmxMapFormat);
PluginManager::removeObject(mTsxTilesetFormat);
DocumentManager::deleteInstance();
TilesetManager::deleteInstance();
Preferences::deleteInstance();
@@ -549,8 +557,7 @@ void MainWindow::newMap()
mDocumentManager->addDocument(mapDocument);
}
bool MainWindow::openFile(const QString &fileName,
FileFormat *format)
bool MainWindow::openFile(const QString &fileName, FileFormat *fileFormat)
{
if (fileName.isEmpty())
return false;
@@ -562,18 +569,44 @@ bool MainWindow::openFile(const QString &fileName,
return true;
}
// todo: determine whether it's a tileset or a map
// ... impossible since sometimes it's just XML or JSON!?
if (!fileFormat) {
// Try to find a plugin that implements support for this format
auto formats = PluginManager::objects<FileFormat>();
for (FileFormat *format : formats) {
if (format->supportsFile(fileName)) {
fileFormat = format;
break;
}
}
}
if (!fileFormat) {
QMessageBox::critical(this, tr("Error Opening File"), tr("Unrecognized file format"));
return false;
}
QString error;
MapDocument *mapDocument = MapDocument::load(fileName, qobject_cast<MapFormat*>(format), &error);
if (!mapDocument) {
QMessageBox::critical(this, tr("Error Opening Map"), error);
Document *document = nullptr;
if (MapFormat *mapFormat = dynamic_cast<MapFormat*>(fileFormat)) {
document = MapDocument::load(fileName, mapFormat, &error);
} else if (TilesetFormat *tilesetFormat = dynamic_cast<TilesetFormat*>(fileFormat)) {
SharedTileset tileset = tilesetFormat->read(fileName);
if (tileset.isNull())
error = tilesetFormat->errorString();
else
document = new TilesetDocument(tileset, fileName);
}
if (!document) {
QMessageBox::critical(this, tr("Error Opening File"), error);
return false;
}
mDocumentManager->addDocument(mapDocument);
mDocumentManager->checkTilesetColumns(mapDocument);
mDocumentManager->addDocument(document);
if (MapDocument *mapDocument = qobject_cast<MapDocument*>(document))
mDocumentManager->checkTilesetColumns(mapDocument);
setRecentFile(fileName);
return true;
@@ -657,9 +690,6 @@ void MainWindow::openFile()
{
QString filter = tr("All Files (*)");
QString selectedFilter = filter;
filter += QLatin1String(";;");
filter += TmxMapFormat().nameFilter();
filter += TsxTilesetFormat().nameFilter();
FormatHelper<FileFormat> helper(FileFormat::Read, filter);
View
@@ -54,6 +54,8 @@ class MapDocumentActionHandler;
class MapScene;
class MapView;
class ObjectTypesEditor;
class TmxMapFormat;
class TsxTilesetFormat;
/**
* The main editor window.
@@ -80,7 +82,7 @@ class MainWindow : public QMainWindow
*
* @return whether the file was successfully opened
*/
bool openFile(const QString &fileName, FileFormat *format);
bool openFile(const QString &fileName, FileFormat *fileFormat);
/**
* Attempt to open the previously opened file.
@@ -213,6 +215,9 @@ private slots:
AutomappingManager *mAutomappingManager;
DocumentManager *mDocumentManager;
TmxMapFormat *mTmxMapFormat;
TsxTilesetFormat *mTsxTilesetFormat;
};
} // namespace Internal
View
@@ -42,7 +42,6 @@
#include "offsetlayer.h"
#include "orthogonalrenderer.h"
#include "painttilelayer.h"
#include "pluginmanager.h"
#include "resizemap.h"
#include "resizetilelayer.h"
#include "rotatemapobject.h"
@@ -153,32 +152,11 @@ MapDocument *MapDocument::load(const QString &fileName,
MapFormat *mapFormat,
QString *error)
{
TmxMapFormat tmxMapFormat;
if (!mapFormat && !tmxMapFormat.supportsFile(fileName)) {
// Try to find a plugin that implements support for this format
auto formats = PluginManager::objects<MapFormat>();
for (MapFormat *format : formats) {
if (format->supportsFile(fileName)) {
mapFormat = format;
break;
}
}
}
Map *map = nullptr;
QString errorString;
if (mapFormat) {
map = mapFormat->read(fileName);
errorString = mapFormat->errorString();
} else {
map = tmxMapFormat.read(fileName);
errorString = tmxMapFormat.errorString();
}
Map *map = mapFormat->read(fileName);
if (!map) {
if (error)
*error = errorString;
*error = mapFormat->errorString();;
return nullptr;
}
@@ -188,6 +166,7 @@ MapDocument *MapDocument::load(const QString &fileName,
if (mapFormat->hasCapabilities(MapFormat::Write))
mapDocument->setWriterFormat(mapFormat);
}
return mapDocument;
}
View
@@ -103,7 +103,7 @@ class MapDocument : public Document
* on error and sets the \a error message.
*/
static MapDocument *load(const QString &fileName,
MapFormat *mapFormat = nullptr,
MapFormat *mapFormat,
QString *error = nullptr);
QString lastExportFileName() const;
View
@@ -58,6 +58,11 @@ class EditorMapReader : public MapReader
} // anonymous namespace
TmxMapFormat::TmxMapFormat(QObject *parent)
: MapFormat(parent)
{
}
Map *TmxMapFormat::read(const QString &fileName)
{
mError.clear();
@@ -113,6 +118,31 @@ Map *TmxMapFormat::fromByteArray(const QByteArray &data)
return map;
}
bool TmxMapFormat::supportsFile(const QString &fileName) const
{
if (fileName.endsWith(QLatin1String(".tmx"), Qt::CaseInsensitive))
return true;
if (fileName.endsWith(QLatin1String(".xml"), Qt::CaseInsensitive)) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QXmlStreamReader xml;
xml.setDevice(&file);
if (xml.readNextStartElement() && xml.name() == QLatin1String("map"))
return true;
}
}
return false;
}
TsxTilesetFormat::TsxTilesetFormat(QObject *parent)
: TilesetFormat(parent)
{
}
SharedTileset TsxTilesetFormat::read(const QString &fileName)
{
@@ -141,3 +171,23 @@ bool TsxTilesetFormat::write(const Tileset &tileset, const QString &fileName)
return result;
}
bool TsxTilesetFormat::supportsFile(const QString &fileName) const
{
if (fileName.endsWith(QLatin1String(".tsx"), Qt::CaseInsensitive))
return true;
if (fileName.endsWith(QLatin1String(".xml"), Qt::CaseInsensitive)) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QXmlStreamReader xml;
xml.setDevice(&file);
if (xml.readNextStartElement() && xml.name() == QLatin1String("tileset"))
return true;
}
}
return false;
}
Oops, something went wrong.

0 comments on commit 137949b

Please sign in to comment.