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

source code of qtbase/src/gui/util/qundogroup.cpp