~valavanisalex/ubuntu/maverick/scidavis/fix-604811

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/***************************************************************************
    File                 : AbstractAspect.h
    Project              : SciDAVis
    --------------------------------------------------------------------
    Copyright            : (C) 2007 by Knut Franke, Tilman Benkert
    Email (use @ for *)  : knut.franke*gmx.de, thzs*gmx.net
    Description          : Base class for all persistent objects in a Project.

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  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, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#ifndef ABSTRACT_ASPECT_H
#define ABSTRACT_ASPECT_H

#include <QObject>
#include <QDateTime>

class AspectPrivate;
class Project;
class QUndoStack;
class QString;
class QDateTime;
class QUndoCommand;
class QIcon;
class QMenu;
namespace future{
class Folder;
}
class XmlStreamReader;
#ifdef Q_OS_MAC32
// A hack in Qt 4.4 and later forces us to include QXmlStream* headers on MacOS instead of simple
// forward declarations. See
// http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html
#include <QXmlStreamWriter>
#else
class QXmlStreamWriter;
#endif
class QAction;

//! Base class of all persistent objects in a Project.
/**
 * Before going into the details, it's useful to understand the ideas behind the
 * \ref aspect "Aspect Framework".
 *
 * Aspects organize themselves into trees, where a parent takes ownership of its children. Usually,
 * though not necessarily, a Project instance will sit at the root of the tree (without a Project
 * ancestor, project() will return 0 and undo does not work). Children are organized using
 * addChild(), removeChild(), child(), indexOfChild() and childCount() on the parent's side as well
 * as the equivalent convenience methods index() and remove() on the child's side.
 * 
 * AbstractAspect manages for every Aspect the properties #name, #comment, #caption_spec and
 * #creation_time. All of these translate into the caption() as described in the documentation
 * of setCaptionSpec().
 *
 * If an undoStack() can be found (usually it is managed by Project), changes to the properties
 * as well as adding/removing children support multi-level undo/redo. In order to support undo/redo
 * for problem-specific data in derived classes, make sure that all changes to your data are done
 * by handing appropriate commands to exec().
 *
 * Optionally,
 * you can supply an icon() to be used by different views (including the ProjectExplorer)
 * and/or reimplement createContextMenu() for a custom context menu of views.
 *
 * The private data of AbstractAspect is contained in a separate class AbstractAspect::Private. 
 * The write access to AbstractAspect::Private should always be done using aspect commands
 * to allow undo/redo.
 */
class AbstractAspect : public QObject
{
	Q_OBJECT

	public:
		class Private;
		friend class Private;

		AbstractAspect(const QString &name);
		virtual ~AbstractAspect();

		//! Return my parent Aspect or 0 if I currently don't have one.
		AbstractAspect * parentAspect() const;
		//! Return the folder the Aspect is contained in or 0 if not.
		/**
		 * The returned folder may be the aspect itself if it inherits Folder.
		*/
		future::Folder * folder();
		//! Return whether the there is a path upwards to the given aspect
		/**
		 * This also returns true if other==this.
		 */
		bool isDescendantOf(AbstractAspect *other);

		//! Add the given Aspect to my list of children.
		void addChild(AbstractAspect* child);
		//! Insert the given Aspect at a specific position in my list of children.
		void insertChild(AbstractAspect *child, int index);
		//! Remove the given Aspect from my list of children.
		/**
		 * Usually, the ownership of the child is transfered to the undo command,
		 * i.e., the aspect is deleted by the undo command. But by setting detach
		 * to true, you can remove a child from the Aspect tree without deleting
		 * it. Of course, that means whovere detaches an Aspect in responsible
		 * for deleting it (or re-attaching it somewhere in the tree).
		 * \sa reparentChild()
		 */
		void removeChild(AbstractAspect* child, bool detach=false);
		//! Remove the Aspect at the given index from my list of children.
		/**
		 * The ownership of the child is transfered to the undo command,
		 * i.e., the aspect is deleted by the undo command.
		 * \sa reparentChild()
		 */
		void removeChild(int index);
		//! Get a child by its position in my list of children.
		AbstractAspect* child(int index) const;
		//! Return the number of child Aspects.
		int childCount() const;
		//! Return the position of child in my list of children.
		int indexOfChild(const AbstractAspect * child) const;
		//! Return my position in my parent's list of children.
		int index() const { return parentAspect() ? parentAspect()->indexOfChild(this) : 0; }
		//! Change the positon of a child in my list of children.
		void moveChild(int from, int to);
		//! Move a child to another aspect and transfer ownership.
		void reparentChild(AbstractAspect *new_parent, AbstractAspect *child, int d_new_index);
		//! Move a child to another aspect and transfer ownership.
		void reparentChild(AbstractAspect *new_parent, AbstractAspect *child);
		//! Get all descendents that inherit the given class
		QList<AbstractAspect *> descendantsThatInherit(const char *class_name);
		//! Remove all child aspects
		virtual void removeAllChildAspects();

		//! Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
		virtual const Project *project() const { return parentAspect() ? parentAspect()->project() : 0; }
		//! Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
		virtual Project *project() { return parentAspect() ? parentAspect()->project() : 0; }
		//! Return the path that leads from the top-most Aspect (usually a Project) to me.
		virtual QString path() const { return parentAspect() ? parentAspect()->path() + "/" + name() : "";  }

		//! Return an icon to be used for decorating my views.
		virtual QIcon icon() const;
		//! Return a new context menu.
		/**
		 * The caller takes ownership of the menu.
		 */
		virtual QMenu *createContextMenu() const;

		QString name() const;
		QString comment() const;
		//! Return the specification string used for constructing the caption().
		/**
		 * See setCaptionSpec() for format.
		 */
		QString captionSpec() const;
		QDateTime creationTime() const;
		QString caption() const;

