1 /* This file is part of the KDE project
2
3 Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
4 Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "SelectionDecorator.h"
23
24 #include <QPainterPath>
25
26 #include <KoShape.h>
27 #include <KoSelection.h>
28 #include <KoResourcePaths.h>
29 #include "kis_algebra_2d.h"
30
31 #include "kis_debug.h"
32 #include <KisHandlePainterHelper.h>
33 #include <KoCanvasResourceProvider.h>
34 #include <KisQPainterStateSaver.h>
35 #include "KoShapeGradientHandles.h"
36 #include <KoCanvasBase.h>
37 #include <KoSvgTextShape.h>
38
39 #include "kis_painting_tweaks.h"
40 #include "kis_coordinates_converter.h"
41 #include "kis_icon_utils.h"
42
43
44
45 #define HANDLE_DISTANCE 10
46
SelectionDecorator(KoCanvasResourceProvider * resourceManager)47 SelectionDecorator::SelectionDecorator(KoCanvasResourceProvider *resourceManager)
48 : m_hotPosition(KoFlake::Center)
49 , m_handleRadius(7)
50 , m_lineWidth(2)
51 , m_showFillGradientHandles(false)
52 , m_showStrokeFillGradientHandles(false)
53 , m_forceShapeOutlines(false)
54 {
55 m_hotPosition =
56 KoFlake::AnchorPosition(
57 resourceManager->resource(KoFlake::HotPosition).toInt());
58 }
59
setSelection(KoSelection * selection)60 void SelectionDecorator::setSelection(KoSelection *selection)
61 {
62 m_selection = selection;
63 }
64
setHandleRadius(int radius)65 void SelectionDecorator::setHandleRadius(int radius)
66 {
67 m_handleRadius = radius;
68 m_lineWidth = qMax(1, (int)(radius / 2));
69 }
70
setShowFillGradientHandles(bool value)71 void SelectionDecorator::setShowFillGradientHandles(bool value)
72 {
73 m_showFillGradientHandles = value;
74 }
75
setShowStrokeFillGradientHandles(bool value)76 void SelectionDecorator::setShowStrokeFillGradientHandles(bool value)
77 {
78 m_showStrokeFillGradientHandles = value;
79 }
80
setShowFillMeshGradientHandles(bool value)81 void SelectionDecorator::setShowFillMeshGradientHandles(bool value)
82 {
83 m_showFillMeshGradientHandles = value;
84 }
85
setCurrentMeshGradientHandles(const KoShapeMeshGradientHandles::Handle & selectedHandle,const KoShapeMeshGradientHandles::Handle & hoveredHandle)86 void SelectionDecorator::setCurrentMeshGradientHandles(const KoShapeMeshGradientHandles::Handle &selectedHandle,
87 const KoShapeMeshGradientHandles::Handle &hoveredHandle)
88 {
89 m_selectedMeshHandle = selectedHandle;
90 m_currentHoveredMeshHandle = hoveredHandle;
91 }
92
paint(QPainter & painter,const KoViewConverter & converter)93 void SelectionDecorator::paint(QPainter &painter, const KoViewConverter &converter)
94 {
95 QList<KoShape*> selectedShapes = m_selection->selectedVisibleShapes();
96 if (selectedShapes.isEmpty()) return;
97
98 const bool haveOnlyOneEditableShape =
99 m_selection->selectedEditableShapes().size() == 1 &&
100 selectedShapes.size() == 1;
101
102 bool editable = false;
103 bool forceBoundngRubberLine = false;
104
105 Q_FOREACH (KoShape *shape, KoShape::linearizeSubtree(selectedShapes)) {
106 if (!haveOnlyOneEditableShape || !m_showStrokeFillGradientHandles) {
107 KisHandlePainterHelper helper =
108 KoShape::createHandlePainterHelperView(&painter, shape, converter, m_handleRadius);
109
110 helper.setHandleStyle(KisHandleStyle::secondarySelection());
111
112 if (!m_forceShapeOutlines) {
113 helper.drawRubberLine(shape->outlineRect());
114 } else {
115 QList<QPolygonF> polys = shape->outline().toSubpathPolygons();
116
117 if (polys.size() == 1) {
118 const QPolygonF poly1 = polys[0];
119 const QPolygonF poly2 = QPolygonF(polys[0].boundingRect());
120 const QPolygonF nonoverlap = poly2.subtracted(poly1);
121
122 forceBoundngRubberLine |= !nonoverlap.isEmpty();
123 }
124
125 Q_FOREACH (const QPolygonF &poly, polys) {
126 helper.drawRubberLine(poly);
127 }
128 }
129 }
130
131 if (shape->isShapeEditable()) {
132 editable = true;
133 }
134 }
135
136 const QRectF handleArea = m_selection->outlineRect();
137
138 // draw extra rubber line around all the shapes
139 if (selectedShapes.size() > 1 || forceBoundngRubberLine) {
140 KisHandlePainterHelper helper =
141 KoShape::createHandlePainterHelperView(&painter, m_selection, converter, m_handleRadius);
142
143 helper.setHandleStyle(KisHandleStyle::primarySelection());
144 helper.drawRubberLine(handleArea);
145 }
146
147 // if we have no editable shape selected there
148 // is no need drawing the selection handles
149 if (editable) {
150 KisHandlePainterHelper helper =
151 KoShape::createHandlePainterHelperView(&painter, m_selection, converter, m_handleRadius);
152 helper.setHandleStyle(KisHandleStyle::primarySelection());
153
154 QPolygonF outline = handleArea;
155
156 {
157 helper.drawHandleRect(outline.value(0));
158 helper.drawHandleRect(outline.value(1));
159 helper.drawHandleRect(outline.value(2));
160 helper.drawHandleRect(outline.value(3));
161 helper.drawHandleRect(0.5 * (outline.value(0) + outline.value(1)));
162 helper.drawHandleRect(0.5 * (outline.value(1) + outline.value(2)));
163 helper.drawHandleRect(0.5 * (outline.value(2) + outline.value(3)));
164 helper.drawHandleRect(0.5 * (outline.value(3) + outline.value(0)));
165
166 QPointF hotPos = KoFlake::anchorToPoint(m_hotPosition, handleArea);
167 helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles());
168 helper.drawHandleRect(hotPos);
169 }
170 }
171
172 if (haveOnlyOneEditableShape) {
173 KoShape *shape = selectedShapes.first();
174
175 if (m_showFillGradientHandles) {
176 paintGradientHandles(shape, KoFlake::Fill, painter, converter);
177 } else if (m_showStrokeFillGradientHandles) {
178 paintGradientHandles(shape, KoFlake::StrokeFill, painter, converter);
179 }
180
181 // paint meshgradient handles
182 if(m_showFillMeshGradientHandles) {
183 paintMeshGradientHandles(shape, KoFlake::Fill, painter, converter);
184 }
185 }
186
187 }
188
paintGradientHandles(KoShape * shape,KoFlake::FillVariant fillVariant,QPainter & painter,const KoViewConverter & converter)189 void SelectionDecorator::paintGradientHandles(KoShape *shape, KoFlake::FillVariant fillVariant, QPainter &painter, const KoViewConverter &converter)
190 {
191 KoShapeGradientHandles gradientHandles(fillVariant, shape);
192 QVector<KoShapeGradientHandles::Handle> handles = gradientHandles.handles();
193
194 KisHandlePainterHelper helper =
195 KoShape::createHandlePainterHelperView(&painter, shape, converter, m_handleRadius);
196
197 const QTransform t = shape->absoluteTransformation().inverted();
198
199 if (gradientHandles.type() == QGradient::LinearGradient) {
200 KIS_SAFE_ASSERT_RECOVER_NOOP(handles.size() == 2);
201
202 if (handles.size() == 2) {
203 helper.setHandleStyle(KisHandleStyle::gradientArrows());
204 helper.drawGradientArrow(t.map(handles[0].pos), t.map(handles[1].pos), 1.5 * m_handleRadius);
205 }
206 }
207
208 helper.setHandleStyle(KisHandleStyle::gradientHandles());
209
210 Q_FOREACH (const KoShapeGradientHandles::Handle &h, handles) {
211 if (h.type == KoShapeGradientHandles::Handle::RadialCenter) {
212 helper.drawGradientCrossHandle(t.map(h.pos), 1.2 * m_handleRadius);
213 } else {
214 helper.drawGradientHandle(t.map(h.pos), 1.2 * m_handleRadius);
215 }
216 }
217 }
218
paintMeshGradientHandles(KoShape * shape,KoFlake::FillVariant fillVariant,QPainter & painter,const KoViewConverter & converter)219 void SelectionDecorator::paintMeshGradientHandles(KoShape *shape,
220 KoFlake::FillVariant fillVariant,
221 QPainter &painter,
222 const KoViewConverter &converter)
223 {
224 KoShapeMeshGradientHandles gradientHandles(fillVariant, shape);
225
226 KisHandlePainterHelper helper =
227 KoShape::createHandlePainterHelperView(&painter, shape, converter, m_handleRadius);
228 helper.setHandleStyle(KisHandleStyle::secondarySelection());
229
230 helper.drawPath(gradientHandles.path());
231
232 // invert them, because we draw in logical coordinates.
233 QTransform t = shape->absoluteTransformation().inverted();
234 QVector<KoShapeMeshGradientHandles::Handle> cornerHandles = gradientHandles.handles();
235 for (const auto& corner: cornerHandles) {
236 if (corner.type == KoShapeMeshGradientHandles::Handle::BezierHandle) {
237 helper.drawHandleSmallCircle(t.map(corner.pos));
238 } else if (corner.type == KoShapeMeshGradientHandles::Handle::Corner) {
239 helper.drawHandleRect(t.map(corner.pos));
240 }
241 }
242
243 helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandlesWithSolidOutline());
244
245 // highlight the selected handle (only corner)
246 if (m_selectedMeshHandle.type == KoShapeMeshGradientHandles::Handle::Corner) {
247 helper.drawHandleRect(t.map(gradientHandles.getHandle(m_selectedMeshHandle.getPosition()).pos));
248 }
249
250 // highlight the path which is being hovered/moved
251 if (m_currentHoveredMeshHandle.type != KoShapeMeshGradientHandles::Handle::None) {
252 QVector<QPainterPath> paths = gradientHandles.getConnectedPath(m_currentHoveredMeshHandle);
253 for (const auto &path: paths) {
254 helper.drawPath(path);
255 }
256 }
257 }
258
setForceShapeOutlines(bool value)259 void SelectionDecorator::setForceShapeOutlines(bool value)
260 {
261 m_forceShapeOutlines = value;
262 }
263