Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deals with #825 and enables reloading image layers #912

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/libtiled/imagelayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ void ImageLayer::resetImage()
mImageSource.clear();
}

bool ImageLayer::loadFromImage(const QString &fileName)
{
return loadFromImage(QImage(fileName), fileName);
}

bool ImageLayer::loadFromImage(const QImage &image, const QString &fileName)
{
mImageSource = fileName;
Expand Down
2 changes: 2 additions & 0 deletions src/libtiled/imagelayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ class TILEDSHARED_EXPORT ImageLayer : public Layer
* @return <code>true</code> if loading was successful, otherwise
* returns <code>false</code>
*/
bool loadFromImage(const QString &fileName);

bool loadFromImage(const QImage &image, const QString &fileName);

/**
Expand Down
29 changes: 23 additions & 6 deletions src/tiled/changeimagelayerproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "mapdocument.h"
#include "imagelayer.h"
#include "imagelayermanager.h"

#include <QCoreApplication>

Expand All @@ -50,24 +51,40 @@ ChangeImageLayerProperties::ChangeImageLayerProperties(
void ChangeImageLayerProperties::redo()
{
mImageLayer->setTransparentColor(mRedoColor);
ImageLayerManager *manager = ImageLayerManager::instance();

if (mRedoPath.isEmpty())
if (mRedoPath.isEmpty()) {
mImageLayer->resetImage();
else
mImageLayer->loadFromImage(QImage(mRedoPath), mRedoPath);
manager->removeReference(mImageLayer);
}
else {
manager->removeReference(mImageLayer);
mImageLayer->loadFromImage(mRedoPath);
manager->addReference(mImageLayer);
}

mMapDocument->emitImageLayerChanged(mImageLayer);
}

void ChangeImageLayerProperties::undo()
{
mImageLayer->setTransparentColor(mUndoColor);
ImageLayerManager *manager = ImageLayerManager::instance();

if (mUndoPath.isEmpty())
if (mUndoPath.isEmpty()){
mImageLayer->resetImage();
else
mImageLayer->loadFromImage(QImage(mUndoPath), mUndoPath);
manager->removeReference(mImageLayer);
}
else {
manager->removeReference(mImageLayer);
mImageLayer->loadFromImage(mUndoPath);
manager->addReference(mImageLayer);
}

mMapDocument->emitImageLayerChanged(mImageLayer);
}

ChangeImageLayerProperties::~ChangeImageLayerProperties()
{
ImageLayerManager::instance()->removeReference(mImageLayer);
}
3 changes: 3 additions & 0 deletions src/tiled/changeimagelayerproperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class ChangeImageLayerProperties : public QUndoCommand
void redo();

private:

~ChangeImageLayerProperties();

MapDocument *mMapDocument;
ImageLayer *mImageLayer;
const QColor mUndoColor;
Expand Down
153 changes: 153 additions & 0 deletions src/tiled/imagelayermanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* tilesetmanager.cpp
* Copyright 2008-2014, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
* Copyright 2009, Edward Hutchins <eah1@yahoo.com>
*
* This file is part of Tiled.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "imagelayermanager.h"

#include "filesystemwatcher.h"
#include "imagelayer.h"

#include <QImage>

using namespace Tiled;
using namespace Tiled::Internal;

ImageLayerManager *ImageLayerManager::mInstance = 0;

ImageLayerManager::ImageLayerManager():
mWatcher(new FileSystemWatcher(this)),
mReloadImageLayersOnChange(false)
{
connect(mWatcher, SIGNAL(fileChanged(QString)),
this, SLOT(fileChanged(QString)));

mChangedFilesTimer.setInterval(500);
mChangedFilesTimer.setSingleShot(true);

connect(&mChangedFilesTimer, SIGNAL(timeout()),
this, SLOT(fileChangedTimeout()));
}

ImageLayerManager::~ImageLayerManager()
{
// Since all MapDocuments should be deleted first, we assert that there are
// no remaining ImageLayer references.
Q_ASSERT(mImageLayers.size() == 0);
}

ImageLayerManager *ImageLayerManager::instance()
{
if (!mInstance)
mInstance = new ImageLayerManager;

return mInstance;
}

void ImageLayerManager::deleteInstance()
{
delete mInstance;
mInstance = 0;
}

ImageLayer *ImageLayerManager::findImageLayer(const QString &fileName) const
{
foreach (ImageLayer *layer, imageLayers())
if (layer->imageSource() == fileName)
return layer;

return 0;
}

void ImageLayerManager::addReference(ImageLayer *layer)
{
mImageLayers.insert(layer);
mWatcher->addPath(layer->imageSource());
}

void ImageLayerManager::removeReference(ImageLayer *layer)
{
if(mImageLayers.contains(layer)) {
mImageLayers.remove(layer);
mWatcher->removePath(layer->imageSource());
}
}

void ImageLayerManager::addReferences(const QList<ImageLayer*> &layers)
{
foreach (ImageLayer *layer, layers)
addReference(layer);
}

void ImageLayerManager::removeReferences(const QList<ImageLayer*> &layers)
{
foreach (ImageLayer *layer, layers)
removeReference(layer);
}

QList<ImageLayer*> ImageLayerManager::imageLayers() const
{
return mImageLayers.toList();
}

void ImageLayerManager::forceImageLayersReload(ImageLayer *layer)
{
if (!mImageLayers.contains(layer))
return;

QString fileName = layer->imageSource();
if (layer->loadFromImage(fileName))
emit imageLayerChanged(layer);
}

void ImageLayerManager::fileChanged(const QString &path)
{
if (!mReloadImageLayersOnChange)
return;

/*
* Use a one-shot timer since GIMP (for example) seems to generate many
* file changes during a save, and some of the intermediate attempts to
* reload the tileset images actually fail (at least for .png files).
*/
mChangedFiles.insert(path);
mChangedFilesTimer.start();
}

