13 #include "KoPointerEvent.h"
14 #include "KoPathShape.h"
15 #include "KoSelection.h"
16 #include "KoDocumentResourceManager.h"
17 #include "KoShapePaintingContext.h"
18 #include "KoShapeStroke.h"
19 #include "KoCanvasBase.h"
20 #include "kis_int_parse_spin_box.h"
22 #include "kis_canvas_resource_provider.h"
23 #include <KisHandlePainterHelper.h>
24 #include "KoPathPointTypeCommand.h"
25 #include <KisAngleSelector.h>
27 #include <klocalizedstring.h>
32 #include <QGridLayout>
52 dirtyRect |= kisGrowRect(d->shape->boundingRect(), handleDocRadius());
55 if (d->hoveredPoint) {
56 dirtyRect |= kisGrowRect(d->hoveredPoint->boundingRect(
false), handleDocRadius());
60 dirtyRect |= kisGrowRect(d->activePoint->boundingRect(
false), handleDocRadius());
62 if (d->pointIsDragged) {
65 dirtyRect |= handlePaintRect(
66 d->activePoint->parent()->shapeToDocument(
67 d->activePoint->controlPoint2()));
72 if (canvas()->snapGuide()->isSnapping()) {
73 dirtyRect |= canvas()->snapGuide()->boundingRect();
86 paintPath(*(d->shape), painter, converter);
89 KisHandlePainterHelper helper =
90 KoShape::createHandlePainterHelperView(&painter, d->shape, converter, d->handleRadius);
92 const bool firstPointActive = d->firstPoint == d->activePoint;
94 if (d->pointIsDragged || firstPointActive) {
95 const bool onlyPaintActivePoints =
false;
96 KoPathPoint::PointTypes paintFlags = KoPathPoint::ControlPoint2;
98 if (d->activePoint->activeControlPoint1()) {
99 paintFlags |= KoPathPoint::ControlPoint1;
102 helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles());
103 d->activePoint->paint(helper, paintFlags, onlyPaintActivePoints);
106 if (!firstPointActive) {
107 helper.setHandleStyle(d->mouseOverFirstPoint ?
108 KisHandleStyle::highlightedPrimaryHandles() :
109 KisHandleStyle::primarySelection());
110 d->firstPoint->paint(helper, KoPathPoint::Node);
114 if (d->hoveredPoint) {
115 KisHandlePainterHelper helper = KoShape::createHandlePainterHelperView(&painter, d->hoveredPoint->parent(), converter, d->handleRadius);
116 helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles());
117 d->hoveredPoint->paint(helper, KoPathPoint::Node);
122 canvas()->snapGuide()->paint(painter, converter);
129 painter.
setTransform(pathShape.absoluteTransformation() *
130 converter.documentToView() *
134 KoShapePaintingContext paintContext;
135 pathShape.paint(painter, paintContext);
138 if (pathShape.stroke()) {
140 pathShape.stroke()->paint(d->shape, painter);
150 if (event->button() == Qt::RightButton) {
155 const bool isOverFirstPoint = d->shape &&
156 handleGrabRect(d->firstPoint->point()).contains(event->point);
158 const bool haveCloseModifier = d->enableClosePathShortcut
160 && d->shape->pointCount() > 2
161 && (
event->modifiers() & Qt::ShiftModifier);
163 if ((event->button() == Qt::LeftButton) && haveCloseModifier && !isOverFirstPoint) {
168 d->finishAfterThisPoint =
false;
171 if (isOverFirstPoint) {
172 d->activePoint->setPoint(d->firstPoint->point());
173 canvas()->updateCanvas(d->shape->boundingRect());
174 canvas()->updateCanvas(canvas()->snapGuide()->boundingRect());
176 if (haveCloseModifier) {
177 d->shape->closeMerge();
179 d->existingStartPoint = 0;
185 d->finishAfterThisPoint =
true;
186 repaintDecorations();
189 QPointF point = canvas()->snapGuide()->snap(event->point, event->modifiers());
192 d->existingEndPoint = d->endPointAtPosition(point);
193 if (d->existingEndPoint.isValid() && d->existingEndPoint != d->existingStartPoint) {
194 point = d->existingEndPoint.path->shapeToDocument(d->existingEndPoint.point->point());
195 d->activePoint->setPoint(point);
199 d->activePoint->setPoint(point);
200 repaintDecorations();
204 KoPathShape *pathShape =
new KoPathShape();
205 d->shape = pathShape;
206 pathShape->setShapeId(KoPathShapeId);
208 KoShapeStrokeSP stroke(
new KoShapeStroke());
209 const qreal size = canvas()->resourceManager()->resource(KoCanvasResource::Size).toReal();
211 stroke->setLineWidth(canvas()->unit().fromUserValue(size));
212 stroke->setColor(canvas()->resourceManager()->foregroundColor().toQColor());
214 pathShape->setStroke(stroke);
215 QPointF point = canvas()->snapGuide()->snap(event->point, event->modifiers());
218 d->existingStartPoint = d->endPointAtPosition(point);
220 if (d->existingStartPoint.isValid()) {
221 point = d->existingStartPoint.path->shapeToDocument(d->existingStartPoint.point->point());
224 d->activePoint = pathShape->moveTo(point);
225 d->firstPoint = d->activePoint;
227 canvas()->snapGuide()->setAdditionalEditedShape(pathShape);
229 d->angleSnapStrategy =
new AngleSnapStrategy(d->angleSnappingDelta, d->angleSnapStatus);
230 canvas()->snapGuide()->addCustomSnapStrategy(d->angleSnapStrategy);
232 repaintDecorations();
235 d->dragStartPoint =
event->point;
237 if (d->angleSnapStrategy)
238 d->angleSnapStrategy->setStartPoint(d->activePoint->point());
244 return ((
bool) d->shape);
255 d->enableClosePathShortcut = value;
261 canvas()->updateCanvas(handlePaintRect(event->point));
270 d->hoveredPoint = d->endPointAtPosition(event->point);
273 canvas()->snapGuide()->snap(event->point, event->modifiers());
274 repaintDecorations();
276 d->mouseOverFirstPoint =
false;
280 d->mouseOverFirstPoint = handleGrabRect(d->firstPoint->point()).contains(event->point);
282 QPointF snappedPosition = canvas()->snapGuide()->snap(event->point, event->modifiers());
284 if (event->buttons() & Qt::LeftButton) {
285 if (d->pointIsDragged ||
286 !handleGrabRect(d->dragStartPoint).contains(event->point)) {
288 d->pointIsDragged =
true;
289 QPointF offset = snappedPosition - d->activePoint->point();
290 d->activePoint->setControlPoint2(d->activePoint->point() + offset);
292 if ((event->modifiers() & Qt::AltModifier) == 0) {
293 d->activePoint->setControlPoint1(d->activePoint->point() - offset);
297 d->activePoint->setPoint(snappedPosition);
299 if (!d->prevPointWasDragged && d->autoSmoothCurves) {
300 KoPathPointIndex index = d->shape->pathPointIndex(d->activePoint);
301 if (index.second > 0) {
303 KoPathPointIndex prevIndex(index.first, index.second - 1);
304 KoPathPoint *prevPoint = d->shape->pointByIndex(prevIndex);
307 KoPathPoint *prevPrevPoint = 0;
309 if (index.second > 1) {
310 KoPathPointIndex prevPrevIndex(index.first, index.second - 2);
311 prevPrevPoint = d->shape->pointByIndex(prevPrevIndex);
315 const QPointF control1 = prevPoint->point() + 0.3 * (prevPrevPoint->point() - prevPoint->point());
316 prevPoint->setControlPoint1(control1);
319 const QPointF control2 = prevPoint->point() + 0.3 * (d->activePoint->point() - prevPoint->point());
320 prevPoint->setControlPoint2(control2);
322 const QPointF activeControl = d->activePoint->point() + 0.3 * (prevPoint->point() - d->activePoint->point());
323 d->activePoint->setControlPoint1(activeControl);
325 KoPathPointTypeCommand::makeCubicPointSmooth(prevPoint);
332 repaintDecorations();
339 if (! d->shape || (event->buttons() & Qt::RightButton))
return;
341 d->prevPointWasDragged = d->pointIsDragged;
342 d->pointIsDragged =
false;
343 KoPathPoint *lastActivePoint = d->activePoint;
345 if (!d->finishAfterThisPoint) {
346 d->activePoint = d->shape->lineTo(event->point);
351 if (lastActivePoint->activeControlPoint1() && lastActivePoint->activeControlPoint2()) {
352 QPointF diff1 = lastActivePoint->point() - lastActivePoint->controlPoint1();
353 QPointF diff2 = lastActivePoint->controlPoint2() - lastActivePoint->point();
354 if (qFuzzyCompare(diff1.
x(), diff2.
x()) && qFuzzyCompare(diff1.
y(), diff2.
y()))
355 lastActivePoint->setProperty(KoPathPoint::IsSymmetric);
358 if (d->finishAfterThisPoint) {
360 d->firstPoint->setControlPoint1(d->activePoint->controlPoint1());
361 delete d->shape->removePoint(d->shape->pathPointIndex(d->activePoint));
362 d->activePoint = d->firstPoint;
364 if (!d->prevPointWasDragged && d->autoSmoothCurves) {
365 KoPathPointTypeCommand::makeCubicPointSmooth(d->activePoint);
368 d->shape->closeMerge();
371 d->existingStartPoint = 0;
376 if (d->angleSnapStrategy && lastActivePoint->activeControlPoint2()) {
377 d->angleSnapStrategy->deactivate();
380 repaintDecorations();
385 if (event->
key() == Qt::Key_Escape) {
397 repaintDecorations();
405 delete d->shape->removePoint(d->shape->pathPointIndex(d->activePoint));
408 repaintDecorations();
421 repaintDecorations();
429 KoPathPointIndex lastPointIndex = d->shape->pathPointIndex(d->activePoint);
431 if (lastPointIndex.second > 1) {
432 lastPointIndex.second--;
433 delete d->shape->removePoint(lastPointIndex);
437 repaintDecorations();
444 KoToolBase::activate(activation, shapes);
447 useCursor(Qt::ArrowCursor);
450 d->handleRadius = handleRadius();
451 d->loadAutoSmoothValueFromConfig();
454 canvas()->snapGuide()->reset();
455 repaintDecorations();
461 KoToolBase::deactivate();
469 case KoDocumentResourceManager::HandleRadius: {
470 d->handleRadius = res.
toUInt();
482 KoPathShape *startShape = 0;
483 KoPathShape *endShape = 0;
484 pathShape->normalize();
487 d->existingStartPoint.validate(canvas());
488 d->existingEndPoint.validate(canvas());
490 if (d->connectPaths(pathShape, d->existingStartPoint, d->existingEndPoint)) {
491 if (d->existingStartPoint.isValid()) {
492 startShape = d->existingStartPoint.path;
494 if (d->existingEndPoint.isValid() && d->existingEndPoint != d->existingStartPoint) {
495 endShape = d->existingEndPoint.path;
499 if (tryMergeOnly && !startShape && !endShape) {
503 KUndo2Command *cmd = canvas()->shapeController()->addShape(pathShape, 0);
504 KIS_SAFE_ASSERT_RECOVER(cmd) {
505 canvas()->updateCanvas(pathShape->boundingRect());
510 KoSelection *selection = canvas()->shapeManager()->selection();
511 selection->deselectAll();
512 selection->select(pathShape);
515 pathShape->setBackground(startShape->background());
516 pathShape->setStroke(startShape->stroke());
517 }
else if (endShape) {
518 pathShape->setBackground(endShape->background());
519 pathShape->setStroke(endShape->stroke());
524 canvas()->shapeController()->removeShape(startShape, cmd);
526 if (endShape && startShape != endShape) {
527 canvas()->shapeController()->removeShape(endShape, cmd);
529 canvas()->addCommand(cmd);
548 smoothCurves->
setChecked(d->autoSmoothCurves);
549 connect(smoothCurves, SIGNAL(toggled(
bool)),
this, SLOT(autoSmoothCurvesChanged(
bool)));
552 list.
append(smoothCurves);
557 layout->
addWidget(
new QLabel(i18n(
"Angle snapping delta:"), angleWidget), 0, 0);
558 KisAngleSelector *angleEdit =
new KisAngleSelector(angleWidget);
559 angleEdit->setAngle(d->angleSnappingDelta);
560 angleEdit->setRange(1, 360);
561 angleEdit->setDecimals(0);
562 angleEdit->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
564 layout->
addWidget(
new QLabel(i18n(
"Activate angle snap:"), angleWidget), 1, 0);
575 connect(angleEdit, SIGNAL(angleChanged(qreal)),
this, SLOT(angleDeltaChanged(qreal)));
576 connect(angleSnap, SIGNAL(stateChanged(
int)),
this, SLOT(angleSnapChanged(
int)));
582 #include <moc_KoCreatePathTool.cpp>