1 /*
2  * document.h
3  * Copyright 2015, 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 #pragma once
22 
23 #include "properties.h"
24 
25 #include <QDateTime>
26 #include <QObject>
27 #include <QSharedPointer>
28 #include <QString>
29 #include <QVariant>
30 
31 #include <memory>
32 
33 class QUndoStack;
34 
35 namespace Tiled {
36 
37 class FileFormat;
38 class Object;
39 class Tile;
40 
41 class ChangeEvent;
42 class EditableAsset;
43 
44 /**
45  * Keeps track of a file and its undo history.
46  */
47 class Document : public QObject,
48                  public QEnableSharedFromThis<Document>
49 {
50     Q_OBJECT
51 
52     Q_PROPERTY(QString fileName READ fileName NOTIFY fileNameChanged)
53     Q_PROPERTY(bool modified READ isModified NOTIFY modifiedChanged)
54 
55 public:
56     enum DocumentType {
57         MapDocumentType,
58         TilesetDocumentType,
59         WorldDocumentType
60     };
61 
62     Document(DocumentType type,
63              const QString &fileName = QString(),
64              QObject *parent = nullptr);
65     ~Document() override;
66 
type()67     DocumentType type() const { return mType; }
68 
69     QString fileName() const;
70     QString canonicalFilePath() const;
71 
72     /**
73      * Returns the name with which to display this document. It is the file name
74      * without its path, or 'untitled' when the document has no file name.
75      */
76     virtual QString displayName() const = 0;
77 
78     /**
79      * Saves the document to the file at \a fileName. Returns whether or not
80      * the file was saved successfully. If not, <i>error</i> will be set to the
81      * error message if it is not 0.
82      *
83      * If the save was successful, the file name of this document will be set
84      * to \a fileName.
85      *
86      * The file format will be the same as this map was opened with.
87      */
88     virtual bool save(const QString &fileName, QString *error = nullptr) = 0;
89 
90     virtual FileFormat *writerFormat() const = 0;
91 
lastSaved()92     QDateTime lastSaved() const { return mLastSaved; }
93 
94     QUndoStack *undoStack() const;
95     bool isModified() const;
96 
97     Q_INVOKABLE virtual Tiled::EditableAsset *editable() = 0;
98 
currentObject()99     Object *currentObject() const { return mCurrentObject; }
100     void setCurrentObject(Object *object);
101     void setCurrentObject(Object *object, Document *owningDocument);
102 
103     virtual QList<Object*> currentObjects() const;
104 
105     void setProperty(Object *object, const QString &name, const QVariant &value);
106     void setProperties(Object *object, const Properties &properties);
107     void removeProperty(Object *object, const QString &name);
108 
109     bool ignoreBrokenLinks() const;
110     void setIgnoreBrokenLinks(bool ignoreBrokenLinks);
111 
112     bool changedOnDisk() const;
113     void setChangedOnDisk(bool changedOnDisk);
114 
115     virtual QString lastExportFileName() const = 0;
116     virtual void setLastExportFileName(const QString &fileName) = 0;
117 
118     virtual FileFormat *exportFormat() const = 0;
119     virtual void setExportFormat(FileFormat *format) = 0;
120 
checkIssues()121     virtual void checkIssues() {}
122 
123     static const QHash<QString, Document *> &documentInstances();
124 
125 signals:
126     void changed(const ChangeEvent &change);
127     void saved();
128 
129     void fileNameChanged(const QString &fileName,
130                          const QString &oldFileName);
131     void modifiedChanged();
132 
133     void currentObjectChanged(Object *object);
134 
135     /**
136      * Makes the Properties window visible and take focus.
137      */
138     void editCurrentObject();
139 
140     void propertyAdded(Object *object, const QString &name);
141     void propertyRemoved(Object *object, const QString &name);
142     void propertyChanged(Object *object, const QString &name);
143     void propertiesChanged(Object *object);
144 
145     void ignoreBrokenLinksChanged(bool ignoreBrokenLinks);
146 
147 protected:
148     void setFileName(const QString &fileName);
149 
150     void checkFilePathProperties(const Object *object) const;
151 
152     QDateTime mLastSaved;
153 
154     Object *mCurrentObject = nullptr;   /**< Current properties object. */
155     Document *mCurrentObjectDocument = nullptr;
156 
157     std::unique_ptr<EditableAsset> mEditable;
158 
159 private:
160     void currentObjectDocumentChanged(const ChangeEvent &change);
161     void currentObjectDocumentDestroyed();
162 
163     const DocumentType mType;
164 
165     QString mFileName;
166     QString mCanonicalFilePath;
167 
168     QUndoStack * const mUndoStack;
169 
170     bool mChangedOnDisk = false;
171     bool mIgnoreBrokenLinks = false;
172 
173     static QHash<QString, Document*> sDocumentInstances;
174 };
175 
176 
fileName()177 inline QString Document::fileName() const
178 {
179     return mFileName;
180 }
181 
canonicalFilePath()182 inline QString Document::canonicalFilePath() const
183 {
184     return mCanonicalFilePath;
185 }
186 
187 /**
188  * Returns the undo stack of this document. Should be used to push any commands
189  * on that modify the document.
190  */
undoStack()191 inline QUndoStack *Document::undoStack() const
192 {
193     return mUndoStack;
194 }
195 
196 /**
197  * Sets the current object for this document (displayed in the Properties view).
198  *
199  * This version should only be used with objects owned by this document. See
200  * setCurrentObject(Object*, Document*) for setting it to an object from
201  * another document.
202  */
setCurrentObject(Object * object)203 inline void Document::setCurrentObject(Object *object)
204 {
205     setCurrentObject(object, this);
206 }
207 
ignoreBrokenLinks()208 inline bool Document::ignoreBrokenLinks() const
209 {
210     return mIgnoreBrokenLinks;
211 }
212 
changedOnDisk()213 inline bool Document::changedOnDisk() const
214 {
215     return mChangedOnDisk;
216 }
217 
documentInstances()218 inline const QHash<QString, Document *> &Document::documentInstances()
219 {
220     return sDocumentInstances;
221 }
222 
223 using DocumentPtr = QSharedPointer<Document>;
224 
225 } // namespace Tiled
226