1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qundogroup.h"
41 #include "qundostack.h"
42 #include "qundostack_p.h"
43 
44 QT_BEGIN_NAMESPACE
45 
46 class QUndoGroupPrivate : public QObjectPrivate
47 {
48     Q_DECLARE_PUBLIC(QUndoGroup)
49 public:
QUndoGroupPrivate()50     QUndoGroupPrivate() : active(nullptr) {}
51 
52     QUndoStack *active;
53     QList<QUndoStack*> stack_list;
54 };
55 
56 /*!
57     \class QUndoGroup
58     \brief The QUndoGroup class is a group of QUndoStack objects.
59     \since 4.2
60     \inmodule QtWidgets
61 
62     For an overview of the Qt's undo framework, see the
63     \l{qundo.html}{overview}.
64 
65     An application often has multiple undo stacks, one for each opened document. At the
66     same time, an application usually has one undo action and one redo action, which
67     triggers undo or redo in the active document.
68 
69     QUndoGroup is a group of QUndoStack objects, one of which may be active. It has
70     an undo() and redo() slot, which calls QUndoStack::undo() and QUndoStack::redo()
71     for the active stack. It also has the functions createUndoAction() and createRedoAction().
72     The actions returned by these functions behave in the same way as those returned by
73     QUndoStack::createUndoAction() and QUndoStack::createRedoAction() of the active
74     stack.
75 
76     Stacks are added to a group with addStack() and removed with removeStack(). A stack
77     is implicitly added to a group when it is created with the group as its parent
78     QObject.
79 
80     It is the programmer's responsibility to specify which stack is active by
81     calling QUndoStack::setActive(), usually when the associated document window receives focus.
82     The active stack may also be set with setActiveStack(), and is returned by activeStack().
83 
84     When a stack is added to a group using addStack(), the group does not take ownership
85     of the stack. This means the stack has to be deleted separately from the group. When
86     a stack is deleted, it is automatically removed from a group. A stack may belong to
87     only one group. Adding it to another group will cause it to be removed from the previous
88     group.
89 
90     A QUndoGroup is also useful in conjunction with QUndoView. If a QUndoView is
91     set to watch a group using QUndoView::setGroup(), it will update itself to display
92     the active stack.
93 */
94 
95 /*!
96     Creates an empty QUndoGroup object with parent \a parent.
97 
98     \sa addStack()
99 */
100 
QUndoGroup(QObject * parent)101 QUndoGroup::QUndoGroup(QObject *parent)
102     : QObject(*new QUndoGroupPrivate(), parent)
103 {
104 }
105 
106 /*!
107     Destroys the QUndoGroup.
108 */
~QUndoGroup()109 QUndoGroup::~QUndoGroup()
110 {
111     // Ensure all QUndoStacks no longer refer to this group.
112     Q_D(QUndoGroup);
113     QList<QUndoStack *>::iterator it = d->stack_list.begin();
114     QList<QUndoStack *>::iterator end = d->stack_list.end();
115     while (it != end) {
116         (*it)->d_func()->group = nullptr;
117         ++it;
118     }
119 }
120 
121 /*!
122     Adds \a stack to this group. The group does not take ownership of the stack. Another
123     way of adding a stack to a group is by specifying the group as the stack's parent
124     QObject in QUndoStack::QUndoStack(). In this case, the stack is deleted when the
125     group is deleted, in the usual manner of QObjects.
126 
127     \sa removeStack(), stacks(), QUndoStack::QUndoStack()
128 */
129 
addStack(QUndoStack * stack)130 void QUndoGroup::addStack(QUndoStack *stack)
131 {
132     Q_D(QUndoGroup);
133 
134     if (d->stack_list.contains(stack))
135         return;
136     d->stack_list.append(stack);
137 
138     if (QUndoGroup *other = stack->d_func()->group)
139         other->removeStack(stack);
140     stack->d_func()->group = this;
141 }
142 
143 /*!
144     Removes \a stack from this group. If the stack was the active stack in the group,
145     the active stack becomes 0.
146 
147     \sa addStack(), stacks(), QUndoStack::~QUndoStack()
148 */
149 
removeStack(QUndoStack * stack)150 void QUndoGroup::removeStack(QUndoStack *stack)
151 {
152     Q_D(QUndoGroup);
153 
154     if (d->stack_list.removeAll(stack) == 0)
155         return;
156     if (stack == d->active)
157         setActiveStack(nullptr);
158     stack->d_func()->group = nullptr;
159 }
160 
161 /*!
162     Returns a list of stacks in this group.
163 
164     \sa addStack(), removeStack()
165 */
166 
stacks() const167 QList<QUndoStack*> QUndoGroup::stacks() const
168 {
169     Q_D(const QUndoGroup);
170     return d->stack_list;
171 }
172 
173 /*!
174     Sets the active stack of this group to \a stack.
175 
176     If the stack is not a member of this group, this function does nothing.
177 
178     Synonymous with calling QUndoStack::setActive() on \a stack.
179 
180     The actions returned by createUndoAction() and createRedoAction() will now behave
181     in the same way as those returned by \a stack's QUndoStack::createUndoAction()
182     and QUndoStack::createRedoAction().
183 
184     \sa QUndoStack::setActive(), activeStack()
185 */
186 
setActiveStack(QUndoStack * stack)187 void QUndoGroup::setActiveStack(QUndoStack *stack)
188 {
189     Q_D(QUndoGroup);
190     if (d->active == stack)
191         return;
192 
193     if (d->active != nullptr) {
194         disconnect(d->active, SIGNAL(canUndoChanged(bool)),
195                     this, SIGNAL(canUndoChanged(bool)));
196         disconnect(d->active, SIGNAL(undoTextChanged(QString)),
197                     this, SIGNAL(undoTextChanged(QString)));
198         disconnect(d->active, SIGNAL(canRedoChanged(bool)),
199                     this, SIGNAL(canRedoChanged(bool)));
200         disconnect(d->active, SIGNAL(redoTextChanged(QString)),
201                     this, SIGNAL(redoTextChanged(QString)));
202         disconnect(d->active, SIGNAL(indexChanged(int)),
203                     this, SIGNAL(indexChanged(int)));
204         disconnect(d->active, SIGNAL(cleanChanged(bool)),
205                     this, SIGNAL(cleanChanged(bool)));
206     }
207 
208     d->active = stack;
209 
210     if (d->active == nullptr) {
211         emit canUndoChanged(false);
212         emit undoTextChanged(QString());
213         emit canRedoChanged(false);
214         emit redoTextChanged(QString());
215         emit cleanChanged(true);
216         emit indexChanged(0);
217     } else {
218         connect(d->active, SIGNAL(canUndoChanged(bool)),
219                 this, SIGNAL(canUndoChanged(bool)));
220         connect(d->active, SIGNAL(undoTextChanged(QString)),
221                 this, SIGNAL(undoTextChanged(QString)));
222         connect(d->active, SIGNAL(canRedoChanged(bool)),
223                 this, SIGNAL(canRedoChanged(bool)));
224         connect(d->active, SIGNAL(redoTextChanged(QString)),
225                 this, SIGNAL(redoTextChanged(QString)));
226         connect(d->active, SIGNAL(indexChanged(int)),
227                 this, SIGNAL(indexChanged(int)));
228         connect(d->active, SIGNAL(cleanChanged(bool)),
229                 this, SIGNAL(cleanChanged(bool)));
230         emit canUndoChanged(d->active->canUndo());
231         emit undoTextChanged(d->active->undoText());
232         emit canRedoChanged(d->active->canRedo());
233         emit redoTextChanged(d->active->redoText());
234         emit cleanChanged(d->active->isClean());
235         emit indexChanged(d->active->index());
236     }
237 
238     emit activeStackChanged(d->active);
239 }
240 
241 /*!
242     Returns the active stack of this group.
243 
244     If none of the stacks are active, or if the group is empty, this function
245     returns \nullptr.
246 
247     \sa setActiveStack(), QUndoStack::setActive()
248 */
249 
activeStack() const250 QUndoStack *QUndoGroup::activeStack() const
251 {
252     Q_D(const QUndoGroup);
253     return d->active;
254 }
255 
256 /*!
257     Calls QUndoStack::undo() on the active stack.
258 
259     If none of the stacks are active, or if the group is empty, this function
260     does nothing.
261 
262     \sa redo(), canUndo(), setActiveStack()
263 */
264 
undo()265 void QUndoGroup::undo()
266 {
267     Q_D(QUndoGroup);
268     if (d->active != nullptr)
269         d->active->undo();
270 }
271 
272 /*!
273     Calls QUndoStack::redo() on the active stack.
274 
275     If none of the stacks are active, or if the group is empty, this function
276     does nothing.
277 
278     \sa undo(), canRedo(), setActiveStack()
279 */
280 
281 
redo()282 void QUndoGroup::redo()
283 {
284     Q_D(QUndoGroup);
285     if (d->active != nullptr)
286         d->active->redo();
287 }
288 
289 /*!
290     Returns the value of the active stack's QUndoStack::canUndo().
291 
292     If none of the stacks are active, or if the group is empty, this function
293     returns \c false.
294 
295     \sa canRedo(), setActiveStack()
296 */
297 
canUndo() const298 bool QUndoGroup::canUndo() const
299 {
300     Q_D(const QUndoGroup);
301     return d->active != nullptr && d->active->canUndo();
302 }
303 
304 /*!
305     Returns the value of the active stack's QUndoStack::canRedo().
306 
307     If none of the stacks are active, or if the group is empty, this function
308     returns \c false.
309 
310     \sa canUndo(), setActiveStack()
311 */
312 
canRedo() const313 bool QUndoGroup::canRedo() const
314 {
315     Q_D(const QUndoGroup);
316     return d->active != nullptr && d->active->canRedo();
317 }
318 
319 /*!
320     Returns the value of the active stack's QUndoStack::undoText().
321 
322     If none of the stacks are active, or if the group is empty, this function
323     returns an empty string.
324 
325     \sa redoText(), setActiveStack()
326 */
327 
undoText() const328 QString QUndoGroup::undoText() const
329 {
330     Q_D(const QUndoGroup);
331     return d->active == nullptr ? QString() : d->active->undoText();
332 }
333 
334 /*!
335     Returns the value of the active stack's QUndoStack::redoText().
336 
337     If none of the stacks are active, or if the group is empty, this function
338     returns an empty string.
339 
340     \sa undoText(), setActiveStack()
341 */
342 
redoText() const343 QString QUndoGroup::redoText() const
344 {
345     Q_D(const QUndoGroup);
346     return d->active == nullptr ? QString() : d->active->redoText();
347 }
348 
349 /*!
350     Returns the value of the active stack's QUndoStack::isClean().
351 
352     If none of the stacks are active, or if the group is empty, this function
353     returns \c true.
354 
355     \sa setActiveStack()
356 */
357 
isClean() const358 bool QUndoGroup::isClean() const
359 {
360     Q_D(const QUndoGroup);
361     return d->active == nullptr || d->active->isClean();
362 }
363 
364 #ifndef QT_NO_ACTION
365 
366 /*!
367     Creates an undo QAction object with parent \a parent.
368 
369     Triggering this action will cause a call to QUndoStack::undo() on the active stack.
370     The text of this action will always be the text of the command which will be undone
371     in the next call to undo(), prefixed by \a prefix. If there is no command available
372     for undo, if the group is empty or if none of the stacks are active, this action will
373     be disabled.
374 
375     If \a prefix is empty, the default template "Undo %1" is used instead of prefix.
376     Before Qt 4.8, the prefix "Undo" was used by default.
377 
378     \sa createRedoAction(), canUndo(), QUndoCommand::text()
379 */
380 
createUndoAction(QObject * parent,const QString & prefix) const381 QAction *QUndoGroup::createUndoAction(QObject *parent, const QString &prefix) const
382 {
383     QUndoAction *result = new QUndoAction(prefix, parent);
384     if (prefix.isEmpty())
385         result->setTextFormat(tr("Undo %1"), tr("Undo", "Default text for undo action"));
386 
387     result->setEnabled(canUndo());
388     result->setPrefixedText(undoText());
389     connect(this, SIGNAL(canUndoChanged(bool)),
390             result, SLOT(setEnabled(bool)));
391     connect(this, SIGNAL(undoTextChanged(QString)),
392             result, SLOT(setPrefixedText(QString)));
393     connect(result, SIGNAL(triggered()), this, SLOT(undo()));
394     return result;
395 }
396 
397 /*!
398     Creates an redo QAction object with parent \a parent.
399 
400     Triggering this action will cause a call to QUndoStack::redo() on the active stack.
401     The text of this action will always be the text of the command which will be redone
402     in the next call to redo(), prefixed by \a prefix. If there is no command available
403     for redo, if the group is empty or if none of the stacks are active, this action will
404     be disabled.
405 
406     If \a prefix is empty, the default template "Redo %1" is used instead of prefix.
407     Before Qt 4.8, the prefix "Redo" was used by default.
408 
409     \sa createUndoAction(), canRedo(), QUndoCommand::text()
410 */
411 
createRedoAction(QObject * parent,const QString & prefix) const412 QAction *QUndoGroup::createRedoAction(QObject *parent, const QString &prefix) const
413 {
414     QUndoAction *result = new QUndoAction(prefix, parent);
415     if (prefix.isEmpty())
416         result->setTextFormat(tr("Redo %1"), tr("Redo", "Default text for redo action"));
417 
418     result->setEnabled(canRedo());
419     result->setPrefixedText(redoText());
420     connect(this, SIGNAL(canRedoChanged(bool)),
421             result, SLOT(setEnabled(bool)));
422     connect(this, SIGNAL(redoTextChanged(QString)),
423             result, SLOT(setPrefixedText(QString)));
424     connect(result, SIGNAL(triggered()), this, SLOT(redo()));
425     return result;
426 }
427 
428 #endif // QT_NO_ACTION
429 
430 /*! \fn void QUndoGroup::activeStackChanged(QUndoStack *stack)
431 
432     This signal is emitted whenever the active stack of the group changes. This can happen
433     when setActiveStack() or QUndoStack::setActive() is called, or when the active stack
434     is removed form the group. \a stack is the new active stack. If no stack is active,
435     \a stack is 0.
436 
437     \sa setActiveStack(), QUndoStack::setActive()
438 */
439 
440 /*! \fn void QUndoGroup::indexChanged(int idx)
441 
442     This signal is emitted whenever the active stack emits QUndoStack::indexChanged()
443     or the active stack changes.
444 
445     \a idx is the new current index, or 0 if the active stack is 0.
446 
447     \sa QUndoStack::indexChanged(), setActiveStack()
448 */
449 
450 /*! \fn void QUndoGroup::cleanChanged(bool clean)
451 
452     This signal is emitted whenever the active stack emits QUndoStack::cleanChanged()
453     or the active stack changes.
454 
455     \a clean is the new state, or true if the active stack is 0.
456 
457     \sa QUndoStack::cleanChanged(), setActiveStack()
458 */
459 
460 /*! \fn void QUndoGroup::canUndoChanged(bool canUndo)
461 
462     This signal is emitted whenever the active stack emits QUndoStack::canUndoChanged()
463     or the active stack changes.
464 
465     \a canUndo is the new state, or false if the active stack is 0.
466 
467     \sa QUndoStack::canUndoChanged(), setActiveStack()
468 */
469 
470 /*! \fn void QUndoGroup::canRedoChanged(bool canRedo)
471 
472     This signal is emitted whenever the active stack emits QUndoStack::canRedoChanged()
473     or the active stack changes.
474 
475     \a canRedo is the new state, or false if the active stack is 0.
476 
477     \sa QUndoStack::canRedoChanged(), setActiveStack()
478 */
479 
480 /*! \fn void QUndoGroup::undoTextChanged(const QString &undoText)
481 
482     This signal is emitted whenever the active stack emits QUndoStack::undoTextChanged()
483     or the active stack changes.
484 
485     \a undoText is the new state, or an empty string if the active stack is 0.
486 
487     \sa QUndoStack::undoTextChanged(), setActiveStack()
488 */
489 
490 /*! \fn void QUndoGroup::redoTextChanged(const QString &redoText)
491 
492     This signal is emitted whenever the active stack emits QUndoStack::redoTextChanged()
493     or the active stack changes.
494 
495     \a redoText is the new state, or an empty string if the active stack is 0.
496 
497     \sa QUndoStack::redoTextChanged(), setActiveStack()
498 */
499 
500 QT_END_NAMESPACE
501 
502 #include "moc_qundogroup.cpp"
503