File indexing completed on 2024-05-19 03:48:33

0001 /*
0002     File                 : aspectcommands.h
0003     Project              : LabPlot
0004     Description          : Undo commands used by AbstractAspect.
0005     Only meant to be used within AbstractAspect.cpp
0006     --------------------------------------------------------------------
0007     SPDX-FileCopyrightText: 2007-2010 Knut Franke <knut.franke@gmx.de>
0008     SPDX-FileCopyrightText: 2007-2009 Tilman Benkert <thzs@gmx.net>
0009     SPDX-FileCopyrightText: 2013-2021 Alexander Semke <alexander.semke@web.de>
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #ifndef ASPECTCOMMANDS_H
0014 #define ASPECTCOMMANDS_H
0015 
0016 #include "AspectPrivate.h"
0017 #include <KLocalizedString>
0018 #include <QUndoCommand>
0019 
0020 class AspectCommonCmd : public QUndoCommand {
0021 public:
0022     AspectCommonCmd(QUndoCommand* parent = nullptr)
0023         : QUndoCommand(parent) {
0024     }
0025     int removeChild(AbstractAspectPrivate* parent, AbstractAspect* child) {
0026         int index = parent->indexOfChild(child);
0027         Q_ASSERT(index != -1);
0028         parent->m_children.removeAll(child);
0029         QObject::disconnect(child, nullptr, nullptr, nullptr);
0030         child->setParentAspect(nullptr);
0031         // QDEBUG(Q_FUNC_INFO << " DONE. CHILD = " << child)
0032         return index;
0033     }
0034 };
0035 
0036 class AspectChildRemoveCmd : public AspectCommonCmd {
0037 public:
0038     AspectChildRemoveCmd(AbstractAspectPrivate* target, AbstractAspect* child, QUndoCommand* parent = nullptr)
0039         : AspectCommonCmd(parent)
0040         , m_target(target)
0041         , m_child(child)
0042         , m_moved(child->isMoved()) {
0043         setText(i18n("%1: remove %2", m_target->m_name, m_child->name()));
0044     }
0045 
0046     ~AspectChildRemoveCmd() override {
0047         // TODO: this makes labplot crashing on project close/save.
0048         //          if (m_removed)
0049         //              delete m_child;
0050     }
0051 
0052     // calling redo transfers ownership of m_child to the undo command
0053     void redo() override {
0054         // QDEBUG(Q_FUNC_INFO << ", TARGET = " << m_target->q << " CHILD = " << m_child << ", PARENT = " << m_child->parentAspect())
0055         AbstractAspect* nextSibling;
0056         if (m_child == m_target->m_children.last())
0057             nextSibling = nullptr;
0058         else
0059             nextSibling = m_target->m_children.at(m_target->indexOfChild(m_child) + 1);
0060 
0061         // emit the "about to be removed" signal also for all children columns so the curves can react.
0062         // no need to notify when the parent is just being moved (move up, moved down in the project explorer),
0063         //(move = delete at the current position + insert at the new position)
0064         if (!m_moved) {
0065             const auto& columns = m_child->children<Column>(AbstractAspect::ChildIndexFlag::Recursive);
0066             for (auto* col : columns)
0067                 Q_EMIT col->parentAspect()->childAspectAboutToBeRemoved(col);
0068         }
0069 
0070         // no need to emit signals if the aspect is hidden, the only exceptions is it's a datapicker point
0071         // and we need to react on its removal in order to update the data spreadsheet.
0072         // TODO: the check for hidden was added originally to avoid crashes in the debug build of Qt because
0073         // of asserts for negative values in the model. It also helps wrt. the performance since we don't need
0074         // to react on such events in the model for hidden aspects. Adding here the exception for the datapicker
0075         // will most probably trigger again crashes in the debug build of Qt if the datapicker is involved but we
0076         // rather accept this "edge case" than having no undo/redo for position changes for datapicker points until
0077         // we have a better solution.
0078         if (!m_child->hidden() || m_child->type() == AspectType::DatapickerPoint)
0079             Q_EMIT m_target->q->childAspectAboutToBeRemoved(m_child);
0080         Q_EMIT m_child->aspectAboutToBeRemoved(m_child);
0081 
0082         m_index = removeChild(m_target, m_child);
0083 
0084         if (!m_child->hidden() || m_child->type() == AspectType::DatapickerPoint)
0085             Q_EMIT m_target->q->childAspectRemoved(m_target->q, nextSibling, m_child);
0086 
0087         // QDEBUG(Q_FUNC_INFO << ", DONE. CHILD = " << m_child)
0088         //      m_removed = true;
0089     }
0090 
0091     // calling undo transfers ownership of m_child back to its parent aspect
0092     void undo() override {
0093         Q_ASSERT(m_index != -1); // m_child must be a child of m_target->q
0094 
0095         if (m_moved)
0096             m_child->setMoved(true);
0097 
0098         Q_EMIT m_target->q->childAspectAboutToBeAdded(m_target->q, nullptr, m_child);
0099         Q_EMIT m_target->q->childAspectAboutToBeAdded(m_target->q, m_index, m_child);
0100         m_target->insertChild(m_index, m_child);
0101         m_child->finalizeAdd();
0102         Q_EMIT m_target->q->childAspectAdded(m_child);
0103 
0104         if (m_moved)
0105             m_child->setMoved(false);
0106         //      m_removed = false;
0107     }
0108 
0109 protected:
0110     AbstractAspectPrivate* m_target{nullptr};
0111     AbstractAspect* m_child{nullptr};
0112     int m_index{-1};
0113     bool m_moved{false};
0114     //  bool m_removed{false};
0115 };
0116 
0117 class AspectChildAddCmd : public AspectChildRemoveCmd {
0118 public:
0119     AspectChildAddCmd(AbstractAspectPrivate* target, AbstractAspect* child, int index, QUndoCommand* parent)
0120         : AspectChildRemoveCmd(target, child, parent) {
0121         setText(i18n("%1: add %2", m_target->m_name, m_child->name()));
0122         m_index = index;
0123         //      m_removed = true;
0124     }
0125 
0126     void redo() override {
0127         AspectChildRemoveCmd::undo();
0128     }
0129 
0130     void undo() override {
0131         AspectChildRemoveCmd::redo();
0132     }
0133 };
0134 
0135 class AspectChildReparentCmd : public AspectCommonCmd {
0136 public:
0137     AspectChildReparentCmd(AbstractAspectPrivate* target,
0138                            AbstractAspectPrivate* new_parent,
0139                            AbstractAspect* child,
0140                            int new_index,
0141                            QUndoCommand* parent = nullptr)
0142         : AspectCommonCmd(parent)
0143         , m_target(target)
0144         , m_new_parent(new_parent)
0145         , m_child(child)
0146         , m_new_index(new_index) {
0147         setText(i18n("%1: move %2 to %3.", m_target->m_name, m_child->name(), m_new_parent->m_name));
0148     }
0149 
0150     // calling redo transfers ownership of m_child to the new parent aspect
0151     void redo() override {
0152         Q_EMIT m_child->childAspectAboutToBeRemoved(m_child);
0153         m_index = removeChild(m_target, m_child);
0154         m_new_parent->insertChild(m_new_index, m_child);
0155         Q_EMIT m_child->childAspectAdded(m_child);
0156     }
0157 
0158     // calling undo transfers ownership of m_child back to its previous parent aspect
0159     void undo() override {
0160         Q_ASSERT(m_index != -1);
0161         Q_EMIT m_child->childAspectAboutToBeRemoved(m_child);
0162         removeChild(m_new_parent, m_child);
0163         m_target->insertChild(m_index, m_child);
0164         Q_EMIT m_child->childAspectAdded(m_child);
0165     }
0166 
0167 protected:
0168     AbstractAspectPrivate* m_target;
0169     AbstractAspectPrivate* m_new_parent;
0170     AbstractAspect* m_child;
0171     int m_index{-1};
0172     int m_new_index;
0173 };
0174 
0175 class AspectNameChangeCmd : public AspectCommonCmd {
0176 public:
0177     AspectNameChangeCmd(AbstractAspectPrivate* aspect, const QString& newName, QUndoCommand* parent = nullptr)
0178         : AspectCommonCmd(parent)
0179         , m_aspect(aspect)
0180         , m_name(newName) {
0181         setText(i18n("%1: rename to %2", m_aspect->m_name, newName));
0182     }
0183 
0184     void redo() override {
0185         Q_EMIT m_aspect->q->aspectDescriptionAboutToChange(m_aspect->q);
0186         const QString name = m_aspect->m_name;
0187         m_aspect->m_name = m_name;
0188         m_name = name;
0189         Q_EMIT m_aspect->q->aspectDescriptionChanged(m_aspect->q);
0190     }
0191 
0192     void undo() override {
0193         redo();
0194     }
0195 
0196 protected:
0197     AbstractAspectPrivate* m_aspect;
0198     QString m_name;
0199 };
0200 
0201 #endif