1 /*
2 * tilesetmanager.cpp
3 * Copyright 2008-2014, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4 * Copyright 2009, Edward Hutchins <eah1@yahoo.com>
5 *
6 * This file is part of libtiled.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21 * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "tilesetmanager.h"
31
32 #include "filesystemwatcher.h"
33 #include "imagecache.h"
34 #include "tile.h"
35 #include "tileanimationdriver.h"
36 #include "tilesetformat.h"
37
38 #include "qtcompat_p.h"
39
40 namespace Tiled {
41
42 TilesetManager *TilesetManager::mInstance;
43
44 /**
45 * Constructor. Only used by the tileset manager itself.
46 */
TilesetManager()47 TilesetManager::TilesetManager():
48 mWatcher(new FileSystemWatcher(this)),
49 mAnimationDriver(new TileAnimationDriver(this)),
50 mReloadTilesetsOnChange(false)
51 {
52 connect(mWatcher, &FileSystemWatcher::pathsChanged,
53 this, &TilesetManager::filesChanged);
54
55 connect(mAnimationDriver, &TileAnimationDriver::update,
56 this, &TilesetManager::advanceTileAnimations);
57 }
58
~TilesetManager()59 TilesetManager::~TilesetManager()
60 {
61 // Assert that there are no remaining tileset instances
62 Q_ASSERT(mTilesets.isEmpty());
63 }
64
65 /**
66 * Requests the tileset manager. When the manager doesn't exist yet, it
67 * will be created.
68 */
instance()69 TilesetManager *TilesetManager::instance()
70 {
71 if (!mInstance)
72 mInstance = new TilesetManager;
73
74 return mInstance;
75 }
76
77 /**
78 * Deletes the tileset manager instance, when it exists.
79 */
deleteInstance()80 void TilesetManager::deleteInstance()
81 {
82 delete mInstance;
83 mInstance = nullptr;
84 }
85
86 /**
87 * Loads the tileset with the given \a fileName. If the tileset is already
88 * loaded, returns that instance.
89 *
90 * When an error occurs during loading it is assigned to the optional \a error
91 * parameter.
92 */
loadTileset(const QString & fileName,QString * error)93 SharedTileset TilesetManager::loadTileset(const QString &fileName, QString *error)
94 {
95 SharedTileset tileset = findTileset(fileName);
96 if (!tileset)
97 tileset = readTileset(fileName, error);
98
99 return tileset;
100 }
101
102 /**
103 * Searches for a tileset matching the given file name.
104 * @return a tileset matching the given file name, or 0 if none exists
105 */
findTileset(const QString & fileName) const106 SharedTileset TilesetManager::findTileset(const QString &fileName) const
107 {
108 for (Tileset *tileset : mTilesets)
109 if (tileset->fileName() == fileName)
110 return tileset->sharedPointer();
111
112 return SharedTileset();
113 }
114
115 /**
116 * Adds a tileset reference. This will make sure the tileset is watched for
117 * changes and can be found using findTileset().
118 */
addTileset(Tileset * tileset)119 void TilesetManager::addTileset(Tileset *tileset)
120 {
121 Q_ASSERT(!mTilesets.contains(tileset));
122 mTilesets.append(tileset);
123 }
124
125 /**
126 * Removes a tileset reference. When the last reference has been removed,
127 * the tileset is no longer watched for changes.
128 */
removeTileset(Tileset * tileset)129 void TilesetManager::removeTileset(Tileset *tileset)
130 {
131 Q_ASSERT(mTilesets.contains(tileset));
132 mTilesets.removeOne(tileset);
133
134 if (tileset->imageSource().isLocalFile())
135 mWatcher->removePath(tileset->imageSource().toLocalFile());
136 }
137
138 /**
139 * Forces a tileset to reload.
140 */
reloadImages(Tileset * tileset)141 void TilesetManager::reloadImages(Tileset *tileset)
142 {
143 if (!mTilesets.contains(tileset))
144 return;
145
146 if (tileset->isCollection()) {
147 for (Tile *tile : tileset->tiles()) {
148 // todo: trigger reload of remote files
149 if (tile->imageSource().isLocalFile()) {
150 const QString localFile = tile->imageSource().toLocalFile();
151 ImageCache::remove(localFile);
152 tile->setImage(ImageCache::loadPixmap(localFile));
153 }
154 }
155 emit tilesetImagesChanged(tileset);
156 } else {
157 ImageCache::remove(tileset->imageSource().toLocalFile());
158 if (tileset->loadImage())
159 emit tilesetImagesChanged(tileset);
160 }
161 }
162
163 /**
164 * Sets whether tilesets are automatically reloaded when their tileset
165 * image changes.
166 */
setReloadTilesetsOnChange(bool enabled)167 void TilesetManager::setReloadTilesetsOnChange(bool enabled)
168 {
169 mReloadTilesetsOnChange = enabled;
170 // TODO: Clear the file system watcher when disabled
171 }
172
173 /**
174 * Sets whether tile animations are running.
175 */
setAnimateTiles(bool enabled)176 void TilesetManager::setAnimateTiles(bool enabled)
177 {
178 // TODO: Avoid running the driver when there are no animated tiles
179 if (enabled)
180 mAnimationDriver->start();
181 else
182 mAnimationDriver->stop();
183 }
184
animateTiles() const185 bool TilesetManager::animateTiles() const
186 {
187 return mAnimationDriver->state() == QAbstractAnimation::Running;
188 }
189
tilesetImageSourceChanged(const Tileset & tileset,const QUrl & oldImageSource)190 void TilesetManager::tilesetImageSourceChanged(const Tileset &tileset,
191 const QUrl &oldImageSource)
192 {
193 Q_ASSERT(mTilesets.contains(const_cast<Tileset*>(&tileset)));
194
195 if (oldImageSource.isLocalFile())
196 mWatcher->removePath(oldImageSource.toLocalFile());
197
198 if (tileset.imageSource().isLocalFile())
199 mWatcher->addPath(tileset.imageSource().toLocalFile());
200 }
201
filesChanged(const QStringList & fileNames)202 void TilesetManager::filesChanged(const QStringList &fileNames)
203 {
204 if (!mReloadTilesetsOnChange)
205 return;
206
207 for (const QString &fileName : fileNames)
208 ImageCache::remove(fileName);
209
210 for (Tileset *tileset : qAsConst(mTilesets)) {
211 const QString fileName = tileset->imageSource().toLocalFile();
212 if (fileNames.contains(fileName))
213 if (tileset->loadImage())
214 emit tilesetImagesChanged(tileset);
215 }
216 }
217
218 /**
219 * Resets all tile animations. Used to keep animations synchronized when they
220 * are edited.
221 */
resetTileAnimations()222 void TilesetManager::resetTileAnimations()
223 {
224 // TODO: This could be more optimal by keeping track of the list of
225 // actually animated tiles
226
227 for (Tileset *tileset : qAsConst(mTilesets)) {
228 bool imageChanged = false;
229
230 for (Tile *tile : tileset->tiles())
231 imageChanged |= tile->resetAnimation();
232
233 if (imageChanged)
234 emit repaintTileset(tileset);
235 }
236 }
237
advanceTileAnimations(int ms)238 void TilesetManager::advanceTileAnimations(int ms)
239 {
240 // TODO: This could be more optimal by keeping track of the list of
241 // actually animated tiles
242
243 for (Tileset *tileset : qAsConst(mTilesets)) {
244 bool imageChanged = false;
245
246 for (Tile *tile : tileset->tiles())
247 imageChanged |= tile->advanceAnimation(ms);
248
249 if (imageChanged)
250 emit repaintTileset(tileset);
251 }
252 }
253
254 } // namespace Tiled
255
256 #include "moc_tilesetmanager.cpp"
257