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 }