1 /*
2  * editablemap.cpp
3  * Copyright 2018, 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 "editablemap.h"
22 
23 #include "addremovelayer.h"
24 #include "addremovetileset.h"
25 #include "automappingmanager.h"
26 #include "changeevents.h"
27 #include "changemapproperty.h"
28 #include "changeselectedarea.h"
29 #include "editablegrouplayer.h"
30 #include "editableimagelayer.h"
31 #include "editablelayer.h"
32 #include "editablemanager.h"
33 #include "editablemapobject.h"
34 #include "editableobjectgroup.h"
35 #include "editableselectedarea.h"
36 #include "editabletilelayer.h"
37 #include "grouplayer.h"
38 #include "imagelayer.h"
39 #include "mapobject.h"
40 #include "maprenderer.h"
41 #include "objectgroup.h"
42 #include "replacetileset.h"
43 #include "resizemap.h"
44 #include "scriptmanager.h"
45 #include "tilelayer.h"
46 #include "tileset.h"
47 #include "tilesetdocument.h"
48 
49 #include <QCoreApplication>
50 #include <QUndoStack>
51 
52 #include "qtcompat_p.h"
53 
54 namespace Tiled {
55 
EditableMap(QObject * parent)56 EditableMap::EditableMap(QObject *parent)
57     : EditableAsset(nullptr, new Map(), parent)
58     , mReadOnly(false)
59     , mSelectedArea(nullptr)
60 {
61     mDetachedMap.reset(map());
62 }
63 
EditableMap(MapDocument * mapDocument,QObject * parent)64 EditableMap::EditableMap(MapDocument *mapDocument, QObject *parent)
65     : EditableAsset(mapDocument, mapDocument->map(), parent)
66     , mReadOnly(false)
67     , mSelectedArea(new EditableSelectedArea(mapDocument, this))
68 {
69     connect(mapDocument, &Document::fileNameChanged, this, &EditableAsset::fileNameChanged);
70     connect(mapDocument, &Document::changed, this, &EditableMap::documentChanged);
71     connect(mapDocument, &MapDocument::layerAdded, this, &EditableMap::attachLayer);
72     connect(mapDocument, &MapDocument::layerRemoved, this, &EditableMap::detachLayer);
73 
74     connect(mapDocument, &MapDocument::currentLayerChanged, this, &EditableMap::onCurrentLayerChanged);
75     connect(mapDocument, &MapDocument::selectedLayersChanged, this, &EditableMap::selectedLayersChanged);
76     connect(mapDocument, &MapDocument::selectedObjectsChanged, this, &EditableMap::selectedObjectsChanged);
77 }
78 
79 /**
80  * Creates a read-only instance of EditableMap that works on the given \a map.
81  *
82  * The map's lifetime must exceed that of the EditableMap instance.
83  */
EditableMap(const Map * map,QObject * parent)84 EditableMap::EditableMap(const Map *map, QObject *parent)
85     : EditableAsset(nullptr, const_cast<Map*>(map), parent)
86     , mReadOnly(true)
87     , mSelectedArea(nullptr)
88 {
89 }
90 
EditableMap(std::unique_ptr<Map> map,QObject * parent)91 EditableMap::EditableMap(std::unique_ptr<Map> map, QObject *parent)
92     : EditableAsset(nullptr, map.get(), parent)
93     , mDetachedMap(std::move(map))
94     , mReadOnly(false)
95     , mSelectedArea(nullptr)
96 {
97 }
98 
~EditableMap()99 EditableMap::~EditableMap()
100 {
101     for (Layer *layer : map()->layers())
102         detachLayer(layer);
103 }
104 
tilesets() const105 QList<QObject *> EditableMap::tilesets() const
106 {
107     QList<QObject *> editableTilesets;
108     auto &editableManager = EditableManager::instance();
109 
110     for (const SharedTileset &tileset : map()->tilesets()) {
111         if (auto document = TilesetDocument::findDocumentForTileset(tileset))
112             editableTilesets.append(document->editable());
113         else
114             editableTilesets.append(editableManager.editableTileset(tileset.data()));
115     }
116     return editableTilesets;
117 }
118 
currentLayer()119 EditableLayer *EditableMap::currentLayer()
120 {
121     if (auto document = mapDocument())
122         return EditableManager::instance().editableLayer(this, document->currentLayer());
123     return nullptr;
124 }
125 
selectedLayers()126 QList<QObject *> EditableMap::selectedLayers()
127 {
128     if (!mapDocument())
129         return QList<QObject*>();
130 
131     QList<QObject*> selectedLayers;
132 
133     auto &editableManager = EditableManager::instance();
134     for (Layer *layer : mapDocument()->selectedLayers())
135         selectedLayers.append(editableManager.editableLayer(this, layer));
136 
137     return selectedLayers;
138 }
139 
selectedObjects()140 QList<QObject *> EditableMap::selectedObjects()
141 {
142     if (!mapDocument())
143         return QList<QObject*>();
144 
145     QList<QObject*> selectedObjects;
146 
147     auto &editableManager = EditableManager::instance();
148     for (MapObject *object : mapDocument()->selectedObjects())
149         selectedObjects.append(editableManager.editableMapObject(this, object));
150 
151     return selectedObjects;
152 }
153 
layerAt(int index)154 EditableLayer *EditableMap::layerAt(int index)
155 {
156     if (index < 0 || index >= layerCount()) {
157         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Index out of range"));
158         return nullptr;
159     }
160 
161     Layer *layer = map()->layerAt(index);
162     return EditableManager::instance().editableLayer(this, layer);
163 }
164 
removeLayerAt(int index)165 void EditableMap::removeLayerAt(int index)
166 {
167     if (index < 0 || index >= layerCount()) {
168         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Index out of range"));
169         return;
170     }
171 
172     if (auto doc = mapDocument()) {
173         push(new RemoveLayer(doc, index, nullptr));
174     } else if (!checkReadOnly()) {
175         auto layer = map()->takeLayerAt(index);
176         EditableManager::instance().release(layer);
177     }
178 }
179 
removeLayer(EditableLayer * editableLayer)180 void EditableMap::removeLayer(EditableLayer *editableLayer)
181 {
182     if (!editableLayer) {
183         ScriptManager::instance().throwNullArgError(0);
184         return;
185     }
186 
187     int index = map()->layers().indexOf(editableLayer->layer());
188     if (index == -1) {
189         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Layer not found"));
190         return;
191     }
192 
193     removeLayerAt(index);
194 }
195 
insertLayerAt(int index,EditableLayer * editableLayer)196 void EditableMap::insertLayerAt(int index, EditableLayer *editableLayer)
197 {
198     if (index < 0 || index > layerCount()) {
199         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Index out of range"));
200         return;
201     }
202 
203     if (!editableLayer) {
204         ScriptManager::instance().throwNullArgError(0);
205         return;
206     }
207 
208     if (editableLayer->map()) {
209         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Layer already part of a map"));
210         return;
211     }
212 
213     // If this map has a valid size but the tile layer that's getting added
214     // doesn't, default the layer's size to the map size.
215     if (editableLayer->isTileLayer()) {
216         auto editableTileLayer = static_cast<EditableTileLayer*>(editableLayer);
217         if (editableTileLayer->size().isNull() && !size().isNull())
218             editableTileLayer->setSize(size());
219     }
220 
221     if (auto doc = mapDocument()) {
222         push(new AddLayer(doc, index, editableLayer->layer(), nullptr));
223     } else if (!checkReadOnly()) {
224         // ownership moves to the map
225         map()->insertLayer(index, editableLayer->release());
226     }
227 }
228 
addLayer(EditableLayer * editableLayer)229 void EditableMap::addLayer(EditableLayer *editableLayer)
230 {
231     insertLayerAt(layerCount(), editableLayer);
232 }
233 
addTileset(EditableTileset * editableTileset)234 bool EditableMap::addTileset(EditableTileset *editableTileset)
235 {
236     if (!editableTileset) {
237         ScriptManager::instance().throwNullArgError(0);
238         return false;
239     }
240     const auto &tileset = editableTileset->tileset()->sharedPointer();
241     if (map()->indexOfTileset(tileset) != -1)
242         return false;   // can't add existing tileset
243 
244     if (auto doc = mapDocument())
245         push(new AddTileset(doc, tileset));
246     else if (!checkReadOnly())
247         map()->addTileset(tileset);
248 
249     return true;
250 }
251 
replaceTileset(EditableTileset * oldEditableTileset,EditableTileset * newEditableTileset)252 bool EditableMap::replaceTileset(EditableTileset *oldEditableTileset,
253                                  EditableTileset *newEditableTileset)
254 {
255     if (!oldEditableTileset) {
256         ScriptManager::instance().throwNullArgError(0);
257         return false;
258     }
259     if (!newEditableTileset) {
260         ScriptManager::instance().throwNullArgError(1);
261         return false;
262     }
263     if (oldEditableTileset == newEditableTileset) {
264         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Invalid argument"));
265         return false;
266     }
267 
268     SharedTileset oldTileset = oldEditableTileset->tileset()->sharedPointer();
269     int indexOfOldTileset = map()->indexOfTileset(oldTileset);
270     if (indexOfOldTileset == -1)
271         return false;   // can't replace non-existing tileset
272 
273     SharedTileset newTileset = newEditableTileset->tileset()->sharedPointer();
274     int indexOfNewTileset = map()->indexOfTileset(newTileset);
275     if (indexOfNewTileset != -1)
276         return false;   // can't replace with tileset that is already part of the map (undo broken)
277 
278     if (auto doc = mapDocument())
279         push(new ReplaceTileset(doc, indexOfOldTileset, newTileset));
280     else if (!checkReadOnly())
281         map()->replaceTileset(oldTileset, newTileset);
282 
283     return true;
284 }
285 
removeTileset(EditableTileset * editableTileset)286 bool EditableMap::removeTileset(EditableTileset *editableTileset)
287 {
288     if (!editableTileset) {
289         ScriptManager::instance().throwNullArgError(0);
290         return false;
291     }
292     Tileset *tileset = editableTileset->tileset();
293     int index = map()->indexOfTileset(tileset->sharedPointer());
294     if (index == -1)
295         return false;   // can't remove non-existing tileset
296 
297     if (map()->isTilesetUsed(tileset))
298         return false;   // not allowed to remove a tileset that's in use
299 
300     if (auto doc = mapDocument())
301         push(new RemoveTileset(doc, index));
302     else if (!checkReadOnly())
303         map()->removeTilesetAt(index);
304 
305     return true;
306 }
307 
usedTilesets() const308 QList<QObject *> EditableMap::usedTilesets() const
309 {
310     const auto tilesets = map()->usedTilesets();
311 
312     QList<QObject *> editableTilesets;
313     for (const SharedTileset &tileset : tilesets)
314         if (auto document = TilesetDocument::findDocumentForTileset(tileset))
315             editableTilesets.append(document->editable());
316     return editableTilesets;
317 }
318 
319 /**
320  * Merges the given map with this map. Automatically adds any tilesets that are
321  * used by the merged map which are not yet part of this map.
322  *
323  * Might replace tilesets in the given \a editableMap, if it is detached.
324  *
325  * Pass \a canJoin as 'true' if the operation is allowed to join with the
326  * previous one on the undo stack.
327  *
328  * @warning Currently only supports tile layers!
329  */
merge(EditableMap * editableMap,bool canJoin)330 void EditableMap::merge(EditableMap *editableMap, bool canJoin)
331 {
332     if (!editableMap) {
333         ScriptManager::instance().throwNullArgError(0);
334         return;
335     }
336     if (!mapDocument()) {   // todo: support this outside of the undo stack
337         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Merge is currently not supported for detached maps"));
338         return;
339     }
340 
341     // unifyTilesets might modify the given map, so need to clone if it has a document.
342     Map *map = editableMap->map();
343     std::unique_ptr<Map> copy;      // manages lifetime
344     if (editableMap->document()) {
345         copy = map->clone();
346         map = copy.get();
347     }
348 
349     QVector<SharedTileset> missingTilesets;
350     mapDocument()->unifyTilesets(map, missingTilesets);
351     mapDocument()->paintTileLayers(map, canJoin, &missingTilesets);
352 }
353 
354 /**
355  * Resize this map to the given \a size, while at the same time shifting
356  * the contents by \a offset. If \a removeObjects is true then all objects
357  * which are outside the map will be removed.
358  */
resize(QSize size,QPoint offset,bool removeObjects)359 void EditableMap::resize(QSize size, QPoint offset, bool removeObjects)
360 {
361     if (checkReadOnly())
362         return;
363     if (!mapDocument()) {   // todo: should be able to resize still
364         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Resize is currently not supported for detached maps"));
365         return;
366     }
367     if (size.isEmpty()) {
368         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Invalid size"));
369         return;
370     }
371 
372     mapDocument()->resizeMap(size, offset, removeObjects);
373 }
374 
autoMap(const RegionValueType & region,const QString & rulesFile)375 void EditableMap::autoMap(const RegionValueType &region, const QString &rulesFile)
376 {
377     if (checkReadOnly())
378         return;
379     if (!mapDocument()) {
380         ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "AutoMapping is currently not supported for detached maps"));
381         return;
382     }
383 
384     if (!mAutomappingManager)
385         mAutomappingManager = new AutomappingManager(this);
386 
387     AutomappingManager &manager = *mAutomappingManager;
388     manager.setMapDocument(mapDocument(), rulesFile);
389 
390     if (region.region().isEmpty())
391         manager.autoMap();
392     else
393         manager.autoMapRegion(region.region());
394 }
395 
screenToTile(qreal x,qreal y) const396 QPointF EditableMap::screenToTile(qreal x, qreal y) const
397 {
398     return renderer()->screenToTileCoords(x, y);
399 }
400 
tileToScreen(qreal x,qreal y) const401 QPointF EditableMap::tileToScreen(qreal x, qreal y) const
402 {
403     return renderer()->tileToScreenCoords(x, y);
404 }
405 
screenToPixel(qreal x,qreal y) const406 QPointF EditableMap::screenToPixel(qreal x, qreal y) const
407 {
408     return renderer()->screenToPixelCoords(x, y);
409 }
410 
pixelToScreen(qreal x,qreal y) const411 QPointF EditableMap::pixelToScreen(qreal x, qreal y) const
412 {
413     return renderer()->pixelToScreenCoords(x, y);
414 }
415 
pixelToTile(qreal x,qreal y) const416 QPointF EditableMap::pixelToTile(qreal x, qreal y) const
417 {
418     return renderer()->pixelToTileCoords(x, y);
419 }
420 
tileToPixel(qreal x,qreal y) const421 QPointF EditableMap::tileToPixel(qreal x, qreal y) const
422 {
423     return renderer()->tileToPixelCoords(x, y);
424 }
425 
setSize(int width,int height)426 void EditableMap::setSize(int width, int height)
427 {
428     if (auto doc = mapDocument()) {
429         push(new ResizeMap(doc, QSize(width, height)));
430     } else if (!checkReadOnly()) {
431         map()->setWidth(width);
432         map()->setHeight(height);
433     }
434 }
435 
setTileWidth(int value)436 void EditableMap::setTileWidth(int value)
437 {
438     if (auto doc = mapDocument())
439         push(new ChangeMapProperty(doc, Map::TileWidthProperty, value));
440     else if (!checkReadOnly())
441         map()->setTileWidth(value);
442 }
443 
setTileHeight(int value)444 void EditableMap::setTileHeight(int value)
445 {
446     if (auto doc = mapDocument())
447         push(new ChangeMapProperty(doc, Map::TileHeightProperty, value));
448     else if (!checkReadOnly())
449         map()->setTileHeight(value);
450 }
451 
setTileSize(int width,int height)452 void EditableMap::setTileSize(int width, int height)
453 {
454     if (checkReadOnly())
455         return;
456 
457     if (auto doc = mapDocument()) {
458         doc->undoStack()->beginMacro(QCoreApplication::translate("Undo Commands",
459                                                                  "Change Tile Size"));
460         setTileWidth(width);
461         setTileHeight(height);
462         doc->undoStack()->endMacro();
463     } else {
464         map()->setTileWidth(width);
465         map()->setTileHeight(height);
466     }
467 }
468 
setInfinite(bool value)469 void EditableMap::setInfinite(bool value)
470 {
471     if (auto doc = mapDocument())
472         push(new ChangeMapProperty(doc, Map::InfiniteProperty, value));
473     else if (!checkReadOnly())
474         map()->setInfinite(value);
475 }
476 
setHexSideLength(int value)477 void EditableMap::setHexSideLength(int value)
478 {
479     if (auto doc = mapDocument())
480         push(new ChangeMapProperty(doc, Map::HexSideLengthProperty, value));
481     else if (!checkReadOnly())
482         map()->setHexSideLength(value);
483 }
484 
setStaggerAxis(StaggerAxis value)485 void EditableMap::setStaggerAxis(StaggerAxis value)
486 {
487     if (auto doc = mapDocument())
488         push(new ChangeMapProperty(doc, static_cast<Map::StaggerAxis>(value)));
489     else if (!checkReadOnly())
490         map()->setStaggerAxis(static_cast<Map::StaggerAxis>(value));
491 }
492 
setStaggerIndex(StaggerIndex value)493 void EditableMap::setStaggerIndex(StaggerIndex value)
494 {
495     if (auto doc = mapDocument())
496         push(new ChangeMapProperty(doc, static_cast<Map::StaggerIndex>(value)));
497     else if (!checkReadOnly())
498         map()->setStaggerIndex(static_cast<Map::StaggerIndex>(value));
499 }
500 
setOrientation(Orientation value)501 void EditableMap::setOrientation(Orientation value)
502 {
503     if (auto doc = mapDocument()) {
504         push(new ChangeMapProperty(doc, static_cast<Map::Orientation>(value)));
505     } else if (!checkReadOnly()) {
506         map()->setOrientation(static_cast<Map::Orientation>(value));
507         mRenderer.reset();
508     }
509 }
510 
setRenderOrder(RenderOrder value)511 void EditableMap::setRenderOrder(RenderOrder value)
512 {
513     if (auto doc = mapDocument())
514         push(new ChangeMapProperty(doc, static_cast<Map::RenderOrder>(value)));
515     else if (!checkReadOnly())
516         map()->setRenderOrder(static_cast<Map::RenderOrder>(value));
517 }
518 
setBackgroundColor(const QColor & value)519 void EditableMap::setBackgroundColor(const QColor &value)
520 {
521     if (auto doc = mapDocument())
522         push(new ChangeMapProperty(doc, value));
523     else if (!checkReadOnly())
524         map()->setBackgroundColor(value);
525 }
526 
setLayerDataFormat(LayerDataFormat value)527 void EditableMap::setLayerDataFormat(LayerDataFormat value)
528 {
529     if (auto doc = mapDocument())
530         push(new ChangeMapProperty(doc, static_cast<Map::LayerDataFormat>(value)));
531     else if (!checkReadOnly())
532         map()->setLayerDataFormat(static_cast<Map::LayerDataFormat>(value));
533 }
534 
setCurrentLayer(EditableLayer * layer)535 void EditableMap::setCurrentLayer(EditableLayer *layer)
536 {
537     QList<QObject*> layers;
538     if (layer)
539         layers.append(layer);
540 
541     setSelectedLayers(layers);
542 }
543 
setSelectedLayers(const QList<QObject * > & layers)544 void EditableMap::setSelectedLayers(const QList<QObject *> &layers)
545 {
546     auto document = mapDocument();
547     if (!document)
548         return;
549 
550     QList<Layer*> plainLayers;
551 
552     for (QObject *layerObject : layers) {
553         auto editableLayer = qobject_cast<EditableLayer*>(layerObject);
554         if (!editableLayer) {
555             ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Not a layer"));
556             return;
557         }
558         if (editableLayer->map() != this) {
559             ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Layer not from this map"));
560             return;
561         }
562 
563         plainLayers.append(editableLayer->layer());
564     }
565 
566     document->switchSelectedLayers(plainLayers);
567 }
568 
setSelectedObjects(const QList<QObject * > & objects)569 void EditableMap::setSelectedObjects(const QList<QObject *> &objects)
570 {
571     auto document = mapDocument();
572     if (!document)
573         return;
574 
575     QList<MapObject*> plainObjects;
576 
577     for (QObject *objectObject : objects) {
578         auto editableMapObject = qobject_cast<EditableMapObject*>(objectObject);
579         if (!editableMapObject) {
580             ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Not an object"));
581             return;
582         }
583         if (editableMapObject->map() != this) {
584             ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Object not from this map"));
585             return;
586         }
587 
588         plainObjects.append(editableMapObject->mapObject());
589     }
590 
591     document->setSelectedObjects(plainObjects);
592 }
593 
documentChanged(const ChangeEvent & change)594 void EditableMap::documentChanged(const ChangeEvent &change)
595 {
596     switch (change.type) {
597     case ChangeEvent::MapChanged:
598         if (static_cast<const MapChangeEvent&>(change).property == Map::OrientationProperty)
599             mRenderer.reset();
600         break;
601     case ChangeEvent::MapObjectsAdded:
602         attachMapObjects(static_cast<const MapObjectsEvent&>(change).mapObjects);
603         break;
604     case ChangeEvent::MapObjectsAboutToBeRemoved:
605         detachMapObjects(static_cast<const MapObjectsEvent&>(change).mapObjects);
606         break;
607     default:
608         break;
609     }
610 }
611 
attachLayer(Layer * layer)612 void EditableMap::attachLayer(Layer *layer)
613 {
614     if (EditableLayer *editable = EditableManager::instance().find(layer))
615         editable->attach(this);
616 
617     if (GroupLayer *groupLayer = layer->asGroupLayer()) {
618         for (Layer *childLayer : groupLayer->layers())
619             attachLayer(childLayer);
620     } else if (ObjectGroup *objectGroup = layer->asObjectGroup()) {
621         attachMapObjects(objectGroup->objects());
622     }
623 }
624 
detachLayer(Layer * layer)625 void EditableMap::detachLayer(Layer *layer)
626 {
627     auto editableLayer = EditableManager::instance().find(layer);
628     if (editableLayer && editableLayer->map() == this)
629         editableLayer->detach();
630 
631     if (GroupLayer *groupLayer = layer->asGroupLayer()) {
632         for (Layer *childLayer : groupLayer->layers())
633             detachLayer(childLayer);
634     } else if (ObjectGroup *objectGroup = layer->asObjectGroup()) {
635         detachMapObjects(objectGroup->objects());
636     }
637 }
638 
attachMapObjects(const QList<MapObject * > & mapObjects)639 void EditableMap::attachMapObjects(const QList<MapObject *> &mapObjects)
640 {
641     const auto &editableManager = EditableManager::instance();
642     for (MapObject *mapObject : mapObjects) {
643         if (EditableMapObject *editable = editableManager.find(mapObject))
644             editable->attach(this);
645     }
646 }
647 
detachMapObjects(const QList<MapObject * > & mapObjects)648 void EditableMap::detachMapObjects(const QList<MapObject *> &mapObjects)
649 {
650     const auto &editableManager = EditableManager::instance();
651     for (MapObject *mapObject : mapObjects) {
652         if (EditableMapObject *editable = editableManager.find(mapObject)) {
653             Q_ASSERT(editable->map() == this);
654             editable->detach();
655         }
656     }
657 }
658 
onCurrentLayerChanged(Layer *)659 void EditableMap::onCurrentLayerChanged(Layer *)
660 {
661     emit currentLayerChanged();
662 }
663 
renderer() const664 MapRenderer *EditableMap::renderer() const
665 {
666     if (!mRenderer)
667         mRenderer = MapRenderer::create(map());
668 
669     return mRenderer.get();
670 }
671 
672 } // namespace Tiled
673 
674 #include "moc_editablemap.cpp"
675