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>
49#include <text/KoSvgTextShape.h>
50
51#include <KoIcon.h>
52
53#include <QMenu>
54#include <QAction>
55#include <FlakeDebug.h>
56#include <klocalizedstring.h>
57#include <QPainter>
58#include <QPainterPath>
59#include <QBitmap>
60#include <QTabWidget>
61
62#include <math.h>
63
64// helper function to calculate the squared distance between two points
65qreal squaredDistance(const QPointF& p1, const QPointF &p2)
66{
67 qreal dx = p1.x()-p2.x();
68 qreal dy = p1.y()-p2.y();
69 return dx*dx + dy*dy;
70}
71
74 : path(0), segmentStart(0), positionOnSegment(0)
75 {
76 }
77
78 bool isValid() {
79 return path && segmentStart;
80 }
81
85};
86
88 : KoToolBase(canvas)
89 , m_pointSelection(this)
90 , m_textOutlineHelper(new KoSvgTextShapeOutlineHelper(canvas))
91{
92 m_actionPathPointCorner = action("pathpoint-corner");
93 m_actionPathPointSmooth = action("pathpoint-smooth");
94 m_actionPathPointSymmetric = action("pathpoint-symmetric");
95 m_actionCurvePoint = action("pathpoint-curve");
96 m_actionLinePoint = action("pathpoint-line");
97 m_actionLineSegment = action("pathsegment-line");
98 m_actionCurveSegment = action("pathsegment-curve");
99 m_actionAddPoint = action("pathpoint-insert");
100 m_actionRemovePoint = action("pathpoint-remove");
101 m_actionBreakPoint = action("path-break-point");
102 m_actionBreakSegment = action("path-break-segment");
103 m_actionBreakSelection = action("path-break-selection");
104 m_actionJoinSegment = action("pathpoint-join");
105 m_actionMergePoints = action("pathpoint-merge");
106 m_actionConvertToPath = action("convert-to-path");
107
108 m_contextMenu.reset(new QMenu());
109 m_textOutlineHelper->setDrawBoundingRect(true);
110 m_textOutlineHelper->setDrawShapeOutlines(false);
111
112 m_selectCursor = QCursor(QIcon(":/cursor-needle.svg").pixmap(32), 0, 0);
113 m_moveCursor = QCursor(QIcon(":/cursor-needle-move.svg").pixmap(32), 0, 0);
114
115 connect(&m_pointSelection, SIGNAL(selectionChanged()), SLOT(repaintDecorations()));
116}
117
121
123{
125
126 PathToolOptionWidget * toolOptions = new PathToolOptionWidget(this);
127 connect(this, SIGNAL(typeChanged(int)), toolOptions, SLOT(setSelectionType(int)));
128 connect(this, SIGNAL(singleShapeChanged(KoPathShape*)), toolOptions, SLOT(setCurrentShape(KoPathShape*)));
129 connect(toolOptions, SIGNAL(sigRequestUpdateActions()), this, SLOT(updateActions()));
131 toolOptions->setWindowTitle(i18n("Edit Shape"));
132 list.append(toolOptions);
133
134 return list;
135}
136
146
148{
149 Q_D(KoToolBase);
152
153 KUndo2Command *initialConversionCommand = createPointToCurveCommand(selectedPoints);
154
155 // conversion should happen before the c-tor
156 // of KoPathPointTypeCommand is executed!
157 if (initialConversionCommand) {
158 initialConversionCommand->redo();
159 }
160
161 KUndo2Command *command =
162 new KoPathPointTypeCommand(selectedPoints, type);
163
164 if (initialConversionCommand) {
165 using namespace KisCommandUtils;
166 CompositeCommand *parent = new CompositeCommand();
167 parent->setText(command->text());
168 parent->addCommand(new SkipFirstRedoWrapper(initialConversionCommand));
169 parent->addCommand(command);
170 command = parent;
171 }
172
173 d->canvas->addCommand(command);
174 }
175}
176
178{
179 Q_D(KoToolBase);
181 if (segments.size() == 1) {
182 qreal positionInSegment = 0.5;
183 if (m_activeSegment && m_activeSegment->isValid()) {
184 positionInSegment = m_activeSegment->positionOnSegment;
185 }
186
187 KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, positionInSegment);
188 d->canvas->addCommand(cmd);
189
190 // TODO: this construction is dangerous. The canvas can remove the command right after
191 // it has been added to it!
193 foreach (KoPathPoint * p, cmd->insertedPoints()) {
194 m_pointSelection.add(p, false);
195 }
196 }
197}
198
200{
201 Q_D(KoToolBase);
202 if (m_pointSelection.size() > 0) {
204 PointHandle *pointHandle = dynamic_cast<PointHandle*>(m_activeHandle.data());
205 if (pointHandle && m_pointSelection.contains(pointHandle->activePoint())) {
206 m_activeHandle.reset();
207 }
209 d->canvas->addCommand(cmd);
210 }
211}
212
214{
215 Q_D(KoToolBase);
218 QList<KoPathPointData> pointToChange;
219
220 QList<KoPathPointData>::const_iterator it(selectedPoints.constBegin());
221 for (; it != selectedPoints.constEnd(); ++it) {
222 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
223 if (point && (point->activeControlPoint1() || point->activeControlPoint2()))
224 pointToChange.append(*it);
225 }
226
227 if (! pointToChange.isEmpty()) {
228 d->canvas->addCommand(new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Line));
229 }
230 }
231}
232
234{
235 Q_D(KoToolBase);
238
239 KUndo2Command *command = createPointToCurveCommand(selectedPoints);
240
241 if (command) {
242 d->canvas->addCommand(command);
243 }
244 }
245}
246
248{
249 KUndo2Command *command = 0;
250 QList<KoPathPointData> pointToChange;
251
252 QList<KoPathPointData>::const_iterator it(points.constBegin());
253 for (; it != points.constEnd(); ++it) {
254 KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex);
255 if (point && (! point->activeControlPoint1() || ! point->activeControlPoint2()))
256 pointToChange.append(*it);
257 }
258
259 if (!pointToChange.isEmpty()) {
260 command = new KoPathPointTypeCommand(pointToChange, KoPathPointTypeCommand::Curve);
261 }
262
263 return command;
264}
265
267{
268 Q_D(KoToolBase);
269 if (m_pointSelection.size() > 1) {
271 if (segments.size() > 0) {
272 d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Line));
273 }
274 }
275}
276
278{
279 Q_D(KoToolBase);
280 if (m_pointSelection.size() > 1) {
282 if (segments.size() > 0) {
283 d->canvas->addCommand(new KoPathSegmentTypeCommand(segments, KoPathSegmentTypeCommand::Curve));
284 }
285 }
286}
287
289{
290 Q_D(KoToolBase);
291
293
294 QList<KoParameterShape*> parameterShapes;
295
296 Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
297 KoParameterShape * parametric = dynamic_cast<KoParameterShape*>(shape);
298 if (parametric && parametric->isParametricShape()) {
299 parameterShapes.append(parametric);
300 }
301 }
302
303 if (!parameterShapes.isEmpty()) {
304 d->canvas->addCommand(new KoParameterToPathCommand(parameterShapes));
305 }
306
307 QList<KoSvgTextShape*> textShapes;
308 Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
309 if (KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape)) {
310 textShapes.append(text);
311 }
312 }
313
314 if (!textShapes.isEmpty()) {
315 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Convert to Path")); // TODO: reuse the text from KoParameterToPathCommand
316 const QList<KoShape*> oldSelectedShapes = implicitCastList<KoShape*>(textShapes);
317
318
319 new KoKeepShapesSelectedCommand(oldSelectedShapes, {}, canvas()->selectedShapesProxy(),
321
322 QList<KoShape*> newSelectedShapes;
323 Q_FOREACH (KoSvgTextShape *shape, textShapes) {
324 KoShape *outlineShape = shape->textOutline();
325
326 KoShapeContainer *parent = shape->parent();
327 canvas()->shapeController()->addShapeDirect(outlineShape, parent, cmd);
328
329 newSelectedShapes << outlineShape;
330 }
331
332 canvas()->shapeController()->removeShapes(oldSelectedShapes, cmd);
333
334 new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(),
336
337 canvas()->addCommand(cmd);
338 }
339
341}
342
343namespace {
344bool checkCanJoinToPoints(const KoPathPointData & pd1, const KoPathPointData & pd2)
345{
346 const KoPathPointIndex & index1 = pd1.pointIndex;
347 const KoPathPointIndex & index2 = pd2.pointIndex;
348
349 KoPathShape *path1 = pd1.pathShape;
350 KoPathShape *path2 = pd2.pathShape;
351
352 // check if subpaths are already closed
353 if (path1->isClosedSubpath(index1.first) || path2->isClosedSubpath(index2.first))
354 return false;
355
356 // check if first point is an endpoint
357 if (index1.second != 0 && index1.second != path1->subpathPointCount(index1.first)-1)
358 return false;
359
360 // check if second point is an endpoint
361 if (index2.second != 0 && index2.second != path2->subpathPointCount(index2.first)-1)
362 return false;
363
364 return true;
365}
366}
367
369{
370 Q_D(KoToolBase);
371
372 if (m_pointSelection.size() != 2)
373 return;
374
376 if (pointData.size() != 2) return;
377
378 const KoPathPointData & pd1 = pointData.at(0);
379 const KoPathPointData & pd2 = pointData.at(1);
380
381 if (!checkCanJoinToPoints(pd1, pd2)) {
382 return;
383 }
384
386
387 KUndo2Command *cmd = 0;
388
389 if (doJoin) {
390 cmd = new KoMultiPathPointJoinCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
391 } else {
392 cmd = new KoMultiPathPointMergeCommand(pd1, pd2, d->canvas->shapeController()->documentBase(), d->canvas->shapeManager()->selection());
393 }
394 d->canvas->addCommand(cmd);
395}
396
398{
399 mergePointsImpl(true);
400}
401
403{
404 mergePointsImpl(false);
405}
406
408{
409 Q_D(KoToolBase);
412 }
413}
414
416{
417 Q_D(KoToolBase);
418
419 if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
421 if (segments.size() == 1) {
422 d->canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0)));
423 }
424 } else if (m_pointSelection.hasSelection()) {
426 }
427}
428
430{
431 Q_D(KoToolBase);
432 // only try to break a segment when 2 points of the same object are selected
433 if (m_pointSelection.objectCount() == 1 && m_pointSelection.size() == 2) {
435 if (segments.size() == 1) {
436 d->canvas->addCommand(new KoPathSegmentBreakCommand(segments.at(0)));
437 }
438 }
439}
440
441void KoPathTool::paint(QPainter &painter, const KoViewConverter &converter)
442{
443 Q_D(KoToolBase);
444 m_textOutlineHelper->setDecorationThickness(decorationThickness());
445 m_textOutlineHelper->setHandleRadius(handleRadius());
446 m_textOutlineHelper->paint(&painter, converter);
447
449
450 Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
454
455 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
456 if (parameterShape && parameterShape->isParametricShape()) {
457 parameterShape->paintHandles(helper);
458 } else {
459 shape->paintPoints(helper);
460 }
461
462 if (!shape->stroke() || !shape->stroke()->isVisible()) {
464 helper.drawPath(shape->outline());
465 }
466 }
467
468 if (m_currentStrategy) {
469 painter.save();
470 m_currentStrategy->paint(painter, converter, canvas()->displayRendererInterface());
471 painter.restore();
472 }
473
475
476 if (m_activeHandle) {
479 } else {
480 m_activeHandle.reset();
481 }
482 } else if (m_activeSegment && m_activeSegment->isValid()) {
483
484 KoPathShape *shape = m_activeSegment->path;
485
486 // if the stroke is invisible, then we already painted the outline of the shape!
487 if (shape->stroke() && shape->stroke()->isVisible()) {
488 KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
489 KoPathSegment segment = shape->segmentByIndex(index).toCubic();
490
492
496
497 QPainterPath path;
498 path.moveTo(segment.first()->point());
499 path.cubicTo(segment.first()->controlPoint2(),
500 segment.second()->controlPoint1(),
501 segment.second()->point());
502
503 helper.drawPath(path);
504 }
505 }
506
507
508
509 if (m_currentStrategy) {
510 painter.save();
511 painter.setTransform(converter.documentToView(), true);
512 d->canvas->snapGuide()->paint(painter, converter, canvas()->displayRendererInterface());
513 painter.restore();
514 }
515}
516
518{
519 const_cast<KoPathToolSelection&>(m_pointSelection).update();
520
521 QRectF newDecorationsRect;
522
523 Q_FOREACH (KoShape *shape, m_pointSelection.selectedShapes()) {
524 newDecorationsRect |= kisGrowRect(shape->boundingRect(), handleDocRadius());
525 }
526
527 Q_FOREACH(const KoPathPoint *point, m_pointSelection.selectedPoints()) {
528 newDecorationsRect |= kisGrowRect(point->boundingRect(false), handleDocRadius());
529 }
530
531 if (m_activeHandle) {
532 newDecorationsRect |= kisGrowRect(m_activeHandle->boundingRect(), handleDocRadius());
533 }
534
535 if (m_activeSegment) {
536 KoPathPointIndex index = m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart);
537 KoPathSegment segment = m_activeSegment->path->segmentByIndex(index);
538
539 QRectF rect = segment.boundingRect();
540 rect = m_activeSegment->path->shapeToDocument(rect);
541
542 newDecorationsRect |= kisGrowRect(rect, handleDocRadius());
543 }
544
545 newDecorationsRect |= m_textOutlineHelper->decorationRect();
546
547 return newDecorationsRect;
548}
549
555
557{
558 // When using touch drawing, we only ever receive move events after the
559 // finger has pressed down. We have to issue an artificial move here so that
560 // the tool's state is updated properly to handle the press.
561 if (event->isTouchEvent()) {
562 mouseMoveEvent(event);
563 }
564
565 if (KoSvgTextShape *shape = m_textOutlineHelper->contourModeButtonHovered(event->point)) {
566 m_textOutlineHelper->toggleTextContourMode(shape);
567 event->accept();
568 }
569 // we are moving if we hit a point and use the left mouse button
570 if (m_activeHandle) {
571 m_currentStrategy.reset(m_activeHandle->handleMousePress(event));
572 } else {
573
574 if (event->button() & Qt::LeftButton) {
575
576 // check if we hit a path segment
577 if (m_activeSegment && m_activeSegment->isValid()) {
578
579 KoPathShape *shape = m_activeSegment->path;
580 KoPathPointIndex index = shape->pathPointIndex(m_activeSegment->segmentStart);
581 KoPathSegment segment = shape->segmentByIndex(index);
582
583 m_pointSelection.add(segment.first(), !(event->modifiers() & Qt::ShiftModifier));
584 m_pointSelection.add(segment.second(), false);
585
586 KoPathPointData data(shape, index);
587 m_currentStrategy.reset(new KoPathSegmentChangeStrategy(this, event->point, data, m_activeSegment->positionOnSegment));
588 } else {
589
590 KoShapeManager *shapeManager = canvas()->shapeManager();
591 KoSelection *selection = shapeManager->selection();
592 KoShape *shape = shapeManager->shapeAt(event->point, KoFlake::ShapeOnTop);
593
594 if (shape && !selection->isSelected(shape)) {
595
596 if (!(event->modifiers() & Qt::ShiftModifier)) {
597 selection->deselectAll();
598 }
599
600 selection->select(shape);
601 } else {
604 }
605 }
606 }
607 }
608}
609
611{
612 if (event->button() & Qt::RightButton)
613 return;
614
615 if (m_currentStrategy) {
616 m_lastPoint = event->point;
617 m_currentStrategy->handleMouseMove(event->point, event->modifiers());
618
620
621 return;
622 }
623
624 if (m_activeSegment) {
625 m_activeSegment.reset();
627 }
628
629 Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
630 QRectF roi = shape->documentToShape(handleGrabRect(event->point));
631 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
632 if (parameterShape && parameterShape->isParametricShape()) {
633 int handleId = parameterShape->handleIdAt(roi);
634 if (handleId != -1) {
636 Q_EMIT statusTextChanged(i18n("Drag to move handle."));
637
638 m_activeHandle.reset(new ParameterHandle(this, parameterShape, handleId));
640 return;
641 }
642 } else {
643 QList<KoPathPoint*> points = shape->pointsAt(roi, true);
644 if (! points.empty()) {
645 // find the nearest control point from all points within the roi
646 KoPathPoint * bestPoint = 0;
648 qreal minDistance = HUGE_VAL;
649 Q_FOREACH (KoPathPoint *p, points) {
650 // the node point must be hit if the point is not selected yet
651 if (! m_pointSelection.contains(p) && ! roi.contains(p->point()))
652 continue;
653
654 // check for the control points first as otherwise it is no longer
655 // possible to change the control points when they are the same as the point
656 if (p->activeControlPoint1() && roi.contains(p->controlPoint1())) {
657 qreal dist = squaredDistance(roi.center(), p->controlPoint1());
658 if (dist < minDistance) {
659 bestPoint = p;
660 bestPointType = KoPathPoint::ControlPoint1;
661 minDistance = dist;
662 }
663 }
664
665 if (p->activeControlPoint2() && roi.contains(p->controlPoint2())) {
666 qreal dist = squaredDistance(roi.center(), p->controlPoint2());
667 if (dist < minDistance) {
668 bestPoint = p;
669 bestPointType = KoPathPoint::ControlPoint2;
670 minDistance = dist;
671 }
672 }
673
674 // check the node point at last
675 qreal dist = squaredDistance(roi.center(), p->point());
676 if (dist < minDistance) {
677 bestPoint = p;
678 bestPointType = KoPathPoint::Node;
679 minDistance = dist;
680 }
681 }
682
683 if (! bestPoint) {
685 return;
686 }
687
689 if (bestPointType == KoPathPoint::Node)
690 Q_EMIT statusTextChanged(i18n("Drag to move point. Shift click to change point type."));
691 else
692 Q_EMIT statusTextChanged(i18n("Drag to move control point."));
693
694 PointHandle *prev = dynamic_cast<PointHandle*>(m_activeHandle.data());
695 if (prev && prev->activePoint() == bestPoint && prev->activePointType() == bestPointType)
696 return; // no change;
697
698 m_activeHandle.reset(new PointHandle(this, bestPoint, bestPointType));
700 return;
701 }
702 }
703 }
704
706
707 if (m_activeHandle) {
708 m_activeHandle.reset();
710 }
711
712 PathSegment *hoveredSegment = segmentAtPoint(event->point);
713 if(hoveredSegment) {
714 useCursor(Qt::PointingHandCursor);
715 Q_EMIT statusTextChanged(i18n("Drag to change curve directly. Double click to insert new path point."));
716 m_activeSegment.reset(hoveredSegment);
718 } else {
719 uint selectedPointCount = m_pointSelection.size();
720 if (selectedPointCount == 0)
721 Q_EMIT statusTextChanged(QString());
722 else {
723 if (!m_actionBreakSelection->shortcut().isEmpty()) {
724 if (selectedPointCount == 1)
725 Q_EMIT statusTextChanged(i18nc("%1 is a shortcut to be pressed", "Press %1 to break path at selected point.", m_actionBreakSelection->shortcut().toString()));
726 else
727 Q_EMIT statusTextChanged(i18nc("%1 is a shortcut to be pressed", "Press %1 to break path at selected segments.", m_actionBreakSelection->shortcut().toString()));
728 } else {
729 Q_EMIT statusTextChanged(QString());
730 }
731 }
732 }
733}
734
736{
737 Q_D(KoToolBase);
738 if (m_currentStrategy) {
739 const bool hadNoSelection = !m_pointSelection.hasSelection();
740 m_currentStrategy->finishInteraction(event->modifiers());
741 KUndo2Command *command = m_currentStrategy->createCommand();
742 if (command)
743 d->canvas->addCommand(command);
744 if (hadNoSelection && dynamic_cast<KoPathPointRubberSelectStrategy*>(m_currentStrategy.data())
746 // the click didn't do anything at all. Allow it to be used by others.
747 event->ignore();
748 }
749 m_currentStrategy.reset();
751 }
752}
753
754void KoPathTool::keyPressEvent(QKeyEvent *event)
755{
756 if (m_currentStrategy) {
757 switch (event->key()) {
758 case Qt::Key_Control:
759 case Qt::Key_Alt:
760 case Qt::Key_Shift:
761 case Qt::Key_Meta:
762 if (! event->isAutoRepeat()) {
763 m_currentStrategy->handleMouseMove(m_lastPoint, event->modifiers());
764 }
765 break;
766 case Qt::Key_Escape:
767 m_currentStrategy->cancelInteraction();
768 m_currentStrategy.reset();
769 break;
770 default:
771 event->ignore();
772 return;
773 }
774 } else {
775 switch (event->key()) {
776#ifndef NDEBUG
777// case Qt::Key_D:
778// if (m_pointSelection.objectCount() == 1) {
779// QList<KoPathPointData> selectedPoints = m_pointSelection.selectedPointsData();
780// KoPathShapePrivate *p = static_cast<KoPathShapePrivate*>(selectedPoints[0].pathShape->priv());
781// p->debugPath();
782// }
783// break;
784#endif
785 default:
786 event->ignore();
787 return;
788 }
789 }
790 event->accept();
791}
792
793void KoPathTool::keyReleaseEvent(QKeyEvent *event)
794{
795 if (m_currentStrategy) {
796 switch (event->key()) {
797 case Qt::Key_Control:
798 case Qt::Key_Alt:
799 case Qt::Key_Shift:
800 case Qt::Key_Meta:
801 if (! event->isAutoRepeat()) {
802 m_currentStrategy->handleMouseMove(m_lastPoint, Qt::NoModifier);
803 }
804 break;
805 default:
806 break;
807 }
808 }
809 event->accept();
810}
811
813{
814 Q_D(KoToolBase);
815 // check if we are doing something else at the moment
816 if (m_currentStrategy) return;
817
818 if (!m_activeHandle && m_activeSegment && m_activeSegment->isValid()) {
819 QList<KoPathPointData> segments;
820 segments.append(
822 m_activeSegment->path->pathPointIndex(m_activeSegment->segmentStart)));
823
824 KoPathPointInsertCommand *cmd = new KoPathPointInsertCommand(segments, m_activeSegment->positionOnSegment);
825 d->canvas->addCommand(cmd);
826
828 foreach (KoPathPoint * p, cmd->insertedPoints()) {
829 m_pointSelection.add(p, false);
830 }
832 } else if (!m_activeHandle && !m_activeSegment) {
834 }
835}
836
838{
839 // the max allowed distance from a segment
840 const QRectF grabRoi = handleGrabRect(point);
841 std::unique_ptr<PathSegment> segment(new PathSegment);
842
843 Q_FOREACH (KoPathShape *shape, m_pointSelection.selectedShapes()) {
844 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
845 if (parameterShape && parameterShape->isParametricShape())
846 continue;
847
848 const KoPathSegment s = shape->segmentAtPoint(point, grabRoi);
849 if (s.isValid()) {
850 segment->path = shape;
851 segment->segmentStart = s.first();
852 segment->positionOnSegment = s.nearestPoint(shape->documentToShape(point));
853 }
854 }
855
856 if (!segment->isValid()) {
857 segment.reset();
858 }
859
860 return segment.release();
861}
862
863void KoPathTool::activate(const QSet<KoShape*> &shapes)
864{
865 KoToolBase::activate(shapes);
866
867 Q_D(KoToolBase);
868
869 d->canvas->snapGuide()->reset();
870
872 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
873 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(updateActions()));
874
875 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(repaintDecorations()));
876 m_canvasConnections.addConnection(d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(repaintDecorations()));
878 initializeWithShapes(QList<KoShape*>(shapes.begin(), shapes.end()));
879 connect(m_actionCurvePoint, SIGNAL(triggered()), this, SLOT(pointToCurve()), Qt::UniqueConnection);
880 connect(m_actionLinePoint, SIGNAL(triggered()), this, SLOT(pointToLine()), Qt::UniqueConnection);
881 connect(m_actionLineSegment, SIGNAL(triggered()), this, SLOT(segmentToLine()), Qt::UniqueConnection);
882 connect(m_actionCurveSegment, SIGNAL(triggered()), this, SLOT(segmentToCurve()), Qt::UniqueConnection);
883 connect(m_actionAddPoint, SIGNAL(triggered()), this, SLOT(insertPoints()), Qt::UniqueConnection);
884 connect(m_actionRemovePoint, SIGNAL(triggered()), this, SLOT(removePoints()), Qt::UniqueConnection);
885 connect(m_actionBreakPoint, SIGNAL(triggered()), this, SLOT(breakAtPoint()), Qt::UniqueConnection);
886 connect(m_actionBreakSegment, SIGNAL(triggered()), this, SLOT(breakAtSegment()), Qt::UniqueConnection);
887 connect(m_actionBreakSelection, SIGNAL(triggered()), this, SLOT(breakAtSelection()), Qt::UniqueConnection);
888 connect(m_actionJoinSegment, SIGNAL(triggered()), this, SLOT(joinPoints()), Qt::UniqueConnection);
889 connect(m_actionMergePoints, SIGNAL(triggered()), this, SLOT(mergePoints()), Qt::UniqueConnection);
890 connect(m_actionConvertToPath, SIGNAL(triggered()), this, SLOT(convertToPath()), Qt::UniqueConnection);
891 connect(m_actionPathPointCorner, SIGNAL(triggered()), this, SLOT(pointTypeChangedCorner()), Qt::UniqueConnection);
892 connect(m_actionPathPointSmooth, SIGNAL(triggered()), this, SLOT(pointTypeChangedSmooth()), Qt::UniqueConnection);
893 connect(m_actionPathPointSymmetric, SIGNAL(triggered()), this, SLOT(pointTypeChangedSymmetric()), Qt::UniqueConnection);
894 connect(&m_pointSelection, SIGNAL(selectionChanged()), this, SLOT(pointSelectionChanged()), Qt::UniqueConnection);
895
896}
897
899{
900 Q_D(KoToolBase);
901 QList<KoShape*> shapes =
902 d->canvas->selectedShapesProxy()->selection()->selectedEditableShapesAndDelegates();
903
904 initializeWithShapes(shapes);
905}
906
908{
909 Q_UNUSED(shape);
910
911 // active handle and selection might have already become invalid, so just
912 // delete them without dereferencing anything...
913
914 m_activeHandle.reset();
915 m_activeSegment.reset();
916}
917
924
926{
927 QList<KoPathShape*> selectedShapes;
928 Q_FOREACH (KoShape *shape, shapes) {
929 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
930
931 if (pathShape && pathShape->isShapeEditable()) {
932 selectedShapes.append(pathShape);
933 }
934 }
935
936 if (selectedShapes != m_pointSelection.selectedShapes()) {
938 m_pointSelection.setSelectedShapes(selectedShapes);
940 }
941
944}
945
947{
948 PathToolOptionWidget::Types type;
950 Q_FOREACH (KoPathShape *shape, selectedShapes) {
951 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
952 type |= parameterShape && parameterShape->isParametricShape() ?
954 }
955
956 Q_EMIT singleShapeChanged(selectedShapes.size() == 1 ? selectedShapes.first() : 0);
957 Q_EMIT typeChanged(type);
958}
959
961{
963
964 bool canBreakAtPoint = false;
965
966 bool hasNonSmoothPoints = false;
967 bool hasNonSymmetricPoints = false;
968 bool hasNonSplitPoints = false;
969
970 bool hasNonLinePoints = false;
971 bool hasNonCurvePoints = false;
972
973 bool canJoinSubpaths = false;
974
975 if (!pointData.isEmpty()) {
976 Q_FOREACH (const KoPathPointData &pd, pointData) {
977 const int subpathIndex = pd.pointIndex.first;
978 const int pointIndex = pd.pointIndex.second;
979
980 canBreakAtPoint |= pd.pathShape->isClosedSubpath(subpathIndex) ||
981 (pointIndex > 0 && pointIndex < pd.pathShape->subpathPointCount(subpathIndex) - 1);
982
984
985 hasNonSmoothPoints |= !(point->properties() & KoPathPoint::IsSmooth);
986 hasNonSymmetricPoints |= !(point->properties() & KoPathPoint::IsSymmetric);
987 hasNonSplitPoints |=
990
991 hasNonLinePoints |= point->activeControlPoint1() || point->activeControlPoint2();
992 hasNonCurvePoints |= !point->activeControlPoint1() && !point->activeControlPoint2();
993 }
994
995 if (pointData.size() == 2) {
996 const KoPathPointData & pd1 = pointData.at(0);
997 const KoPathPointData & pd2 = pointData.at(1);
998
999 canJoinSubpaths = checkCanJoinToPoints(pd1, pd2);
1000 }
1001 }
1002
1003 m_actionPathPointCorner->setEnabled(hasNonSplitPoints);
1004 m_actionPathPointSmooth->setEnabled(hasNonSmoothPoints);
1005 m_actionPathPointSymmetric->setEnabled(hasNonSymmetricPoints);
1006
1007 m_actionRemovePoint->setEnabled(!pointData.isEmpty());
1008
1009 m_actionBreakPoint->setEnabled(canBreakAtPoint);
1010
1011 m_actionCurvePoint->setEnabled(hasNonCurvePoints);
1012 m_actionLinePoint->setEnabled(hasNonLinePoints);
1013
1014 m_actionJoinSegment->setEnabled(canJoinSubpaths);
1015 m_actionMergePoints->setEnabled(canJoinSubpaths);
1016
1018
1019
1020 bool canSplitAtSegment = false;
1021 bool canConvertSegmentToLine = false;
1022 bool canConvertSegmentToCurve= false;
1023
1024 if (!segments.isEmpty()) {
1025
1026 canSplitAtSegment = segments.size() == 1;
1027
1028 bool hasLines = false;
1029 bool hasCurves = false;
1030
1031 Q_FOREACH (const KoPathPointData &pd, segments) {
1033 hasLines |= segment.degree() == 1;
1034 hasCurves |= segment.degree() > 1;
1035 }
1036
1037 canConvertSegmentToLine = !segments.isEmpty() && hasCurves;
1038 canConvertSegmentToCurve= !segments.isEmpty() && hasLines;
1039 }
1040
1041 m_actionAddPoint->setEnabled(canSplitAtSegment);
1042
1043 m_actionLineSegment->setEnabled(canConvertSegmentToLine);
1044 m_actionCurveSegment->setEnabled(canConvertSegmentToCurve);
1045
1046 m_actionBreakSegment->setEnabled(canSplitAtSegment);
1047 m_actionBreakSelection->setEnabled(canSplitAtSegment | canBreakAtPoint);
1048
1050 bool haveConvertibleShapes = false;
1051 Q_FOREACH (KoShape *shape, selection->selectedEditableShapes()) {
1052 KoParameterShape * parameterShape = dynamic_cast<KoParameterShape*>(shape);
1053 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(shape);
1054 if (textShape ||
1055 (parameterShape && parameterShape->isParametricShape())) {
1056
1057 haveConvertibleShapes = true;
1058 break;
1059 }
1060 }
1061 m_actionConvertToPath->setEnabled(haveConvertibleShapes);
1062}
1063
1065{
1066 Q_D(KoToolBase);
1067
1072 m_activeHandle.reset();
1073 m_activeSegment.reset();
1074 m_currentStrategy.reset();
1075 d->canvas->snapGuide()->reset();
1076
1077 disconnect(m_actionCurvePoint, 0, this, 0);
1078 disconnect(m_actionLinePoint, 0, this, 0);
1079 disconnect(m_actionLineSegment, 0, this, 0);
1080 disconnect(m_actionCurveSegment, 0, this, 0);
1081 disconnect(m_actionAddPoint, 0, this, 0);
1082 disconnect(m_actionRemovePoint, 0, this, 0);
1083 disconnect(m_actionBreakPoint, 0, this, 0);
1084 disconnect(m_actionBreakSegment, 0, this, 0);
1085 disconnect(m_actionBreakSelection, 0, this, 0);
1086 disconnect(m_actionJoinSegment, 0, this, 0);
1087 disconnect(m_actionMergePoints, 0, this, 0);
1088 disconnect(m_actionConvertToPath, 0, this, 0);
1089 disconnect(m_actionPathPointCorner, 0, this, 0);
1090 disconnect(m_actionPathPointSmooth, 0, this, 0);
1091 disconnect(m_actionPathPointSymmetric, 0, this, 0);
1092 disconnect(&m_pointSelection, 0, this, 0);
1093
1095}
1096
1097void KoPathTool::canvasResourceChanged(int key, const QVariant & /*res*/)
1098{
1101 }
1102}
1103
1105{
1106 Q_D(KoToolBase);
1107 updateActions();
1108 d->canvas->snapGuide()->setIgnoredPathPoints(QList<KoPathPoint*>(m_pointSelection.selectedPoints().begin(), m_pointSelection.selectedPoints().end()));
1110}
1111
1112namespace {
1113void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2)
1114{
1115 if (a1->isEnabled() || a2->isEnabled()) {
1116 menu->addAction(a1);
1117 menu->addAction(a2);
1118 menu->addSeparator();
1119 }
1120}
1121
1122void addActionsGroupIfEnabled(QMenu *menu, QAction *a1, QAction *a2, QAction *a3)
1123{
1124 if (a1->isEnabled() || a2->isEnabled()) {
1125 menu->addAction(a1);
1126 menu->addAction(a2);
1127 menu->addAction(a3);
1128 menu->addSeparator();
1129 }
1130}
1131}
1132
1134{
1135 if (m_activeHandle) {
1136 m_activeHandle->trySelectHandle();
1137 }
1138
1139 if (m_activeSegment && m_activeSegment->isValid()) {
1140 KoPathShape *shape = m_activeSegment->path;
1141 KoPathSegment segment = shape->segmentByIndex(shape->pathPointIndex(m_activeSegment->segmentStart));
1142
1143 m_pointSelection.add(segment.first(), true);
1144 m_pointSelection.add(segment.second(), false);
1145 }
1146
1147 if (m_contextMenu) {
1148 m_contextMenu->clear();
1149
1150 addActionsGroupIfEnabled(m_contextMenu.data(),
1154
1155 addActionsGroupIfEnabled(m_contextMenu.data(),
1158
1159 addActionsGroupIfEnabled(m_contextMenu.data(),
1162
1163 addActionsGroupIfEnabled(m_contextMenu.data(),
1166
1167 addActionsGroupIfEnabled(m_contextMenu.data(),
1170
1171 addActionsGroupIfEnabled(m_contextMenu.data(),
1174
1176
1177 m_contextMenu->addSeparator();
1178 }
1179
1180 return m_contextMenu.data();
1181}
1182
1187
1192
1194{
1195 // noop!
1196}
1197
1202
1204{
1205 // noop!
1206}
1207
1212
1214{
1217 return true;
1218}
1219
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(KisHandlePalette palette=KisHandlePalette())
static KisHandleStyle & primarySelection(KisHandlePalette palette=KisHandlePalette())
void addConnection(Sender sender, Signal signal, Receiver receiver, Method method, Qt::ConnectionType type=Qt::AutoConnection)
QPointer< KoShapeController > shapeController
virtual KoShapeManager * shapeManager() const =0
virtual KoColorDisplayRendererInterface * displayRendererInterface() const
displayRendererInterface The display renderer interface has a number of color conversion functions wh...
virtual void addCommand(KUndo2Command *command)=0
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
virtual KisHandlePalette handlePaletteForDisplayColorSpace() const =0
handlePaletteForDisplayColorSpace
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.
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.
void paint(QPainter &painter, const KoViewConverter &converter, qreal handleRadius, KoColorDisplayRendererInterface *renderInterface)
Draw the selected points.
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 the Enter key or an...
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:965
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:885
QPointF documentToShape(const QPointF &point) const
Transforms point from document coordinates to shape coordinates.
Definition KoShape.cpp:1011
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:300
KoShapeContainer * parent() const
Definition KoShape.cpp:857
static KisHandlePainterHelper createHandlePainterHelperView(QPainter *painter, KoShape *shape, const KoViewConverter &converter, qreal handleRadius=0.0, int decorationThickness=1)
Definition KoShape.cpp:977
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
rgba palette[MAX_PALETTE]
Definition palette.c:35
KoPathPoint * segmentStart