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