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

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  * SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
0005  * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
0006  *
0007  * SPDX-License-Identifier: LGPL-2.0-or-later
0008  */
0009 
0010 #include "KoPathToolSelection.h"
0011 #include "KoPathTool.h"
0012 #include <KoParameterShape.h>
0013 #include <KoPathPoint.h>
0014 #include <KoPathPointData.h>
0015 #include <KoViewConverter.h>
0016 #include <KoCanvasBase.h>
0017 #include <KoDocumentResourceManager.h>
0018 #include <KoShapeController.h>
0019 #include <QPainter>
0020 #include <KisHandlePainterHelper.h>
0021 
0022 KoPathToolSelection::KoPathToolSelection(KoPathTool * tool)
0023         : m_tool(tool)
0024 {
0025 }
0026 
0027 KoPathToolSelection::~KoPathToolSelection()
0028 {
0029 }
0030 
0031 void KoPathToolSelection::paint(QPainter &painter, const KoViewConverter &converter, qreal handleRadius)
0032 {
0033     int decorationThickness = m_tool? m_tool->decorationThickness(): 1;
0034     PathShapePointMap::iterator it(m_shapePointMap.begin());
0035     for (; it != m_shapePointMap.end(); ++it) {
0036         KisHandlePainterHelper helper =
0037             KoShape::createHandlePainterHelperView(&painter, it.key(), converter, handleRadius, decorationThickness);
0038         helper.setHandleStyle(KisHandleStyle::selectedPrimaryHandles());
0039 
0040         Q_FOREACH (KoPathPoint *p, it.value()) {
0041             p->paint(helper, KoPathPoint::All);
0042         }
0043     }
0044 }
0045 
0046 void KoPathToolSelection::add(KoPathPoint * point, bool clear)
0047 {
0048     if(! point)
0049         return;
0050 
0051     bool alreadyIn = false;
0052     if (clear) {
0053         if (size() == 1 && m_selectedPoints.contains(point)) {
0054             alreadyIn = true;
0055         } else {
0056             this->clear();
0057         }
0058     } else {
0059         alreadyIn = m_selectedPoints.contains(point);
0060     }
0061 
0062     if (!alreadyIn) {
0063         m_selectedPoints.insert(point);
0064         KoPathShape * pathShape = point->parent();
0065         PathShapePointMap::iterator it(m_shapePointMap.find(pathShape));
0066         if (it == m_shapePointMap.end()) {
0067             it = m_shapePointMap.insert(pathShape, QSet<KoPathPoint *>());
0068         }
0069         it.value().insert(point);
0070         emit selectionChanged();
0071     }
0072 }
0073 
0074 void KoPathToolSelection::remove(KoPathPoint * point)
0075 {
0076     if (m_selectedPoints.remove(point)) {
0077         KoPathShape * pathShape = point->parent();
0078         m_shapePointMap[pathShape].remove(point);
0079         if (m_shapePointMap[pathShape].size() == 0) {
0080             m_shapePointMap.remove(pathShape);
0081         }
0082         emit selectionChanged();
0083     }
0084 }
0085 
0086 void KoPathToolSelection::clear()
0087 {
0088     m_selectedPoints.clear();
0089     m_shapePointMap.clear();
0090     emit selectionChanged();
0091 }
0092 
0093 void KoPathToolSelection::selectPoints(const QRectF &rect, bool clearSelection)
0094 {
0095     if (clearSelection) {
0096         clear();
0097     }
0098 
0099     blockSignals(true);
0100     Q_FOREACH (KoPathShape* shape, m_selectedShapes) {
0101         KoParameterShape *parameterShape = dynamic_cast<KoParameterShape*>(shape);
0102         if (parameterShape && parameterShape->isParametricShape())
0103             continue;
0104         Q_FOREACH (KoPathPoint* point, shape->pointsAt(shape->documentToShape(rect)))
0105             add(point, false);
0106     }
0107     blockSignals(false);
0108     emit selectionChanged();
0109 }
0110 
0111 void KoPathToolSelection::selectAll()
0112 {
0113     blockSignals(true);
0114     Q_FOREACH (KoPathShape* shape, m_selectedShapes) {
0115         KoParameterShape *parameterShape = dynamic_cast<KoParameterShape*>(shape);
0116         if (parameterShape && parameterShape->isParametricShape())
0117             continue;
0118         Q_FOREACH (KoPathPoint* point, shape->pointsAt(shape->outlineRect().adjusted(-2, -2, 2, 2)))
0119             add(point, false);
0120     }
0121     blockSignals(false);
0122     emit selectionChanged();
0123 }
0124 
0125 int KoPathToolSelection::objectCount() const
0126 {
0127     return m_shapePointMap.size();
0128 }
0129 
0130 int KoPathToolSelection::size() const
0131 {
0132     return m_selectedPoints.size();
0133 }
0134 
0135 bool KoPathToolSelection::contains(KoPathPoint * point)
0136 {
0137     return m_selectedPoints.contains(point);
0138 }
0139 
0140 const QSet<KoPathPoint *> & KoPathToolSelection::selectedPoints() const
0141 {
0142     return m_selectedPoints;
0143 }
0144 
0145 QList<KoPathPointData> KoPathToolSelection::selectedPointsData() const
0146 {
0147     QList<KoPathPointData> pointData;
0148     Q_FOREACH (KoPathPoint* p, m_selectedPoints) {
0149         KoPathShape * pathShape = p->parent();
0150         pointData.append(KoPathPointData(pathShape, pathShape->pathPointIndex(p)));
0151     }
0152     return pointData;
0153 }
0154 
0155 QList<KoPathPointData> KoPathToolSelection::selectedSegmentsData() const
0156 {
0157     QList<KoPathPointData> pointData;
0158 
0159     QList<KoPathPointData> pd(selectedPointsData());
0160     std::sort(pd.begin(), pd.end());
0161 
0162     KoPathPointData last(0, KoPathPointIndex(-1, -1));
0163     KoPathPointData lastSubpathStart(0, KoPathPointIndex(-1, -1));
0164 
0165     QList<KoPathPointData>::const_iterator it(pd.constBegin());
0166     for (; it != pd.constEnd(); ++it) {
0167         if (it->pointIndex.second == 0)
0168             lastSubpathStart = *it;
0169 
0170         if (last.pathShape == it->pathShape
0171                 && last.pointIndex.first == it->pointIndex.first
0172                 && last.pointIndex.second + 1 == it->pointIndex.second) {
0173             pointData.append(last);
0174         }
0175 
0176         if (lastSubpathStart.pathShape == it->pathShape
0177                 && it->pathShape->pointByIndex(it->pointIndex)->properties() & KoPathPoint::CloseSubpath
0178                 && (it->pathShape->pointByIndex(it->pointIndex)->properties() & KoPathPoint::StartSubpath) == 0) {
0179             pointData.append(*it);
0180         }
0181 
0182         last = *it;
0183     }
0184 
0185     return pointData;
0186 }
0187 
0188 QList<KoPathShape*> KoPathToolSelection::selectedShapes() const
0189 {
0190     return m_selectedShapes;
0191 }
0192 
0193 void KoPathToolSelection::setSelectedShapes(const QList<KoPathShape*> shapes)
0194 {
0195     Q_FOREACH(KoPathShape *shape, m_selectedShapes) {
0196         shape->removeShapeChangeListener(this);
0197     }
0198 
0199     m_selectedShapes = shapes;
0200 
0201     Q_FOREACH(KoPathShape *shape, m_selectedShapes) {
0202         shape->addShapeChangeListener(this);
0203     }
0204 }
0205 
0206 void KoPathToolSelection::update()
0207 {
0208     bool selectionHasChanged = false;
0209 
0210     PathShapePointMap::iterator it(m_shapePointMap.begin());
0211     while (it != m_shapePointMap.end()) {
0212         KoParameterShape *parameterShape = dynamic_cast<KoParameterShape*>(it.key());
0213         bool isParametricShape = parameterShape && parameterShape->isParametricShape();
0214         if (! m_selectedShapes.contains(it.key()) || isParametricShape) {
0215             QSet<KoPathPoint *>::iterator pointIt(it.value().begin());
0216             for (; pointIt != it.value().end(); ++pointIt) {
0217                 m_selectedPoints.remove(*pointIt);
0218             }
0219             it = m_shapePointMap.erase(it);
0220             selectionHasChanged = true;
0221         } else {
0222             QSet<KoPathPoint *>::iterator pointIt(it.value().begin());
0223             while (pointIt != it.value().end()) {
0224                 if ((*pointIt)->parent()->pathPointIndex(*pointIt) == KoPathPointIndex(-1, -1)) {
0225                     m_selectedPoints.remove(*pointIt);
0226                     pointIt = it.value().erase(pointIt);
0227                     selectionHasChanged = true;
0228                 } else {
0229                     ++pointIt;
0230                 }
0231             }
0232             ++it;
0233         }
0234     }
0235 
0236     if (selectionHasChanged)
0237         emit selectionChanged();
0238 }
0239 
0240 bool KoPathToolSelection::hasSelection()
0241 {
0242     return !m_selectedPoints.isEmpty();
0243 }
0244 
0245 void KoPathToolSelection::recommendPointSelectionChange(KoPathShape *shape, const QList<KoPathPointIndex> &newSelection)
0246 {
0247     QSet<KoPathPoint*> selectedShapePoints = m_shapePointMap.value(shape, QSet<KoPathPoint*>());
0248 
0249     Q_FOREACH (KoPathPoint *point, selectedShapePoints) {
0250         remove(point);
0251     }
0252 
0253     Q_FOREACH (const KoPathPointIndex &index, newSelection) {
0254         KoPathPoint *point = shape->pointByIndex(index);
0255         KIS_SAFE_ASSERT_RECOVER(point) { continue; }
0256 
0257         add(point, false);
0258     }
0259 
0260     emit selectionChanged();
0261 }
0262 
0263 void KoPathToolSelection::notifyPathPointsChanged(KoPathShape *shape)
0264 {
0265     QSet<KoPathPoint*> selectedShapePoints = m_shapePointMap.value(shape, QSet<KoPathPoint*>());
0266 
0267     Q_FOREACH (KoPathPoint *point, selectedShapePoints) {
0268         m_selectedPoints.remove(point);
0269     }
0270     m_shapePointMap.remove(shape);
0271 
0272     m_tool->notifyPathPointsChanged(shape);
0273 
0274     emit selectionChanged();
0275 }
0276 
0277 void KoPathToolSelection::notifyShapeChanged(KoShape::ChangeType type, KoShape *shape)
0278 {
0279     if (type == KoShape::Deleted) {
0280         // we cannot select non-path shapes, so static cast is safe here
0281         KIS_SAFE_ASSERT_RECOVER_NOOP(shape->shapeId() == KoPathShapeId);
0282 
0283         if (KoPathShape *pathShape = static_cast<KoPathShape*>(shape)) {
0284 
0285             QSet<KoPathPoint*> selectedShapePoints = m_shapePointMap.value(pathShape, QSet<KoPathPoint*>());
0286             Q_FOREACH (KoPathPoint *point, selectedShapePoints) {
0287                 m_selectedPoints.remove(point);
0288             }
0289             m_shapePointMap.remove(pathShape);
0290             m_selectedShapes.removeAll(pathShape);
0291         }
0292     }
0293 
0294     KoPathShape::PointSelectionChangeListener::notifyShapeChanged(type, shape);
0295 }