		//! \name undo related
		//@{
		//! Return the undo stack of the Project, or 0 if this Aspect is not part of a Project.
		/**
		 * It's also possible to construct undo-enabled Aspect trees without Project.
		 * The only requirement is that the root Aspect reimplements undoStack() to get the
		 * undo stack from somewhere (the default implementation just delegates to parentAspect()).
		 */
		virtual QUndoStack *undoStack() const { return parentAspect() ? parentAspect()->undoStack() : 0; }
		//! Execute the given command, pushing it on the undoStack() if available.
		void exec(QUndoCommand *command);
		//! Begin an undo stack macro (series of commands)
		void beginMacro(const QString& text);
		//! End the undo stack macro
		void endMacro();
		//@}

		//! Retrieve a global setting.
		static QVariant global(const QString &key);
		//! Update a global setting.
		static void setGlobal(const QString &key, const QVariant &value);
		//! Set default value for a global setting.
		static void setGlobalDefault(const QString &key, const QVariant &value);

		//! \name serialize/deserialize
		//@{
		//! Save as XML
		virtual void save(QXmlStreamWriter *) const {};
		//! Load from XML
		/**
		 * XmlStreamReader supports errors as well as warnings. If only
		 * warnings (non-critial errors) occur, this function must return 
		 * the reader at the end element corresponding to the current 
		 * element at the time the function was called. 
		 *
		 * This function is normally intended to be called directly 
		 * after the ctor. If you want to call load on an aspect that
		 * has been altered, you must make sure beforehand that
		 * it is in the same state as after creation, e.g., remove
		 * all its child aspects.
		 *
		 * \return false on error
		 */
		virtual bool load(XmlStreamReader *) { return false; };
	protected:
		//! Load name, creation time and caption spec from XML
		/**
		 * \return false on error
		 */
		bool readBasicAttributes(XmlStreamReader * reader);
		//! Save name, creation time and caption spec to XML
		void writeBasicAttributes(QXmlStreamWriter * writer) const;
		//! Save the comment to XML
		void writeCommentElement(QXmlStreamWriter * writer) const;
		//! Load comment from an XML element
		bool readCommentElement(XmlStreamReader * reader);
		//@}

	public slots:
		void setName(const QString &value);
		void setComment(const QString &value);
		//! Set the specification string used for constructing the caption().
		/**
		 * The caption is constructed by substituting some magic tokens in the specification:
		 *
		 * - \%n - name()
		 * - \%c - comment()
		 * - \%t - creationTime()
		 *
		 * Additionally, you can use the construct %C{<text>}. <text> is only shown in the caption
		 * if comment() is not empty (name and creation time should never be empty).
		 *
		 * The default caption specification is "%n%C{ - }%c".
		 */
		void setCaptionSpec(const QString &value);
		//! Remove me from my parent's list of children.
		virtual void remove() { if(parentAspect()) parentAspect()->removeChild(parentAspect()->indexOfChild(this)); }
		//! Make the specified name unique among my children by incrementing a trailing number.
		QString uniqueNameFor(const QString &current_name) const;

	public:
		void importV0x0001XXCreationTime(const QString& str)
		{
			setCreationTime(QDateTime::fromString(str, Qt::LocalDate));
		}


	signals:
		//! Emit this before the name, comment or caption spec is changed
		void aspectDescriptionAboutToChange(const AbstractAspect *aspect);
		//! Emit this when the name, comment or caption spec changed
		void aspectDescriptionChanged(const AbstractAspect *aspect);
		//! Emit this when a parent aspect is about to get a new child inserted
		void aspectAboutToBeAdded(const AbstractAspect *parent, int index);
		//! Emit this from a newly added aspect
		void aspectAdded(const AbstractAspect *aspect);
		//! Emit this from a parent after adding a new child to it
		void aspectAdded(const AbstractAspect *parent, int index);
		//! Emit this from an aspect about to be removed from its parent's children
		void aspectAboutToBeRemoved(const AbstractAspect *aspect);
		//! Emit this from a parent before removing its child
		void aspectAboutToBeRemoved(const AbstractAspect *parent, int index);
		//! Emit this from the parent after removing a child
		void aspectRemoved(const AbstractAspect *parent, int index);
		//! Emit this to give status information to the user.
		void statusInfo(const QString &text);

	protected:
		//! Set the creation time
		/**
		 * The creation time will automatically be set when the aspect object
		 * is created. This function is usually only needed when the aspect
		 * is loaded from a file.
		 */
		void setCreationTime(const QDateTime& time);
		//! Called after a new child has been inserted or added.
		/**
		 * Unlike the aspectAdded() signals, this method does not get called inside undo/redo actions;
		 * allowing subclasses to execute undo commands of their own.
		 */
		virtual void completeAspectInsertion(AbstractAspect * aspect, int index) { Q_UNUSED(aspect); Q_UNUSED(index); }
		//! Called before a child is removed.
		/**
		 * Unlike the aspectAboutToBeRemoved() signals, this method does not get called inside undo/redo actions;
		 * allowing subclasses to execute undo commands of their own.
		 */
		virtual void prepareAspectRemoval(AbstractAspect * aspect) { Q_UNUSED(aspect); }
		//! Implementations should call this whenever status information should be given to the user.
		/**
		 * This will cause statusInfo() to be emitted. Typically, this will cause the specified string
		 * to be displayed in a status bar, a log window or some similar non-blocking way so as not to
		 * disturb the workflow.
		 */
		void info(const QString &text) { emit statusInfo(text); }

	private:
		Private * d_aspect_private;
};

#endif // ifndef ABSTRACT_ASPECT_H