1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "pathselectionmanipulator.h"
27
28 #include "pathitem.h"
29
30 #include <QtDebug>
31
32 namespace QmlDesigner {
33
PathSelectionManipulator(PathItem * pathItem)34 PathSelectionManipulator::PathSelectionManipulator(PathItem *pathItem)
35 : m_pathItem(pathItem),
36 m_isMultiSelecting(false),
37 m_isMoving(false)
38 {
39 }
40
clear()41 void PathSelectionManipulator::clear()
42 {
43 clearSingleSelection();
44 clearMultiSelection();
45 m_isMultiSelecting = false;
46 m_isMoving = false;
47 }
48
clearSingleSelection()49 void PathSelectionManipulator::clearSingleSelection()
50 {
51 m_singleSelectedPoints.clear();
52 m_automaticallyAddedSinglePoints.clear();
53 }
54
clearMultiSelection()55 void PathSelectionManipulator::clearMultiSelection()
56 {
57 m_multiSelectedPoints.clear();
58 }
59
createSelectionPoint(const ControlPoint & controlPoint)60 static SelectionPoint createSelectionPoint(const ControlPoint &controlPoint)
61 {
62 SelectionPoint selectionPoint;
63 selectionPoint.controlPoint = controlPoint;
64 selectionPoint.startPosition = controlPoint.coordinate();
65
66 return selectionPoint;
67 }
68
addMultiSelectionControlPoint(const ControlPoint & controlPoint)69 void PathSelectionManipulator::addMultiSelectionControlPoint(const ControlPoint &controlPoint)
70 {
71 m_multiSelectedPoints.append(createSelectionPoint(controlPoint));
72 }
73
getControlPoint(const QList<ControlPoint> & selectedPoints,const ControlPoint & controlPoint,int indexOffset,bool isClosedPath)74 static ControlPoint getControlPoint(const QList<ControlPoint> &selectedPoints, const ControlPoint &controlPoint, int indexOffset, bool isClosedPath)
75 {
76 int controlPointIndex = selectedPoints.indexOf(controlPoint);
77 if (controlPointIndex >= 0) {
78 int offsetIndex = controlPointIndex + indexOffset;
79 if (offsetIndex >= 0 && offsetIndex < selectedPoints.count())
80 return selectedPoints.at(offsetIndex);
81 else if (isClosedPath) {
82 if (offsetIndex == -1)
83 return selectedPoints.constLast();
84 else if (offsetIndex < selectedPoints.count())
85 return selectedPoints.at(1);
86 }
87 }
88
89 return ControlPoint();
90 }
91
singleSelectedPoints()92 QList<SelectionPoint> PathSelectionManipulator::singleSelectedPoints()
93 {
94 return m_singleSelectedPoints;
95 }
96
automaticallyAddedSinglePoints()97 QList<SelectionPoint> PathSelectionManipulator::automaticallyAddedSinglePoints()
98 {
99 return m_automaticallyAddedSinglePoints;
100 }
101
allSelectionSinglePoints()102 QList<SelectionPoint> PathSelectionManipulator::allSelectionSinglePoints()
103 {
104
105 return m_singleSelectedPoints + m_automaticallyAddedSinglePoints;
106 }
107
multiSelectedPoints()108 QList<SelectionPoint> PathSelectionManipulator::multiSelectedPoints()
109 {
110 return m_multiSelectedPoints;
111 }
112
allSelectionPoints()113 QList<SelectionPoint> PathSelectionManipulator::allSelectionPoints()
114 {
115 return m_singleSelectedPoints + m_multiSelectedPoints + m_automaticallyAddedSinglePoints;
116 }
117
allControlPoints()118 QList<ControlPoint> PathSelectionManipulator::allControlPoints()
119 {
120 QList<ControlPoint> controlPoints;
121
122 foreach (const SelectionPoint &selectionPoint, m_singleSelectedPoints)
123 controlPoints.append(selectionPoint.controlPoint);
124
125 foreach (const SelectionPoint &selectionPoint, m_automaticallyAddedSinglePoints)
126 controlPoints.append(selectionPoint.controlPoint);
127
128 foreach (const SelectionPoint &selectionPoint, m_multiSelectedPoints)
129 controlPoints.append(selectionPoint.controlPoint);
130
131 return controlPoints;
132 }
133
hasSingleSelection() const134 bool PathSelectionManipulator::hasSingleSelection() const
135 {
136 return !m_singleSelectedPoints.isEmpty();
137 }
138
hasMultiSelection() const139 bool PathSelectionManipulator::hasMultiSelection() const
140 {
141 return !m_multiSelectedPoints.isEmpty();
142 }
143
startMultiSelection(const QPointF & startPoint)144 void PathSelectionManipulator::startMultiSelection(const QPointF &startPoint)
145 {
146 m_startPoint = startPoint;
147 m_isMultiSelecting = true;
148 }
149
updateMultiSelection(const QPointF & updatePoint)150 void PathSelectionManipulator::updateMultiSelection(const QPointF &updatePoint)
151 {
152 clearMultiSelection();
153
154 m_updatePoint = updatePoint;
155
156 QRectF selectionRect(m_startPoint, updatePoint);
157
158 foreach (const ControlPoint &controlPoint, m_pathItem->controlPoints()) {
159 if (selectionRect.contains(controlPoint.coordinate()))
160 addMultiSelectionControlPoint(controlPoint);
161 }
162 }
163
endMultiSelection()164 void PathSelectionManipulator::endMultiSelection()
165 {
166 m_isMultiSelecting = false;
167 }
168
169 SelectionPoint::SelectionPoint() = default;
170
SelectionPoint(const ControlPoint & controlPoint)171 SelectionPoint::SelectionPoint(const ControlPoint &controlPoint)
172 : controlPoint(controlPoint)
173 {
174 }
175
operator ==(const SelectionPoint & firstSelectionPoint,const SelectionPoint & secondSelectionPoint)176 bool operator ==(const SelectionPoint &firstSelectionPoint, const SelectionPoint &secondSelectionPoint)
177 {
178 return firstSelectionPoint.controlPoint == secondSelectionPoint.controlPoint;
179 }
180
multiSelectionStartPoint() const181 QPointF PathSelectionManipulator::multiSelectionStartPoint() const
182 {
183 return m_startPoint;
184 }
185
isMultiSelecting() const186 bool PathSelectionManipulator::isMultiSelecting() const
187 {
188 return m_isMultiSelecting;
189 }
190
multiSelectionRectangle() const191 QRectF PathSelectionManipulator::multiSelectionRectangle() const
192 {
193 return QRectF(m_startPoint, m_updatePoint);
194 }
195
setStartPoint(const QPointF & startPoint)196 void PathSelectionManipulator::setStartPoint(const QPointF &startPoint)
197 {
198 m_startPoint = startPoint;
199 }
200
startPoint() const201 QPointF PathSelectionManipulator::startPoint() const
202 {
203 return m_startPoint;
204 }
205
snapFactor(Qt::KeyboardModifiers keyboardModifier)206 double snapFactor(Qt::KeyboardModifiers keyboardModifier)
207 {
208 if (keyboardModifier.testFlag(Qt::ControlModifier))
209 return 10.;
210
211 return 1.;
212 }
213
roundedVector(const QPointF & vector,double factor=1.)214 QPointF roundedVector(const QPointF &vector, double factor = 1.)
215 {
216 QPointF roundedPosition;
217
218 roundedPosition.setX(qRound(vector.x() / factor) * factor);
219 roundedPosition.setY(qRound(vector.y() / factor) * factor);
220
221 return roundedPosition;
222 }
223
manipulatedVector(const QPointF & vector,Qt::KeyboardModifiers keyboardModifier)224 QPointF manipulatedVector(const QPointF &vector, Qt::KeyboardModifiers keyboardModifier)
225 {
226 QPointF manipulatedVector = roundedVector(vector, snapFactor(keyboardModifier));
227
228 if (keyboardModifier.testFlag(Qt::ShiftModifier))
229 manipulatedVector.setX(0.);
230
231 if (keyboardModifier.testFlag(Qt::AltModifier))
232 manipulatedVector.setY(0.);
233
234 return manipulatedVector;
235 }
236
moveControlPoints(const QList<SelectionPoint> & movePoints,const QPointF & offsetVector)237 static void moveControlPoints(const QList<SelectionPoint> &movePoints, const QPointF &offsetVector)
238 {
239 foreach (SelectionPoint movePoint, movePoints)
240 movePoint.controlPoint.setCoordinate(movePoint.startPosition + offsetVector);
241 }
242
startMoving(const QPointF & startPoint)243 void PathSelectionManipulator::startMoving(const QPointF &startPoint)
244 {
245 m_isMoving = true;
246 m_startPoint = startPoint;
247 }
248
updateMoving(const QPointF & updatePoint,Qt::KeyboardModifiers keyboardModifier)249 void PathSelectionManipulator::updateMoving(const QPointF &updatePoint, Qt::KeyboardModifiers keyboardModifier)
250 {
251 m_updatePoint = updatePoint;
252 QPointF offsetVector = manipulatedVector(updatePoint - m_startPoint, keyboardModifier) ;
253 moveControlPoints(allSelectionPoints(), offsetVector);
254 }
255
endMoving()256 void PathSelectionManipulator::endMoving()
257 {
258 updateMultiSelectedStartPoint();
259 m_isMoving = false;
260 }
261
isMoving() const262 bool PathSelectionManipulator::isMoving() const
263 {
264 return m_isMoving;
265 }
266
updateMultiSelectedStartPoint()267 void PathSelectionManipulator::updateMultiSelectedStartPoint()
268 {
269 QList<SelectionPoint> oldSelectionPoints = m_multiSelectedPoints;
270
271 m_multiSelectedPoints.clear();
272
273 foreach (SelectionPoint selectionPoint, oldSelectionPoints) {
274 selectionPoint.startPosition = selectionPoint.controlPoint.coordinate();
275 m_multiSelectedPoints.append(selectionPoint);
276 }
277 }
278
addSingleControlPoint(const ControlPoint & controlPoint)279 void PathSelectionManipulator::addSingleControlPoint(const ControlPoint &controlPoint)
280 {
281 m_singleSelectedPoints.append(createSelectionPoint(controlPoint));
282 }
283
addSingleControlPointSmartly(const ControlPoint & editPoint)284 void PathSelectionManipulator::addSingleControlPointSmartly(const ControlPoint &editPoint)
285 {
286 m_singleSelectedPoints.append(createSelectionPoint(editPoint));
287
288 if (editPoint.isEditPoint()) {
289 ControlPoint previousControlPoint = getControlPoint(m_pathItem->controlPoints(), editPoint, -1, m_pathItem->isClosedPath());
290 if (previousControlPoint.isValid())
291 m_automaticallyAddedSinglePoints.append(createSelectionPoint(previousControlPoint));
292
293 ControlPoint nextControlPoint= getControlPoint(m_pathItem->controlPoints(), editPoint, 1, m_pathItem->isClosedPath());
294 if (nextControlPoint.isValid())
295 m_automaticallyAddedSinglePoints.append(createSelectionPoint(nextControlPoint));
296 }
297 }
298
299 } // namespace QMlDesigner
300