File indexing completed on 2024-05-26 04:26:20

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2006 Jan Hambrecht <jaham@gmx.net>
0003  * SPDX-FileCopyrightText: 2006, 2007 Thorsten Zachmann <zachmann@kde.org>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "KoSubpathJoinCommand.h"
0009 #include <klocalizedstring.h>
0010 #include "kis_assert.h"
0011 #include "KoPathMergeUtils.h"
0012 
0013 
0014 KoSubpathJoinCommand::KoSubpathJoinCommand(const KoPathPointData &pointData1, const KoPathPointData &pointData2, KUndo2Command *parent)
0015         : KUndo2Command(parent)
0016         , m_pointData1(pointData1)
0017         , m_pointData2(pointData2)
0018         , m_splitIndex(KoPathPointIndex(-1, -1))
0019         , m_oldProperties1(KoPathPoint::Normal)
0020         , m_oldProperties2(KoPathPoint::Normal)
0021         , m_reverse(0)
0022 {
0023     KIS_ASSERT(m_pointData1.pathShape == m_pointData2.pathShape);
0024     KoPathShape * pathShape = m_pointData1.pathShape;
0025     KIS_ASSERT(!pathShape->isClosedSubpath(m_pointData1.pointIndex.first));
0026     KIS_ASSERT(m_pointData1.pointIndex.second == 0 ||
0027                m_pointData1.pointIndex.second == pathShape->subpathPointCount(m_pointData1.pointIndex.first) - 1);
0028     KIS_ASSERT(!pathShape->isClosedSubpath(m_pointData2.pointIndex.first));
0029     KIS_ASSERT(m_pointData2.pointIndex.second == 0 ||
0030                m_pointData2.pointIndex.second == pathShape->subpathPointCount(m_pointData2.pointIndex.first) - 1);
0031     //TODO check that points are not the same
0032 
0033     if (m_pointData2 < m_pointData1) {
0034         std::swap(m_pointData1, m_pointData2);
0035     }
0036 
0037     if (!closeSubpathMode()) {
0038         if (m_pointData1.pointIndex.second == 0 &&
0039             pathShape->subpathPointCount(m_pointData1.pointIndex.first) > 1)
0040             m_reverse |= ReverseFirst;
0041         if (m_pointData2.pointIndex.second != 0)
0042             m_reverse |= ReverseSecond;
0043         setText(kundo2_i18n("Join subpaths"));
0044     } else {
0045         setText(kundo2_i18n("Close subpath"));
0046     }
0047 
0048     KoPathPoint * point1 = pathShape->pointByIndex(m_pointData1.pointIndex);
0049     KoPathPoint * point2 = pathShape->pointByIndex(m_pointData2.pointIndex);
0050 
0051     m_savedControlPoint1 = KritaUtils::fetchControlPoint(point1, m_reverse & ReverseFirst);
0052     m_savedControlPoint2 = KritaUtils::fetchControlPoint(point2, !(m_reverse & ReverseSecond));
0053 
0054     m_oldProperties1 = point1->properties();
0055     m_oldProperties2 = point2->properties();
0056 }
0057 
0058 KoSubpathJoinCommand::~KoSubpathJoinCommand()
0059 {
0060 }
0061 
0062 
0063 
0064 void KoSubpathJoinCommand::redo()
0065 {
0066     KUndo2Command::redo();
0067     KoPathShape * pathShape = m_pointData1.pathShape;
0068 
0069     KoPathPoint * point1 = pathShape->pointByIndex(m_pointData1.pointIndex);
0070     KoPathPoint * point2 = pathShape->pointByIndex(m_pointData2.pointIndex);
0071 
0072     KIS_SAFE_ASSERT_RECOVER_RETURN(point1);
0073     KIS_SAFE_ASSERT_RECOVER_RETURN(point2);
0074 
0075     // if the endpoint has a control point create a control point for the new segment to be
0076     // at the symmetric position to the exiting one
0077 
0078     if (closeSubpathMode()) {
0079         KritaUtils::makeSymmetric(point1, false);
0080         KritaUtils::makeSymmetric(point2, true);
0081     } else {
0082         KritaUtils::makeSymmetric(point1, !(m_reverse & ReverseFirst));
0083         KritaUtils::makeSymmetric(point2, m_reverse & ReverseSecond);
0084     }
0085 
0086     if (closeSubpathMode()) {
0087         pathShape->closeSubpath(m_pointData1.pointIndex);
0088     } else {
0089         if (m_reverse & ReverseFirst) {
0090             pathShape->reverseSubpath(m_pointData1.pointIndex.first);
0091         }
0092         if (m_reverse & ReverseSecond) {
0093             pathShape->reverseSubpath(m_pointData2.pointIndex.first);
0094         }
0095         pathShape->moveSubpath(m_pointData2.pointIndex.first, m_pointData1.pointIndex.first + 1);
0096         m_splitIndex = m_pointData1.pointIndex;
0097         m_splitIndex.second = pathShape->subpathPointCount(m_pointData1.pointIndex.first) - 1;
0098         pathShape->join(m_pointData1.pointIndex.first);
0099     }
0100 
0101     QList<KoPathPointIndex> pointIndexes;
0102     pointIndexes << pathShape->pathPointIndex(point1);
0103     pointIndexes << pathShape->pathPointIndex(point2);
0104     pathShape->recommendPointSelectionChange(pointIndexes);
0105 
0106     pathShape->normalize();
0107     pathShape->update();
0108 }
0109 
0110 void KoSubpathJoinCommand::undo()
0111 {
0112     KUndo2Command::undo();
0113     KoPathShape * pathShape = m_pointData1.pathShape;
0114     pathShape->update();
0115 
0116     if (closeSubpathMode()) {
0117         pathShape->openSubpath(m_pointData1.pointIndex);
0118     } else {
0119         pathShape->breakAfter(m_splitIndex);
0120         pathShape->moveSubpath(m_pointData1.pointIndex.first + 1, m_pointData2.pointIndex.first);
0121 
0122         if (m_reverse & ReverseSecond) {
0123             pathShape->reverseSubpath(m_pointData2.pointIndex.first);
0124         }
0125         if (m_reverse & ReverseFirst) {
0126             pathShape->reverseSubpath(m_pointData1.pointIndex.first);
0127         }
0128     }
0129     KoPathPoint * point1 = pathShape->pointByIndex(m_pointData1.pointIndex);
0130     KoPathPoint * point2 = pathShape->pointByIndex(m_pointData2.pointIndex);
0131 
0132     KIS_SAFE_ASSERT_RECOVER_RETURN(point1);
0133     KIS_SAFE_ASSERT_RECOVER_RETURN(point2);
0134 
0135     // restore the old end points
0136     if (closeSubpathMode()) {
0137         KritaUtils::restoreControlPoint(point1, true, m_savedControlPoint1);
0138         KritaUtils::restoreControlPoint(point2, false, m_savedControlPoint2);
0139     } else {
0140         KritaUtils::restoreControlPoint(point1, m_reverse & ReverseFirst, m_savedControlPoint1);
0141         KritaUtils::restoreControlPoint(point2, !(m_reverse & ReverseSecond), m_savedControlPoint2);
0142     }
0143 
0144     point1->setProperties(m_oldProperties1);
0145     point2->setProperties(m_oldProperties2);
0146 
0147     QList<KoPathPointIndex> pointIndexes;
0148     pointIndexes << pathShape->pathPointIndex(point1);
0149     pointIndexes << pathShape->pathPointIndex(point2);
0150     pathShape->recommendPointSelectionChange(pointIndexes);
0151 
0152     pathShape->normalize();
0153     pathShape->update();
0154 }
0155 
0156 bool KoSubpathJoinCommand::closeSubpathMode() const
0157 {
0158     return m_pointData1.pointIndex.first == m_pointData2.pointIndex.first;
0159 }
0160