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