1 /*
2  * editableobject.cpp
3  * Copyright 2019, Thorbjørn Lindeijer <bjorn@lindeijer.nl>
4  *
5  * This file is part of Tiled.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "editableobject.h"
22 
23 #include "changeproperties.h"
24 #include "editableasset.h"
25 #include "editablemanager.h"
26 #include "editablemapobject.h"
27 #include "map.h"
28 #include "mapobject.h"
29 #include "objectgroup.h"
30 #include "scriptmanager.h"
31 
32 #include <QCoreApplication>
33 
34 namespace Tiled {
35 
EditableObject(EditableAsset * asset,Object * object,QObject * parent)36 EditableObject::EditableObject(EditableAsset *asset,
37                                Object *object,
38                                QObject *parent)
39     : QObject(parent)
40     , mAsset(asset)
41     , mObject(object)
42 {
43 }
44 
isReadOnly() const45 bool EditableObject::isReadOnly() const
46 {
47     return asset() && asset()->isReadOnly();
48 }
49 
setProperty(const QString & name,const QVariant & value)50 void EditableObject::setProperty(const QString &name, const QVariant &value)
51 {
52     if (Document *doc = document())
53         asset()->push(new SetProperty(doc, { mObject }, name, fromScript(value)));
54     else
55         mObject->setProperty(name, fromScript(value));
56 }
57 
setProperties(const QVariantMap & properties)58 void EditableObject::setProperties(const QVariantMap &properties)
59 {
60     if (Document *doc = document())
61         asset()->push(new ChangeProperties(doc, QString(), mObject, fromScript(properties)));
62     else
63         mObject->setProperties(fromScript(properties));
64 }
65 
removeProperty(const QString & name)66 void EditableObject::removeProperty(const QString &name)
67 {
68     if (Document *doc = document())
69         asset()->push(new RemoveProperty(doc, { mObject }, name));
70     else if (!checkReadOnly())
71         mObject->removeProperty(name);
72 }
73 
document() const74 Document *EditableObject::document() const
75 {
76     return asset() ? asset()->document() : nullptr;
77 }
78 
checkReadOnly() const79 bool EditableObject::checkReadOnly() const
80 {
81     if (isReadOnly()) {
82         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Asset is read-only"));
83         return true;
84     }
85     return false;
86 }
87 
mapForObject(Object * object)88 static Map *mapForObject(Object *object)
89 {
90     if (!object)
91         return nullptr;
92 
93     switch (object->typeId()) {
94     case Object::LayerType:
95         return static_cast<Layer*>(object)->map();
96     case Object::MapObjectType:
97         return static_cast<MapObject*>(object)->map();
98     case Object::MapType:
99         return static_cast<Map*>(object);
100     case Object::ObjectTemplateType:
101     case Object::TilesetType:
102     case Object::TileType:
103     case Object::WangSetType:
104     case Object::WangColorType:
105         break;
106     }
107     return nullptr;
108 }
109 
toScript(const QVariant & value) const110 QVariant EditableObject::toScript(const QVariant &value) const
111 {
112     const int type = value.userType();
113 
114     if (type == QMetaType::QVariantMap)
115         return toScript(value.toMap());
116 
117     if (type == objectRefTypeId()) {
118         const auto ref = value.value<ObjectRef>();
119         MapObject *referencedObject = nullptr;
120 
121         if (auto map = mapForObject(object())) {
122             referencedObject = map->findObjectById(ref.id);
123         } else if (object()->typeId() == Object::MapObjectType) {
124             if (auto objectGroup = static_cast<MapObject*>(object())->objectGroup()) {
125                 for (auto mapObject : *objectGroup) {
126                     if (mapObject->id() == ref.id) {
127                         referencedObject = mapObject;
128                         break;
129                     }
130                 }
131             }
132         }
133 
134         if (referencedObject) {
135             auto editable = EditableManager::instance().editableMapObject(asset(), referencedObject);
136             return QVariant::fromValue(editable);
137         }
138     }
139 
140     return value;
141 }
142 
fromScript(const QVariant & value) const143 QVariant EditableObject::fromScript(const QVariant &value) const
144 {
145     if (value.userType() == QMetaType::QVariantMap)
146         return fromScript(value.toMap());
147 
148     if (auto editableMapObject = value.value<EditableMapObject*>())
149         return QVariant::fromValue(ObjectRef { editableMapObject->id() });
150 
151     return value;
152 }
153 
toScript(const QVariantMap & value) const154 QVariantMap EditableObject::toScript(const QVariantMap &value) const
155 {
156     QVariantMap converted(value);
157     for (auto i = converted.begin(); i != converted.end(); ++i)
158         i.value() = toScript(i.value());
159     return converted;
160 }
161 
fromScript(const QVariantMap & value) const162 QVariantMap EditableObject::fromScript(const QVariantMap &value) const
163 {
164     QVariantMap converted(value);
165     for (auto i = converted.begin(); i != converted.end(); ++i)
166         i.value() = fromScript(i.value());
167     return converted;
168 }
169 
170 } // namespace Tiled
171 
172 #include "moc_editableobject.cpp"
173