1 /*
2  * changemapobject.cpp
3  * Copyright 2009, Thorbjørn Lindeijer <thorbjorn@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 "changemapobject.h"
22 
23 #include "mapdocument.h"
24 #include "mapobjectmodel.h"
25 #include "objecttemplate.h"
26 
27 #include <QCoreApplication>
28 
29 #include "changeevents.h"
30 #include "qtcompat_p.h"
31 
32 using namespace Tiled;
33 
ChangeMapObject(Document * document,MapObject * mapObject,MapObject::Property property,const QVariant & value)34 ChangeMapObject::ChangeMapObject(Document *document,
35                                  MapObject *mapObject,
36                                  MapObject::Property property,
37                                  const QVariant &value)
38     : QUndoCommand(QCoreApplication::translate("Undo Commands",
39                                                "Change Object"))
40     , mDocument(document)
41     , mMapObject(mapObject)
42     , mProperty(property)
43     , mValue(value)
44     , mOldChangeState(mapObject->propertyChanged(property))
45     , mNewChangeState(true)
46 {
47     switch (property) {
48     case MapObject::VisibleProperty:
49         if (value.toBool())
50             setText(QCoreApplication::translate("Undo Commands", "Show Object"));
51         else
52             setText(QCoreApplication::translate("Undo Commands", "Hide Object"));
53         break;
54     default:
55         break;
56     }
57 }
58 
swap()59 void ChangeMapObject::swap()
60 {
61     QVariant oldValue = mMapObject->mapObjectProperty(mProperty);
62     mMapObject->setMapObjectProperty(mProperty, mValue);
63     std::swap(mValue, oldValue);
64 
65     mMapObject->setPropertyChanged(mProperty, mNewChangeState);
66     std::swap(mOldChangeState, mNewChangeState);
67 
68     emit mDocument->changed(MapObjectsChangeEvent(mMapObject, mProperty));
69 }
70 
71 
ChangeMapObjectCells(Document * document,const QVector<MapObjectCell> & changes,QUndoCommand * parent)72 ChangeMapObjectCells::ChangeMapObjectCells(Document *document,
73                                            const QVector<MapObjectCell> &changes,
74                                            QUndoCommand *parent)
75     : QUndoCommand(parent)
76     , mDocument(document)
77     , mChanges(changes)
78 {
79 }
80 
objectList(const QVector<MapObjectCell> & changes)81 static QList<MapObject*> objectList(const QVector<MapObjectCell> &changes)
82 {
83     QList<MapObject*> result;
84     result.reserve(changes.size());
85 
86     for (const MapObjectCell &change : changes)
87         result.append(change.object);
88 
89     return result;
90 }
91 
swap()92 void ChangeMapObjectCells::swap()
93 {
94     for (int i = 0; i < mChanges.size(); ++i) {
95         MapObjectCell &change = mChanges[i];
96 
97         auto cell = change.object->cell();
98         change.object->setCell(change.cell);
99         change.cell = cell;
100 
101         auto changed = change.object->propertyChanged(MapObject::CellProperty);
102         change.object->setPropertyChanged(MapObject::CellProperty, change.propertyChanged);
103         change.propertyChanged = changed;
104     }
105 
106     emit mDocument->changed(MapObjectsChangeEvent(objectList(mChanges), MapObject::CellProperty));
107 }
108 
109 
ChangeMapObjectsTile(Document * document,const QList<MapObject * > & mapObjects,Tile * tile)110 ChangeMapObjectsTile::ChangeMapObjectsTile(Document *document,
111                                            const QList<MapObject *> &mapObjects,
112                                            Tile *tile)
113     : QUndoCommand(QCoreApplication::translate("Undo Commands",
114                                                "Change %n Object/s Tile",
115                                                nullptr, mapObjects.size()))
116     , mDocument(document)
117     , mMapObjects(mapObjects)
118     , mTile(tile)
119 {
120     for (MapObject *object : qAsConst(mMapObjects)) {
121         Cell cell = object->cell();
122         mOldCells.append(cell);
123         Tile *tile = cell.tile();
124         // Update the size if the object's tile is valid and the sizes match
125         mUpdateSize.append(tile && object->size() == tile->size());
126 
127         mOldChangedProperties.append(object->changedProperties());
128     }
129 }
130 
setObjectCell(MapObject * object,const Cell & cell,const bool updateSize)131 static void setObjectCell(MapObject *object,
132                           const Cell &cell,
133                           const bool updateSize)
134 {
135     object->setCell(cell);
136 
137     if (updateSize)
138         object->setSize(cell.tile()->size());
139 }
140 
undo()141 void ChangeMapObjectsTile::undo()
142 {
143     restoreTiles();
144     QUndoCommand::undo(); // undo child commands
145 }
146 
redo()147 void ChangeMapObjectsTile::redo()
148 {
149     QUndoCommand::redo(); // redo child commands
150     changeTiles();
151 }
152 
restoreTiles()153 void ChangeMapObjectsTile::restoreTiles()
154 {
155     for (int i = 0; i < mMapObjects.size(); ++i) {
156         setObjectCell(mMapObjects[i], mOldCells[i], mUpdateSize[i]);
157         mMapObjects[i]->setChangedProperties(mOldChangedProperties[i]);
158     }
159 
160     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects,
161                                                   MapObject::CellProperty | MapObject::SizeProperty));
162 }
163 
changeTiles()164 void ChangeMapObjectsTile::changeTiles()
165 {
166     for (int i = 0; i < mMapObjects.size(); ++i) {
167         Cell cell = mMapObjects[i]->cell();
168         cell.setTile(mTile);
169         setObjectCell(mMapObjects[i], cell, mUpdateSize[i]);
170         mMapObjects[i]->setPropertyChanged(MapObject::CellProperty);
171         if (mUpdateSize[i])
172             mMapObjects[i]->setPropertyChanged(MapObject::SizeProperty);
173     }
174 
175     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects,
176                                                   MapObject::CellProperty | MapObject::SizeProperty));
177 }
178 
DetachObjects(Document * document,const QList<MapObject * > & mapObjects,QUndoCommand * parent)179 DetachObjects::DetachObjects(Document *document,
180                              const QList<MapObject *> &mapObjects,
181                              QUndoCommand *parent)
182     : QUndoCommand(QCoreApplication::translate("Undo Commands",
183                                                "Detach %n Template Instance(s)",
184                                                nullptr, mapObjects.size()), parent)
185     , mDocument(document)
186     , mMapObjects(mapObjects)
187 {
188     for (const MapObject *object : mapObjects) {
189         mObjectTemplates.append(object->objectTemplate());
190         mProperties.append(object->properties());
191     }
192 }
193 
redo()194 void DetachObjects::redo()
195 {
196     QUndoCommand::redo(); // redo child commands
197 
198     for (MapObject *object : qAsConst(mMapObjects))
199         object->detachFromTemplate();
200 
201     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects, MapObject::TemplateProperty));
202 }
203 
undo()204 void DetachObjects::undo()
205 {
206     for (int i = 0; i < mMapObjects.size(); ++i) {
207         MapObject *object = mMapObjects.at(i);
208         object->setObjectTemplate(mObjectTemplates.at(i));
209         object->setProperties(mProperties.at(i));
210         object->syncWithTemplate();
211     }
212 
213     QUndoCommand::undo(); // undo child commands
214 
215     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects, MapObject::TemplateProperty));
216 }
217 
ResetInstances(Document * document,const QList<MapObject * > & mapObjects,QUndoCommand * parent)218 ResetInstances::ResetInstances(Document *document,
219                                const QList<MapObject *> &mapObjects,
220                                QUndoCommand *parent)
221     : QUndoCommand(QCoreApplication::translate("Undo Commands",
222                                                "Reset %n Instances",
223                                                nullptr, mapObjects.size()), parent)
224     , mDocument(document)
225     , mMapObjects(mapObjects)
226 {
227     for (const MapObject *object : mapObjects)
228         mOldMapObjects.append(object->clone());
229 }
230 
~ResetInstances()231 ResetInstances::~ResetInstances()
232 {
233     qDeleteAll(mOldMapObjects);
234 }
235 
redo()236 void ResetInstances::redo()
237 {
238     MapObject::ChangedProperties affectedProperties = MapObject::CustomProperties;
239 
240     for (auto object : mMapObjects) {
241         // Template instances initially don't hold any custom properties
242         object->clearProperties();
243 
244         affectedProperties |= object->changedProperties();
245 
246         // Reset built-in properties
247         object->setChangedProperties(MapObject::ChangedProperties());
248         object->syncWithTemplate();
249     }
250 
251     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects, affectedProperties));
252 
253     // This signal forces updating custom properties in the properties dock
254 //    emit mMapDocument->selectedObjectsChanged();
255 }
256 
undo()257 void ResetInstances::undo()
258 {
259     MapObject::ChangedProperties affectedProperties = MapObject::CustomProperties;
260 
261     for (int i = 0; i < mMapObjects.size(); ++i) {
262         mMapObjects.at(i)->copyPropertiesFrom(mOldMapObjects.at(i));
263         affectedProperties |= mOldMapObjects.at(i)->changedProperties();
264     }
265 
266     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects, affectedProperties));
267 }
268 
269 
ReplaceObjectsWithTemplate(Document * document,const QList<MapObject * > & mapObjects,ObjectTemplate * objectTemplate,QUndoCommand * parent)270 ReplaceObjectsWithTemplate::ReplaceObjectsWithTemplate(Document *document,
271                                                        const QList<MapObject *> &mapObjects,
272                                                        ObjectTemplate *objectTemplate,
273                                                        QUndoCommand *parent)
274     : QUndoCommand(QCoreApplication::translate("Undo Commands",
275                                                "Replace %n Object(s) With Template",
276                                                nullptr, mapObjects.size()), parent)
277     , mDocument(document)
278     , mMapObjects(mapObjects)
279     , mObjectTemplate(objectTemplate)
280 {
281     for (const MapObject *object : mapObjects)
282         mOldMapObjects.append(object->clone());
283 }
284 
~ReplaceObjectsWithTemplate()285 ReplaceObjectsWithTemplate::~ReplaceObjectsWithTemplate()
286 {
287     qDeleteAll(mOldMapObjects);
288 }
289 
redo()290 void ReplaceObjectsWithTemplate::redo()
291 {
292     for (auto object : mMapObjects) {
293         object->clearProperties();
294         object->setChangedProperties(MapObject::ChangedProperties());
295         object->setObjectTemplate(mObjectTemplate);
296         object->syncWithTemplate();
297     }
298 
299     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects, MapObject::AllProperties));
300 }
301 
undo()302 void ReplaceObjectsWithTemplate::undo()
303 {
304     for (int i = 0; i < mMapObjects.size(); ++i)
305         mMapObjects.at(i)->copyPropertiesFrom(mOldMapObjects.at(i));
306 
307     emit mDocument->changed(MapObjectsChangeEvent(mMapObjects, MapObject::AllProperties));
308 }
309