summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@qt.io>2020-03-10 15:40:56 +0100
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2020-03-30 12:36:29 +0200
commit3f73995a03c4b40e2648cb9af5d3e7ca393bf597 (patch)
treef7f9f4c63a5506287de4d44d4f01cdc57e093a27 /src/gui
parentadc1be3c33b4237a13e423c140bcb1c06aeb11d9 (diff)
Move undo framework out of Qt Widgets
- Moves QUndo* classes (except QUndoView) from src/widgets/utils to src/gui/utils - Moves related auto tests from widgets to gui - Replaces QUndoAction with lambdas that do text prefixing [ChangeLog][Undo Framework] QUndo* classes (except QUndoView) were moved from Qt Widgets to Qt GUI. Done-with: volker.hilsheimer@qt.io Fixes: QTBUG-40040 Change-Id: I3bd8d4d32c64f8dee548f62159a1df2126da89d8 Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/CMakeLists.txt9
-rw-r--r--src/gui/configure.cmake20
-rw-r--r--src/gui/configure.json20
-rw-r--r--src/gui/util/qundogroup.cpp515
-rw-r--r--src/gui/util/qundogroup.h100
-rw-r--r--src/gui/util/qundostack.cpp1390
-rw-r--r--src/gui/util/qundostack.h157
-rw-r--r--src/gui/util/qundostack_p.h104
-rw-r--r--src/gui/util/util.pri13
9 files changed, 2328 insertions, 0 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 700b06fe11..efa7b1c41f 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -793,6 +793,15 @@ qt_extend_target(Gui CONDITION NOT GCC OR NOT QT_COMPILER_VERSION_MAJOR STREQUAL
"painting/qdrawhelper.cpp"
)
+qt_extend_target(Gui CONDITION QT_FEATURE_undocommand
+ SOURCES
+ util/qundostack.cpp util/qundostack.h util/qundostack_p.h
+)
+
+qt_extend_target(Gui CONDITION QT_FEATURE_undogroup
+ SOURCES
+ util/qundogroup.cpp util/qundogroup.h
+)
qt_create_tracepoints(Gui qtgui.tracepoints)
qt_add_docs(Gui
diff --git a/src/gui/configure.cmake b/src/gui/configure.cmake
index 45ca061ed7..eb7d2b5549 100644
--- a/src/gui/configure.cmake
+++ b/src/gui/configure.cmake
@@ -1134,6 +1134,26 @@ qt_feature("multiprocess" PRIVATE
PURPOSE "Provides support for detecting the desktop environment, launching external processes and opening URLs."
CONDITION NOT INTEGRITY AND NOT rtems
)
+qt_feature("undocommand" PUBLIC
+ SECTION "Utilities"
+ LABEL "QUndoCommand"
+ PURPOSE "Applies (redo or) undo of a single change in a document."
+)
+qt_feature_definition("undocommand" "QT_NO_UNDOCOMMAND" NEGATE VALUE "1")
+qt_feature("undostack" PUBLIC
+ SECTION "Utilities"
+ LABEL "QUndoStack"
+ PURPOSE "Provides the ability to (redo or) undo a list of changes in a document."
+ CONDITION QT_FEATURE_undocommand
+)
+qt_feature_definition("undostack" "QT_NO_UNDOSTACK" NEGATE VALUE "1")
+qt_feature("undogroup" PUBLIC
+ SECTION "Utilities"
+ LABEL "QUndoGroup"
+ PURPOSE "Provides the ability to cluster QUndoCommands."
+ CONDITION QT_FEATURE_undostack
+)
+qt_feature_definition("undogroup" "QT_NO_UNDOGROUP" NEGATE VALUE "1")
qt_feature("whatsthis" PUBLIC
SECTION "Widget Support"
LABEL "QWhatsThis"
diff --git a/src/gui/configure.json b/src/gui/configure.json
index 500251a280..cf33408991 100644
--- a/src/gui/configure.json
+++ b/src/gui/configure.json
@@ -1821,6 +1821,26 @@
"purpose": "Internal painting support for 64 bit (16 bpc) rasterization.",
"section": "Painting",
"output": [ "privateFeature" ]
+ },
+ "undocommand": {
+ "label": "QUndoCommand",
+ "purpose": "Applies (redo or) undo of a single change in a document.",
+ "section": "Utilities",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "undostack": {
+ "label": "QUndoStack",
+ "purpose": "Provides the ability to (redo or) undo a list of changes in a document.",
+ "section": "Utilities",
+ "condition": "features.undocommand",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "undogroup": {
+ "label": "QUndoGroup",
+ "purpose": "Provides the ability to cluster QUndoCommands.",
+ "section": "Utilities",
+ "condition": "features.undostack",
+ "output": [ "publicFeature", "feature" ]
}
},
diff --git a/src/gui/util/qundogroup.cpp b/src/gui/util/qundogroup.cpp
new file mode 100644
index 0000000000..2f4c81b046
--- /dev/null
+++ b/src/gui/util/qundogroup.cpp
@@ -0,0 +1,515 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qundogroup.h"
+#include "qundostack.h"
+#include "qundostack_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QUndoGroupPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QUndoGroup)
+public:
+ QUndoGroupPrivate() : active(nullptr) {}
+
+ QUndoStack *active;
+ QList<QUndoStack*> stack_list;
+};
+
+/*!
+ \class QUndoGroup
+ \brief The QUndoGroup class is a group of QUndoStack objects.
+ \since 4.2
+ \inmodule QtWidgets
+
+ For an overview of the Qt's undo framework, see the
+ \l{qundo.html}{overview}.
+
+ An application often has multiple undo stacks, one for each opened document. At the
+ same time, an application usually has one undo action and one redo action, which
+ triggers undo or redo in the active document.
+
+ QUndoGroup is a group of QUndoStack objects, one of which may be active. It has
+ an undo() and redo() slot, which calls QUndoStack::undo() and QUndoStack::redo()
+ for the active stack. It also has the functions createUndoAction() and createRedoAction().
+ The actions returned by these functions behave in the same way as those returned by
+ QUndoStack::createUndoAction() and QUndoStack::createRedoAction() of the active
+ stack.
+
+ Stacks are added to a group with addStack() and removed with removeStack(). A stack
+ is implicitly added to a group when it is created with the group as its parent
+ QObject.
+
+ It is the programmer's responsibility to specify which stack is active by
+ calling QUndoStack::setActive(), usually when the associated document window receives focus.
+ The active stack may also be set with setActiveStack(), and is returned by activeStack().
+
+ When a stack is added to a group using addStack(), the group does not take ownership
+ of the stack. This means the stack has to be deleted separately from the group. When
+ a stack is deleted, it is automatically removed from a group. A stack may belong to
+ only one group. Adding it to another group will cause it to be removed from the previous
+ group.
+
+ A QUndoGroup is also useful in conjunction with QUndoView. If a QUndoView is
+ set to watch a group using QUndoView::setGroup(), it will update itself to display
+ the active stack.
+*/
+
+/*!
+ Creates an empty QUndoGroup object with parent \a parent.
+
+ \sa addStack()
+*/
+
+QUndoGroup::QUndoGroup(QObject *parent)
+ : QObject(*new QUndoGroupPrivate(), parent)
+{
+}
+
+/*!
+ Destroys the QUndoGroup.
+*/
+QUndoGroup::~QUndoGroup()
+{
+ // Ensure all QUndoStacks no longer refer to this group.
+ Q_D(QUndoGroup);
+ QList<QUndoStack *>::iterator it = d->stack_list.begin();
+ QList<QUndoStack *>::iterator end = d->stack_list.end();
+ while (it != end) {
+ (*it)->d_func()->group = nullptr;
+ ++it;
+ }
+}
+
+/*!
+ Adds \a stack to this group. The group does not take ownership of the stack. Another
+ way of adding a stack to a group is by specifying the group as the stack's parent
+ QObject in QUndoStack::QUndoStack(). In this case, the stack is deleted when the
+ group is deleted, in the usual manner of QObjects.
+
+ \sa removeStack(), stacks(), QUndoStack::QUndoStack()
+*/
+
+void QUndoGroup::addStack(QUndoStack *stack)
+{
+ Q_D(QUndoGroup);
+
+ if (d->stack_list.contains(stack))
+ return;
+ d->stack_list.append(stack);
+
+ if (QUndoGroup *other = stack->d_func()->group)
+ other->removeStack(stack);
+ stack->d_func()->group = this;
+}
+
+/*!
+ Removes \a stack from this group. If the stack was the active stack in the group,
+ the active stack becomes 0.
+
+ \sa addStack(), stacks(), QUndoStack::~QUndoStack()
+*/
+
+void QUndoGroup::removeStack(QUndoStack *stack)
+{
+ Q_D(QUndoGroup);
+
+ if (d->stack_list.removeAll(stack) == 0)
+ return;
+ if (stack == d->active)
+ setActiveStack(nullptr);
+ stack->d_func()->group = nullptr;
+}
+
+/*!
+ Returns a list of stacks in this group.
+
+ \sa addStack(), removeStack()
+*/
+
+QList<QUndoStack*> QUndoGroup::stacks() const
+{
+ Q_D(const QUndoGroup);
+ return d->stack_list;
+}
+
+/*!
+ Sets the active stack of this group to \a stack.
+
+ If the stack is not a member of this group, this function does nothing.
+
+ Synonymous with calling QUndoStack::setActive() on \a stack.
+
+ The actions returned by createUndoAction() and createRedoAction() will now behave
+ in the same way as those returned by \a stack's QUndoStack::createUndoAction()
+ and QUndoStack::createRedoAction().
+
+ \sa QUndoStack::setActive(), activeStack()
+*/
+
+void QUndoGroup::setActiveStack(QUndoStack *stack)
+{
+ Q_D(QUndoGroup);
+ if (d->active == stack)
+ return;
+
+ if (d->active != nullptr) {
+ disconnect(d->active, SIGNAL(canUndoChanged(bool)),
+ this, SIGNAL(canUndoChanged(bool)));
+ disconnect(d->active, SIGNAL(undoTextChanged(QString)),
+ this, SIGNAL(undoTextChanged(QString)));
+ disconnect(d->active, SIGNAL(canRedoChanged(bool)),
+ this, SIGNAL(canRedoChanged(bool)));
+ disconnect(d->active, SIGNAL(redoTextChanged(QString)),
+ this, SIGNAL(redoTextChanged(QString)));
+ disconnect(d->active, SIGNAL(indexChanged(int)),
+ this, SIGNAL(indexChanged(int)));
+ disconnect(d->active, SIGNAL(cleanChanged(bool)),
+ this, SIGNAL(cleanChanged(bool)));
+ }
+
+ d->active = stack;
+
+ if (d->active == nullptr) {
+ emit canUndoChanged(false);
+ emit undoTextChanged(QString());
+ emit canRedoChanged(false);
+ emit redoTextChanged(QString());
+ emit cleanChanged(true);
+ emit indexChanged(0);
+ } else {
+ connect(d->active, SIGNAL(canUndoChanged(bool)),
+ this, SIGNAL(canUndoChanged(bool)));
+ connect(d->active, SIGNAL(undoTextChanged(QString)),
+ this, SIGNAL(undoTextChanged(QString)));
+ connect(d->active, SIGNAL(canRedoChanged(bool)),
+ this, SIGNAL(canRedoChanged(bool)));
+ connect(d->active, SIGNAL(redoTextChanged(QString)),
+ this, SIGNAL(redoTextChanged(QString)));
+ connect(d->active, SIGNAL(indexChanged(int)),
+ this, SIGNAL(indexChanged(int)));
+ connect(d->active, SIGNAL(cleanChanged(bool)),
+ this, SIGNAL(cleanChanged(bool)));
+ emit canUndoChanged(d->active->canUndo());
+ emit undoTextChanged(d->active->undoText());
+ emit canRedoChanged(d->active->canRedo());
+ emit redoTextChanged(d->active->redoText());
+ emit cleanChanged(d->active->isClean());
+ emit indexChanged(d->active->index());
+ }
+
+ emit activeStackChanged(d->active);
+}
+
+/*!
+ Returns the active stack of this group.
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns \nullptr.
+
+ \sa setActiveStack(), QUndoStack::setActive()
+*/
+
+QUndoStack *QUndoGroup::activeStack() const
+{
+ Q_D(const QUndoGroup);
+ return d->active;
+}
+
+#ifndef QT_NO_ACTION
+
+/*!
+ Creates an undo QAction object with parent \a parent.
+
+ Triggering this action will cause a call to QUndoStack::undo() on the active stack.
+ The text of this action will always be the text of the command which will be undone
+ in the next call to undo(), prefixed by \a prefix. If there is no command available
+ for undo, if the group is empty or if none of the stacks are active, this action will
+ be disabled.
+
+ If \a prefix is empty, the default template "Undo %1" is used instead of prefix.
+ Before Qt 4.8, the prefix "Undo" was used by default.
+
+ \sa createRedoAction(), canUndo(), QUndoCommand::text()
+*/
+
+QAction *QUndoGroup::createUndoAction(QObject *parent, const QString &prefix) const
+{
+ QAction *action = new QAction(parent);
+ action->setEnabled(canUndo());
+
+ QString effectivePrefix = prefix;
+ QString defaultText;
+ if (prefix.isEmpty()) {
+ effectivePrefix = tr("Undo %1");
+ defaultText = tr("Undo", "Default text for undo action");
+ }
+
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, undoText());
+
+ connect(this, &QUndoGroup::canUndoChanged, action, &QAction::setEnabled);
+ connect(this, &QUndoGroup::undoTextChanged, action, [=](const QString &text) {
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
+ });
+ connect(action, &QAction::triggered, this, &QUndoGroup::undo);
+
+ return action;
+}
+
+/*!
+ Creates an redo QAction object with parent \a parent.
+
+ Triggering this action will cause a call to QUndoStack::redo() on the active stack.
+ The text of this action will always be the text of the command which will be redone
+ in the next call to redo(), prefixed by \a prefix. If there is no command available
+ for redo, if the group is empty or if none of the stacks are active, this action will
+ be disabled.
+
+ If \a prefix is empty, the default template "Redo %1" is used instead of prefix.
+ Before Qt 4.8, the prefix "Redo" was used by default.
+
+ \sa createUndoAction(), canRedo(), QUndoCommand::text()
+*/
+
+QAction *QUndoGroup::createRedoAction(QObject *parent, const QString &prefix) const
+{
+ QAction *action = new QAction(parent);
+ action->setEnabled(canRedo());
+
+ QString effectivePrefix = prefix;
+ QString defaultText;
+ if (prefix.isEmpty()) {
+ effectivePrefix = tr("Redo %1");
+ defaultText = tr("Redo", "Default text for redo action");
+ }
+
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, redoText());
+
+ connect(this, &QUndoGroup::canRedoChanged, action, &QAction::setEnabled);
+ connect(this, &QUndoGroup::redoTextChanged, action, [=](const QString &text) {
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
+ });
+ connect(action, &QAction::triggered, this, &QUndoGroup::redo);
+ return action;
+}
+
+#endif // QT_NO_ACTION
+
+/*!
+ Calls QUndoStack::undo() on the active stack.
+
+ If none of the stacks are active, or if the group is empty, this function
+ does nothing.
+
+ \sa redo(), canUndo(), setActiveStack()
+*/
+
+void QUndoGroup::undo()
+{
+ Q_D(QUndoGroup);
+ if (d->active != nullptr)
+ d->active->undo();
+}
+
+/*!
+ Calls QUndoStack::redo() on the active stack.
+
+ If none of the stacks are active, or if the group is empty, this function
+ does nothing.
+
+ \sa undo(), canRedo(), setActiveStack()
+*/
+
+
+void QUndoGroup::redo()
+{
+ Q_D(QUndoGroup);
+ if (d->active != nullptr)
+ d->active->redo();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::canUndo().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns \c false.
+
+ \sa canRedo(), setActiveStack()
+*/
+
+bool QUndoGroup::canUndo() const
+{
+ Q_D(const QUndoGroup);
+ return d->active != nullptr && d->active->canUndo();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::canRedo().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns \c false.
+
+ \sa canUndo(), setActiveStack()
+*/
+
+bool QUndoGroup::canRedo() const
+{
+ Q_D(const QUndoGroup);
+ return d->active != nullptr && d->active->canRedo();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::undoText().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns an empty string.
+
+ \sa redoText(), setActiveStack()
+*/
+
+QString QUndoGroup::undoText() const
+{
+ Q_D(const QUndoGroup);
+ return d->active == nullptr ? QString() : d->active->undoText();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::redoText().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns an empty string.
+
+ \sa undoText(), setActiveStack()
+*/
+
+QString QUndoGroup::redoText() const
+{
+ Q_D(const QUndoGroup);
+ return d->active == nullptr ? QString() : d->active->redoText();
+}
+
+/*!
+ Returns the value of the active stack's QUndoStack::isClean().
+
+ If none of the stacks are active, or if the group is empty, this function
+ returns \c true.
+
+ \sa setActiveStack()
+*/
+
+bool QUndoGroup::isClean() const
+{
+ Q_D(const QUndoGroup);
+ return d->active == nullptr || d->active->isClean();
+}
+
+/*! \fn void QUndoGroup::activeStackChanged(QUndoStack *stack)
+
+ This signal is emitted whenever the active stack of the group changes. This can happen
+ when setActiveStack() or QUndoStack::setActive() is called, or when the active stack
+ is removed form the group. \a stack is the new active stack. If no stack is active,
+ \a stack is 0.
+
+ \sa setActiveStack(), QUndoStack::setActive()
+*/
+
+/*! \fn void QUndoGroup::indexChanged(int idx)
+
+ This signal is emitted whenever the active stack emits QUndoStack::indexChanged()
+ or the active stack changes.
+
+ \a idx is the new current index, or 0 if the active stack is 0.
+
+ \sa QUndoStack::indexChanged(), setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::cleanChanged(bool clean)
+
+ This signal is emitted whenever the active stack emits QUndoStack::cleanChanged()
+ or the active stack changes.
+
+ \a clean is the new state, or true if the active stack is 0.
+
+ \sa QUndoStack::cleanChanged(), setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::canUndoChanged(bool canUndo)
+
+ This signal is emitted whenever the active stack emits QUndoStack::canUndoChanged()
+ or the active stack changes.
+
+ \a canUndo is the new state, or false if the active stack is 0.
+
+ \sa QUndoStack::canUndoChanged(), setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::canRedoChanged(bool canRedo)
+
+ This signal is emitted whenever the active stack emits QUndoStack::canRedoChanged()
+ or the active stack changes.
+
+ \a canRedo is the new state, or false if the active stack is 0.
+
+ \sa QUndoStack::canRedoChanged(), setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::undoTextChanged(const QString &undoText)
+
+ This signal is emitted whenever the active stack emits QUndoStack::undoTextChanged()
+ or the active stack changes.
+
+ \a undoText is the new state, or an empty string if the active stack is 0.
+
+ \sa QUndoStack::undoTextChanged(), setActiveStack()
+*/
+
+/*! \fn void QUndoGroup::redoTextChanged(const QString &redoText)
+
+ This signal is emitted whenever the active stack emits QUndoStack::redoTextChanged()
+ or the active stack changes.
+
+ \a redoText is the new state, or an empty string if the active stack is 0.
+
+ \sa QUndoStack::redoTextChanged(), setActiveStack()
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qundogroup.cpp"
diff --git a/src/gui/util/qundogroup.h b/src/gui/util/qundogroup.h
new file mode 100644
index 0000000000..ef5b053610
--- /dev/null
+++ b/src/gui/util/qundogroup.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNDOGROUP_H
+#define QUNDOGROUP_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+QT_REQUIRE_CONFIG(undogroup);
+
+QT_BEGIN_NAMESPACE
+
+class QUndoGroupPrivate;
+class QUndoStack;
+class QAction;
+
+class Q_GUI_EXPORT QUndoGroup : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QUndoGroup)
+
+public:
+ explicit QUndoGroup(QObject *parent = nullptr);
+ ~QUndoGroup();
+
+ void addStack(QUndoStack *stack);
+ void removeStack(QUndoStack *stack);
+ QList<QUndoStack*> stacks() const;
+ QUndoStack *activeStack() const;
+
+#ifndef QT_NO_ACTION
+ QAction *createUndoAction(QObject *parent, const QString &prefix = QString()) const;
+ QAction *createRedoAction(QObject *parent, const QString &prefix = QString()) const;
+#endif // QT_NO_ACTION
+
+ bool canUndo() const;
+ bool canRedo() const;
+ QString undoText() const;
+ QString redoText() const;
+ bool isClean() const;
+
+public Q_SLOTS:
+ void undo();
+ void redo();
+ void setActiveStack(QUndoStack *stack);
+
+Q_SIGNALS:
+ void activeStackChanged(QUndoStack *stack);
+ void indexChanged(int idx);
+ void cleanChanged(bool clean);
+ void canUndoChanged(bool canUndo);
+ void canRedoChanged(bool canRedo);
+ void undoTextChanged(const QString &undoText);
+ void redoTextChanged(const QString &redoText);
+
+private:
+ Q_DISABLE_COPY(QUndoGroup)
+};
+
+QT_END_NAMESPACE
+
+#endif // QUNDOGROUP_H
diff --git a/src/gui/util/qundostack.cpp b/src/gui/util/qundostack.cpp
new file mode 100644
index 0000000000..f7b5024389
--- /dev/null
+++ b/src/gui/util/qundostack.cpp
@@ -0,0 +1,1390 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qdebug.h>
+#include "qundostack.h"
+#if QT_CONFIG(undogroup)
+#include "qundogroup.h"
+#endif
+#include "qundostack_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QUndoCommand
+ \brief The QUndoCommand class is the base class of all commands stored on a QUndoStack.
+ \since 4.2
+
+ \inmodule QtWidgets
+
+ For an overview of Qt's Undo Framework, see the
+ \l{Overview of Qt's Undo Framework}{overview document}.
+
+ A QUndoCommand represents a single editing action on a document; for example,
+ inserting or deleting a block of text in a text editor. QUndoCommand can apply
+ a change to the document with redo() and undo the change with undo(). The
+ implementations for these functions must be provided in a derived class.
+
+ \snippet code/src_gui_util_qundostack.cpp 0
+
+ A QUndoCommand has an associated text(). This is a short string
+ describing what the command does. It is used to update the text
+ properties of the stack's undo and redo actions; see
+ QUndoStack::createUndoAction() and QUndoStack::createRedoAction().
+
+ QUndoCommand objects are owned by the stack they were pushed on.
+ QUndoStack deletes a command if it has been undone and a new command is pushed. For example:
+
+\snippet code/src_gui_util_qundostack.cpp 1
+
+ In effect, when a command is pushed, it becomes the top-most command
+ on the stack.
+
+ To support command compression, QUndoCommand has an id() and the virtual function
+ mergeWith(). These functions are used by QUndoStack::push().
+
+ To support command macros, a QUndoCommand object can have any number of child
+ commands. Undoing or redoing the parent command will cause the child
+ commands to be undone or redone. A command can be assigned
+ to a parent explicitly in the constructor. In this case, the command
+ will be owned by the parent.
+
+ The parent in this case is usually an empty command, in that it doesn't
+ provide its own implementation of undo() and redo(). Instead, it uses
+ the base implementations of these functions, which simply call undo() or
+ redo() on all its children. The parent should, however, have a meaningful
+ text().
+
+ \snippet code/src_gui_util_qundostack.cpp 2
+
+ Another way to create macros is to use the convenience functions
+ QUndoStack::beginMacro() and QUndoStack::endMacro().
+
+ \sa QUndoStack
+*/
+
+/*!
+ Constructs a QUndoCommand object with the given \a parent and \a text.
+
+ If \a parent is not \nullptr, this command is appended to parent's
+ child list. The parent command then owns this command and will delete
+ it in its destructor.
+
+ \sa ~QUndoCommand()
+*/
+
+QUndoCommand::QUndoCommand(const QString &text, QUndoCommand *parent)
+ : QUndoCommand(parent)
+{
+ setText(text);
+}
+
+/*!
+ Constructs a QUndoCommand object with parent \a parent.
+
+ If \a parent is not \nullptr, this command is appended to parent's
+ child list. The parent command then owns this command and will delete
+ it in its destructor.
+
+ \sa ~QUndoCommand()
+*/
+
+QUndoCommand::QUndoCommand(QUndoCommand *parent)
+{
+ d = new QUndoCommandPrivate;
+ if (parent != nullptr)
+ parent->d->child_list.append(this);
+}
+
+/*!
+ Destroys the QUndoCommand object and all child commands.
+
+ \sa QUndoCommand()
+*/
+
+QUndoCommand::~QUndoCommand()
+{
+ qDeleteAll(d->child_list);
+ delete d;
+}
+
+/*!
+ \since 5.9
+
+ Returns whether the command is obsolete.
+
+ The boolean is used for the automatic removal of commands that are not necessary in the
+ stack anymore. The isObsolete function is checked in the functions QUndoStack::push(),
+ QUndoStack::undo(), QUndoStack::redo(), and QUndoStack::setIndex().
+
+ \sa setObsolete(), mergeWith(), QUndoStack::push(), QUndoStack::undo(), QUndoStack::redo()
+*/
+
+bool QUndoCommand::isObsolete() const
+{
+ return d->obsolete;
+}
+
+/*!
+ \since 5.9
+
+ Sets whether the command is obsolete to \a obsolete.
+
+ \sa isObsolete(), mergeWith(), QUndoStack::push(), QUndoStack::undo(), QUndoStack::redo()
+*/
+
+void QUndoCommand::setObsolete(bool obsolete)
+{
+ d->obsolete = obsolete;
+}
+
+/*!
+ Returns the ID of this command.
+
+ A command ID is used in command compression. It must be an integer unique to
+ this command's class, or -1 if the command doesn't support compression.
+
+ If the command supports compression this function must be overridden in the
+ derived class to return the correct ID. The base implementation returns -1.
+
+ QUndoStack::push() will only try to merge two commands if they have the
+ same ID, and the ID is not -1.
+
+ \sa mergeWith(), QUndoStack::push()
+*/
+
+int QUndoCommand::id() const
+{
+ return -1;
+}
+
+/*!
+ Attempts to merge this command with \a command. Returns \c true on
+ success; otherwise returns \c false.
+
+ If this function returns \c true, calling this command's redo() must have the same
+ effect as redoing both this command and \a command.
+ Similarly, calling this command's undo() must have the same effect as undoing
+ \a command and this command.
+
+ QUndoStack will only try to merge two commands if they have the same id, and
+ the id is not -1.
+
+ The default implementation returns \c false.
+
+ \snippet code/src_gui_util_qundostack.cpp 3
+
+ \sa id(), QUndoStack::push()
+*/
+
+bool QUndoCommand::mergeWith(const QUndoCommand *command)
+{
+ Q_UNUSED(command)
+ return false;
+}
+
+/*!
+ Applies a change to the document. This function must be implemented in
+ the derived class. Calling QUndoStack::push(),
+ QUndoStack::undo() or QUndoStack::redo() from this function leads to
+ undefined beahavior.
+
+ The default implementation calls redo() on all child commands.
+
+ \sa undo()
+*/
+
+void QUndoCommand::redo()
+{
+ for (int i = 0; i < d->child_list.size(); ++i)
+ d->child_list.at(i)->redo();
+}
+
+/*!
+ Reverts a change to the document. After undo() is called, the state of
+ the document should be the same as before redo() was called. This function must
+ be implemented in the derived class. Calling QUndoStack::push(),
+ QUndoStack::undo() or QUndoStack::redo() from this function leads to
+ undefined beahavior.
+
+ The default implementation calls undo() on all child commands in reverse order.
+
+ \sa redo()
+*/
+
+void QUndoCommand::undo()
+{
+ for (int i = d->child_list.size() - 1; i >= 0; --i)
+ d->child_list.at(i)->undo();
+}
+
+/*!
+ Returns a short text string describing what this command does; for example,
+ "insert text".
+
+ The text is used for names of items in QUndoView.
+
+ \sa actionText(), setText(), QUndoStack::createUndoAction(), QUndoStack::createRedoAction()
+*/
+
+QString QUndoCommand::text() const
+{
+ return d->text;
+}
+
+/*!
+ \since 4.8
+
+ Returns a short text string describing what this command does; for example,
+ "insert text".
+
+ The text is used when the text properties of the stack's undo and redo
+ actions are updated.
+
+ \sa text(), setText(), QUndoStack::createUndoAction(), QUndoStack::createRedoAction()
+*/
+
+QString QUndoCommand::actionText() const
+{
+ return d->actionText;
+}
+
+/*!
+ Sets the command's text to be the \a text specified.
+
+ The specified text should be a short user-readable string describing what this
+ command does.
+
+ If you need to have two different strings for text() and actionText(), separate
+ them with "\\n" and pass into this function. Even if you do not use this feature
+ for English strings during development, you can still let translators use two
+ different strings in order to match specific languages' needs.
+ The described feature and the function actionText() are available since Qt 4.8.
+
+ \sa text(), actionText(), QUndoStack::createUndoAction(), QUndoStack::createRedoAction()
+*/
+
+void QUndoCommand::setText(const QString &text)
+{
+ int cdpos = text.indexOf(QLatin1Char('\n'));
+ if (cdpos > 0) {
+ d->text = text.left(cdpos);
+ d->actionText = text.mid(cdpos + 1);
+ } else {
+ d->text = text;
+ d->actionText = text;
+ }
+}
+
+/*!
+ \since 4.4
+
+ Returns the number of child commands in this command.
+
+ \sa child()
+*/
+
+int QUndoCommand::childCount() const
+{
+ return d->child_list.count();
+}
+
+/*!
+ \since 4.4
+
+ Returns the child command at \a index.
+
+ \sa childCount(), QUndoStack::command()
+*/
+
+const QUndoCommand *QUndoCommand::child(int index) const
+{
+ if (index < 0 || index >= d->child_list.count())
+ return nullptr;
+ return d->child_list.at(index);
+}
+
+#if QT_CONFIG(undostack)
+
+/*!
+ \class QUndoStack
+ \brief The QUndoStack class is a stack of QUndoCommand objects.
+ \since 4.2
+
+ \inmodule QtWidgets
+
+ For an overview of Qt's Undo Framework, see the
+ \l{Overview of Qt's Undo Framework}{overview document}.
+
+ An undo stack maintains a stack of commands that have been applied to a
+ document.
+
+ New commands are pushed on the stack using push(). Commands can be
+ undone and redone using undo() and redo(), or by triggering the
+ actions returned by createUndoAction() and createRedoAction().
+
+ QUndoStack keeps track of the \a current command. This is the command
+ which will be executed by the next call to redo(). The index of this
+ command is returned by index(). The state of the edited object can be
+ rolled forward or back using setIndex(). If the top-most command on the
+ stack has already been redone, index() is equal to count().
+
+ QUndoStack provides support for undo and redo actions, command
+ compression, command macros, and supports the concept of a
+ \e{clean state}.
+
+ \section1 Undo and Redo Actions
+
+ QUndoStack provides convenient undo and redo QAction objects, which
+ can be inserted into a menu or a toolbar. When commands are undone or
+ redone, QUndoStack updates the text properties of these actions
+ to reflect what change they will trigger. The actions are also disabled
+ when no command is available for undo or redo. These actions
+ are returned by QUndoStack::createUndoAction() and QUndoStack::createRedoAction().
+
+ \section1 Command Compression and Macros
+
+ Command compression is useful when several commands can be compressed
+ into a single command that can be undone and redone in a single operation.
+ For example, when a user types a character in a text editor, a new command
+ is created. This command inserts the character into the document at the
+ cursor position. However, it is more convenient for the user to be able
+ to undo or redo typing of whole words, sentences, or paragraphs.
+ Command compression allows these single-character commands to be merged
+ into a single command which inserts or deletes sections of text.
+ For more information, see QUndoCommand::mergeWith() and push().
+
+ A command macro is a sequence of commands, all of which are undone and
+ redone in one go. Command macros are created by giving a command a list
+ of child commands.
+ Undoing or redoing the parent command will cause the child commands to
+ be undone or redone. Command macros may be created explicitly
+ by specifying a parent in the QUndoCommand constructor, or by using the
+ convenience functions beginMacro() and endMacro().
+
+ Although command compression and macros appear to have the same effect to the
+ user, they often have different uses in an application. Commands that
+ perform small changes to a document may be usefully compressed if there is
+ no need to individually record them, and if only larger changes are relevant
+ to the user.
+ However, for commands that need to be recorded individually, or those that
+ cannot be compressed, it is useful to use macros to provide a more convenient
+ user experience while maintaining a record of each command.
+
+ \section1 Clean State
+
+ QUndoStack supports the concept of a clean state. When the
+ document is saved to disk, the stack can be marked as clean using
+ setClean(). Whenever the stack returns to this state through undoing and
+ redoing commands, it emits the signal cleanChanged(). This signal
+ is also emitted when the stack leaves the clean state. This signal is
+ usually used to enable and disable the save actions in the application,
+ and to update the document's title to reflect that it contains unsaved
+ changes.
+
+ \section1 Obsolete Commands
+
+ QUndoStack is able to delete commands from the stack if the command is no
+ longer needed. One example may be to delete a command when two commands are
+ merged together in such a way that the merged command has no function. This
+ can be seen with move commands where the user moves their mouse to one part
+ of the screen and then moves it to the original position. The merged command
+ results in a mouse movement of 0. This command can be deleted since it serves
+ no purpose. Another example is with networking commands that fail due to connection
+ issues. In this case, the command is to be removed from the stack because the redo()
+ and undo() functions have no function since there was connection issues.
+
+ A command can be marked obsolete with the QUndoCommand::setObsolete() function.
+ The QUndoCommand::isObsolete() flag is checked in QUndoStack::push(),
+ QUndoStack::undo(), QUndoStack::redo(), and QUndoStack::setIndex() after calling
+ QUndoCommand::undo(), QUndoCommand::redo() and QUndoCommand:mergeWith() where
+ applicable.
+
+ If a command is set obsolete and the clean index is greater than or equal to the
+ current command index, then the clean index will be reset when the command is
+ deleted from the stack.
+
+ \sa QUndoCommand, QUndoView
+*/
+
+/*! \internal
+ Sets the current index to \a idx, emitting appropriate signals. If \a clean is true,
+ makes \a idx the clean index as well.
+*/
+
+void QUndoStackPrivate::setIndex(int idx, bool clean)
+{
+ Q_Q(QUndoStack);
+
+ bool was_clean = index == clean_index;
+
+ if (idx != index) {
+ index = idx;
+ emit q->indexChanged(index);
+ emit q->canUndoChanged(q->canUndo());
+ emit q->undoTextChanged(q->undoText());
+ emit q->canRedoChanged(q->canRedo());
+ emit q->redoTextChanged(q->redoText());
+ }
+
+ if (clean)
+ clean_index = index;
+
+ bool is_clean = index == clean_index;
+ if (is_clean != was_clean)
+ emit q->cleanChanged(is_clean);
+}
+
+/*! \internal
+ If the number of commands on the stack exceedes the undo limit, deletes commands from
+ the bottom of the stack.
+
+ Returns \c true if commands were deleted.
+*/
+
+bool QUndoStackPrivate::checkUndoLimit()
+{
+ if (undo_limit <= 0 || !macro_stack.isEmpty() || undo_limit >= command_list.count())
+ return false;
+
+ int del_count = command_list.count() - undo_limit;
+
+ for (int i = 0; i < del_count; ++i)
+ delete command_list.takeFirst();
+
+ index -= del_count;
+ if (clean_index != -1) {
+ if (clean_index < del_count)
+ clean_index = -1; // we've deleted the clean command
+ else
+ clean_index -= del_count;
+ }
+
+ return true;
+}
+
+/*!
+ Constructs an empty undo stack with the parent \a parent. The
+ stack will initially be in the clean state. If \a parent is a
+ QUndoGroup object, the stack is automatically added to the group.
+
+ \sa push()
+*/
+
+QUndoStack::QUndoStack(QObject *parent)
+ : QObject(*(new QUndoStackPrivate), parent)
+{
+#if QT_CONFIG(undogroup)
+ if (QUndoGroup *group = qobject_cast<QUndoGroup*>(parent))
+ group->addStack(this);
+#endif
+}
+
+/*!
+ Destroys the undo stack, deleting any commands that are on it. If the
+ stack is in a QUndoGroup, the stack is automatically removed from the group.
+
+ \sa QUndoStack()
+*/
+
+QUndoStack::~QUndoStack()
+{
+#if QT_CONFIG(undogroup)
+ Q_D(QUndoStack);
+ if (d->group != nullptr)
+ d->group->removeStack(this);
+#endif
+ clear();
+}
+
+/*!
+ Clears the command stack by deleting all commands on it, and returns the stack
+ to the clean state.
+
+ Commands are not undone or redone; the state of the edited object remains
+ unchanged.
+
+ This function is usually used when the contents of the document are
+ abandoned.
+
+ \sa QUndoStack()
+*/
+
+void QUndoStack::clear()
+{
+ Q_D(QUndoStack);
+
+ if (d->command_list.isEmpty())
+ return;
+
+ bool was_clean = isClean();
+
+ d->macro_stack.clear();
+ qDeleteAll(d->command_list);
+ d->command_list.clear();
+
+ d->index = 0;
+ d->clean_index = 0;
+
+ emit indexChanged(0);
+ emit canUndoChanged(false);
+ emit undoTextChanged(QString());
+ emit canRedoChanged(false);
+ emit redoTextChanged(QString());
+
+ if (!was_clean)
+ emit cleanChanged(true);
+}
+
+/*!
+ Pushes \a cmd on the stack or merges it with the most recently executed command.
+ In either case, executes \a cmd by calling its redo() function.
+
+ If \a cmd's id is not -1, and if the id is the same as that of the
+ most recently executed command, QUndoStack will attempt to merge the two
+ commands by calling QUndoCommand::mergeWith() on the most recently executed
+ command. If QUndoCommand::mergeWith() returns \c true, \a cmd is deleted.
+
+ After calling QUndoCommand::redo() and, if applicable, QUndoCommand::mergeWith(),
+ QUndoCommand::isObsolete() will be called for \a cmd or the merged command.
+ If QUndoCommand::isObsolete() returns \c true, then \a cmd or the merged command
+ will be deleted from the stack.
+
+ In all other cases \a cmd is simply pushed on the stack.
+
+ If commands were undone before \a cmd was pushed, the current command and
+ all commands above it are deleted. Hence \a cmd always ends up being the
+ top-most on the stack.
+
+ Once a command is pushed, the stack takes ownership of it. There
+ are no getters to return the command, since modifying it after it has
+ been executed will almost always lead to corruption of the document's
+ state.
+
+ \sa QUndoCommand::id(), QUndoCommand::mergeWith()
+*/
+
+void QUndoStack::push(QUndoCommand *cmd)
+{
+ Q_D(QUndoStack);
+ if (!cmd->isObsolete())
+ cmd->redo();
+
+ bool macro = !d->macro_stack.isEmpty();
+
+ QUndoCommand *cur = nullptr;
+ if (macro) {
+ QUndoCommand *macro_cmd = d->macro_stack.constLast();
+ if (!macro_cmd->d->child_list.isEmpty())
+ cur = macro_cmd->d->child_list.constLast();
+ } else {
+ if (d->index > 0)
+ cur = d->command_list.at(d->index - 1);
+ while (d->index < d->command_list.size())
+ delete d->command_list.takeLast();
+ if (d->clean_index > d->index)
+ d->clean_index = -1; // we've deleted the clean state
+ }
+
+ bool try_merge = cur != nullptr
+ && cur->id() != -1
+ && cur->id() == cmd->id()
+ && (macro || d->index != d->clean_index);
+
+ if (try_merge && cur->mergeWith(cmd)) {
+ delete cmd;
+
+ if (macro) {
+ if (cur->isObsolete())
+ delete d->macro_stack.constLast()->d->child_list.takeLast();
+ } else {
+ if (cur->isObsolete()) {
+ delete d->command_list.takeLast();
+
+ d->setIndex(d->index - 1, false);
+ } else {
+ emit indexChanged(d->index);
+ emit canUndoChanged(canUndo());
+ emit undoTextChanged(undoText());
+ emit canRedoChanged(canRedo());
+ emit redoTextChanged(redoText());
+ }
+ }
+ } else if (cmd->isObsolete()) {
+ delete cmd; // command should be deleted and NOT added to the stack
+ } else {
+ if (macro) {
+ d->macro_stack.constLast()->d->child_list.append(cmd);
+ } else {
+ d->command_list.append(cmd);
+ d->checkUndoLimit();
+ d->setIndex(d->index + 1, false);
+ }
+ }
+}
+
+/*!
+ Marks the stack as clean and emits cleanChanged() if the stack was
+ not already clean.
+
+ This is typically called when a document is saved, for example.
+
+ Whenever the stack returns to this state through the use of undo/redo
+ commands, it emits the signal cleanChanged(). This signal is also
+ emitted when the stack leaves the clean state.
+
+ \sa isClean(), resetClean(), cleanIndex()
+*/
+
+void QUndoStack::setClean()
+{
+ Q_D(QUndoStack);
+ if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
+ qWarning("QUndoStack::setClean(): cannot set clean in the middle of a macro");
+ return;
+ }
+
+ d->setIndex(d->index, true);
+}
+
+/*!
+ \since 5.8
+
+ Leaves the clean state and emits cleanChanged() if the stack was clean.
+ This method resets the clean index to -1.
+
+ This is typically called in the following cases, when a document has been:
+ \list
+ \li created basing on some template and has not been saved,
+ so no filename has been associated with the document yet.
+ \li restored from a backup file.
+ \li changed outside of the editor and the user did not reload it.
+ \endlist
+
+ \sa isClean(), setClean(), cleanIndex()
+*/
+
+void QUndoStack::resetClean()
+{
+ Q_D(QUndoStack);
+ const bool was_clean = isClean();
+ d->clean_index = -1;
+ if (was_clean)
+ emit cleanChanged(false);
+}
+
+/*!
+ \since 5.12
+ \property QUndoStack::clean
+ \brief the clean status of this stack.
+
+ This property indicates whether or not the stack is clean. For example, a
+ stack is clean when a document has been saved.
+
+ \sa isClean(), setClean(), resetClean(), cleanIndex()
+*/
+
+/*!
+ If the stack is in the clean state, returns \c true; otherwise returns \c false.
+
+ \sa setClean(), cleanIndex()
+*/
+
+bool QUndoStack::isClean() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return false;
+ return d->clean_index == d->index;
+}
+
+/*!
+ Returns the clean index. This is the index at which setClean() was called.
+
+ A stack may not have a clean index. This happens if a document is saved,
+ some commands are undone, then a new command is pushed. Since
+ push() deletes all the undone commands before pushing the new command, the stack
+ can't return to the clean state again. In this case, this function returns -1.
+ The -1 may also be returned after an explicit call to resetClean().
+
+ \sa isClean(), setClean()
+*/
+
+int QUndoStack::cleanIndex() const
+{
+ Q_D(const QUndoStack);
+ return d->clean_index;
+}
+
+/*!
+ Undoes the command below the current command by calling QUndoCommand::undo().
+ Decrements the current command index.
+
+ If the stack is empty, or if the bottom command on the stack has already been
+ undone, this function does nothing.
+
+ After the command is undone, if QUndoCommand::isObsolete() returns \c true,
+ then the command will be deleted from the stack. Additionally, if the clean
+ index is greater than or equal to the current command index, then the clean
+ index is reset.
+
+ \sa redo(), index()
+*/
+
+void QUndoStack::undo()
+{
+ Q_D(QUndoStack);
+ if (d->index == 0)
+ return;
+
+ if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
+ qWarning("QUndoStack::undo(): cannot undo in the middle of a macro");
+ return;
+ }
+
+ int idx = d->index - 1;
+ QUndoCommand *cmd = d->command_list.at(idx);
+
+ if (!cmd->isObsolete())
+ cmd->undo();
+
+ if (cmd->isObsolete()) { // A separate check is done b/c the undo command may set obsolete flag
+ delete d->command_list.takeAt(idx);
+
+ if (d->clean_index > idx)
+ resetClean();
+ }
+
+ d->setIndex(idx, false);
+}
+
+/*!
+ Redoes the current command by calling QUndoCommand::redo(). Increments the current
+ command index.
+
+ If the stack is empty, or if the top command on the stack has already been
+ redone, this function does nothing.
+
+ If QUndoCommand::isObsolete() returns true for the current command, then
+ the command will be deleted from the stack. Additionally, if the clean
+ index is greater than or equal to the current command index, then the clean
+ index is reset.
+
+ \sa undo(), index()
+*/
+
+void QUndoStack::redo()
+{
+ Q_D(QUndoStack);
+ if (d->index == d->command_list.size())
+ return;
+
+ if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
+ qWarning("QUndoStack::redo(): cannot redo in the middle of a macro");
+ return;
+ }
+
+ int idx = d->index;
+ QUndoCommand *cmd = d->command_list.at(idx);
+
+ if (!cmd->isObsolete())
+ cmd->redo(); // A separate check is done b/c the undo command may set obsolete flag
+
+ if (cmd->isObsolete()) {
+ delete d->command_list.takeAt(idx);
+
+ if (d->clean_index > idx)
+ resetClean();
+ } else {
+ d->setIndex(d->index + 1, false);
+ }
+}
+
+/*!
+ Returns the number of commands on the stack. Macro commands are counted as
+ one command.
+
+ \sa index(), setIndex(), command()
+*/
+
+int QUndoStack::count() const
+{
+ Q_D(const QUndoStack);
+ return d->command_list.size();
+}
+
+/*!
+ Returns the index of the current command. This is the command that will be
+ executed on the next call to redo(). It is not always the top-most command
+ on the stack, since a number of commands may have been undone.
+
+ \sa undo(), redo(), count()
+*/
+
+int QUndoStack::index() const
+{
+ Q_D(const QUndoStack);
+ return d->index;
+}
+
+/*!
+ Repeatedly calls undo() or redo() until the current command index reaches
+ \a idx. This function can be used to roll the state of the document forwards
+ of backwards. indexChanged() is emitted only once.
+
+ \sa index(), count(), undo(), redo()
+*/
+
+void QUndoStack::setIndex(int idx)
+{
+ Q_D(QUndoStack);
+ if (Q_UNLIKELY(!d->macro_stack.isEmpty())) {
+ qWarning("QUndoStack::setIndex(): cannot set index in the middle of a macro");
+ return;
+ }
+
+ if (idx < 0)
+ idx = 0;
+ else if (idx > d->command_list.size())
+ idx = d->command_list.size();
+
+ int i = d->index;
+ while (i < idx) {
+ QUndoCommand *cmd = d->command_list.at(i);
+
+ if (!cmd->isObsolete())
+ cmd->redo(); // A separate check is done b/c the undo command may set obsolete flag
+
+ if (cmd->isObsolete()) {
+ delete d->command_list.takeAt(i);
+
+ if (d->clean_index > i)
+ resetClean();
+
+ idx--; // Subtract from idx because we removed a command
+ } else {
+ i++;
+ }
+ }
+
+ while (i > idx) {
+ QUndoCommand *cmd = d->command_list.at(--i);
+
+ cmd->undo();
+ if (cmd->isObsolete()) {
+ delete d->command_list.takeAt(i);
+
+ if (d->clean_index > i)
+ resetClean();
+ }
+ }
+
+ d->setIndex(idx, false);
+}
+
+/*!
+ \since 5.12
+ \property QUndoStack::canUndo
+ \brief whether this stack can undo.
+
+ This property indicates whether or not there is a command that can be
+ undone.
+
+ \sa canUndo(), index(), canRedo()
+*/
+
+/*!
+ Returns \c true if there is a command available for undo; otherwise returns \c false.
+
+ This function returns \c false if the stack is empty, or if the bottom command
+ on the stack has already been undone.
+
+ Synonymous with index() == 0.
+
+ \sa index(), canRedo()
+*/
+
+bool QUndoStack::canUndo() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return false;
+ return d->index > 0;
+}
+
+/*!
+ \since 5.12
+ \property QUndoStack::canRedo
+ \brief whether this stack can redo.
+
+ This property indicates whether or not there is a command that can be
+ redone.
+
+ \sa canRedo(), index(), canUndo()
+*/
+
+/*!
+ Returns \c true if there is a command available for redo; otherwise returns \c false.
+
+ This function returns \c false if the stack is empty or if the top command
+ on the stack has already been redone.
+
+ Synonymous with index() == count().
+
+ \sa index(), canUndo()
+*/
+
+bool QUndoStack::canRedo() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return false;
+ return d->index < d->command_list.size();
+}
+
+/*!
+ \since 5.12
+ \property QUndoStack::undoText
+ \brief the undo text of the next command that is undone.
+
+ This property holds the text of the command which will be undone in the
+ next call to undo().
+
+ \sa undoText(), QUndoCommand::actionText(), redoText()
+*/
+
+/*!
+ Returns the text of the command which will be undone in the next call to undo().
+
+ \sa QUndoCommand::actionText(), redoText()
+*/
+
+QString QUndoStack::undoText() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return QString();
+ if (d->index > 0)
+ return d->command_list.at(d->index - 1)->actionText();
+ return QString();
+}
+
+/*!
+ \since 5.12
+ \property QUndoStack::redoText
+ \brief the redo text of the next command that is redone.
+
+ This property holds the text of the command which will be redone in the
+ next call to redo().
+
+ \sa redoText(), QUndoCommand::actionText(), undoText()
+*/
+
+/*!
+ Returns the text of the command which will be redone in the next call to redo().
+
+ \sa QUndoCommand::actionText(), undoText()
+*/
+
+QString QUndoStack::redoText() const
+{
+ Q_D(const QUndoStack);
+ if (!d->macro_stack.isEmpty())
+ return QString();
+ if (d->index < d->command_list.size())
+ return d->command_list.at(d->index)->actionText();
+ return QString();
+}
+
+#ifndef QT_NO_ACTION
+
+/*!
+ \internal
+
+ Sets the text property of \a action to \a text, applying \a prefix, and falling back to \a defaultText if \a text is empty.
+*/
+void QUndoStackPrivate::setPrefixedText(QAction *action, const QString &prefix, const QString &defaultText, const QString &text)
+{
+ if (defaultText.isEmpty()) {
+ QString s = prefix;
+ if (!prefix.isEmpty() && !text.isEmpty())
+ s.append(QLatin1Char(' '));
+ s.append(text);
+ action->setText(s);
+ } else {
+ if (text.isEmpty())
+ action->setText(defaultText);
+ else
+ action->setText(prefix.arg(text));
+ }
+};
+
+/*!
+ Creates an undo QAction object with the given \a parent.
+
+ Triggering this action will cause a call to undo(). The text of this action
+ is the text of the command which will be undone in the next call to undo(),
+ prefixed by the specified \a prefix. If there is no command available for undo,
+ this action will be disabled.
+
+ If \a prefix is empty, the default template "Undo %1" is used instead of prefix.
+ Before Qt 4.8, the prefix "Undo" was used by default.
+
+ \sa createRedoAction(), canUndo(), QUndoCommand::text()
+*/
+
+QAction *QUndoStack::createUndoAction(QObject *parent, const QString &prefix) const
+{
+ QAction *action = new QAction(parent);
+ action->setEnabled(canUndo());
+
+ QString effectivePrefix = prefix;
+ QString defaultText;
+ if (prefix.isEmpty()) {
+ effectivePrefix = tr("Undo %1");
+ defaultText = tr("Undo", "Default text for undo action");
+ }
+
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, undoText());
+
+ connect(this, &QUndoStack::canUndoChanged, action, &QAction::setEnabled);
+ connect(this, &QUndoStack::undoTextChanged, action, [=](const QString &text) {
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
+ });
+ connect(action, &QAction::triggered, this, &QUndoStack::undo);
+
+ return action;
+}
+
+/*!
+ Creates an redo QAction object with the given \a parent.
+
+ Triggering this action will cause a call to redo(). The text of this action
+ is the text of the command which will be redone in the next call to redo(),
+ prefixed by the specified \a prefix. If there is no command available for redo,
+ this action will be disabled.
+
+ If \a prefix is empty, the default template "Redo %1" is used instead of prefix.
+ Before Qt 4.8, the prefix "Redo" was used by default.
+
+ \sa createUndoAction(), canRedo(), QUndoCommand::text()
+*/
+
+QAction *QUndoStack::createRedoAction(QObject *parent, const QString &prefix) const
+{
+ QAction *action = new QAction(parent);
+ action->setEnabled(canRedo());
+
+ QString effectivePrefix = prefix;
+ QString defaultText;
+ if (prefix.isEmpty()) {
+ effectivePrefix = tr("Redo %1");
+ defaultText = tr("Redo", "Default text for redo action");
+ }
+
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, redoText());
+
+ connect(this, &QUndoStack::canRedoChanged, action, &QAction::setEnabled);
+ connect(this, &QUndoStack::redoTextChanged, action, [=](const QString &text) {
+ QUndoStackPrivate::setPrefixedText(action, effectivePrefix, defaultText, text);
+ });
+ connect(action, &QAction::triggered, this, &QUndoStack::redo);
+
+ return action;
+}
+
+#endif // QT_NO_ACTION
+
+/*!
+ Begins composition of a macro command with the given \a text description.
+
+ An empty command described by the specified \a text is pushed on the stack.
+ Any subsequent commands pushed on the stack will be appended to the empty
+ command's children until endMacro() is called.
+
+ Calls to beginMacro() and endMacro() may be nested, but every call to
+ beginMacro() must have a matching call to endMacro().
+
+ While a macro is being composed, the stack is disabled. This means that:
+ \list
+ \li indexChanged() and cleanChanged() are not emitted,
+ \li canUndo() and canRedo() return false,
+ \li calling undo() or redo() has no effect,
+ \li the undo/redo actions are disabled.
+ \endlist
+
+ The stack becomes enabled and appropriate signals are emitted when endMacro()
+ is called for the outermost macro.
+
+ \snippet code/src_gui_util_qundostack.cpp 4
+
+ This code is equivalent to:
+
+ \snippet code/src_gui_util_qundostack.cpp 5
+
+ \sa endMacro()
+*/
+
+void QUndoStack::beginMacro(const QString &text)
+{
+ Q_D(QUndoStack);
+ QUndoCommand *cmd = new QUndoCommand();
+ cmd->setText(text);
+
+ if (d->macro_stack.isEmpty()) {
+ while (d->index < d->command_list.size())
+ delete d->command_list.takeLast();
+ if (d->clean_index > d->index)
+ d->clean_index = -1; // we've deleted the clean state
+ d->command_list.append(cmd);
+ } else {
+ d->macro_stack.constLast()->d->child_list.append(cmd);
+ }
+ d->macro_stack.append(cmd);
+
+ if (d->macro_stack.count() == 1) {
+ emit canUndoChanged(false);
+ emit undoTextChanged(QString());
+ emit canRedoChanged(false);
+ emit redoTextChanged(QString());
+ }
+}
+
+/*!
+ Ends composition of a macro command.
+
+ If this is the outermost macro in a set nested macros, this function emits
+ indexChanged() once for the entire macro command.
+
+ \sa beginMacro()
+*/
+
+void QUndoStack::endMacro()
+{
+ Q_D(QUndoStack);
+ if (Q_UNLIKELY(d->macro_stack.isEmpty())) {
+ qWarning("QUndoStack::endMacro(): no matching beginMacro()");
+ return;
+ }
+
+ d->macro_stack.removeLast();
+
+ if (d->macro_stack.isEmpty()) {
+ d->checkUndoLimit();
+ d->setIndex(d->index + 1, false);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Returns a const pointer to the command at \a index.
+
+ This function returns a const pointer, because modifying a command,
+ once it has been pushed onto the stack and executed, almost always
+ causes corruption of the state of the document, if the command is
+ later undone or redone.
+
+ \sa QUndoCommand::child()
+*/
+const QUndoCommand *QUndoStack::command(int index) const
+{
+ Q_D(const QUndoStack);
+
+ if (index < 0 || index >= d->command_list.count())
+ return nullptr;
+ return d->command_list.at(index);
+}
+
+/*!
+ Returns the text of the command at index \a idx.
+
+ \sa beginMacro()
+*/
+
+QString QUndoStack::text(int idx) const
+{
+ Q_D(const QUndoStack);
+
+ if (idx < 0 || idx >= d->command_list.size())
+ return QString();
+ return d->command_list.at(idx)->text();
+}
+
+/*!
+ \property QUndoStack::undoLimit
+ \brief the maximum number of commands on this stack.
+ \since 4.3
+
+ When the number of commands on a stack exceedes the stack's undoLimit, commands are
+ deleted from the bottom of the stack. Macro commands (commands with child commands)
+ are treated as one command. The default value is 0, which means that there is no
+ limit.
+
+ This property may only be set when the undo stack is empty, since setting it on a
+ non-empty stack might delete the command at the current index. Calling setUndoLimit()
+ on a non-empty stack prints a warning and does nothing.
+*/
+
+void QUndoStack::setUndoLimit(int limit)
+{
+ Q_D(QUndoStack);
+
+ if (Q_UNLIKELY(!d->command_list.isEmpty())) {
+ qWarning("QUndoStack::setUndoLimit(): an undo limit can only be set when the stack is empty");
+ return;
+ }
+
+ if (limit == d->undo_limit)
+ return;
+ d->undo_limit = limit;
+ d->checkUndoLimit();
+}
+
+int QUndoStack::undoLimit() const
+{
+ Q_D(const QUndoStack);
+
+ return d->undo_limit;
+}
+
+/*!
+ \property QUndoStack::active
+ \brief the active status of this stack.
+
+ An application often has multiple undo stacks, one for each opened document. The active
+ stack is the one associated with the currently active document. If the stack belongs
+ to a QUndoGroup, calls to QUndoGroup::undo() or QUndoGroup::redo() will be forwarded
+ to this stack when it is active. If the QUndoGroup is watched by a QUndoView, the view
+ will display the contents of this stack when it is active. If the stack does not belong to
+ a QUndoGroup, making it active has no effect.
+
+ It is the programmer's responsibility to specify which stack is active by
+ calling setActive(), usually when the associated document window receives focus.
+
+ \sa QUndoGroup
+*/
+
+void QUndoStack::setActive(bool active)
+{
+#if !QT_CONFIG(undogroup)
+ Q_UNUSED(active);
+#else
+ Q_D(QUndoStack);
+
+ if (d->group != nullptr) {
+ if (active)
+ d->group->setActiveStack(this);
+ else if (d->group->activeStack() == this)
+ d->group->setActiveStack(nullptr);
+ }
+#endif
+}
+
+bool QUndoStack::isActive() const
+{
+#if !QT_CONFIG(undogroup)
+ return true;
+#else
+ Q_D(const QUndoStack);
+ return d->group == nullptr || d->group->activeStack() == this;
+#endif
+}
+
+/*!
+ \fn void QUndoStack::indexChanged(int idx)
+
+ This signal is emitted whenever a command modifies the state of the document.
+ This happens when a command is undone or redone. When a macro
+ command is undone or redone, or setIndex() is called, this signal
+ is emitted only once.
+
+ \a idx specifies the index of the current command, ie. the command which will be
+ executed on the next call to redo().
+
+ \sa index(), setIndex()
+*/
+
+/*!
+ \fn void QUndoStack::cleanChanged(bool clean)
+
+ This signal is emitted whenever the stack enters or leaves the clean state.
+ If \a clean is true, the stack is in a clean state; otherwise this signal
+ indicates that it has left the clean state.
+
+ \sa isClean(), setClean()
+*/
+
+/*!
+ \fn void QUndoStack::undoTextChanged(const QString &undoText)
+
+ This signal is emitted whenever the value of undoText() changes. It is
+ used to update the text property of the undo action returned by createUndoAction().
+ \a undoText specifies the new text.
+*/
+
+/*!
+ \fn void QUndoStack::canUndoChanged(bool canUndo)
+
+ This signal is emitted whenever the value of canUndo() changes. It is
+ used to enable or disable the undo action returned by createUndoAction().
+ \a canUndo specifies the new value.
+*/
+
+/*!
+ \fn void QUndoStack::redoTextChanged(const QString &redoText)
+
+ This signal is emitted whenever the value of redoText() changes. It is
+ used to update the text property of the redo action returned by createRedoAction().
+ \a redoText specifies the new text.
+*/
+
+/*!
+ \fn void QUndoStack::canRedoChanged(bool canRedo)
+
+ This signal is emitted whenever the value of canRedo() changes. It is
+ used to enable or disable the redo action returned by createRedoAction().
+ \a canRedo specifies the new value.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qundostack.cpp"
+
+#endif // QT_CONFIG(undostack)
diff --git a/src/gui/util/qundostack.h b/src/gui/util/qundostack.h
new file mode 100644
index 0000000000..c137e7d942
--- /dev/null
+++ b/src/gui/util/qundostack.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNDOSTACK_H
+#define QUNDOSTACK_H
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+QT_REQUIRE_CONFIG(undocommand);
+
+QT_BEGIN_NAMESPACE
+
+class QAction;
+class QUndoCommandPrivate;
+class QUndoStackPrivate;
+
+class Q_GUI_EXPORT QUndoCommand
+{
+ QUndoCommandPrivate *d;
+
+public:
+ explicit QUndoCommand(QUndoCommand *parent = nullptr);
+ explicit QUndoCommand(const QString &text, QUndoCommand *parent = nullptr);
+ virtual ~QUndoCommand();
+
+ virtual void undo();
+ virtual void redo();
+
+ QString text() const;
+ QString actionText() const;
+ void setText(const QString &text);
+
+ bool isObsolete() const;
+ void setObsolete(bool obsolete);
+
+ virtual int id() const;
+ virtual bool mergeWith(const QUndoCommand *other);
+
+ int childCount() const;
+ const QUndoCommand *child(int index) const;
+
+private:
+ Q_DISABLE_COPY(QUndoCommand)
+ friend class QUndoStack;
+};
+
+#if QT_CONFIG(undostack)
+
+class Q_GUI_EXPORT QUndoStack : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QUndoStack)
+ Q_PROPERTY(bool active READ isActive WRITE setActive)
+ Q_PROPERTY(int undoLimit READ undoLimit WRITE setUndoLimit)
+ Q_PROPERTY(bool canUndo READ canUndo NOTIFY canUndoChanged)
+ Q_PROPERTY(bool canRedo READ canRedo NOTIFY canRedoChanged)
+ Q_PROPERTY(QString undoText READ undoText NOTIFY undoTextChanged)
+ Q_PROPERTY(QString redoText READ redoText NOTIFY redoTextChanged)
+ Q_PROPERTY(bool clean READ isClean NOTIFY cleanChanged)
+
+public:
+ explicit QUndoStack(QObject *parent = nullptr);
+ ~QUndoStack();
+ void clear();
+
+ void push(QUndoCommand *cmd);
+
+ bool canUndo() const;
+ bool canRedo() const;
+ QString undoText() const;
+ QString redoText() const;
+
+ int count() const;
+ int index() const;
+ QString text(int idx) const;
+
+#ifndef QT_NO_ACTION
+ QAction *createUndoAction(QObject *parent, const QString &prefix = QString()) const;
+ QAction *createRedoAction(QObject *parent, const QString &prefix = QString()) const;
+#endif // QT_NO_ACTION
+
+ bool isActive() const;
+ bool isClean() const;
+ int cleanIndex() const;
+
+ void beginMacro(const QString &text);
+ void endMacro();
+
+ void setUndoLimit(int limit);
+ int undoLimit() const;
+
+ const QUndoCommand *command(int index) const;
+
+public Q_SLOTS:
+ void setClean();
+ void resetClean();
+ void setIndex(int idx);
+ void undo();
+ void redo();
+ void setActive(bool active = true);
+
+Q_SIGNALS:
+ void indexChanged(int idx);
+ void cleanChanged(bool clean);
+ void canUndoChanged(bool canUndo);
+ void canRedoChanged(bool canRedo);
+ void undoTextChanged(const QString &undoText);
+ void redoTextChanged(const QString &redoText);
+
+private:
+ Q_DISABLE_COPY(QUndoStack)
+ friend class QUndoGroup;
+};
+
+#endif // QT_CONFIG(undostack)
+
+QT_END_NAMESPACE
+
+#endif // QUNDOSTACK_H
diff --git a/src/gui/util/qundostack_p.h b/src/gui/util/qundostack_p.h
new file mode 100644
index 0000000000..5f2c438a83
--- /dev/null
+++ b/src/gui/util/qundostack_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNDOSTACK_P_H
+#define QUNDOSTACK_P_H
+
+#include <QtGui/private/qtguiglobal_p.h>
+#include <private/qobject_p.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qstring.h>
+#if QT_CONFIG(action)
+# include <QtGui/qaction.h>
+#endif
+
+#include "qundostack.h"
+
+QT_BEGIN_NAMESPACE
+class QUndoCommand;
+class QUndoGroup;
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+class QUndoCommandPrivate
+{
+public:
+ QUndoCommandPrivate() : id(-1), obsolete(false) {}
+ QList<QUndoCommand*> child_list;
+ QString text;
+ QString actionText;
+ int id;
+ bool obsolete;
+};
+
+#if QT_CONFIG(undostack)
+
+class QUndoStackPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QUndoStack)
+public:
+ QUndoStackPrivate() : index(0), clean_index(0), group(nullptr), undo_limit(0) {}
+
+ QList<QUndoCommand*> command_list;
+ QList<QUndoCommand*> macro_stack;
+ int index;
+ int clean_index;
+ QUndoGroup *group;
+ int undo_limit;
+
+ void setIndex(int idx, bool clean);
+ bool checkUndoLimit();
+
+#ifndef QT_NO_ACTION
+ static void setPrefixedText(QAction *action, const QString &prefix, const QString &defaultText, const QString &text);
+#endif
+};
+
+QT_END_NAMESPACE
+#endif // QT_CONFIG(undostack)
+#endif // QUNDOSTACK_P_H
diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri
index d3402133d6..a799c3e591 100644
--- a/src/gui/util/util.pri
+++ b/src/gui/util/util.pri
@@ -46,3 +46,16 @@ qtConfig(regularexpression) {
SOURCES += \
util/qshadergenerator.cpp
}
+
+qtConfig(undocommand) {
+ HEADERS += \
+ util/qundostack.h \
+ util/qundostack_p.h
+
+ SOURCES += util/qundostack.cpp
+}
+
+qtConfig(undogroup) {
+ HEADERS += util/qundogroup.h
+ SOURCES += util/qundogroup.cpp
+}