1 /*
2 * exporthelper.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 "exporthelper.h"
22
23 #include "mapobject.h"
24 #include "objectgroup.h"
25
26 namespace Tiled {
27
28 /**
29 * @return the format options that should be used when writing the file.
30 */
formatOptions() const31 FileFormat::Options ExportHelper::formatOptions() const
32 {
33 FileFormat::Options options;
34 if (mOptions.testFlag(Preferences::ExportMinimized))
35 options |= FileFormat::WriteMinimized;
36 return options;
37 }
38
39 /**
40 * Prepares a tileset for export.
41 *
42 * \a savingTileset means that this tileset is being saved to its own file
43 * rather saved as part of a map. In this case, we want to apply some export
44 * options that we would skip for external tilesets.
45 */
prepareExportTileset(const SharedTileset & tileset,bool savingTileset) const46 SharedTileset ExportHelper::prepareExportTileset(const SharedTileset &tileset,
47 bool savingTileset) const
48 {
49 const bool hasExportSettings = !(tileset->exportFileName.isEmpty()
50 && tileset->exportFormat.isEmpty());
51
52 if (!mOptions && !hasExportSettings)
53 return tileset;
54
55 // When the tileset is embedded we're effectively always saving it
56 savingTileset |= !tileset->isExternal();
57
58 if (!savingTileset && !mOptions.testFlag(Preferences::EmbedTilesets))
59 return tileset; // Leave external tileset alone
60
61 if (savingTileset && !(mOptions & (Preferences::DetachTemplateInstances |
62 Preferences::ResolveObjectTypesAndProperties))
63 && !hasExportSettings) {
64 // We're saving this tileset as-is, so leave it alone
65 return tileset;
66 }
67
68 // Either needs to be embedded or is already embedded and we may need to
69 // make other changes to the tileset
70 SharedTileset exportTileset = tileset->clone();
71 exportTileset->setOriginalTileset(tileset);
72
73 // We don't want to save the export options in the exported file
74 if (hasExportSettings) {
75 exportTileset->exportFileName.clear();
76 exportTileset->exportFormat.clear();
77 }
78
79 if (mOptions.testFlag(Preferences::DetachTemplateInstances)) {
80 for (Tile *tile : exportTileset->tiles()) {
81 if (!tile->objectGroup())
82 continue;
83
84 for (MapObject *object : *tile->objectGroup())
85 if (object->isTemplateInstance())
86 object->detachFromTemplate();
87 }
88 }
89
90 if (mOptions.testFlag(Preferences::ResolveObjectTypesAndProperties)) {
91 for (Tile *tile : exportTileset->tiles()) {
92 if (!tile->objectGroup())
93 continue;
94
95 for (MapObject *object : *tile->objectGroup())
96 resolveTypeAndProperties(object);
97 }
98 }
99
100 return exportTileset;
101 }
102
prepareExportMap(const Map * map,std::unique_ptr<Map> & exportMap) const103 const Map *ExportHelper::prepareExportMap(const Map *map, std::unique_ptr<Map> &exportMap) const
104 {
105 const bool hasExportSettings = !(map->exportFileName.isEmpty()
106 && map->exportFormat.isEmpty());
107
108 // If no export options are active, return the same map
109 if (!(mOptions & ~Preferences::ExportMinimized) && !hasExportSettings)
110 return map;
111
112 // Make a copy to which export options are applied
113 exportMap = map->clone();
114
115 // We don't want to save the export options in the exported file
116 if (hasExportSettings) {
117 exportMap->exportFileName.clear();
118 exportMap->exportFormat.clear();
119 }
120
121 if (mOptions.testFlag(Preferences::DetachTemplateInstances)) {
122 for (Layer *layer : exportMap->objectGroups()) {
123 for (MapObject *object : *static_cast<ObjectGroup*>(layer)) {
124 if (object->isTemplateInstance()) {
125 // In case of templated tile objects, the map may not yet
126 // have a reference to the used tileset.
127 if (Tile *tile = object->cell().tile())
128 exportMap->addTileset(tile->tileset()->sharedPointer());
129
130 object->detachFromTemplate();
131 }
132 }
133 }
134 }
135
136 if (mOptions.testFlag(Preferences::ResolveObjectTypesAndProperties))
137 for (Layer *layer : exportMap->objectGroups())
138 for (MapObject *object : *static_cast<ObjectGroup*>(layer))
139 resolveTypeAndProperties(object);
140
141 const auto tilesets = exportMap->tilesets(); // needs a copy
142 for (const SharedTileset &tileset : tilesets) {
143 auto exportTileset = prepareExportTileset(tileset, false);
144 if (exportTileset != tileset)
145 exportMap->replaceTileset(tileset, exportTileset);
146 }
147
148 // Return a pointer to the copy
149 return exportMap.get();
150 }
151
resolveTypeAndProperties(MapObject * object) const152 void ExportHelper::resolveTypeAndProperties(MapObject *object) const
153 {
154 Tile *tile = object->cell().tile();
155
156 // Inherit type from tile if not set on object (not inheriting
157 // type from tile of tile object template here, for that the
158 // "Detach templates" option needs to be used as well)
159 if (object->type().isEmpty() && tile &&
160 (!object->isTemplateInstance() || object->propertyChanged(MapObject::CellProperty)))
161 object->setType(tile->type());
162
163 Properties properties;
164
165 // Inherit properties from type
166 if (!object->type().isEmpty()) {
167 for (int i = Object::objectTypes().size() - 1; i >= 0; --i) {
168 auto const &type = Object::objectTypes().at(i);
169 if (type.name == object->type())
170 mergeProperties(properties, type.defaultProperties);
171 }
172 }
173
174 // Inherit properties from tile
175 if (tile)
176 mergeProperties(properties, tile->properties());
177
178 // Override with own properties
179 mergeProperties(properties, object->properties());
180
181 object->setProperties(properties);
182 }
183
184 } // namespace Tiled
185