Krita Source Code Documentation
Loading...
Searching...
No Matches
KoPathTool.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2006-2012 Jan Hambrecht <jaham@gmx.net>
3 * SPDX-FileCopyrightText: 2006, 2007 Thorsten Zachmann <zachmann@kde.org>
4 * SPDX-FileCopyrightText: 2007, 2010 Thomas Zander <zander@kde.org>
5 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org>
6 *
7 * SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10#include "KoPathTool.h"
11#include "KoCanvasBase.h"
14#include "KoParameterShape.h"
15#include "KoPathPoint.h"
18#include "KoPathShape_p.h"
19#include "KoPathToolHandle.h"
20#include "KoPointerEvent.h"
22#include "KoSelection.h"
23#include "KoShapeController.h"
24#include "KoShapeGroup.h"
25#include "KoShapeManager.h"
26#include "KoSnapGuide.h"
27#include "KoToolBase_p.h"
28#include "KoToolManager.h"
29#include "KoViewConverter.h"
39#include "kis_action_registry.h"
40#include "kis_command_utils.h"
41#include "kis_pointer_utils.h"
43#include <KoShapeStrokeModel.h>
48#include <text/KoSvgTextShape.h>
49
50#include <KoIcon.h>
51
52#include <QMenu>
53#include <QAction>
54#include <FlakeDebug.h>
55#include <klocalizedstring.h>
56#include <QPainter>
57#include <QPainterPath>
58#include <QBitmap>
59#include <QTabWidget>
60
61#include <math.h>
62
63// helper function to calculate the squared distance between two points
64qreal squaredDistance(const QPointF& p1, const QPointF &p2)
65{
66 qreal dx = p1.x()-p2.x();
67 qreal dy = p1.y()-p2.y();
68 return dx*dx + dy*dy;
69}
70
73 : path(0), segmentStart(0), positionOnSegment(0)
74 {
75 }
76
77 bool isValid() {
78 return path && segmentStart;
79 }
80
84};
85
87 : KoToolBase(canvas)
88 , m_pointSelection(this)
89 , m_textOutlineHelper(new KoSvgTextShapeOutlineHelper(canvas))
90{
91 m_actionPathPointCorner = action("pathpoint-corner");
92 m_actionPathPointSmooth = action("pathpoint-smooth");
93 m_actionPathPointSymmetric = action("pathpoint-symmetric");
94 m_actionCurvePoint = action("pathpoint-curve");
95 m_actionLinePoint = action("pathpoint-line");
96 m_actionLineSegment = action("pathsegment-line");
97 m_actionCurveSegment = action("pathsegment-curve");
98 m_actionAddPoint = action("pathpoint-insert");
99 m_actionRemovePoint = action("pathpoint-remove");
100 m_actionBreakPoint = action("path-break-point");
101 m_actionBreakSegment = action("path-break-segment");
102 m_actionBreakSelection = action("path-break-selection");
103 m_actionJoinSegment = action("pathpoint-join");
104 m_actionMergePoints = action("pathpoint-merge");
105 m_actionConvertToPath = action("convert-to-path");
106
107 m_contextMenu.reset(new QMenu());
108 m_textOutlineHelper->setDrawBoundingRect(true);
109 m_textOutlineHelper->setDrawShapeOutlines(false);
110
111 m_selectCursor = QCursor(QIcon(":/cursor-needle.svg").pixmap(32), 0, 0);
112 m_moveCursor = QCursor(QIcon(":/cursor-needle-move.svg").pixmap(32), 0, 0);
113
114 connect(&m_pointSelection, SIGNAL(selectionChanged()), SLOT(repaintDecorations()));
115}
116
120
122{
124
125 PathToolOptionWidget * toolOptions = new PathToolOptionWidget(this);
126 connect(this, SIGNAL(typeChanged(int)), toolOptions, SLOT(setSelectionType(int)));
127 connect(this, SIGNAL(singleShapeChanged(KoPathShape*)), toolOptions, SLOT(setCurrentShape(KoPathShape*)));
128 connect(toolOptions, SIGNAL(sigRequestUpdateActions()), this, SLOT(updateActions()));
130 toolOptions->setWindowTitle(i18n("Edit Shape"));
131 list.append(toolOptions);
132
133 return list;
134}
135
145
147{
148 Q_D(KoToolBase);
151
152 KUndo2Command *initialConversionCommand = createPointToCurveCommand(selectedPoints);
153
154 // conversion should happen before the c-tor
155 // of KoPathPointTypeCommand is executed!
156 if (initialConversionCommand) {
157 initialConversionCommand->redo();
158 }
159
160 KUndo2Command *command =
161 new KoPathPointTypeCommand(selectedPoints, type);
162
163 if (initialConversionCommand) {
164 using namespace KisCommandUtils;
165 CompositeCommand *parent = new CompositeCommand();
166 parent->setText(command->text());
167 parent->addCommand(new SkipFirstRedoWrapper(initialConversionCommand));
168 parent->addCommand(command);
169 command = parent;
170 }
171
172 d->canvas->addCommand(command);
173 }
174}
175
177{
178 Q_D(KoToolBase);
180 if (segments.size() == 1) {
181 qreal positionInSegment = 0.5;
182 if (m_activeSegment && m_activeSegment->isValid()) {
183 positionInSegment = m_activeSegment->positionOnSegment;
184 }
185
186 KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, positionInSegment);
187 d->canvas->addCommand(cmd);
188
189 // TODO: this construction is dangerous. The canvas can remove the command right after
190 // it has been added to it!
192 foreach (KoPathPoint * p, cmd->insertedPoints()) {
193 m_pointSelection.add(p, false);
194 }
195 }
196}
197
199{
200 Q_D(KoToolBase);
201 if (m_pointSelection.size() > 0) {
203 PointHandle *pointHandle = dynamic_cast<PointHandle*>(m_activeHandle.data());
204 if (pointHandle && m_pointSelection.contains(pointHandle->activePoint())) {
205 m_activeHandle.reset();
206 }
208 d->canvas->addCommand(cmd);
209 }
210}
211
213{
214 Q_D(KoToolBase);
217 QList<KoPathPointData> pointToChange;
218
219 QList<KoPathPointData>::const_iterator it(selectedPoints.constBegin());
220 for (; it != selectedPoints.constEnd(); ++it) {
221 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
222 if (point && (point->activeControlPoint1() || point->activeControlPoint2()))
223 pointToChange.append(*it);
224 }
225
226 if (! pointToChange.isEmpty()) {
227 d->canvas->addCommand(new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Line));
228 }
229 }
230}
231
233{
234 Q_D(KoToolBase);
237
238 KUndo2Command *command = createPointToCurveCommand(selectedPoints);
239
240 if (command) {
241 d->canvas->addCommand(command);
242 }
243 }
244}
245
247{
248 KUndo2Command *command = 0;
249 QList<KoPathPointData> pointToChange;
250
251 QList<KoPathPointData>::const_iterator it(points.constBegin());
252 for (; it != points.constEnd(); ++it) {
253 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
254 if (point && (! point->activeControlPoint1() || ! point->activeControlPoint2()))
255 pointToChange.append(*it);
256 }
257
258 if (!pointToChange.isEmpty()) {
259 command = new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Curve);
260 }
261
262 return command;
263}
264
266{
267 Q_D(KoToolBase);
268 if (m_pointSelection.size() > 1) {
270 if (segments.size() > 0) {
271 d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Line));
272 }
273 }
274}
275
277{
278 Q_D(KoToolBase);
279 if (m_pointSelection.size() > 1) {
281 if (segments.size() > 0) {
282 d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Curve));
283 }
284 }
285}
286
288{
289 Q_D(KoToolBase);
290
292
293 QList<KoParameterShape*> parameterShapes;
294
295 Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
296 KoParameterShape * parametric = dynamic_cast<KoParameterShape*>(shape);
297 if (parametric && parametric->isParametricShape()) {
298 parameterShapes.append(parametric);
299 }
300 }
301
302 if (!parameterShapes.isEmpty()) {
303 d->canvas->addCommand(new KoParameterToPathCommand(parameterShapes));
304 }
305
306 QList<KoSvgTextShape*> textShapes;
307 Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
308 if (KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape)) {
309 textShapes.append(text);
310 }
311 }
312
313 if (!textShapes.isEmpty()) {
314 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Convert to Path")); // TODO: reuse the text from KoParameterToPathCommand
315 const QList<KoShape*> oldSelectedShapes = implicitCastList<KoShape*>(textShapes);
316
317
318 new KoKeepShapesSelectedCommand(oldSelectedShapes, {}, canvas()->selectedShapesProxy(),
320
321 QList<KoShape*> newSelectedShapes;
322 Q_FOREACH (KoSvgTextShape *shape, textShapes) {
323 KoShape *outlineShape = shape->textOutline();
324
325 KoShapeContainer *parent = shape->parent();
326 canvas()->shapeController()->addShapeDirect(outlineShape, parent, cmd);
327
328 newSelectedShapes << outlineShape;
329 }
330
331 canvas()->shapeController()->removeShapes(oldSelectedShapes, cmd);
332
333 new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(),
335
336 canvas()->addCommand(cmd);
337 }
338
340}
341
342namespace {
343bool checkCanJoinToPoints(const KoPathPointData & pd1, const KoPathPointData & pd2)
344{
345 const KoPathPointIndex & index1 = pd1.pointIndex;
346 const KoPathPointIndex & index2 = pd2.pointIndex;
347
348 KoPathShape *path1 = pd1.pathShape;
349 KoPathShape *path2 = pd2.pathShape;
350
351 // check if subpaths are already closed
352 if (path1->isClosedSubpath(index1.first) || path2->isClosedSubpath(index2.first))
353 return false;
354
355 // check if first point is an endpoint
356 if (index1.second != 0 && index1.second != path1->subpathPointCount(index1.first)-1)
357 return false;
358
359 // check if second point is an endpoint
360 if (index2.second != 0 && index2.second != path2->subpathPointCount(index2.first)-1)
361 return false;
362
363 return true;
364}
365}
366
368{
369 Q_D(KoToolBase);
370
371 if (m_pointSelection.size() != 2)
372 return;
373
375 if (pointData.size() != 2) return;
376
377 const KoPathPointData & pd1 = pointData.at(0);
378 const KoPathPointData & pd2 = pointData.at(1);
379
380 if (!checkCanJoinToPoints(pd1, pd2)) {
381 return;
382 }
383
385
386 KUndo2Command *cmd = 0;
387
388 if (doJoin) {
389 cmd = new KoMultiPathPointJoinCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
390 } else {
391 cmd = new KoMultiPathPointMergeCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
392 }
393 d->canvas->addCommand(cmd);
394}
395
397{
398 mergePointsImpl(true);
399}
400
402{
403 mergePointsImpl(false);
404}
405
407{
408 Q_D(KoToolBase);
411 }
412}
413
415{
416 Q_D(KoToolBase);
417
418 if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
420 if (segments.size() == 1) {
421 d->canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0)));
422 }
423 } else if (m_pointSelection.hasSelection()) {
425 }
426}
427
429{
430 Q_D(KoToolBase);
431 // only try to break a segment when 2 points of the same object are selected
432 if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
434 if (segments.size() == 1) {
435 d->canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0)));
436 }
437 }
438}
439
440void KoPathTool::paint(QPainter &painter, const KoViewConverter &converter)
441{
442 Q_D(KoToolBase);
443 m_textOutlineHelper->setDecorationThickness(decorationThickness());
444 m_textOutlineHelper->setHandleRadius(handleRadius());
445 m_textOutlineHelper->paint(&painter, converter);
446
447 Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
451
452 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
453 if (parameterShape && parameterShape->isParametricShape()) {
454 parameterShape->paintHandles(helper);
455 } else {
456 shape->paintPoints(helper);
457 }
458
459 if (!shape->stroke() || !shape->stroke()->isVisible()) {
461 helper.drawPath(shape->outline());
462 }
463 }
464
465 if (m_currentStrategy) {
466 painter.save();
467 m_currentStrategy->paint(painter, converter);
468 painter.restore();
469 }
470
471 m_pointSelection.paint(painter, converter, handleRadius());
472
473 if (m_activeHandle) {
475 m_activeHandle->paint(painter, converter, handleRadius(), decorationThickness());
476 } else {
477 m_activeHandle.reset();
478 }
479 } else if (m_activeSegment && m_activeSegment->isValid()) {
480
481 KoPathShape *shape = m_activeSegment->path;
482
483 // if the stroke is invisible, then we already painted the outline of the shape!
484 if (shape->stroke() && shape->stroke()->isVisible()) {
485 KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
486 KoPathSegment segment = shape->segmentByIndex(index).toCubic();
487
489
493
494 QPainterPath path;
495 path.moveTo(segment.first()->point());
496 path.cubicTo(segment.first()->controlPoint2(),
497 segment.second()->controlPoint1(),
498 segment.second()->point());
499
500 helper.drawPath(path);
501 }
502 }
503
504
505
506 if (m_currentStrategy) {
507 painter.save();
508 painter.setTransform(converter.documentToView(), true);
509 d->canvas->snapGuide()->paint(painter, converter);
510 painter.restore();
511 }
512}
513
515{
516 const_cast<KoPathToolSelection&>(m_pointSelection).update();
517
518 QRectF newDecorationsRect;
519
520 Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
521 newDecorationsRect |= kisGrowRect(shape->boundingRect(), handleDocRadius());
522 }
523
524 Q_FOREACH(const KoPathPoint *point, m_pointSelection.selectedPoints()) {
525 newDecorationsRect |= kisGrowRect(point->boundingRect(false), handleDocRadius());
526 }
527
528 if (m_activeHandle) {
529 newDecorationsRect |= kisGrowRect(m_activeHandle->boundingRect(), handleDocRadius());
530 }
531
532 if (m_activeSegment) {
533 KoPathPointIndex index = m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart);
534 KoPathSegment segment = m_activeSegment->path->segmentByIndex(index);
535
536 QRectF rect = segment.boundingRect();
537 rect = m_activeSegment->path->shapeToDocument(rect);
538
539 newDecorationsRect |= kisGrowRect(rect, handleDocRadius());
540 }
541
542 newDecorationsRect |= m_textOutlineHelper->decorationRect();
543
544 return newDecorationsRect;
545}
546
552
554{
555 // When using touch drawing, we only ever receive move events after the
556 // finger has pressed down. We have to issue an artificial move here so that
557 // the tool's state is updated properly to handle the press.
558 if (event->isTouchEvent()) {
559 mouseMoveEvent(event);
560 }
561
562 if (KoSvgTextShape *shape = m_textOutlineHelper->contourModeButtonHovered(event->point)) {
563 m_textOutlineHelper->toggleTextContourMode(shape);
564 event->accept();
565 }
566 // we are moving if we hit a point and use the left mouse button
567 if (m_activeHandle) {
568 m_currentStrategy.reset(m_activeHandle->handleMousePress(event));
569 } else {
570
571 if (event->button() & Qt::LeftButton) {
572
573 // check if we hit a path segment
574 if (m_activeSegment && m_activeSegment->isValid()) {
575
576 KoPathShape *shape = m_activeSegment->path;
577 KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
578 KoPathSegment segment = shape->segmentByIndex(index);
579
580 m_pointSelection.add(segment.first(), !(event->modifiers() & Qt::ShiftModifier));
581 m_pointSelection.add(segment.second(), false);
582
583 KoPathPointData data(shape, index);
584 m_currentStrategy.reset(new KoPathSegmentChangeStrategy(this, event->point, data, m_activeSegment->positionOnSegment));
585 } else {
586
587 KoShapeManager *shapeManager = canvas()->shapeManager();
588 KoSelection *selection = shapeManager->selection();
589 KoShape *shape = shapeManager->shapeAt(event->point, KoFlake::ShapeOnTop);
590
591 if (shape && !selection->isSelected(shape)) {
592
593 if (!(event->modifiers() & Qt::ShiftModifier)) {
594 selection->deselectAll();
595 }
596
597 selection->select(shape);
598 } else {
601 }
602 }
603 }
604 }
605}
606
608{
609 if (event->button() & Qt::RightButton)
610 return;
611
612 if (m_currentStrategy) {
613 m_lastPoint = event->point;
614 m_currentStrategy->handleMouseMove(event->point, event->modifiers());
615
617
618 return;
619 }
620
621 if (m_activeSegment) {
622 m_activeSegment.reset();
624 }
625
626 Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
627 QRectF roi = shape->documentToShape(handleGrabRect(event->point));
628 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
629 if (parameterShape && parameterShape->isParametricShape()) {
630 int handleId = parameterShape->handleIdAt(roi);
631 if (handleId != -1) {
633 Q_EMIT statusTextChanged(i18n("Drag to move handle."));
634
635 m_activeHandle.reset(new ParameterHandle(this, parameterShape, handleId));
637 return;
638 }
639 } else {
640 QList<KoPathPoint*> points = shape->pointsAt(roi, true);
641 if (! points.empty()) {
642 // find the nearest control point from all points within the roi
643 KoPathPoint * bestPoint = 0;
645 qreal minDistance = HUGE_VAL;
646 Q_FOREACH (KoPathPoint *p, points) {
647 // the node point must be hit if the point is not selected yet
648 if (! m_pointSelection.contains(p) && ! roi.contains(p->point()))
649 continue;
650
651 // check for the control points first as otherwise it is no longer
652 // possible to change the control points when they are the same as the point
653 if (p->activeControlPoint1() && roi.contains(p->controlPoint1())) {
654 qreal dist = squaredDistance(roi.center(), p->controlPoint1());
655 if (dist < minDistance) {
656 bestPoint = p;
657 bestPointType = KoPathPoint::ControlPoint1;
658 minDistance = dist;
659 }
660 }
661
662 if (p->activeControlPoint2() && roi.contains(p->controlPoint2())) {
663 qreal dist = squaredDistance(roi.center(), p->controlPoint2());
664 if (dist < minDistance) {
665 bestPoint = p;
666 bestPointType = KoPathPoint::ControlPoint2;
667 minDistance = dist;
668 }
669 }
670
671 // check the node point at last
672 qreal dist = squaredDistance(roi.center(), p->point());
673 if (dist < minDistance) {
674 bestPoint = p;
675 bestPointType = KoPathPoint::Node;
676 minDistance = dist;
677 }
678 }
679
680 if (! bestPoint) {
682 return;
683 }
684
686 if (bestPointType == KoPathPoint::Node)
687 Q_EMIT statusTextChanged(i18n("Drag to move point. Shift click to change point type."));
688 else
689 Q_EMIT statusTextChanged(i18n("Drag to move control point."));
690
691 PointHandle *prev = dynamic_cast<PointHandle*>(m_activeHandle.data());
692 if (prev && prev->activePoint() == bestPoint && prev->activePointType() == bestPointType)
693 return; // no change;
694
695 m_activeHandle.reset(new PointHandle(this, bestPoint, bestPointType));
697 return;
698 }
699 }
700 }
701
703
704 if (m_activeHandle) {
705 m_activeHandle.reset();
707 }
708
709 PathSegment *hoveredSegment = segmentAtPoint(event->point);
710 if(hoveredSegment) {
711 useCursor(Qt::PointingHandCursor);
712 Q_EMIT statusTextChanged(i18n("Drag to change curve directly. Double click to insert new path point."));
713 m_activeSegment.reset(hoveredSegment);
715 } else {
716 uint selectedPointCount = m_pointSelection.size();
717 if (selectedPointCount == 0)
718 Q_EMIT statusTextChanged(QString());
719 else {
720 if (!m_actionBreakSelection->shortcut().isEmpty()) {
721 if (selectedPointCount == 1)
722 Q_EMIT statusTextChanged(i18nc("%1 is a shortcut to be pressed", "Press %1 to break path at selected point.", m_actionBreakSelection->shortcut().toString()));
723 else
724 Q_EMIT statusTextChanged(i18nc("%1 is a shortcut to be pressed", "Press %1 to break path at selected segments.", m_actionBreakSelection->shortcut().toString()));
725 } else {
726 Q_EMIT statusTextChanged(QString());
727 }
728 }
729 }
730}
731
733{
734 Q_D(KoToolBase);
735 if (m_currentStrategy) {
736 const bool hadNoSelection = !m_pointSelection.hasSelection();
737 m_currentStrategy->finishInteraction(event->modifiers());
738 KUndo2Command *command = m_currentStrategy->createCommand();
739 if (command)
740 d->canvas->addCommand(command);
741 if (hadNoSelection && dynamic_cast<KoPathPointRubberSelectStrategy*>(m_currentStrategy.data())
743 // the click didn't do anything at all. Allow it to be used by others.
744 event->ignore();
745 }
746 m_currentStrategy.reset();
748 }
749}
750
751void KoPathTool::keyPressEvent(QKeyEvent *event)
752{
753 if (m_currentStrategy) {
754 switch (event->key()) {
755 case Qt::Key_Control:
756 case Qt::Key_Alt:
757 case Qt::Key_Shift:
758 case Qt::Key_Meta:
759 if (! event->isAutoRepeat()) {
760 m_currentStrategy->handleMouseMove(m_lastPoint, event->modifiers());
761 }
762 break;
763 case Qt::Key_Escape:
764 m_currentStrategy->cancelInteraction();
765 m_currentStrategy.reset();
766 break;
767 default:
768 event->ignore();
769 return;
770 }
771 } else {
772 switch (event->key()) {
773#ifndef NDEBUG
774// case Qt::Key_D:
775// if (m_pointSelection.objectCount() == 1) {
776// QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
777// KoPathShapePrivate *p = static_cast<KoPathShapePrivate*>(selectedPoints[0].pathShape->priv());
778// p->debugPath();
779// }
780// break;
781#endif
782 default:
783 event->ignore();
784 return;
785 }
786 }
787 event->accept();
788}
789
790void KoPathTool::keyReleaseEvent(QKeyEvent *event)
791{
792 if (m_currentStrategy) {
793 switch (event->key()) {
794 case Qt::Key_Control:
795 case Qt::Key_Alt:
796 case Qt::Key_Shift:
797 case Qt::Key_Meta:
798 if (! event->isAutoRepeat()) {
799 m_currentStrategy->handleMouseMove(m_lastPoint, Qt::NoModifier);
800 }
801 break;
802 default:
803 break;
804 }
805 }
806 event->accept();
807}
808
810{
811 Q_D(KoToolBase);
812 // check if we are doing something else at the moment
813 if (m_currentStrategy) return;
814
815 if (!m_activeHandle && m_activeSegment && m_activeSegment->isValid()) {
816 QList<KoPathPointData> segments;
817 segments.append(
819 m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart)));
820
821 KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, m_activeSegment->positionOnSegment);
822 d->canvas->addCommand(cmd);
823
825 foreach (KoPathPoint * p, cmd->insertedPoints()) {
826 m_pointSelection.add(p, false);
827 }
829 } else if (!m_activeHandle && !m_activeSegment) {
831 }
832}
833
835{
836 // the max allowed distance from a segment
837 const QRectF grabRoi = handleGrabRect(point);
838 QScopedPointer<PathSegment> segment(new PathSegment);
839
840 Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
841 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
842 if (parameterShape && parameterShape->isParametricShape())
843 continue;
844
845 const KoPathSegment s = shape->segmentAtPoint(point, grabRoi);
846 if (s.isValid()) {
847 segment->path = shape;
848 segment->segmentStart = s.first();
849 segment->positionOnSegment = s.nearestPoint(shape->documentToShape(point));
850 }
851 }
852
853 if (!segment->isValid()) {
854 segment.reset();
855 }
856
857 return segment.take();
858}
859
860void KoPathTool::activate(const QSet<KoShape*> &shapes)
861{
862 KoToolBase::activate(shapes);
863
864 Q_D(KoToolBase);
865
866 d->canvas->snapGuide()->reset();
867
869 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
870 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(updateActions()));
871
872 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(repaintDecorations()));
873 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(repaintDecorations()));
875 initializeWithShapes(QList<KoShape*>(shapes.begin(), shapes.end()));
876 connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve()), Qt::UniqueConnection);
877 connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine()), Qt::UniqueConnection);
878 connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine()), Qt::UniqueConnection);
879 connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve()), Qt::UniqueConnection);
880 connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints()), Qt::UniqueConnection);
881 connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints()), Qt::UniqueConnection);
882 connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint()), Qt::UniqueConnection);
883 connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment()), Qt::UniqueConnection);
884 connect(m_actionBreakSelection, SIGNAL(triggered()), this, SLOT(breakAtSelection()), Qt::UniqueConnection);
885 connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints()), Qt::UniqueConnection);
886 connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints()), Qt::UniqueConnection);
887 connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath()), Qt::UniqueConnection);
888 connect(m_actionPathPointCorner, SIGNAL(triggered()), this, SLOT(pointTypeChangedCorner()), Qt::UniqueConnection);
889 connect(m_actionPathPointSmooth, SIGNAL(triggered()), this, SLOT(pointTypeChangedSmooth()), Qt::UniqueConnection);
890 connect(m_actionPathPointSymmetric, SIGNAL(triggered()), this, SLOT(pointTypeChangedSymmetric()), Qt::UniqueConnection);
891 connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged()), Qt::UniqueConnection);
892
893}
894
896{
897 Q_D(KoToolBase);
898 QList<KoShape*> shapes =
899 d->canvas->selectedShapesProxy()->selection()->selectedEditableShapesAndDelegates();
900
901 initializeWithShapes(shapes);
902}
903
905{
906 Q_UNUSED(shape);
907
908 // active handle and selection might have already become invalid, so just
909 // delete them without dereferencing anything...
910
911 m_activeHandle.reset();
912 m_activeSegment.reset();
913}
914
921
923{
924 QList<KoPathShape*> selectedShapes;
925 Q_FOREACH (KoShape *shape, shapes) {
926 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
927
928 if (pathShape && pathShape->isShapeEditable()) {
929 selectedShapes.append(pathShape);
930 }
931 }
932
933 if (selectedShapes != m_pointSelection.selectedShapes()) {
935 m_pointSelection.setSelectedShapes(selectedShapes);
937 }
938
941}
942
944{
945 PathToolOptionWidget::Types type;
947 Q_FOREACH (KoPathShape *shape, selectedShapes) {
948 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
949 type |= parameterShape && parameterShape->isParametricShape() ?
951 }
952
953 Q_EMIT singleShapeChanged(selectedShapes.size() == 1 ? selectedShapes.first() : 0);
954 Q_EMIT typeChanged(type);
955}
956
958{
960
961 bool canBreakAtPoint = false;
962
963 bool hasNonSmoothPoints = false;
964 bool hasNonSymmetricPoints = false;
965 bool hasNonSplitPoints = false;
966
967 bool hasNonLinePoints = false;
968 bool hasNonCurvePoints = false;
969
970 bool canJoinSubpaths = false;
971
972 if (!pointData.isEmpty()) {
973 Q_FOREACH (const KoPathPointData &pd, pointData) {
974 const int subpathIndex = pd.pointIndex.first;
975 const int pointIndex = pd.pointIndex.second;
976
977 canBreakAtPoint |= pd.pathShape->isClosedSubpath(subpathIndex) ||
978 (pointIndex > 0 && pointIndex < pd.pathShape->subpathPointCount(subpathIndex) - 1);
979
981
982 hasNonSmoothPoints |= !(point->properties() & KoPathPoint::IsSmooth);
983 hasNonSymmetricPoints |= !(point->properties() & KoPathPoint::IsSymmetric);
984 hasNonSplitPoints |=
987
988 hasNonLinePoints |= point->activeControlPoint1() || point->activeControlPoint2();
989 hasNonCurvePoints |= !point->activeControlPoint1() && !point->activeControlPoint2();
990 }
991
992 if (pointData.size() == 2) {
993 const KoPathPointData & pd1 = pointData.at(0);
994 const KoPathPointData & pd2 = pointData.at(1);
995
996 canJoinSubpaths = checkCanJoinToPoints(pd1, pd2);
997 }
998 }
999
1000 m_actionPathPointCorner->setEnabled(hasNonSplitPoints);
1001 m_actionPathPointSmooth->setEnabled(hasNonSmoothPoints);
1002 m_actionPathPointSymmetric->setEnabled(hasNonSymmetricPoints);
1003
1004 m_actionRemovePoint->setEnabled(!pointData.isEmpty());
1005
1006 m_actionBreakPoint->setEnabled(canBreakAtPoint);
1007
1008 m_actionCurvePoint->setEnabled(hasNonCurvePoints);
1009 m_actionLinePoint->setEnabled(hasNonLinePoints);
1010
1011 m_actionJoinSegment->setEnabled(canJoinSubpaths);
1012 m_actionMergePoints->setEnabled(canJoinSubpaths);
1013
1015
1016
1017 bool canSplitAtSegment = false;
1018 bool canConvertSegmentToLine = false;
1019 bool canConvertSegmentToCurve= false;
1020
1021 if (!segments.isEmpty()) {
1022
1023 canSplitAtSegment = segments.size() == 1;
1024
1025 bool hasLines = false;
1026 bool hasCurves = false;
1027
1028 Q_FOREACH (const KoPathPointData &pd, segments) {
1030 hasLines |= segment.degree() == 1;
1031 hasCurves |= segment.degree() > 1;
1032 }
1033
1034 canConvertSegmentToLine = !segments.isEmpty() && hasCurves;
1035 canConvertSegmentToCurve= !segments.isEmpty() && hasLines;
1036 }
1037
1038 m_actionAddPoint->setEnabled(canSplitAtSegment);
1039
1040 m_actionLineSegment->setEnabled(canConvertSegmentToLine);
1041 m_actionCurveSegment->setEnabled(canConvertSegmentToCurve);
1042
1043 m_actionBreakSegment->setEnabled(canSplitAtSegment);
1044 m_actionBreakSelection->setEnabled(canSplitAtSegment | canBreakAtPoint);
1045
1047 bool haveConvertibleShapes = false;
1048 Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
1049 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
1050 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(shape);
1051 if (textShape ||
1052 (parameterShape && parameterShape->isParametricShape())) {
1053
1054 haveConvertibleShapes = true;
1055 break;
1056 }
1057 }
1058 m_actionConvertToPath->setEnabled(haveConvertibleShapes);
1059}
1060
1062{
1063 Q_D(KoToolBase);
1064
1069 m_activeHandle.reset();
1070 m_activeSegment.reset();
1071 m_currentStrategy.reset();
1072 d->canvas->snapGuide()->reset();
1073
1074 disconnect(m_actionCurvePoint, 0, this, 0);
1075 disconnect(m_actionLinePoint, 0, this, 0);
1076 disconnect(m_actionLineSegment, 0, this, 0);
1077 disconnect(m_actionCurveSegment, 0, this, 0);
1078 disconnect(m_actionAddPoint, 0, this, 0);
1079 disconnect(m_actionRemovePoint, 0, this, 0);
1080 disconnect(m_actionBreakPoint, 0, this, 0);
1081 disconnect(m_actionBreakSegment, 0, this, 0);
1082 disconnect(m_actionBreakSelection, 0, this, 0);
1083 disconnect(m_actionJoinSegment, 0, this, 0);
1084 disconnect(m_actionMergePoints, 0, this, 0);
1085 disconnect(m_actionConvertToPath, 0, this, 0);
1086 disconnect(m_actionPathPointCorner, 0, this, 0);
1087 disconnect(m_actionPathPointSmooth, 0, this, 0);
1088 disconnect(m_actionPathPointSymmetric, 0, this, 0);
1089 disconnect(&m_pointSelection, 0, this, 0);
1090
1092}
1093
1094void KoPathTool::canvasResourceChanged(int key, const QVariant & /*res*/)
1095{
1098 }
1099}
1100
1102{
1103 Q_D(KoToolBase);
1104 updateActions();
1105 d->canvas->snapGuide()->setIgnoredPathPoints(QList<KoPathPoint*>(m_pointSelection.selectedPoints().begin(), m_pointSelection.selectedPoints().end()));
1107}
1108
1109namespace {
1110void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2)
1111{
1112 if (a1->isEnabled() || a2->isEnabled()) {
1113 menu->addAction(a1);
1114 menu->addAction(a2);
1115 menu->addSeparator();
1116 }
1117}
1118
1119void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2, QAction *a3)
1120{
1121 if (a1->isEnabled() || a2->isEnabled()) {
1122 menu->addAction(a1);
1123 menu->addAction(a2);
1124 menu->addAction(a3);
1125 menu->addSeparator();
1126 }
1127}
1128}
1129
1131{
1132 if (m_activeHandle) {
1133 m_activeHandle->trySelectHandle();
1134 }
1135
1136 if (m_activeSegment && m_activeSegment->isValid()) {
1137 KoPathShape *shape = m_activeSegment->path;
1138 KoPathSegment segment = shape->segmentByIndex(shape->pathPointIndex(m_activeSegment->segmentStart));
1139
1140 m_pointSelection.add(segment.first(), true);
1141 m_pointSelection.add(segment.second(), false);
1142 }
1143
1144 if (m_contextMenu) {
1145 m_contextMenu->clear();
1146
1147 addActionsGroupIfEnabled(m_contextMenu.data(),
1151
1152 addActionsGroupIfEnabled(m_contextMenu.data(),
1155
1156 addActionsGroupIfEnabled(m_contextMenu.data(),
1159
1160 addActionsGroupIfEnabled(m_contextMenu.data(),
1163
1164 addActionsGroupIfEnabled(m_contextMenu.data(),
1167
1168 addActionsGroupIfEnabled(m_contextMenu.data(),
1171
1173
1174 m_contextMenu->addSeparator();
1175 }
1176
1177 return m_contextMenu.data();
1178}
1179
1184
1189
1191{
1192 // noop!
1193}
1194
1199
1201{
1202 // noop!
1203}
1204
1209
1211{
1214 return true;
1215}
1216
const Params2D p
QPointF p2
QPointF p1
unsigned int uint
QPair< int, int > KoPathPointIndex
Definition KoPathShape.h:28
qreal squaredDistance(const QPointF &p1, const QPointF &p2)
KUndo2MagicString text() const
virtual void redo()
The KisHandlePainterHelper class is a special helper for painting handles around objects....
void drawPath(const QPainterPath &path)
void setHandleStyle(const KisHandleStyle &style)
static KisHandleStyle & secondarySelection()
static KisHandleStyle & primarySelection()
void addConnection(Sender sender, Signal signal, Receiver receiver, Method method, Qt::ConnectionType type=Qt::AutoConnection)
QPointer< KoShapeController > shapeController
virtual KoShapeManager * shapeManager() const =0
virtual void addCommand(KUndo2Command *command)=0
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
void paintHandles(KisHandlePainterHelper &handlesHelper)
Paint the handles.
bool isParametricShape() const
Check if object is a parametric shape.
int handleIdAt(const QRectF &rect) const
Get the id of the handle within the given rect.
The undo / redo command for changing a KoParameterShape into a KoPathShape.
Command to break a subpath at points.
Describe a KoPathPoint by a KoPathShape and its indices.
KoPathPointIndex pointIndex
position of the point in the path shape
KoPathShape * pathShape
path shape the path point belongs too
The undo / redo command for inserting path points.
QList< KoPathPoint * > insertedPoints() const
Returns list of inserted points.
static KUndo2Command * createCommand(const QList< KoPathPointData > &pointDataList, KoShapeController *shapeController, KUndo2Command *parent=0)
Create command for removing points from path shapes.
Strategy to rubber select points of a path shape.
The undo / redo command for changing the path point type.
PointType
The type of the point.
A KoPathPoint represents a point in a path.
PointProperties properties
QRectF boundingRect(bool active=true) const
Get the bounding rect of the point.
QPointF point
QPointF controlPoint1
@ IsSmooth
it is smooth, both control points on a line through the point
Definition KoPathPoint.h:41
@ IsSymmetric
it is symmetric, like smooth but control points have same distance to point
Definition KoPathPoint.h:42
PointType
the type for identifying part of a KoPathPoint
Definition KoPathPoint.h:47
@ ControlPoint2
the second control point
Definition KoPathPoint.h:51
@ ControlPoint1
the first control point
Definition KoPathPoint.h:50
@ Node
the node point
Definition KoPathPoint.h:49
bool activeControlPoint1
bool activeControlPoint2
QPointF controlPoint2
The undo / redo command for breaking a subpath by removing the segment.
Strategy for deforming a segment of a path shape.
The undo / redo command for changing segments to curves/lines.
A KoPathSegment consist of two neighboring KoPathPoints.
KoPathPoint * first
int degree() const
Returns the degree of the segment: 1 = line, 2 = quadratic, 3 = cubic, -1 = invalid.
qreal nearestPoint(const QPointF &point) const
KoPathPoint * second
bool isValid() const
Returns if segment is valid, e.g. has two valid points.
QRectF boundingRect() const
Returns the axis aligned tight bounding rect.
KoPathSegment toCubic() const
Returns cubic bezier curve segment of this segment.
The position of a path point within a path shape.
Definition KoPathShape.h:63
int subpathPointCount(int subpathIndex) const
Returns the number of points in a subpath.
bool isClosedSubpath(int subpathIndex) const
Checks if a subpath is closed.
KoPathSegment segmentAtPoint(const QPointF &point, const QRectF &grabRoi) const
QList< KoPathPoint * > pointsAt(const QRectF &rect, const bool useControlPoints=false) const
Returns the path points within the given rectangle.
QPainterPath outline() const override
reimplemented
KoPathSegment segmentByIndex(const KoPathPointIndex &pointIndex) const
Returns the segment specified by a path point index.
virtual void paintPoints(KisHandlePainterHelper &handlesHelper)
KoPathPointIndex pathPointIndex(const KoPathPoint *point) const
Returns the path point index of a given path point.
KoPathPoint * pointByIndex(const KoPathPointIndex &pointIndex) const
Returns the path point specified by a path point index.
Handle the selection of points.
int objectCount() const
Get the number of path objects in the selection.
void paint(QPainter &painter, const KoViewConverter &converter, qreal handleRadius)
Draw the selected points.
const QSet< KoPathPoint * > & selectedPoints() const
Get all selected points.
bool contains(KoPathPoint *point)
Check if a point is in the selection.
bool hasSelection() override
reimplemented from KoToolSelection
int size() const
Get the number of path points in the selection.
QList< KoPathPointData > selectedPointsData() const
Get the point data of all selected points.
void clear()
Clear the selection.
QList< KoPathShape * > selectedShapes() const
Returns list of selected shapes.
void add(KoPathPoint *point, bool clear)
Add a point to the selection.
void setSelectedShapes(const QList< KoPathShape * > shapes)
Sets list of selected shapes.
QList< KoPathPointData > selectedSegmentsData() const
Get the point data of all selected segments.
void canvasResourceChanged(int key, const QVariant &res) override
void mousePressEvent(KoPointerEvent *event) override
QAction * m_actionJoinSegment
Definition KoPathTool.h:139
QScopedPointer< KoInteractionStrategy > m_currentStrategy
the rubber selection strategy
Definition KoPathTool.h:125
void updateActions()
void mouseMoveEvent(KoPointerEvent *event) override
QCursor m_selectCursor
Definition KoPathTool.h:115
QAction * m_actionCurvePoint
Definition KoPathTool.h:130
void pointTypeChangedSmooth()
QAction * m_actionLinePoint
Definition KoPathTool.h:131
void activate(const QSet< KoShape * > &shapes) override
QAction * m_actionRemovePoint
Definition KoPathTool.h:135
KoPathToolSelection m_pointSelection
the point selection
Definition KoPathTool.h:114
void updateOptionsWidget()
QAction * m_actionLineSegment
Definition KoPathTool.h:132
void breakAtSelection()
QRectF decorationsRect() const override
QScopedPointer< QMenu > m_contextMenu
Definition KoPathTool.h:143
QMenu * popupActionsMenu() override
void requestStrokeCancellation() override
void requestStrokeEnd() override
void segmentToCurve()
void keyPressEvent(QKeyEvent *event) override
QAction * m_actionPathPointSymmetric
Definition KoPathTool.h:129
QScopedPointer< PathSegment > m_activeSegment
Definition KoPathTool.h:120
QScopedPointer< KoSvgTextShapeOutlineHelper > m_textOutlineHelper
Definition KoPathTool.h:144
QAction * m_actionBreakSegment
Definition KoPathTool.h:137
void breakAtPoint()
~KoPathTool() override
void pointToLine()
void convertToPath()
void deselect() override
deselect the tool should clear the selection if it has one.
QAction * m_actionAddPoint
Definition KoPathTool.h:134
void joinPoints()
void singleShapeChanged(KoPathShape *path)
KUndo2Command * createPointToCurveCommand(const QList< KoPathPointData > &points)
PathSegment * segmentAtPoint(const QPointF &point)
KisSignalAutoConnectionsStore m_canvasConnections
Definition KoPathTool.h:145
bool selectAll() override
selectAll select all data the tool can select.
QAction * m_actionPathPointSmooth
Definition KoPathTool.h:128
void pointSelectionChanged()
void segmentToLine()
void pointToCurve()
QPointF m_lastPoint
needed for interaction strategy
Definition KoPathTool.h:119
QAction * m_actionCurveSegment
Definition KoPathTool.h:133
void pointTypeChangedCorner()
void slotSelectionChanged()
void mouseDoubleClickEvent(KoPointerEvent *event) override
QCursor m_moveCursor
Definition KoPathTool.h:142
void mouseReleaseEvent(KoPointerEvent *event) override
void breakAtSegment()
KoToolSelection * selection() override
QList< QPointer< QWidget > > createOptionWidgets() override
reimplemented
void clearActivePointSelectionReferences()
void explicitUserStrokeEndRequest() override
explicitUserStrokeEndRequest is called by the input manager when the user presses Enter key or any eq...
KoPathTool(KoCanvasBase *canvas)
void pointTypeChangedSymmetric()
void deactivate() override
void mergePointsImpl(bool doJoin)
void removePoints()
void initializeWithShapes(const QList< KoShape * > shapes)
void pointTypeChanged(KoPathPointTypeCommand::PointType type)
QAction * m_actionMergePoints
Definition KoPathTool.h:140
QScopedPointer< KoPathToolHandle > m_activeHandle
the currently active handle
Definition KoPathTool.h:118
void keyReleaseEvent(QKeyEvent *event) override
void deleteSelection() override
void mergePoints()
KoShapeFillResourceConnector m_shapeFillResourceConnector
Definition KoPathTool.h:146
void notifyPathPointsChanged(KoPathShape *shape)
void requestUndoDuringStroke() override
void paint(QPainter &painter, const KoViewConverter &converter) override
QAction * m_actionBreakPoint
Definition KoPathTool.h:136
QAction * m_actionConvertToPath
Definition KoPathTool.h:141
void repaintDecorations() override
QAction * m_actionPathPointCorner
Definition KoPathTool.h:127
void insertPoints()
void typeChanged(int types)
QAction * m_actionBreakSelection
Definition KoPathTool.h:138
Qt::MouseButton button() const
return button pressed (see QMouseEvent::button());
bool isTouchEvent() const
Qt::KeyboardModifiers modifiers() const
QPointF point
The point in document coordinates.
virtual KoSelection * selection()=0
KoShape * shapeAt(const QPointF &position, KoFlake::ShapeSelection selection=KoFlake::ShapeOnTop, bool omitHiddenShapes=true)
KoSelection * selection
virtual bool isShapeEditable(bool recursive=true) const
checks recursively if the shape or one of its parents is not visible or locked
Definition KoShape.cpp:970
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:890
QPointF documentToShape(const QPointF &point) const
Transforms point from document coordinates to shape coordinates.
Definition KoShape.cpp:1016
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:299
KoShapeContainer * parent() const
Definition KoShape.cpp:862
static KisHandlePainterHelper createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius=0.0, int decorationThickness=1)
Definition KoShape.cpp:982
The KoSvgTextShapeOutlineHelper class helper class that draws the text outlines and contour mode butt...
KoShape * textOutline() const
textOutline This turns the text object into non-text KoShape(s) to the best of its abilities.
qreal handleDocRadius() const
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
void selectionChanged(bool hasSelection)
void statusTextChanged(const QString &statusText)
virtual void repaintDecorations()
int handleRadius() const
Convenience function to get the current handle radius.
void useCursor(const QCursor &cursor)
virtual void activate(const QSet< KoShape * > &shapes)
QRectF handleGrabRect(const QPointF &position) const
virtual void deactivate()
QAction * action(const QString &name) const
int decorationThickness() const
decorationThickness The minimum thickness for tool decoration lines, this is derived from the screen ...
void switchToolRequested(const QString &id)
static KoToolManager * instance()
Return the toolmanager singleton.
virtual QPointF documentToView(const QPointF &documentPoint) const
KoPathPoint * activePoint() const
KoPathPoint::PointType activePointType() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
KUndo2MagicString kundo2_i18n(const char *text)
@ DecorationThickness
Integer, the thickness of single px decorations, will be adjusted by HiDPI settings....
@ HandleRadius
The handle radius used for drawing handles of any kind.
@ ShapeOnTop
return the shape highest z-ordering, regardless of selection.
Definition KoFlake.h:74
KoPathPoint * segmentStart