File indexing completed on 2024-06-09 04:20:44

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2006, 2008 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 "KoPathPointRemoveCommand.h"
0009 #include "KoSubpathRemoveCommand.h"
0010 #include "KoShapeController.h"
0011 #include "KoPathPoint.h"
0012 #include <klocalizedstring.h>
0013 
0014 class KoPathPointRemoveCommandPrivate
0015 {
0016 public:
0017     KoPathPointRemoveCommandPrivate() : deletePoints(false) { }
0018     ~KoPathPointRemoveCommandPrivate() {
0019         if (deletePoints)
0020             qDeleteAll(points);
0021     }
0022     QList<KoPathPointData> pointDataList;
0023     QList<KoPathPoint*> points;
0024     bool deletePoints;
0025 };
0026 
0027 KUndo2Command *KoPathPointRemoveCommand::createCommand(
0028     const QList<KoPathPointData> &pointDataList,
0029     KoShapeController *shapeController,
0030     KUndo2Command *parent)
0031 {
0032     /*
0033      * We want to decide if we have to:
0034      * 1. delete only some points of a path or
0035      * 2. delete one or more complete subpath or
0036      * 3. delete a complete path
0037      */
0038 
0039     QList<KoPathPointData> sortedPointData(pointDataList);
0040     std::sort(sortedPointData.begin(), sortedPointData.end());
0041 
0042     KoPathPointData last(0, KoPathPointIndex(-1, -1));
0043     // add last at the end so that the point date before last will also be put in
0044     // the right places.
0045     sortedPointData.append(last);
0046 
0047     QList<KoPathPointData> pointsOfSubpath; // points of current subpath
0048     QList<KoPathPointData> subpathsOfPath;  // subpaths of current path
0049     QList<KoPathPointData> pointsToDelete;  // single points to delete
0050     QList<KoPathPointData> subpathToDelete; // single subpaths to delete
0051     QList<KoShape*> shapesToDelete;         // single paths to delete
0052 
0053     last = sortedPointData.first();
0054 
0055     QList<KoPathPointData>::const_iterator it(sortedPointData.constBegin());
0056     for (; it != sortedPointData.constEnd(); ++it) {
0057         // check if we have come to the next subpath of the same or another path
0058         if (last.pathShape != it->pathShape || last.pointIndex.first != it->pointIndex.first) {
0059             // check if all points of the last subpath should be deleted
0060             if (last.pathShape->subpathPointCount(last.pointIndex.first) == pointsOfSubpath.size()) {
0061                 // all points of subpath to be deleted -> mark subpath as to be deleted
0062                 subpathsOfPath.append(pointsOfSubpath.first());
0063             } else {
0064                 // not all points of subpath to be deleted -> add them to the delete point list
0065                 pointsToDelete += pointsOfSubpath;
0066             }
0067             // clear the suboath point list
0068             pointsOfSubpath.clear();
0069         }
0070 
0071         // check if we have come to the next shape
0072         if (last.pathShape != it->pathShape) {
0073             // check if all subpath of the shape should be deleted
0074             if (last.pathShape->subpathCount() == subpathsOfPath.size()) {
0075                 // all subpaths of path to be deleted -> add shape to delete shape list
0076                 shapesToDelete.append(last.pathShape);
0077             } else {
0078                 // not all subpaths of path to be deleted -> add them to delete subpath list
0079                 subpathToDelete += subpathsOfPath;
0080             }
0081             subpathsOfPath.clear();
0082         }
0083         if (! it->pathShape)
0084             continue;
0085         // keep reference to last point
0086         last = *it;
0087         // add this point to the current subpath point list
0088         pointsOfSubpath.append(*it);
0089     }
0090 
0091     KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Remove points"), parent);
0092 
0093     if (pointsToDelete.size() > 0) {
0094         new KoPathPointRemoveCommand(pointsToDelete, cmd);
0095     }
0096     Q_FOREACH (const KoPathPointData & pd, subpathToDelete) {
0097         new KoSubpathRemoveCommand(pd.pathShape, pd.pointIndex.first, cmd);
0098     }
0099     if (shapesToDelete.size() > 0) {
0100         shapeController->removeShapes(shapesToDelete, cmd);
0101     }
0102 
0103     return cmd;
0104 }
0105 
0106 KoPathPointRemoveCommand::KoPathPointRemoveCommand(const QList<KoPathPointData> & pointDataList,
0107         KUndo2Command *parent)
0108     : KUndo2Command(parent),
0109     d(new KoPathPointRemoveCommandPrivate())
0110 {
0111     QList<KoPathPointData>::const_iterator it(pointDataList.begin());
0112     for (; it != pointDataList.end(); ++it) {
0113         KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
0114         if (point) {
0115             d->pointDataList.append(*it);
0116             d->points.append(0);
0117         }
0118     }
0119     std::sort(d->pointDataList.begin(), d->pointDataList.end());
0120     setText(kundo2_i18n("Remove points"));
0121 }
0122 
0123 KoPathPointRemoveCommand::~KoPathPointRemoveCommand()
0124 {
0125     delete d;
0126 }
0127 
0128 void KoPathPointRemoveCommand::redo()
0129 {
0130     KUndo2Command::redo();
0131     KoPathShape * lastPathShape = 0;
0132     int updateBefore = d->pointDataList.size();
0133     for (int i = d->pointDataList.size() - 1; i >= 0; --i) {
0134         const KoPathPointData &pd = d->pointDataList.at(i);
0135         pd.pathShape->update();
0136         d->points[i] = pd.pathShape->removePoint(pd.pointIndex);
0137 
0138         if (lastPathShape != pd.pathShape) {
0139             if (lastPathShape) {
0140                 QPointF offset = lastPathShape->normalize();
0141 
0142                 QTransform matrix;
0143                 matrix.translate(-offset.x(), -offset.y());
0144                 for (int j = i + 1; j < updateBefore; ++j) {
0145                     d->points.at(j)->map(matrix);
0146                 }
0147                 lastPathShape->update();
0148                 updateBefore = i + 1;
0149             }
0150             lastPathShape = pd.pathShape;
0151         }
0152     }
0153 
0154     if (lastPathShape) {
0155         QPointF offset = lastPathShape->normalize();
0156 
0157         QTransform matrix;
0158         matrix.translate(-offset.x(), -offset.y());
0159         for (int j = 0; j < updateBefore; ++j) {
0160             d->points.at(j)->map(matrix);
0161         }
0162         lastPathShape->update();
0163     }
0164 
0165     d->deletePoints = true;
0166 }
0167 
0168 void KoPathPointRemoveCommand::undo()
0169 {
0170     KUndo2Command::undo();
0171     KoPathShape * lastPathShape = 0;
0172 
0173     QMap<KoPathShape *, QList<KoPathPointIndex>> pointsMap;
0174 
0175     for (int i = 0; i < d->pointDataList.size(); ++i) {
0176         const KoPathPointData &pd = d->pointDataList.at(i);
0177         if (lastPathShape && lastPathShape != pd.pathShape) {
0178             lastPathShape->normalize();
0179             lastPathShape->update();
0180         }
0181         pd.pathShape->insertPoint(d->points[i], pd.pointIndex);
0182         lastPathShape = pd.pathShape;
0183 
0184         pointsMap[pd.pathShape].append(pd.pointIndex);
0185     }
0186 
0187     if (lastPathShape) {
0188         lastPathShape->normalize();
0189         lastPathShape->update();
0190     }
0191 
0192     for (auto it = pointsMap.constBegin(); it != pointsMap.constEnd(); ++it) {
0193         it.key()->recommendPointSelectionChange(it.value());
0194     }
0195 
0196     d->deletePoints = false;
0197 }