1/****************************************************************************
2**
3** Copyright (C) 2020 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
44QT_BEGIN_NAMESPACE
45
46class QUndoGroupPrivate : public QObjectPrivate
47{
48 Q_DECLARE_PUBLIC(QUndoGroup)
49public:
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
101QUndoGroup::QUndoGroup(QObject *parent)
102 : QObject(*new QUndoGroupPrivate(), parent)
103{
104}
105
106/*!
107 Destroys the QUndoGroup.
108*/
109QUndoGroup::~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
130void 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
150void 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
167QList<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
187void 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
250QUndoStack *QUndoGroup::activeStack() const
251{
252 Q_D(const QUndoGroup);
253 return d->active;
254}
255
256#ifndef QT_NO_ACTION
257
258/*!
259 Creates an undo QAction object with parent \a parent.
260
261 Triggering this action will cause a call to QUndoStack::undo() on the active stack.
262 The text of this action will always be the text of the command which will be undone
263 in the next call to undo(), prefixed by \a prefix. If there is no command available
264 for undo, if the group is empty or if none of the stacks are active, this action will
265 be disabled.
266
267 If \a prefix is empty, the default template "Undo %1" is used instead of prefix.
268 Before Qt 4.8, the prefix "Undo" was used by default.
269
270 \sa createRedoAction(), canUndo(), QUndoCommand::text()
271*/
272
273QAction *QUndoGroup::createUndoAction(QObject *parent, const QString &prefix) const
274{
275 QAction *action = new QAction(parent);
276 action->setEnabled(canUndo());
277
278 QString effectivePrefix = prefix;
279 QString defaultText;
280 if (prefix.isEmpty()) {
281 effectivePrefix = tr("Undo %1");
282 defaultText = tr("Undo", "Default text for undo action");
283 }
284
285 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, undoText());
286
287 connect(this, &QUndoGroup::canUndoChanged, action, &QAction::setEnabled);
288 connect(this, &QUndoGroup::undoTextChanged, action, [=](const QString &text) {
289 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
290 });
291 connect(action, &QAction::triggered, this, &QUndoGroup::undo);
292
293 return action;
294}
295
296/*!
297 Creates an redo QAction object with parent \a parent.
298
299 Triggering this action will cause a call to QUndoStack::redo() on the active stack.
300 The text of this action will always be the text of the command which will be redone
301 in the next call to redo(), prefixed by \a prefix. If there is no command available
302 for redo, if the group is empty or if none of the stacks are active, this action will
303 be disabled.
304
305 If \a prefix is empty, the default template "Redo %1" is used instead of prefix.
306 Before Qt 4.8, the prefix "Redo" was used by default.
307
308 \sa createUndoAction(), canRedo(), QUndoCommand::text()
309*/
310
311QAction *QUndoGroup::createRedoAction(QObject *parent, const QString &prefix) const
312{
313 QAction *action = new QAction(parent);
314 action->setEnabled(canRedo());
315
316 QString effectivePrefix = prefix;
317 QString defaultText;
318 if (prefix.isEmpty()) {
319 effectivePrefix = tr("Redo %1");
320 defaultText = tr("Redo", "Default text for redo action");
321 }
322
323 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, redoText());
324
325 connect(this, &QUndoGroup::canRedoChanged, action, &QAction::setEnabled);
326 connect(this, &QUndoGroup::redoTextChanged, action, [=](const QString &text) {
327 QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
328 });
329 connect(action, &QAction::triggered, this, &QUndoGroup::redo);
330 return action;
331}
332
333#endif // QT_NO_ACTION
334
335/*!
336 Calls QUndoStack::undo() on the active stack.
337
338 If none of the stacks are active, or if the group is empty, this function
339 does nothing.
340
341 \sa redo(), canUndo(), setActiveStack()
342*/
343
344void QUndoGroup::undo()
345{
346 Q_D(QUndoGroup);
347 if (d->active != nullptr)
348 d->active->undo();
349}
350
351/*!
352 Calls QUndoStack::redo() on the active stack.
353
354 If none of the stacks are active, or if the group is empty, this function
355 does nothing.
356
357 \sa undo(), canRedo(), setActiveStack()
358*/
359
360
361void QUndoGroup::redo()
362{
363 Q_D(QUndoGroup);
364 if (d->active != nullptr)
365 d->active->redo();
366}
367
368/*!
369 Returns the value of the active stack's QUndoStack::canUndo().
370
371 If none of the stacks are active, or if the group is empty, this function
372 returns \c false.
373
374 \sa canRedo(), setActiveStack()
375*/
376
377bool QUndoGroup::canUndo() const
378{
379 Q_D(const QUndoGroup);
380 return d->active != nullptr && d->active->canUndo();
381}
382
383/*!
384 Returns the value of the active stack's QUndoStack::canRedo().
385
386 If none of the stacks are active, or if the group is empty, this function
387 returns \c false.
388
389 \sa canUndo(), setActiveStack()
390*/
391
392bool QUndoGroup::canRedo() const
393{
394 Q_D(const QUndoGroup);
395 return d->active != nullptr && d->active->canRedo();
396}
397
398/*!
399 Returns the value of the active stack's QUndoStack::undoText().
400
401 If none of the stacks are active, or if the group is empty, this function
402 returns an empty string.
403
404 \sa redoText(), setActiveStack()
405*/
406
407QString QUndoGroup::undoText() const
408{
409 Q_D(const QUndoGroup);
410 return d->active == nullptr ? QString() : d->active->undoText();
411}
412
413/*!
414 Returns the value of the active stack's QUndoStack::redoText().
415
416 If none of the stacks are active, or if the group is empty, this function
417 returns an empty string.
418
419 \sa undoText(), setActiveStack()
420*/
421
422QString QUndoGroup::redoText() const
423{
424 Q_D(const QUndoGroup);
425 return d->active == nullptr ? QString() : d->active->redoText();
426}
427
428/*!
429 Returns the value of the active stack's QUndoStack::isClean().
430
431 If none of the stacks are active, or if the group is empty, this function
432 returns \c true.
433
434 \sa setActiveStack()
435*/
436
437bool QUndoGroup::isClean() const
438{
439 Q_D(const QUndoGroup);
440 return d->active == nullptr || d->active->isClean();
441}
442
443/*! \fn void QUndoGroup::activeStackChanged(QUndoStack *stack)
444
445 This signal is emitted whenever the active stack of the group changes. This can happen
446 when setActiveStack() or QUndoStack::setActive() is called, or when the active stack
447 is removed form the group. \a stack is the new active stack. If no stack is active,
448 \a stack is 0.
449
450 \sa setActiveStack(), QUndoStack::setActive()
451*/
452
453/*! \fn void QUndoGroup::indexChanged(int idx)
454
455 This signal is emitted whenever the active stack emits QUndoStack::indexChanged()
456 or the active stack changes.
457
458 \a idx is the new current index, or 0 if the active stack is 0.
459
460 \sa QUndoStack::indexChanged(), setActiveStack()
461*/
462
463/*! \fn void QUndoGroup::cleanChanged(bool clean)
464
465 This signal is emitted whenever the active stack emits QUndoStack::cleanChanged()
466 or the active stack changes.
467
468 \a clean is the new state, or true if the active stack is 0.
469
470 \sa QUndoStack::cleanChanged(), setActiveStack()
471*/
472
473/*! \fn void QUndoGroup::canUndoChanged(bool canUndo)
474
475 This signal is emitted whenever the active stack emits QUndoStack::canUndoChanged()
476 or the active stack changes.
477
478 \a canUndo is the new state, or false if the active stack is 0.
479
480 \sa QUndoStack::canUndoChanged(), setActiveStack()
481*/
482
483/*! \fn void QUndoGroup::canRedoChanged(bool canRedo)
484
485 This signal is emitted whenever the active stack emits QUndoStack::canRedoChanged()
486 or the active stack changes.
487
488 \a canRedo is the new state, or false if the active stack is 0.
489
490 \sa QUndoStack::canRedoChanged(), setActiveStack()
491*/
492
493/*! \fn void QUndoGroup::undoTextChanged(const QString &undoText)
494
495 This signal is emitted whenever the active stack emits QUndoStack::undoTextChanged()
496 or the active stack changes.
497
498 \a undoText is the new state, or an empty string if the active stack is 0.
499
500 \sa QUndoStack::undoTextChanged(), setActiveStack()
501*/
502
503/*! \fn void QUndoGroup::redoTextChanged(const QString &redoText)
504
505 This signal is emitted whenever the active stack emits QUndoStack::redoTextChanged()
506 or the active stack changes.
507
508 \a redoText is the new state, or an empty string if the active stack is 0.
509
510 \sa QUndoStack::redoTextChanged(), setActiveStack()
511*/
512
513QT_END_NAMESPACE
514
515#include "moc_qundogroup.cpp"
516