void ImageLayerManager::fileChangedTimeout()
{
QMap<QString, ImageLayer*> loadedImages;
foreach (ImageLayer *layer, imageLayers()) {
QString fileName = layer->imageSource();
if(!loadedImages.keys().contains(fileName)) {
loadedImages.insert(fileName, layer);
if (mChangedFiles.contains(fileName))
if (layer->loadFromImage(fileName))
emit imageLayerChanged(layer);
}
else {
if (mChangedFiles.contains(fileName)) {
layer->loadFromImage(loadedImages.value(fileName)->image().toImage(), fileName);
emit imageLayerChanged(layer);
}
}

}
mChangedFiles.clear();
}
150 changes: 150 additions & 0 deletions src/tiled/imagelayermanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* imagelayermanager.h
* Copyright 2008-2014, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
* Copyright 2009, Edward Hutchins <eah1@yahoo.com>
*
* This file is part of Tiled.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef IMAGELAYERMANAGER_H
#define IMAGELAYERMANAGER_H

#include <QObject>
#include <QList>
#include <QMap>
#include <QString>
#include <QSet>
#include <QTimer>

namespace Tiled {

class ImageLayer;

namespace Internal {

class FileSystemWatcher;

/**
* The imageLayer manager keeps track of all imageLayers used by loaded maps. It also
* watches the imageLayer images for changes and will attempt to reload them when
* they change.
*/
class ImageLayerManager : public QObject
{
Q_OBJECT

public:
/**
* Requests the ImageLayer manager. When the manager doesn't exist yet, it
* will be created.
*/
static ImageLayerManager *instance();

/**
* Deletes the ImageLayer manager instance, when it exists.
*/
static void deleteInstance();

/**
* Searches for a ImageLayer matching the given file name.
* @return a ImageLayer matching the given file name, or 0 if none exists
*/
ImageLayer *findImageLayer(const QString &fileName) const;

/**
* Adds a ImageLayer reference. This will make sure the ImageLayer doesn't get
* deleted.
*/
void addReference(ImageLayer *layer);

/**
* Removes a ImageLayer reference. This needs to be done before a ImageLayer can
* be deleted.
*/
void removeReference(ImageLayer *layer);

/**
* Convenience method to add references to multiple ImageLayers.
* @see addReference
*/
void addReferences(const QList<ImageLayer*> &layers);

/**
* Convenience method to remove references from multiple ImageLayers.
* @see removeReference
*/
void removeReferences(const QList<ImageLayer*> &layers);

/**
* Returns all currently available ImageLayers.
*/
QList<ImageLayer*> imageLayers() const;

/**
* Forces a ImageLayer to reload.
*/
void forceImageLayersReload(ImageLayer *layer);

/**
* Sets whether ImageLayers are automatically reloaded when their ImageLayer
* image changes.
*/
void setReloadImageLayersOnChange(bool enabled) { mReloadImageLayersOnChange = enabled; }
bool reloadImageLayersOnChange() const { return mReloadImageLayersOnChange; }

signals:
/**
* Emitted when a ImageLayer's images have changed and views need updating.
*/
void imageLayerChanged(ImageLayer *layer);

/**
* Emitted when any images in the given \a ImageLayer have changed.
*/
void repaintImageLayer(ImageLayer *layer);

private slots:
void fileChanged(const QString &path);
void fileChangedTimeout();

private:
Q_DISABLE_COPY(ImageLayerManager)

/**
* Constructor. Only used by the ImageLayer manager itself.
*/
ImageLayerManager();

/**
* Destructor.
*/
~ImageLayerManager();

static ImageLayerManager *mInstance;

/**
* Stores the ImageLayers and maps them to the number of references.
*/
QSet<ImageLayer*> mImageLayers;
FileSystemWatcher *mWatcher;
QSet<QString> mChangedFiles;
QTimer mChangedFilesTimer;
bool mReloadImageLayersOnChange;
};
} // namespace Internal
} // namespace Tiled

#endif // IMAGELAYERMANAGER_H
Loading