1
/***************************************************************************
2
File : AbstractAspect.h
4
--------------------------------------------------------------------
5
Copyright : (C) 2007 by Knut Franke, Tilman Benkert
6
Email (use @ for *) : knut.franke*gmx.de, thzs*gmx.net
7
Description : Base class for all persistent objects in a Project.
9
***************************************************************************/
11
/***************************************************************************
13
* This program is free software; you can redistribute it and/or modify *
14
* it under the terms of the GNU General Public License as published by *
15
* the Free Software Foundation; either version 2 of the License, or *
16
* (at your option) any later version. *
18
* This program is distributed in the hope that it will be useful, *
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21
* GNU General Public License for more details. *
23
* You should have received a copy of the GNU General Public License *
24
* along with this program; if not, write to the Free Software *
25
* Foundation, Inc., 51 Franklin Street, Fifth Floor, *
26
* Boston, MA 02110-1301 USA *
28
***************************************************************************/
29
#ifndef ABSTRACT_ASPECT_H
30
#define ABSTRACT_ASPECT_H
46
class XmlStreamReader;
48
// A hack in Qt 4.4 and later forces us to include QXmlStream* headers on MacOS instead of simple
49
// forward declarations. See
50
// http://lists.trolltech.com/qt-interest/2008-07/thread00798-0.html
51
#include <QXmlStreamWriter>
53
class QXmlStreamWriter;
57
//! Base class of all persistent objects in a Project.
59
* Before going into the details, it's useful to understand the ideas behind the
60
* \ref aspect "Aspect Framework".
62
* Aspects organize themselves into trees, where a parent takes ownership of its children. Usually,
63
* though not necessarily, a Project instance will sit at the root of the tree (without a Project
64
* ancestor, project() will return 0 and undo does not work). Children are organized using
65
* addChild(), removeChild(), child(), indexOfChild() and childCount() on the parent's side as well
66
* as the equivalent convenience methods index() and remove() on the child's side.
68
* AbstractAspect manages for every Aspect the properties #name, #comment, #caption_spec and
69
* #creation_time. All of these translate into the caption() as described in the documentation
70
* of setCaptionSpec().
72
* If an undoStack() can be found (usually it is managed by Project), changes to the properties
73
* as well as adding/removing children support multi-level undo/redo. In order to support undo/redo
74
* for problem-specific data in derived classes, make sure that all changes to your data are done
75
* by handing appropriate commands to exec().
78
* you can supply an icon() to be used by different views (including the ProjectExplorer)
79
* and/or reimplement createContextMenu() for a custom context menu of views.
81
* The private data of AbstractAspect is contained in a separate class AbstractAspect::Private.
82
* The write access to AbstractAspect::Private should always be done using aspect commands
85
class AbstractAspect : public QObject
93
AbstractAspect(const QString &name);
94
virtual ~AbstractAspect();
96
//! Return my parent Aspect or 0 if I currently don't have one.
97
AbstractAspect * parentAspect() const;
98
//! Return the folder the Aspect is contained in or 0 if not.
100
* The returned folder may be the aspect itself if it inherits Folder.
102
future::Folder * folder();
103
//! Return whether the there is a path upwards to the given aspect
105
* This also returns true if other==this.
107
bool isDescendantOf(AbstractAspect *other);
109
//! Add the given Aspect to my list of children.
110
void addChild(AbstractAspect* child);
111
//! Insert the given Aspect at a specific position in my list of children.
112
void insertChild(AbstractAspect *child, int index);
113
//! Remove the given Aspect from my list of children.
115
* Usually, the ownership of the child is transfered to the undo command,
116
* i.e., the aspect is deleted by the undo command. But by setting detach
117
* to true, you can remove a child from the Aspect tree without deleting
118
* it. Of course, that means whovere detaches an Aspect in responsible
119
* for deleting it (or re-attaching it somewhere in the tree).
120
* \sa reparentChild()
122
void removeChild(AbstractAspect* child, bool detach=false);
123
//! Remove the Aspect at the given index from my list of children.
125
* The ownership of the child is transfered to the undo command,
126
* i.e., the aspect is deleted by the undo command.
127
* \sa reparentChild()
129
void removeChild(int index);
130
//! Get a child by its position in my list of children.
131
AbstractAspect* child(int index) const;
132
//! Return the number of child Aspects.
133
int childCount() const;
134
//! Return the position of child in my list of children.
135
int indexOfChild(const AbstractAspect * child) const;
136
//! Return my position in my parent's list of children.
137
int index() const { return parentAspect() ? parentAspect()->indexOfChild(this) : 0; }
138
//! Change the positon of a child in my list of children.
139
void moveChild(int from, int to);
140
//! Move a child to another aspect and transfer ownership.
141
void reparentChild(AbstractAspect *new_parent, AbstractAspect *child, int d_new_index);
142
//! Move a child to another aspect and transfer ownership.
143
void reparentChild(AbstractAspect *new_parent, AbstractAspect *child);
144
//! Get all descendents that inherit the given class
145
QList<AbstractAspect *> descendantsThatInherit(const char *class_name);
146
//! Remove all child aspects
147
virtual void removeAllChildAspects();
149
//! Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
150
virtual const Project *project() const { return parentAspect() ? parentAspect()->project() : 0; }
151
//! Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
152
virtual Project *project() { return parentAspect() ? parentAspect()->project() : 0; }
153
//! Return the path that leads from the top-most Aspect (usually a Project) to me.
154
virtual QString path() const { return parentAspect() ? parentAspect()->path() + "/" + name() : ""; }
156
//! Return an icon to be used for decorating my views.
157
virtual QIcon icon() const;
158
//! Return a new context menu.
160
* The caller takes ownership of the menu.
162
virtual QMenu *createContextMenu() const;
164
QString name() const;
165
QString comment() const;
166
//! Return the specification string used for constructing the caption().
168
* See setCaptionSpec() for format.
170
QString captionSpec() const;
171
QDateTime creationTime() const;
172
QString caption() const;
174
//! \name undo related
176
//! Return the undo stack of the Project, or 0 if this Aspect is not part of a Project.
178
* It's also possible to construct undo-enabled Aspect trees without Project.
179
* The only requirement is that the root Aspect reimplements undoStack() to get the
180
* undo stack from somewhere (the default implementation just delegates to parentAspect()).
182
virtual QUndoStack *undoStack() const { return parentAspect() ? parentAspect()->undoStack() : 0; }
183
//! Execute the given command, pushing it on the undoStack() if available.
184
void exec(QUndoCommand *command);
185
//! Begin an undo stack macro (series of commands)
186
void beginMacro(const QString& text);
187
//! End the undo stack macro
191
//! Retrieve a global setting.
192
static QVariant global(const QString &key);
193
//! Update a global setting.
194
static void setGlobal(const QString &key, const QVariant &value);
195
//! Set default value for a global setting.
196
static void setGlobalDefault(const QString &key, const QVariant &value);
198
//! \name serialize/deserialize
201
virtual void save(QXmlStreamWriter *) const {};
204
* XmlStreamReader supports errors as well as warnings. If only
205
* warnings (non-critial errors) occur, this function must return
206
* the reader at the end element corresponding to the current
207
* element at the time the function was called.
209
* This function is normally intended to be called directly
210
* after the ctor. If you want to call load on an aspect that
211
* has been altered, you must make sure beforehand that
212
* it is in the same state as after creation, e.g., remove
213
* all its child aspects.
215
* \return false on error
217
virtual bool load(XmlStreamReader *) { return false; };
219
//! Load name, creation time and caption spec from XML
221
* \return false on error
223
bool readBasicAttributes(XmlStreamReader * reader);
224
//! Save name, creation time and caption spec to XML
225
void writeBasicAttributes(QXmlStreamWriter * writer) const;
226
//! Save the comment to XML
227
void writeCommentElement(QXmlStreamWriter * writer) const;
228
//! Load comment from an XML element
229
bool readCommentElement(XmlStreamReader * reader);
233
void setName(const QString &value);
234
void setComment(const QString &value);
235
//! Set the specification string used for constructing the caption().
237
* The caption is constructed by substituting some magic tokens in the specification:
241
* - \%t - creationTime()
243
* Additionally, you can use the construct %C{<text>}. <text> is only shown in the caption
244
* if comment() is not empty (name and creation time should never be empty).
246
* The default caption specification is "%n%C{ - }%c".
248
void setCaptionSpec(const QString &value);
249
//! Remove me from my parent's list of children.
250
virtual void remove() { if(parentAspect()) parentAspect()->removeChild(parentAspect()->indexOfChild(this)); }
251
//! Make the specified name unique among my children by incrementing a trailing number.
252
QString uniqueNameFor(const QString ¤t_name) const;
255
void importV0x0001XXCreationTime(const QString& str)
257
setCreationTime(QDateTime::fromString(str, Qt::LocalDate));
262
//! Emit this before the name, comment or caption spec is changed
263
void aspectDescriptionAboutToChange(const AbstractAspect *aspect);
264
//! Emit this when the name, comment or caption spec changed
265
void aspectDescriptionChanged(const AbstractAspect *aspect);
266
//! Emit this when a parent aspect is about to get a new child inserted
267
void aspectAboutToBeAdded(const AbstractAspect *parent, int index);
268
//! Emit this from a newly added aspect
269
void aspectAdded(const AbstractAspect *aspect);
270
//! Emit this from a parent after adding a new child to it
271
void aspectAdded(const AbstractAspect *parent, int index);
272
//! Emit this from an aspect about to be removed from its parent's children
273
void aspectAboutToBeRemoved(const AbstractAspect *aspect);
274
//! Emit this from a parent before removing its child
275
void aspectAboutToBeRemoved(const AbstractAspect *parent, int index);
276
//! Emit this from the parent after removing a child
277
void aspectRemoved(const AbstractAspect *parent, int index);
278
//! Emit this to give status information to the user.
279
void statusInfo(const QString &text);
282
//! Set the creation time
284
* The creation time will automatically be set when the aspect object
285
* is created. This function is usually only needed when the aspect
286
* is loaded from a file.
288
void setCreationTime(const QDateTime& time);
289
//! Called after a new child has been inserted or added.
291
* Unlike the aspectAdded() signals, this method does not get called inside undo/redo actions;
292
* allowing subclasses to execute undo commands of their own.
294
virtual void completeAspectInsertion(AbstractAspect * aspect, int index) { Q_UNUSED(aspect); Q_UNUSED(index); }
295
//! Called before a child is removed.
297
* Unlike the aspectAboutToBeRemoved() signals, this method does not get called inside undo/redo actions;
298
* allowing subclasses to execute undo commands of their own.
300
virtual void prepareAspectRemoval(AbstractAspect * aspect) { Q_UNUSED(aspect); }
301
//! Implementations should call this whenever status information should be given to the user.
303
* This will cause statusInfo() to be emitted. Typically, this will cause the specified string
304
* to be displayed in a status bar, a log window or some similar non-blocking way so as not to
305
* disturb the workflow.
307
void info(const QString &text) { emit statusInfo(text); }
310
Private * d_aspect_private;
313
#endif // ifndef ABSTRACT_ASPECT_H