/* * editabletile.cpp * Copyright 2019, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "editabletile.h" #include "changetile.h" #include "changetileanimation.h" #include "changetileimagesource.h" #include "changetileobjectgroup.h" #include "changetileprobability.h" #include "editablemanager.h" #include "editableobjectgroup.h" #include "editabletileset.h" #include "imagecache.h" #include "objectgroup.h" #include "scriptimage.h" #include "scriptmanager.h" #include #include namespace Tiled { EditableTile::EditableTile(EditableTileset *tileset, Tile *tile, QObject *parent) : EditableObject(tileset, tile, parent) { } EditableTile::~EditableTile() { EditableManager::instance().mEditableTiles.remove(tile()); } EditableObjectGroup *EditableTile::objectGroup() const { if (!mAttachedObjectGroup) { mAttachedObjectGroup = tile()->objectGroup(); } else { Q_ASSERT(mAttachedObjectGroup == tile()->objectGroup()); } return EditableManager::instance().editableObjectGroup(asset(), mAttachedObjectGroup); } QJSValue EditableTile::frames() const { QJSEngine *engine = ScriptManager::instance().engine(); const auto &frames = tile()->frames(); QJSValue array = engine->newArray(frames.size()); for (int i = 0; i < frames.size(); ++i) { QJSValue frameObject = engine->newObject(); frameObject.setProperty(QStringLiteral("tileId"), frames.at(i).tileId); frameObject.setProperty(QStringLiteral("duration"), frames.at(i).duration); array.setProperty(i, frameObject); } return array; } EditableTileset *EditableTile::tileset() const { return static_cast(asset()); } void EditableTile::setImage(ScriptImage *image) { // WARNING: This function has no undo! tile()->setImage(QPixmap::fromImage(image->image())); } void EditableTile::detach() { Q_ASSERT(tileset()); auto &editableManager = EditableManager::instance(); editableManager.mEditableTiles.remove(tile()); setAsset(nullptr); mDetachedTile.reset(tile()->clone(nullptr)); setObject(mDetachedTile.get()); editableManager.mEditableTiles.insert(tile(), this); // Move over any attached editable object group if (auto editable = editableManager.find(mAttachedObjectGroup)) { editableManager.mEditableLayers.remove(mAttachedObjectGroup); editable->setAsset(nullptr); editable->setObject(tile()->objectGroup()); editableManager.mEditableLayers.insert(tile()->objectGroup(), editable); mAttachedObjectGroup = tile()->objectGroup(); } else { mAttachedObjectGroup = nullptr; } } void EditableTile::attach(EditableTileset *tileset) { Q_ASSERT(!asset() && tileset); setAsset(tileset); mDetachedTile.release(); } void EditableTile::detachObjectGroup() { if (auto editable = EditableManager::instance().find(mAttachedObjectGroup)) editable->detach(); mAttachedObjectGroup = nullptr; } void EditableTile::setType(const QString &type) { if (TilesetDocument *doc = tilesetDocument()) asset()->push(new ChangeTileType(doc, { tile() }, type)); else if (!checkReadOnly()) tile()->setType(type); } void EditableTile::setImageFileName(const QString &fileName) { if (TilesetDocument *doc = tilesetDocument()) { if (!tileset()->tileset()->isCollection()) { ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Tileset needs to be an image collection")); return; } asset()->push(new ChangeTileImageSource(doc, tile(), QUrl::fromLocalFile(fileName))); } else if (!checkReadOnly()) { tile()->setImage(ImageCache::loadPixmap(fileName)); tile()->setImageSource(QUrl::fromLocalFile(fileName)); } } void EditableTile::setProbability(qreal probability) { if (TilesetDocument *doc = tilesetDocument()) asset()->push(new ChangeTileProbability(doc, { tile() }, probability)); else if (!checkReadOnly()) tile()->setProbability(probability); } void EditableTile::setObjectGroup(EditableObjectGroup *editableObjectGroup) { if (!editableObjectGroup) { ScriptManager::instance().throwNullArgError(0); return; } if (!editableObjectGroup->isOwning()) { ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "ObjectGroup is in use")); return; } if (checkReadOnly()) return; std::unique_ptr og(static_cast(editableObjectGroup->release())); if (TilesetDocument *doc = tilesetDocument()) { asset()->push(new ChangeTileObjectGroup(doc, tile(), std::move(og))); } else { detachObjectGroup(); tile()->setObjectGroup(std::move(og)); } Q_ASSERT(editableObjectGroup->objectGroup() == tile()->objectGroup()); Q_ASSERT(!editableObjectGroup->isOwning()); } void EditableTile::setFrames(QJSValue value) { if (!value.isArray()) { ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Array expected")); return; } if (checkReadOnly()) return; QVector frames; const int length = value.property(QStringLiteral("length")).toInt(); for (int i = 0; i < length; ++i) { const auto frameValue = value.property(i); const Frame frame { frameValue.property(QStringLiteral("tileId")).toInt(), frameValue.property(QStringLiteral("duration")).toInt() }; if (frame.tileId < 0 || frame.duration < 0) { ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Invalid value (negative)")); return; } frames.append(frame); } if (TilesetDocument *doc = tilesetDocument()) asset()->push(new ChangeTileAnimation(doc, tile(), frames)); else tile()->setFrames(frames); } TilesetDocument *EditableTile::tilesetDocument() const { return tileset() ? tileset()->tilesetDocument() : nullptr; } } // namespace Tiled #include "moc_editabletile.cpp"