Krita Source Code Documentation
Loading...
Searching...
No Matches
DefaultTool.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2
3 SPDX-FileCopyrightText: 2006-2008 Thorsten Zachmann <zachmann@kde.org>
4 SPDX-FileCopyrightText: 2006-2010 Thomas Zander <zander@kde.org>
5 SPDX-FileCopyrightText: 2008-2009 Jan Hambrecht <jaham@gmx.net>
6 SPDX-FileCopyrightText: 2008 C. Boemann <cbo@boemann.dk>
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9*/
10
11#include "DefaultTool.h"
14#include "SelectionDecorator.h"
15#include "ShapeMoveStrategy.h"
16#include "ShapeRotateStrategy.h"
17#include "ShapeShearStrategy.h"
18#include "ShapeResizeStrategy.h"
19
20#include <KoPointerEvent.h>
21#include <KoToolSelection.h>
22#include <KoToolManager.h>
23#include <KoSelection.h>
24#include <KoShapeController.h>
25#include <KoShapeManager.h>
27#include <KoShapeGroup.h>
28#include <KoShapeLayer.h>
29#include <KoPathShape.h>
30#include <KoDrag.h>
31#include <KoCanvasBase.h>
34#include <KoSvgTextShape.h>
45#include <KoSnapGuide.h>
47#include "kis_action_registry.h"
48#include "kis_node.h"
49#include "kis_node_manager.h"
50#include "KisViewManager.h"
51#include "kis_canvas2.h"
56
58
59#include <KoIcon.h>
60
61#include <QPainterPath>
62#include <QPointer>
63#include <QAction>
64#include <QKeyEvent>
65#include <QTimer>
66#include <KisSignalMapper.h>
67#include <KoResourcePaths.h>
68
69#include <KoCanvasController.h>
70#include <kactioncollection.h>
71#include <QMenu>
72
73#include <math.h>
74#include "kis_assert.h"
75#include "kis_global.h"
76#include "kis_debug.h"
77#include "krita_utils.h"
78
79#include <QVector2D>
80
81#define HANDLE_DISTANCE 10
82#define HANDLE_DISTANCE_SQ (HANDLE_DISTANCE * HANDLE_DISTANCE)
83
84#define INNER_HANDLE_DISTANCE_SQ 16
85
86namespace {
87static const QString EditFillGradientFactoryId = "edit_fill_gradient";
88static const QString EditStrokeGradientFactoryId = "edit_stroke_gradient";
89static const QString EditFillMeshGradientFactoryId = "edit_fill_meshgradient";
90
91enum TransformActionType {
92 TransformRotate90CW,
93 TransformRotate90CCW,
94 TransformRotate180,
95 TransformMirrorX,
96 TransformMirrorY,
97 TransformReset
98};
99
100enum BooleanOp {
101 BooleanUnion,
102 BooleanIntersection,
103 BooleanSubtraction
104};
105
106}
107
109{
110public:
112 : KoInteractionStrategy(parent)
113 {
114 }
115
117 {
118 return 0;
119 }
120
121 void handleMouseMove(const QPointF & /*mouseLocation*/, Qt::KeyboardModifiers /*modifiers*/) override {}
122 void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {}
123
124 void paint(QPainter &painter, const KoViewConverter &converter) override {
125 Q_UNUSED(painter);
126 Q_UNUSED(converter);
127 }
128};
129
131{
132public:
133 explicit SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid)
134 : KoShapeRubberSelectStrategy(parent, clicked, useSnapToGrid)
135 {
136 }
137
138 void paint(QPainter &painter, const KoViewConverter &converter) override {
139 KoShapeRubberSelectStrategy::paint(painter, converter);
140 }
141
142 void cancelInteraction() override
143 {
144 tool()->canvas()->updateCanvas(selectedRectangle() | tool()->decorationsRect());
145 }
146
147 void finishInteraction(Qt::KeyboardModifiers modifiers = QFlags<Qt::KeyboardModifier>()) override
148 {
149 Q_UNUSED(modifiers);
150 DefaultTool *defaultTool = dynamic_cast<DefaultTool*>(tool());
152
153 KoSelection * selection = defaultTool->koSelection();
154
155 const bool useContainedMode = currentMode() == CoveringSelection;
156
157 QList<KoShape *> shapes =
158 defaultTool->shapeManager()->
159 shapesAt(selectedRectangle(), true, useContainedMode);
160
161 Q_FOREACH (KoShape * shape, shapes) {
162 if (!shape->isSelectable()) continue;
163
164 selection->select(shape);
165 }
166
167 tool()->canvas()->updateCanvas(selectedRectangle() | tool()->decorationsRect());
168 }
169};
170#include <KoGradientBackground.h>
173
175{
176public:
178 int priority, const QString &id, DefaultTool *_q)
180 q(_q),
181 m_fillVariant(fillVariant)
182 {
183 }
184
198
199 bool hoverEvent(KoPointerEvent *ev) override
200 {
202 return false;
203 }
204
205 bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
206 {
207 Q_UNUSED(painter);
208 Q_UNUSED(converter);
209 return false;
210 }
211
212 bool tryUseCustomCursor() override {
214 q->useCursor(Qt::OpenHandCursor);
215 return true;
216 }
217
218 return false;
219 }
220
221private:
222
225 QList<KoShape*> shapes = selection->selectedEditableShapes();
226
227 KoShape *shape = 0;
228 if (shapes.size() == 1) {
229 shape = shapes.first();
230 }
231
232 return shape;
233 }
234
237
238 KoShape *shape = onlyEditableShape();
239 if (shape) {
240 KoFlake::SelectionHandle globalHandle = q->handleAt(pos);
241 const qreal distanceThresholdSq =
242 globalHandle == KoFlake::NoHandle ?
244
245 const KoViewConverter *converter = q->canvas()->viewConverter();
246 const QPointF viewPoint = converter->documentToView(pos);
247 qreal minDistanceSq = std::numeric_limits<qreal>::max();
248
250 auto handless = sh.handles();
251 Q_FOREACH (const KoShapeGradientHandles::Handle &handle, sh.handles()) {
252 const QPointF handlePoint = converter->documentToView(handle.pos);
253 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
254
255 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
256 result = handle;
257 minDistanceSq = distanceSq;
258 }
259 }
260 }
261
262 return result;
263 }
264
265private:
269};
270
273
275{
276public:
278 int priority,
279 const QString& id,
280 DefaultTool* _q)
282 , m_fillVariant(fillVariant)
283 , q(_q)
284 {
285 }
286
303
304 bool hoverEvent(KoPointerEvent *ev) override
305 {
306 // for custom cursor
308
309 // refresh
312 }
313
314 m_currentHandle = handle;
316
317 // highlight the decoration which is being hovered
320 }
321 return false;
322 }
323
324 bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
325 {
326 Q_UNUSED(painter);
327 Q_UNUSED(converter);
328 return false;
329 }
330
331 bool tryUseCustomCursor() override
332 {
334 q->useCursor(Qt::OpenHandCursor);
335 return true;
336 }
337
338 return false;
339 }
340
341
342private:
344 // FIXME: copy of KoShapeGradientHandles
346 QList<KoShape*> shapes = selection->selectedEditableShapes();
347
348 KoShape *shape = 0;
349 if (shapes.size() == 1) {
350 shape = shapes.first();
351 }
352
353 return shape;
354 }
355
357 {
358 // FIXME: copy of KoShapeGradientHandles. use a template?
360
361 KoShape *shape = onlyEditableShape();
362 if (shape) {
363 KoFlake::SelectionHandle globalHandle = q->handleAt(pos);
364 const qreal distanceThresholdSq =
365 globalHandle == KoFlake::NoHandle ?
367
368 const KoViewConverter *converter = q->canvas()->viewConverter();
369 const QPointF viewPoint = converter->documentToView(pos);
370 qreal minDistanceSq = std::numeric_limits<qreal>::max();
371
373
374 for (const auto& handle: sh.handles()) {
375 const QPointF handlePoint = converter->documentToView(handle.pos);
376 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
377
378 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
379 result = handle;
380 minDistanceSq = distanceSq;
381 }
382 }
383 }
384
385 return result;
386 }
387
388private:
392};
393
395{
396public:
398 : KoToolSelection(parent)
399 , m_selection(parent->koSelection())
400 {
401 }
402
403 bool hasSelection() override
404 {
405 if (m_selection) {
406 return m_selection->count();
407 }
408 return false;
409 }
410
411private:
413};
414
415DefaultTool::DefaultTool(KoCanvasBase *canvas, bool connectToSelectedShapesProxy)
416 : KoInteractionTool(canvas)
417 , m_lastHandle(KoFlake::NoHandle)
418 , m_hotPosition(KoFlake::TopLeft)
419 , m_mouseWasInsideHandles(false)
420 , m_selectionHandler(new SelectionHandler(this))
421 , m_tabbedOptionWidget(0)
422 , m_textPropertyInterface(new DefaultToolTextPropertiesInterface(this))
423{
424 setupActions();
425
426 QPixmap rotatePixmap, shearPixmap;
427 rotatePixmap.load(":/cursor_rotate.png");
428 Q_ASSERT(!rotatePixmap.isNull());
429 shearPixmap.load(":/cursor_shear.png");
430 Q_ASSERT(!shearPixmap.isNull());
431
432 m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45)));
433 m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90)));
434 m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135)));
435 m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180)));
436 m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225)));
437 m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270)));
438 m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315)));
439 m_rotateCursors[7] = QCursor(rotatePixmap);
440 /*
441 m_rotateCursors[0] = QCursor(Qt::RotateNCursor);
442 m_rotateCursors[1] = QCursor(Qt::RotateNECursor);
443 m_rotateCursors[2] = QCursor(Qt::RotateECursor);
444 m_rotateCursors[3] = QCursor(Qt::RotateSECursor);
445 m_rotateCursors[4] = QCursor(Qt::RotateSCursor);
446 m_rotateCursors[5] = QCursor(Qt::RotateSWCursor);
447 m_rotateCursors[6] = QCursor(Qt::RotateWCursor);
448 m_rotateCursors[7] = QCursor(Qt::RotateNWCursor);
449 */
450 m_shearCursors[0] = QCursor(shearPixmap);
451 m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45)));
452 m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90)));
453 m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135)));
454 m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180)));
455 m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225)));
456 m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270)));
457 m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315)));
458 m_sizeCursors[0] = Qt::SizeVerCursor;
459 m_sizeCursors[1] = Qt::SizeBDiagCursor;
460 m_sizeCursors[2] = Qt::SizeHorCursor;
461 m_sizeCursors[3] = Qt::SizeFDiagCursor;
462 m_sizeCursors[4] = Qt::SizeVerCursor;
463 m_sizeCursors[5] = Qt::SizeBDiagCursor;
464 m_sizeCursors[6] = Qt::SizeHorCursor;
465 m_sizeCursors[7] = Qt::SizeFDiagCursor;
466
467 if (connectToSelectedShapesProxy) {
469
471 connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), m_textPropertyInterface, SLOT(slotSelectionChanged()));
472 connect(canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(repaintDecorations()));
473 }
474}
475
479
481{
482 if (value) {
485 1, EditFillGradientFactoryId, this));
486 } else {
487 removeInteractionFactory(EditFillGradientFactoryId);
488 }
490}
491
493{
494 if (value) {
497 0, EditStrokeGradientFactoryId, this));
498 } else {
499 removeInteractionFactory(EditStrokeGradientFactoryId);
500 }
502}
503
505{
506 if (value) {
508 m_tabbedOptionWidget, SLOT(slotMeshGradientHandleSelected(KoShapeMeshGradientHandles::Handle)));
511 EditFillMeshGradientFactoryId, this));
512 } else {
514 m_tabbedOptionWidget, SLOT(slotMeshGradientHandleSelected(KoShapeMeshGradientHandles::Handle)));
515 removeInteractionFactory(EditFillMeshGradientFactoryId);
516 }
517}
518
523
525{
527
528 if (shapes.isEmpty()) return;
529
531 KUndo2Command *parentCommand = new KUndo2Command();
532 bool convertableShape = false;
534 Q_FOREACH(KoShape *shape, shapes) {
535 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(shape);
536 if (textShape && textShape->textType() != type) {
537 KoSvgConvertTextTypeCommand *cmd = new KoSvgConvertTextTypeCommand(textShape, type, 0, parentCommand);
538 if (!convertableShape) {
539 convertableShape = true;
540 parentCommand->setText(cmd->text());
541 }
542 }
543 }
544
546 if (convertableShape) {
547 canvas()->addCommand(parentCommand);
548 }
549}
550
552{
553 return true;
554}
555
556void DefaultTool::addMappedAction(KisSignalMapper *mapper, const QString &actionId, int commandType)
557{
558 QAction *a =action(actionId);
559 connect(a, SIGNAL(triggered()), mapper, SLOT(map()));
560 mapper->setMapping(a, commandType);
561}
562
564{
566
573
575
580
585
587
588 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_cw", TransformRotate90CW);
589 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_ccw", TransformRotate90CCW);
590 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_180", TransformRotate180);
591 addMappedAction(m_transformSignalsMapper, "object_transform_mirror_horizontally", TransformMirrorX);
592 addMappedAction(m_transformSignalsMapper, "object_transform_mirror_vertically", TransformMirrorY);
593 addMappedAction(m_transformSignalsMapper, "object_transform_reset", TransformReset);
594
596
597 addMappedAction(m_booleanSignalsMapper, "object_unite", BooleanUnion);
598 addMappedAction(m_booleanSignalsMapper, "object_intersect", BooleanIntersection);
599 addMappedAction(m_booleanSignalsMapper, "object_subtract", BooleanSubtraction);
600
605
606 m_contextMenu.reset(new QMenu());
607}
608
610{
611 QPointF selectionCenter = koSelection()->absolutePosition();
612 QPointF direction;
613
614 switch (handle) {
616 if (useEdgeRotation) {
619 } else {
620 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
621 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRight) - handlePosition);
622 direction = handlePosition - selectionCenter;
623 }
624 break;
626 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized()).toPointF();
627 break;
629 if (useEdgeRotation) {
632 } else {
633 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRight);
634 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
635 direction = handlePosition - selectionCenter;
636 }
637 break;
639 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized()).toPointF();
640 break;
642 if (useEdgeRotation) {
645 } else {
646 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeft);
647 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
648 direction = handlePosition - selectionCenter;
649 }
650 break;
652 direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - selectionCenter;
653 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized()).toPointF();
654 break;
656 if (useEdgeRotation) {
659 } else {
660 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
661 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeft) - handlePosition);
662 direction = handlePosition - selectionCenter;
663 }
664 break;
666 direction = koSelection()->absolutePosition(KoFlake::TopLeft) - selectionCenter;
667 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized()).toPointF();
668 break;
670 return 0.0;
671 break;
672 }
673
674 qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI;
675
676 switch (handle) {
678 if (useEdgeRotation) {
679 rotation -= 0.0;
680 } else {
681 rotation -= 270.0;
682 }
683 break;
685 rotation -= 315.0;
686 break;
688 if (useEdgeRotation) {
689 rotation -= 90.0;
690 } else {
691 rotation -= 0.0;
692 }
693 break;
695 rotation -= 45.0;
696 break;
698 if (useEdgeRotation) {
699 rotation -= 180.0;
700 } else {
701 rotation -= 90.0;
702 }
703 break;
705 rotation -= 135.0;
706 break;
708 if (useEdgeRotation) {
709 rotation -= 270.0;
710 } else {
711 rotation -= 180.0;
712 }
713 break;
715 rotation -= 225.0;
716 break;
717 default:
718 ;
719 }
720
721 if (rotation < 0.0) {
722 rotation += 360.0;
723 }
724
725 return rotation;
726}
727
729{
730 if (tryUseCustomCursor()) return;
731
732 QCursor cursor = Qt::ArrowCursor;
733
734 QString statusText;
735
737 if (selection && selection->count() > 0) { // has a selection
738 bool editable = !selection->selectedEditableShapes().isEmpty();
739
742 int rotOctant = 8 + int(8.5 + m_angle / 45);
743
744 bool rotateHandle = false;
745 bool shearHandle = false;
746 switch (m_lastHandle) {
748 cursor = m_shearCursors[(0 + rotOctant) % 8];
749 shearHandle = true;
750 break;
752 cursor = m_rotateCursors[(1 + rotOctant) % 8];
753 rotateHandle = true;
754 break;
756 cursor = m_shearCursors[(2 + rotOctant) % 8];
757 shearHandle = true;
758 break;
760 cursor = m_rotateCursors[(3 + rotOctant) % 8];
761 rotateHandle = true;
762 break;
764 cursor = m_shearCursors[(4 + rotOctant) % 8];
765 shearHandle = true;
766 break;
768 cursor = m_rotateCursors[(5 + rotOctant) % 8];
769 rotateHandle = true;
770 break;
772 cursor = m_shearCursors[(6 + rotOctant) % 8];
773 shearHandle = true;
774 break;
776 cursor = m_rotateCursors[(7 + rotOctant) % 8];
777 rotateHandle = true;
778 break;
780 cursor = Qt::ArrowCursor;
781 break;
782 }
783 if (rotateHandle) {
784 statusText = i18n("Left click rotates around center, right click around highlighted position.");
785 }
786 if (shearHandle) {
787 statusText = i18n("Click and drag to shear selection.");
788 }
789
790
791 } else {
792 statusText = i18n("Click and drag to resize selection.");
794 int rotOctant = 8 + int(8.5 + m_angle / 45);
795 bool cornerHandle = false;
796 switch (m_lastHandle) {
798 cursor = m_sizeCursors[(0 + rotOctant) % 8];
799 break;
801 cursor = m_sizeCursors[(1 + rotOctant) % 8];
802 cornerHandle = true;
803 break;
805 cursor = m_sizeCursors[(2 + rotOctant) % 8];
806 break;
808 cursor = m_sizeCursors[(3 + rotOctant) % 8];
809 cornerHandle = true;
810 break;
812 cursor = m_sizeCursors[(4 + rotOctant) % 8];
813 break;
815 cursor = m_sizeCursors[(5 + rotOctant) % 8];
816 cornerHandle = true;
817 break;
819 cursor = m_sizeCursors[(6 + rotOctant) % 8];
820 break;
822 cursor = m_sizeCursors[(7 + rotOctant) % 8];
823 cornerHandle = true;
824 break;
826 cursor = Qt::SizeAllCursor;
827 statusText = i18n("Click and drag to move selection.");
828 break;
829 }
830 if (cornerHandle) {
831 statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position.");
832 }
833 }
834 if (!editable) {
835 cursor = Qt::ArrowCursor;
836 }
837 } else {
838 // there used to be guides... :'''(
839 }
841 if (currentStrategy() == 0) {
842 Q_EMIT statusTextChanged(statusText);
843 }
844}
845
846void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter)
847{
849 if (selection) {
850 m_decorator.reset(new SelectionDecorator(canvas()->resourceManager()));
851
852 {
858 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
859 KisNodeSP node = kisCanvas->viewManager()->nodeManager()->activeNode();
860 const bool isSelectionMask = node && node->inherits("KisSelectionMask");
861 m_decorator->setForceShapeOutlines(isSelectionMask);
862 }
863
864 m_decorator->setSelection(selection);
865 m_decorator->setHandleRadius(handleRadius());
866 m_decorator->setDecorationThickness(decorationThickness());
867 m_decorator->setShowFillGradientHandles(hasInteractionFactory(EditFillGradientFactoryId));
868 m_decorator->setShowStrokeFillGradientHandles(hasInteractionFactory(EditStrokeGradientFactoryId));
869 m_decorator->setShowFillMeshGradientHandles(hasInteractionFactory(EditFillMeshGradientFactoryId));
870 m_decorator->setCurrentMeshGradientHandles(m_selectedMeshHandle, m_hoveredMeshHandle);
871 m_decorator->paint(painter, converter);
872 }
873
874 KoInteractionTool::paint(painter, converter);
875
876 painter.save();
877 painter.setTransform(converter.documentToView(), true);
878 canvas()->snapGuide()->paint(painter, converter);
879 painter.restore();
880}
881
883{
884 // if the currently active node has a shape manager, then it is
885 // probably our client :)
886
887 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
888 return bool(kisCanvas->localShapeManager());
889}
890
894
896{
897 // this tool only works on a vector layer right now, so give a warning if another layer type is trying to use it
898 if (!isValidForCurrentLayer()) {
899 KisCanvas2 *kiscanvas = static_cast<KisCanvas2 *>(canvas());
900 kiscanvas->viewManager()->showFloatingMessage(
901 i18n("This tool only works on vector layers. You probably want the move tool."),
902 QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter);
903 return;
904 }
905
907 updateCursor();
908}
909
911{
913 if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) {
914 QRectF bound = handlesSize();
915
916 if (bound.contains(event->point)) {
917 bool inside;
918 KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside);
919
920 if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) {
921 m_lastHandle = newDirection;
923 }
924 } else {
927
928 // there used to be guides... :'''(
929 }
930 } else {
931 // there used to be guides... :'''(
932 }
933
934
935 updateCursor();
936}
937
939{
941 if (!selection || !selection->count()) return QRectF();
942
944
945 QRectF bound = m_selectionOutline.boundingRect();
946
947 // expansion Border
948 if (!canvas() || !canvas()->viewConverter()) {
949 return bound;
950 }
951
952 QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE));
953 bound.adjust(-border.x(), -border.y(), border.x(), border.y());
954 return bound;
955}
956
962
964{
966
968 if (shape && selection && !selection->isSelected(shape)) {
969
970 if (!(event->modifiers() & Qt::ShiftModifier)) {
971 selection->deselectAll();
972 }
973
974 selection->select(shape);
975 }
976
978}
979
980bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers)
981{
982 bool result = false;
983
984 qreal x = 0.0, y = 0.0;
985 if (direction == Qt::Key_Left) {
986 x = -5;
987 } else if (direction == Qt::Key_Right) {
988 x = 5;
989 } else if (direction == Qt::Key_Up) {
990 y = -5;
991 } else if (direction == Qt::Key_Down) {
992 y = 5;
993 }
994
995 if (x != 0.0 || y != 0.0) { // actually move
996
997 if ((modifiers & Qt::ShiftModifier) != 0) {
998 x *= 10;
999 y *= 10;
1000 } else if ((modifiers & Qt::AltModifier) != 0) { // more precise
1001 x /= 5;
1002 y /= 5;
1003 }
1004
1006
1007 if (!shapes.isEmpty()) {
1008 canvas()->addCommand(new KoShapeMoveCommand(shapes, QPointF(x, y)));
1009 result = true;
1010 }
1011 }
1012
1013 return result;
1014}
1015
1016void DefaultTool::keyPressEvent(QKeyEvent *event)
1017{
1019 if (currentStrategy() == 0) {
1020 switch (event->key()) {
1021 case Qt::Key_Left:
1022 case Qt::Key_Right:
1023 case Qt::Key_Up:
1024 case Qt::Key_Down:
1025 if (moveSelection(event->key(), event->modifiers())) {
1026 event->accept();
1027 }
1028 break;
1029 default:
1030 return;
1031 }
1032 }
1033}
1034
1036{
1037 QRectF dirtyRect;
1038
1039 if (koSelection() && koSelection()->count() > 0) {
1043 dirtyRect = const_cast<DefaultTool*>(this)->handlesSize();
1044 }
1045
1046 if (canvas()->snapGuide()->isSnapping()) {
1047 dirtyRect |= canvas()->snapGuide()->boundingRect();
1048 }
1049
1050 return dirtyRect;
1051}
1052
1054{
1055 // all the selected shapes, not only editable!
1057
1058 if (!shapes.isEmpty()) {
1059 KoDrag drag;
1060 drag.setSvg(shapes);
1061 drag.addToClipboard();
1062 }
1063}
1064
1066{
1067 QList<KoShape *> shapes;
1068 foreach (KoShape *s, koSelection()->selectedShapes()) {
1069 if (s->isGeometryProtected()) {
1070 continue;
1071 }
1072 shapes << s;
1073 }
1074 if (!shapes.empty()) {
1075 canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes));
1076 }
1077}
1078
1080{
1081 // we no longer have to do anything as tool Proxy will do it for us
1082 return false;
1083}
1084
1086{
1087 Q_ASSERT(canvas());
1088 Q_ASSERT(canvas()->selectedShapesProxy());
1089 Q_FOREACH(KoShape *shape, canvas()->shapeManager()->shapes()) {
1090 if (!shape->isSelectable()) continue;
1092 }
1094
1095 return true;
1096}
1097
1099{
1100 Q_ASSERT(canvas());
1101 Q_ASSERT(canvas()->selectedShapesProxy());
1104}
1105
1107{
1108 Q_ASSERT(canvas());
1109 Q_ASSERT(canvas()->selectedShapesProxy());
1110 return canvas()->selectedShapesProxy()->selection();
1111}
1112
1113KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning)
1114{
1115 // check for handles in this order; meaning that when handles overlap the one on top is chosen
1116 static const KoFlake::SelectionHandle handleOrder[] = {
1126 };
1127
1128 const KoViewConverter *converter = canvas()->viewConverter();
1130
1131 if (!selection || !selection->count() || !converter) {
1132 return KoFlake::NoHandle;
1133 }
1134
1136
1137 if (innerHandleMeaning) {
1138 QPainterPath path;
1139 path.addPolygon(m_selectionOutline);
1140 *innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point));
1141 }
1142
1143 const QPointF viewPoint = converter->documentToView(point);
1144
1145 for (int i = 0; i < KoFlake::NoHandle; ++i) {
1146 KoFlake::SelectionHandle handle = handleOrder[i];
1147
1148 const QPointF handlePoint = converter->documentToView(m_selectionBox[handle]);
1149 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
1150
1151 // if just inside the outline
1152 if (distanceSq < HANDLE_DISTANCE_SQ) {
1153
1154 if (innerHandleMeaning) {
1155 if (distanceSq < INNER_HANDLE_DISTANCE_SQ) {
1156 *innerHandleMeaning = true;
1157 }
1158 }
1159
1160 return handle;
1161 }
1162 }
1163 return KoFlake::NoHandle;
1164}
1165
1167{
1169
1170 QTransform matrix = selection->absoluteTransformation();
1171 m_selectionOutline = matrix.map(QPolygonF(selection->outlineRect()));
1172 m_angle = 0.0;
1173
1174 QPolygonF outline = m_selectionOutline; //shorter name in the following :)
1175 m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2;
1176 m_selectionBox[KoFlake::TopRightHandle] = outline.value(1);
1177 m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2;
1178 m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2);
1179 m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2;
1180 m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3);
1181 m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2;
1182 m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0);
1183 if (selection->count() == 1) {
1184#if 0 // TODO detect mirroring
1186
1187 if (s->scaleX() < 0) { // vertically mirrored: swap left / right
1191 }
1192 if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom
1196 }
1197#endif
1198 }
1199}
1200
1201void DefaultTool::activate(const QSet<KoShape *> &shapes)
1202{
1203 KoToolBase::activate(shapes);
1204
1205 QAction *actionBringToFront = action("object_order_front");
1206 connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront()), Qt::UniqueConnection);
1207
1208 QAction *actionRaise = action("object_order_raise");
1209 connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp()), Qt::UniqueConnection);
1210
1211 QAction *actionLower = action("object_order_lower");
1212 connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown()));
1213
1214 QAction *actionSendToBack = action("object_order_back");
1215 connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack()), Qt::UniqueConnection);
1216
1217 QAction *actionGroupBottom = action("object_group");
1218 connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup()), Qt::UniqueConnection);
1219
1220 QAction *actionUngroupBottom = action("object_ungroup");
1221 connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup()), Qt::UniqueConnection);
1222
1223 QAction *actionSplit = action("object_split");
1224 connect(actionSplit, SIGNAL(triggered()), this, SLOT(selectionSplitShapes()), Qt::UniqueConnection);
1225
1226 connect(m_alignSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionAlign(int)));
1227 connect(m_distributeSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionDistribute(int)));
1228 connect(m_transformSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionTransform(int)));
1229 connect(m_booleanSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionBooleanOp(int)));
1230 connect(m_textTypeSignalsMapper, SIGNAL(mapped(int)), SLOT(slotChangeTextType(int)));
1231
1234 useCursor(Qt::ArrowCursor);
1236 updateActions();
1237
1238 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->canvas());
1239 if (canvas2) {
1242 }
1243
1246 }
1247}
1248
1250{
1252
1253 QAction *actionBringToFront = action("object_order_front");
1254 disconnect(actionBringToFront, 0, this, 0);
1255
1256 QAction *actionRaise = action("object_order_raise");
1257 disconnect(actionRaise, 0, this, 0);
1258
1259 QAction *actionLower = action("object_order_lower");
1260 disconnect(actionLower, 0, this, 0);
1261
1262 QAction *actionSendToBack = action("object_order_back");
1263 disconnect(actionSendToBack, 0, this, 0);
1264
1265 QAction *actionGroupBottom = action("object_group");
1266 disconnect(actionGroupBottom, 0, this, 0);
1267
1268 QAction *actionUngroupBottom = action("object_ungroup");
1269 disconnect(actionUngroupBottom, 0, this, 0);
1270
1271 QAction *actionSplit = action("object_split");
1272 disconnect(actionSplit, 0, this, 0);
1273
1274 disconnect(m_alignSignalsMapper, 0, this, 0);
1275 disconnect(m_distributeSignalsMapper, 0, this, 0);
1276 disconnect(m_transformSignalsMapper, 0, this, 0);
1277 disconnect(m_booleanSignalsMapper, 0, this, 0);
1278 disconnect(m_textTypeSignalsMapper, 0, this, 0);
1279
1280 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->canvas());
1281 if (canvas2) {
1284 }
1285
1288 }
1289}
1290
1292{
1294 if (!selection) return;
1295
1296 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1297 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
1298 if (selectedShapes.isEmpty()) return;
1299
1300 const int groupZIndex = selectedShapes.last()->zIndex();
1301
1302 KoShapeGroup *group = new KoShapeGroup();
1303 group->setZIndex(groupZIndex);
1304 // TODO what if only one shape is left?
1305 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes"));
1306 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1307 canvas()->shapeController()->addShapeDirect(group, 0, cmd);
1308 new KoShapeGroupCommand(group, selectedShapes, true, cmd);
1309 new KoKeepShapesSelectedCommand({}, {group}, canvas()->selectedShapesProxy(), true, cmd);
1310 canvas()->addCommand(cmd);
1311
1312 // update selection so we can ungroup immediately again
1313 selection->deselectAll();
1314 selection->select(group);
1315}
1316
1318{
1320 if (!selection) return;
1321
1322 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1323 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
1324
1325 KUndo2Command *cmd = 0;
1326 QList<KoShape*> newShapes;
1327
1328 // add a ungroup command for each found shape container to the macro command
1329 Q_FOREACH (KoShape *shape, selectedShapes) {
1330 KoShapeGroup *group = dynamic_cast<KoShapeGroup *>(shape);
1331 if (group) {
1332 if (!cmd) {
1333 cmd = new KUndo2Command(kundo2_i18n("Ungroup shapes"));
1334 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1335 }
1336 newShapes << group->shapes();
1337 new KoShapeUngroupCommand(group, group->shapes(),
1339 cmd);
1340 canvas()->shapeController()->removeShape(group, cmd);
1341 }
1342 }
1343 if (cmd) {
1344 new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
1345 canvas()->addCommand(cmd);
1346 }
1347}
1348
1349void DefaultTool::selectionTransform(int transformAction)
1350{
1352 if (!selection) return;
1353
1354 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1355 if (editableShapes.isEmpty()) {
1356 return;
1357 }
1358
1359 QTransform applyTransform;
1360 bool shouldReset = false;
1361 KUndo2MagicString actionName = kundo2_noi18n("BUG: No transform action");
1362
1363
1364 switch (TransformActionType(transformAction)) {
1365 case TransformRotate90CW:
1366 applyTransform.rotate(90.0);
1367 actionName = kundo2_i18n("Rotate Object 90° CW");
1368 break;
1369 case TransformRotate90CCW:
1370 applyTransform.rotate(-90.0);
1371 actionName = kundo2_i18n("Rotate Object 90° CCW");
1372 break;
1373 case TransformRotate180:
1374 applyTransform.rotate(180.0);
1375 actionName = kundo2_i18n("Rotate Object 180°");
1376 break;
1377 case TransformMirrorX:
1378 applyTransform.scale(-1.0, 1.0);
1379 actionName = kundo2_i18n("Mirror Object Horizontally");
1380 break;
1381 case TransformMirrorY:
1382 applyTransform.scale(1.0, -1.0);
1383 actionName = kundo2_i18n("Mirror Object Vertically");
1384 break;
1385 case TransformReset:
1386 shouldReset = true;
1387 actionName = kundo2_i18n("Reset Object Transformations");
1388 break;
1389 }
1390
1391 if (!shouldReset && applyTransform.isIdentity()) return;
1392
1393 QList<QTransform> oldTransforms;
1394 QList<QTransform> newTransforms;
1395
1396 const QRectF outlineRect = KoShape::absoluteOutlineRect(editableShapes);
1397 const QPointF centerPoint = outlineRect.center();
1398 const QTransform centerTrans = QTransform::fromTranslate(centerPoint.x(), centerPoint.y());
1399 const QTransform centerTransInv = QTransform::fromTranslate(-centerPoint.x(), -centerPoint.y());
1400
1401 // we also add selection to the list of transformed shapes, so that its outline is updated correctly
1402 QList<KoShape*> transformedShapes = editableShapes;
1403 transformedShapes << selection;
1404
1405 Q_FOREACH (KoShape *shape, transformedShapes) {
1406 oldTransforms.append(shape->transformation());
1407
1408 QTransform t;
1409
1410 if (!shouldReset) {
1411 const QTransform world = shape->absoluteTransformation();
1412 t = world * centerTransInv * applyTransform * centerTrans * world.inverted() * shape->transformation();
1413 } else {
1414 const QPointF center = shape->outlineRect().center();
1415 const QPointF offset = shape->transformation().map(center) - center;
1416 t = QTransform::fromTranslate(offset.x(), offset.y());
1417 }
1418
1419 newTransforms.append(t);
1420 }
1421
1422 KoShapeTransformCommand *cmd = new KoShapeTransformCommand(transformedShapes, oldTransforms, newTransforms);
1423 cmd->setText(actionName);
1424 canvas()->addCommand(cmd);
1425}
1426
1428{
1430 if (!selection) return;
1431
1432 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1433 if (editableShapes.isEmpty()) {
1434 return;
1435 }
1436
1437 QVector<QPainterPath> srcOutlines;
1438 QPainterPath dstOutline;
1439 KUndo2MagicString actionName = kundo2_noi18n("BUG: boolean action name");
1440
1441 // TODO: implement a reference shape selection dialog!
1442 const int referenceShapeIndex = 0;
1443 KoShape *referenceShape = editableShapes[referenceShapeIndex];
1444
1445 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
1447 const QTransform booleanWorkaroundTransform =
1449
1450 Q_FOREACH (KoShape *shape, editableShapes) {
1451 srcOutlines <<
1452 booleanWorkaroundTransform.map(
1453 shape->absoluteTransformation().map(
1454 shape->outline()));
1455 }
1456
1457 if (booleanOp == BooleanUnion) {
1458 Q_FOREACH (const QPainterPath &path, srcOutlines) {
1459 dstOutline |= path;
1460 }
1461 actionName = kundo2_i18n("Unite Shapes");
1462 } else if (booleanOp == BooleanIntersection) {
1463 for (int i = 0; i < srcOutlines.size(); i++) {
1464 if (i == 0) {
1465 dstOutline = srcOutlines[i];
1466 } else {
1467 dstOutline &= srcOutlines[i];
1468 }
1469 }
1470
1471 // there is a bug in Qt, sometimes it leaves the resulting
1472 // outline open, so just close it explicitly.
1473 dstOutline.closeSubpath();
1474
1475 actionName = kundo2_i18n("Intersect Shapes");
1476
1477 } else if (booleanOp == BooleanSubtraction) {
1478 for (int i = 0; i < srcOutlines.size(); i++) {
1479 dstOutline = srcOutlines[referenceShapeIndex];
1480 if (i != referenceShapeIndex) {
1481 dstOutline -= srcOutlines[i];
1482 }
1483 }
1484
1485 actionName = kundo2_i18n("Subtract Shapes");
1486 }
1487
1488 dstOutline = booleanWorkaroundTransform.inverted().map(dstOutline);
1489
1490 KoShape *newShape = 0;
1491
1492 if (!dstOutline.isEmpty()) {
1493 newShape = KoPathShape::createShapeFromPainterPath(dstOutline);
1494 }
1495
1496 KUndo2Command *cmd = new KUndo2Command(actionName);
1497
1498 new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1499
1500 QList<KoShape*> newSelectedShapes;
1501
1502 if (newShape) {
1503 newShape->setBackground(referenceShape->background());
1504 newShape->setStroke(referenceShape->stroke());
1505 newShape->setZIndex(referenceShape->zIndex());
1506
1507 KoShapeContainer *parent = referenceShape->parent();
1508 canvas()->shapeController()->addShapeDirect(newShape, parent, cmd);
1509
1510 newSelectedShapes << newShape;
1511 }
1512
1513 canvas()->shapeController()->removeShapes(editableShapes, cmd);
1514
1515 new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(), true, cmd);
1516
1517 canvas()->addCommand(cmd);
1518}
1519
1521{
1523 if (!selection) return;
1524
1525 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1526 if (editableShapes.isEmpty()) {
1527 return;
1528 }
1529
1530 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Split Shapes"));
1531
1532 new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1533 QList<KoShape*> newShapes;
1534
1535 Q_FOREACH (KoShape *shape, editableShapes) {
1536 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
1537 if (!pathShape) return;
1538
1539 QList<KoPathShape*> splitShapes;
1540 if (pathShape->separate(splitShapes)) {
1541 QList<KoShape*> normalShapes = implicitCastList<KoShape*>(splitShapes);
1542
1543 KoShapeContainer *parent = shape->parent();
1544 canvas()->shapeController()->addShapesDirect(normalShapes, parent, cmd);
1545 canvas()->shapeController()->removeShape(shape, cmd);
1546 newShapes << normalShapes;
1547 }
1548 }
1549
1550 new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
1551
1552 canvas()->addCommand(cmd);
1553}
1554
1556{
1558 static_cast<KoShapeAlignCommand::Align>(_align);
1559
1561 if (!selection) return;
1562
1563 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1564 if (editableShapes.isEmpty()) {
1565 return;
1566 }
1567
1568 // TODO add an option to the widget so that one can align to the page
1569 // with multiple selected shapes too
1570
1571 QRectF bb;
1572
1573 // single selected shape is automatically aligned to document rect
1574 if (editableShapes.count() == 1) {
1575 if (!canvas()->resourceManager()->hasResource(KoCanvasResource::PageSize)) {
1576 return;
1577 }
1578 bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResource::PageSize));
1579 } else {
1580 bb = KoShape::absoluteOutlineRect(editableShapes);
1581 }
1582
1583 KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb);
1584 canvas()->addCommand(cmd);
1585}
1586
1588{
1590 static_cast<KoShapeDistributeCommand::Distribute>(_distribute);
1591
1593 if (!selection) return;
1594
1595 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1596 if (editableShapes.size() < 3) {
1597 return;
1598 }
1599
1600 QRectF bb = KoShape::absoluteOutlineRect(editableShapes);
1601 KoShapeDistributeCommand *cmd = new KoShapeDistributeCommand(editableShapes, distribute, bb);
1602 canvas()->addCommand(cmd);
1603}
1604
1609
1614
1619
1624
1626{
1628 if (!selection) {
1629 return;
1630 }
1631
1632 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1633 if (selectedShapes.isEmpty()) {
1634 return;
1635 }
1636
1637 KUndo2Command *cmd = KoShapeReorderCommand::createCommand(selectedShapes, shapeManager(), order);
1638 if (cmd) {
1639 canvas()->addCommand(cmd);
1640 }
1641}
1642
1644{
1645 QList<QPointer<QWidget> > widgets;
1646
1648
1649 if (isActivated()) {
1651 }
1652 widgets.append(m_tabbedOptionWidget);
1653
1655 SIGNAL(sigSwitchModeEditFillGradient(bool)),
1656 SLOT(slotActivateEditFillGradient(bool)));
1657
1659 SIGNAL(sigSwitchModeEditStrokeGradient(bool)),
1660 SLOT(slotActivateEditStrokeGradient(bool)));
1661
1663 SIGNAL(sigSwitchModeEditFillGradient(bool)),
1665 // TODO: strokes!!
1666
1668 SIGNAL(sigMeshGradientResetted()),
1670
1671 return widgets;
1672}
1673
1674void DefaultTool::canvasResourceChanged(int key, const QVariant &res)
1675{
1676 if (key == HotPosition) {
1679 }
1680}
1681
1683{
1685 if (!selection) return nullptr;
1686
1687 bool insideSelection = false;
1688 KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection);
1689
1690 bool editableShape = !selection->selectedEditableShapes().isEmpty();
1691
1692 const bool selectMultiple = event->modifiers() & Qt::ShiftModifier;
1693 const bool selectNextInStack = event->modifiers() & Qt::ControlModifier;
1694 const bool avoidSelection = event->modifiers() & Qt::AltModifier;
1695
1696 if (selectNextInStack) {
1697 // change the hot selection position when middle clicking on a handle
1698 KoFlake::AnchorPosition newHotPosition = m_hotPosition;
1699 switch (handle) {
1701 newHotPosition = KoFlake::Top;
1702 break;
1704 newHotPosition = KoFlake::TopRight;
1705 break;
1707 newHotPosition = KoFlake::Right;
1708 break;
1710 newHotPosition = KoFlake::BottomRight;
1711 break;
1713 newHotPosition = KoFlake::Bottom;
1714 break;
1716 newHotPosition = KoFlake::BottomLeft;
1717 break;
1719 newHotPosition = KoFlake::Left;
1720 break;
1722 newHotPosition = KoFlake::TopLeft;
1723 break;
1724 case KoFlake::NoHandle:
1725 default:
1726 // check if we had hit the center point
1727 const KoViewConverter *converter = canvas()->viewConverter();
1728 QPointF pt = converter->documentToView(event->point);
1729
1730 // TODO: use calculated values instead!
1731 QPointF centerPt = converter->documentToView(selection->absolutePosition());
1732
1733 if (kisSquareDistance(pt, centerPt) < HANDLE_DISTANCE_SQ) {
1734 newHotPosition = KoFlake::Center;
1735 }
1736
1737 break;
1738 }
1739
1740 if (m_hotPosition != newHotPosition) {
1741 canvas()->resourceManager()->setResource(HotPosition, newHotPosition);
1742 return new NopInteractionStrategy(this);
1743 }
1744 }
1745
1746 if (!avoidSelection && editableShape) {
1747 // manipulation of selected shapes goes first
1748 if (handle != KoFlake::NoHandle) {
1749 // resizing or shearing only with left mouse button
1750 if (insideSelection) {
1751 bool forceUniformScaling = m_tabbedOptionWidget && m_tabbedOptionWidget->useUniformScaling();
1752 return new ShapeResizeStrategy(this, selection, event->point, handle, forceUniformScaling);
1753 }
1754
1755 if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle ||
1757
1758 return new ShapeShearStrategy(this, selection, event->point, handle);
1759 }
1760
1761 // rotating is allowed for right mouse button too
1762 if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle ||
1764
1765 return new ShapeRotateStrategy(this, selection, event->point, event->buttons());
1766 }
1767 }
1768
1769 if (!selectMultiple && !selectNextInStack) {
1770
1771 if (insideSelection) {
1772 return new ShapeMoveStrategy(this, selection, event->point);
1773 }
1774 }
1775 }
1776
1777 KoShape *shape = shapeManager()->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop);
1778
1779 if (avoidSelection || (!shape && handle == KoFlake::NoHandle)) {
1780 if (!selectMultiple) {
1781 selection->deselectAll();
1782 }
1783 return new SelectionInteractionStrategy(this, event->point, false);
1784 }
1785
1786 if (selection->isSelected(shape)) {
1787 if (selectMultiple) {
1788 selection->deselect(shape);
1789 }
1790 } else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected
1791 if (!selectMultiple) {
1792 selection->deselectAll();
1793 }
1794 selection->select(shape);
1795 // tablet selection isn't precise and may lead to a move, preventing that
1796 if (event->isTabletEvent()) {
1797 return new NopInteractionStrategy(this);
1798 }
1799 return new ShapeMoveStrategy(this, selection, event->point);
1800 }
1801 return 0;
1802}
1803
1805{
1806 QList<KoShape*> editableShapes;
1807
1808 if (koSelection()) {
1809 editableShapes = koSelection()->selectedEditableShapes();
1810 }
1811
1812 const bool hasEditableShapes = !editableShapes.isEmpty();
1813
1814 action("object_order_front")->setEnabled(hasEditableShapes);
1815 action("object_order_raise")->setEnabled(hasEditableShapes);
1816 action("object_order_lower")->setEnabled(hasEditableShapes);
1817 action("object_order_back")->setEnabled(hasEditableShapes);
1818
1819 action("object_transform_rotate_90_cw")->setEnabled(hasEditableShapes);
1820 action("object_transform_rotate_90_ccw")->setEnabled(hasEditableShapes);
1821 action("object_transform_rotate_180")->setEnabled(hasEditableShapes);
1822 action("object_transform_mirror_horizontally")->setEnabled(hasEditableShapes);
1823 action("object_transform_mirror_vertically")->setEnabled(hasEditableShapes);
1824 action("object_transform_reset")->setEnabled(hasEditableShapes);
1825
1826 const bool multipleSelected = editableShapes.size() > 1;
1827
1828 const bool alignmentEnabled =
1829 multipleSelected ||
1830 (!editableShapes.isEmpty() &&
1832
1833 action("object_align_horizontal_left")->setEnabled(alignmentEnabled);
1834 action("object_align_horizontal_center")->setEnabled(alignmentEnabled);
1835 action("object_align_horizontal_right")->setEnabled(alignmentEnabled);
1836 action("object_align_vertical_top")->setEnabled(alignmentEnabled);
1837 action("object_align_vertical_center")->setEnabled(alignmentEnabled);
1838 action("object_align_vertical_bottom")->setEnabled(alignmentEnabled);
1839
1840 const bool distributionEnabled = editableShapes.size() > 2;
1841
1842 action("object_distribute_horizontal_left")->setEnabled(distributionEnabled);
1843 action("object_distribute_horizontal_center")->setEnabled(distributionEnabled);
1844 action("object_distribute_horizontal_right")->setEnabled(distributionEnabled);
1845 action("object_distribute_horizontal_gaps")->setEnabled(distributionEnabled);
1846
1847 action("object_distribute_vertical_top")->setEnabled(distributionEnabled);
1848 action("object_distribute_vertical_center")->setEnabled(distributionEnabled);
1849 action("object_distribute_vertical_bottom")->setEnabled(distributionEnabled);
1850 action("object_distribute_vertical_gaps")->setEnabled(distributionEnabled);
1851
1852 updateDistinctiveActions(editableShapes);
1853
1854 Q_EMIT selectionChanged(editableShapes.size());
1855}
1856
1858 const bool multipleSelected = editableShapes.size() > 1;
1859
1860 action("object_group")->setEnabled(multipleSelected);
1861
1862 action("object_unite")->setEnabled(multipleSelected);
1863 action("object_intersect")->setEnabled(multipleSelected);
1864 action("object_subtract")->setEnabled(multipleSelected);
1865
1866 bool hasShapesWithMultipleSegments = false;
1867 Q_FOREACH (KoShape *shape, editableShapes) {
1868 KoPathShape *pathShape = dynamic_cast<KoPathShape *>(shape);
1869 if (pathShape && pathShape->subpathCount() > 1) {
1870 hasShapesWithMultipleSegments = true;
1871 break;
1872 }
1873 }
1874 action("object_split")->setEnabled(hasShapesWithMultipleSegments);
1875
1876
1877 bool hasGroupShape = false;
1878 foreach (KoShape *shape, editableShapes) {
1879 if (dynamic_cast<KoShapeGroup *>(shape)) {
1880 hasGroupShape = true;
1881 break;
1882 }
1883 }
1884 action("object_ungroup")->setEnabled(hasGroupShape);
1885
1886 bool enablePreformatted = false;
1887 bool enablePrePositioned = false;
1888 bool enableInlineWrapped = false;
1889 Q_FOREACH (KoShape *shape, editableShapes) {
1890 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape *>(shape);
1891 if (textShape) {
1892 if (textShape->textType() != KoSvgTextShape::PreformattedText && !enablePreformatted) {
1893 enablePreformatted = true;
1894 }
1895 if (textShape && textShape->textType() != KoSvgTextShape::PrePositionedText && !enablePrePositioned) {
1896 enablePrePositioned = true;
1897 }
1898 if (textShape && textShape->textType() != KoSvgTextShape::PrePositionedText && !enableInlineWrapped) {
1899 enableInlineWrapped = true;
1900 }
1901 }
1902 }
1903 action("text_type_preformatted")->setEnabled(enablePreformatted);
1904 action("text_type_pre_positioned")->setEnabled(enablePrePositioned);
1905 action("text_type_inline_wrap")->setEnabled(enableInlineWrapped);
1906}
1907
1908
1913
1915{
1916 if (m_contextMenu) {
1917 m_contextMenu->clear();
1918
1919 m_contextMenu->addSection(i18n("Vector Shape Actions"));
1920 m_contextMenu->addSeparator();
1921
1922 QMenu *transform = m_contextMenu->addMenu(i18n("Transform"));
1923
1924 transform->addAction(action("object_transform_rotate_90_cw"));
1925 transform->addAction(action("object_transform_rotate_90_ccw"));
1926 transform->addAction(action("object_transform_rotate_180"));
1927 transform->addSeparator();
1928 transform->addAction(action("object_transform_mirror_horizontally"));
1929 transform->addAction(action("object_transform_mirror_vertically"));
1930 transform->addSeparator();
1931 transform->addAction(action("object_transform_reset"));
1932
1933 if (action("object_unite")->isEnabled() ||
1934 action("object_intersect")->isEnabled() ||
1935 action("object_subtract")->isEnabled() ||
1936 action("object_split")->isEnabled()) {
1937
1938 QMenu *transform = m_contextMenu->addMenu(i18n("Logical Operations"));
1939 transform->addAction(action("object_unite"));
1940 transform->addAction(action("object_intersect"));
1941 transform->addAction(action("object_subtract"));
1942 transform->addAction(action("object_split"));
1943 }
1944
1945 m_contextMenu->addSeparator();
1946
1947 m_contextMenu->addAction(action("edit_cut"));
1948 m_contextMenu->addAction(action("edit_copy"));
1949 m_contextMenu->addAction(action("edit_paste"));
1950 m_contextMenu->addAction(action("paste_at"));
1951
1952 m_contextMenu->addSeparator();
1953
1954 m_contextMenu->addAction(action("object_order_front"));
1955 m_contextMenu->addAction(action("object_order_raise"));
1956 m_contextMenu->addAction(action("object_order_lower"));
1957 m_contextMenu->addAction(action("object_order_back"));
1958
1959 if (action("object_group")->isEnabled() || action("object_ungroup")->isEnabled()) {
1960 m_contextMenu->addSeparator();
1961 m_contextMenu->addAction(action("object_group"));
1962 m_contextMenu->addAction(action("object_ungroup"));
1963 }
1964 m_contextMenu->addSeparator();
1965 m_contextMenu->addAction(action("convert_shapes_to_vector_selection"));
1966 }
1967
1968 return m_contextMenu.data();
1969}
1970
1971void DefaultTool::addTransformActions(QMenu *menu) const {
1972 menu->addAction(action("object_transform_rotate_90_cw"));
1973 menu->addAction(action("object_transform_rotate_90_ccw"));
1974 menu->addAction(action("object_transform_rotate_180"));
1975 menu->addSeparator();
1976 menu->addAction(action("object_transform_mirror_horizontally"));
1977 menu->addAction(action("object_transform_mirror_vertically"));
1978 menu->addSeparator();
1979 menu->addAction(action("object_transform_reset"));
1980}
1981
1983{
1985 QString tool = KoToolManager::instance()->preferredToolForSelection(shapes);
1986 QTimer::singleShot(0, [tool = std::move(tool)]() {
1988 });
1989}
1990
1992
1994 : parent(parent)
1995 , compressor(10, KisSignalCompressor::POSTPONE){}
1996
2000};
2001
2004 , d(new Private(parent))
2005{
2006 connect(&d->compressor, SIGNAL(timeout()), this, SIGNAL(textSelectionChanged()));
2007}
2008
2013
2015{
2017 if (!d->parent->selection()->hasSelection()) return props;
2018
2019 for (auto it = d->shapes.begin(); it != d->shapes.end(); it++) {
2020 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(*it);
2021 if (!textShape) continue;
2022 KoSvgTextProperties p = textShape->textProperties();
2023 props.append(p);
2024 }
2025
2026 return props;
2027}
2028
2033
2034void DefaultToolTextPropertiesInterface::setPropertiesOnSelected(KoSvgTextProperties properties, QSet<KoSvgTextProperties::PropertyId> removeProperties)
2035{
2036 if (d->shapes.isEmpty()) return;
2037 KUndo2Command *cmd = new KoShapeMergeTextPropertiesCommand(d->shapes, properties, removeProperties);
2038 if (cmd) {
2039 d->parent->canvas()->addCommand(cmd);
2040 }
2041}
2042
2044{
2045 return false;
2046}
2047
2049{
2050 Q_UNUSED(pos)
2051 Q_UNUSED(anchor)
2052 d->compressor.start();
2053}
2054
2056{
2057 d->compressor.start();
2058}
2059
2061{
2062 Q_UNUSED(type)
2063 Q_UNUSED(shape)
2064 d->compressor.start();
2065}
2066
2068{
2069 Q_FOREACH(KoShape *shape, d->shapes) {
2070 shape->removeShapeChangeListener(this);
2071 }
2072 d->shapes.clear();
2073}
2074
2076{
2077 Q_FOREACH(KoShape *shape, d->shapes) {
2078 shape->removeShapeChangeListener(this);
2079 }
2080 QList<KoShape*> shapes = d->parent->canvas()->selectedShapesProxy()->selection()->selectedEditableShapes();
2081 d->shapes = shapes;
2082
2083 Q_FOREACH(KoShape *shape, d->shapes) {
2084 shape->addShapeChangeListener(this);
2085 }
2086 d->compressor.start();
2087}
#define HANDLE_DISTANCE
#define INNER_HANDLE_DISTANCE_SQ
#define HANDLE_DISTANCE_SQ
float value(const T *src, size_t ch)
const Params2D p
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KoShapeGradientHandles::Handle m_currentHandle
bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
MoveGradientHandleInteractionFactory(KoFlake::FillVariant fillVariant, int priority, const QString &id, DefaultTool *_q)
KoInteractionStrategy * createStrategy(KoPointerEvent *ev) override
KoShapeGradientHandles::Handle handleAt(const QPointF &pos)
bool hoverEvent(KoPointerEvent *ev) override
KoInteractionStrategy * createStrategy(KoPointerEvent *ev) override
KoShapeMeshGradientHandles::Handle handleAt(const QPointF &pos) const
MoveMeshGradientHandleInteractionFactory(KoFlake::FillVariant fillVariant, int priority, const QString &id, DefaultTool *_q)
bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
KoShapeMeshGradientHandles::Handle m_currentHandle
virtual void updateDistinctiveActions(const QList< KoShape * > &editableShapes)
void addMappedAction(KisSignalMapper *mapper, const QString &actionId, int type)
KoFlake::SelectionHandle m_lastHandle
virtual bool isValidForCurrentLayer() const
void selectionSendToBack()
void explicitUserStrokeEndRequest() override
explicitUserStrokeEndRequest is called by the input manager when the user presses Enter key or any eq...
void slotActivateEditStrokeGradient(bool value)
void canvasResourceChanged(int key, const QVariant &res) override
bool m_mouseWasInsideHandles
QScopedPointer< QMenu > m_contextMenu
KoFlake::AnchorPosition m_hotPosition
KisSignalMapper * m_textTypeSignalsMapper
void deleteSelection() override
reimplemented
void selectionDistribute(int _distribute)
bool paste() override
reimplemented
void deselect() override
reimplemented
KisSignalMapper * m_booleanSignalsMapper
void updateActions()
Update actions on selection change.
KisSignalMapper * m_distributeSignalsMapper
QCursor m_shearCursors[8]
KoFlake::SelectionHandle handleAt(const QPointF &point, bool *innerHandleMeaning=0)
void keyPressEvent(QKeyEvent *event) override
~DefaultTool() override
QCursor m_sizeCursors[8]
bool moveSelection(int direction, Qt::KeyboardModifiers modifiers)
KoShapeMeshGradientHandles::Handle m_selectedMeshHandle
void mousePressEvent(KoPointerEvent *event) override
bool wantsAutoScroll() const override
DefaultToolTabbedWidget * m_tabbedOptionWidget
virtual KoSelection * koSelection() const
DefaultTool(KoCanvasBase *canvas, bool connectToSelectedShapesProxy=false)
qreal rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation)
Returns rotation angle of given handle of the current selection.
QList< QPointer< QWidget > > createOptionWidgets() override
QRectF decorationsRect() const override
void mouseMoveEvent(KoPointerEvent *event) override
void mouseDoubleClickEvent(KoPointerEvent *event) override
friend class SelectionInteractionStrategy
void selectionTransform(int transformAction)
void addTransformActions(QMenu *menu) const
QPointF m_selectionBox[8]
KoToolSelection * selection() override
reimplemented
void slotActivateEditFillMeshGradient(bool value)
void selectionGroup()
QMenu * popupActionsMenu() override
QRectF handlesSize()
Returns selection rectangle adjusted by handle proximity threshold.
void paint(QPainter &painter, const KoViewConverter &converter) override
void deactivate() override
DefaultToolTextPropertiesInterface * m_textPropertyInterface
bool selectAll() override
reimplemented
void selectionAlign(int _align)
void selectionMoveDown()
void slotActivateEditFillGradient(bool value)
void recalcSelectionBox(KoSelection *selection)
void selectionBooleanOp(int booleanOp)
KisSignalMapper * m_alignSignalsMapper
QCursor m_rotateCursors[8]
void meshgradientHandleSelected(KoShapeMeshGradientHandles::Handle)
QScopedPointer< SelectionDecorator > m_decorator
void slotResetMeshGradientState()
void updateCursor()
void setupActions()
void activate(const QSet< KoShape * > &shapes) override
KisSignalMapper * m_transformSignalsMapper
void selectionReorder(KoShapeReorderCommand::MoveShapeType order)
void mouseReleaseEvent(KoPointerEvent *event) override
void copy() const override
reimplemented
KoToolSelection * m_selectionHandler
void selectionBringToFront()
QPolygonF m_selectionOutline
virtual KoShapeManager * shapeManager() const
void selectionUngroup()
KoShapeMeshGradientHandles::Handle m_hoveredMeshHandle
void slotChangeTextType(int index)
void selectionSplitShapes()
void selectionMoveUp()
KoInteractionStrategy * createStrategy(KoPointerEvent *event) override
void setText(const KUndo2MagicString &text)
KUndo2MagicString text() const
KisImageWSP image() const
KoShapeManager * localShapeManager() const
KisViewManager * viewManager() const
KisNodeSP activeNode()
Convenience function to get the active layer or mask.
The KisSignalMapper class bundles signals from identifiable senders.
void setMapping(QObject *sender, int id)
void setTextPropertiesInterface(KoSvgTextPropertiesInterface *interface)
setTextPropertiesInterface set the text properties interface. This should be done on tool activation....
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
KisTextPropertiesManager * textPropertyManager() const
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
KoSnapGuide * snapGuide
QPointer< KoShapeController > shapeController
virtual KoShapeManager * shapeManager() const =0
virtual const KoViewConverter * viewConverter() const =0
virtual void updateCanvas(const QRectF &rc)=0
virtual void addCommand(KUndo2Command *command)=0
QPointer< KoCanvasResourceProvider > resourceManager
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
bool setSvg(const QList< KoShape * > shapes)
Definition KoDrag.cpp:47
void addToClipboard()
Definition KoDrag.cpp:86
void mouseReleaseEvent(KoPointerEvent *event) override
void mouseMoveEvent(KoPointerEvent *event) override
bool hasInteractionFactory(const QString &id)
void removeInteractionFactory(const QString &id)
void paint(QPainter &painter, const KoViewConverter &converter) override
void mousePressEvent(KoPointerEvent *event) override
void addInteractionFactory(KoInteractionStrategyFactory *factory)
void keyPressEvent(QKeyEvent *event) override
KoInteractionStrategy * currentStrategy()
The position of a path point within a path shape.
Definition KoPathShape.h:63
bool separate(QList< KoPathShape * > &separatedPaths)
Creates separate path shapes, one for each existing subpath.
int subpathCount() const
Returns the number of subpaths in the path.
static KoPathShape * createShapeFromPainterPath(const QPainterPath &path)
Creates path shape from given QPainterPath.
Qt::MouseButtons buttons() const
return buttons pressed (see QMouseEvent::buttons());
bool isTabletEvent() const
Qt::KeyboardModifiers modifiers() const
QPointF point
The point in document coordinates.
virtual KoSelection * selection()=0
void deselectAll()
clear the selections list
const QList< KoShape * > selectedEditableShapesAndDelegates() const
void select(KoShape *shape)
const QList< KoShape * > selectedEditableShapes() const
KoShape * firstSelectedShape() const
const QList< KoShape * > selectedShapes() const
The undo / redo command for aligning shapes.
Align
The different alignment options for this command.
@ VerticalCenterAlignment
Align centered vertically.
@ HorizontalLeftAlignment
Align left.
@ HorizontalCenterAlignment
Align Centered horizontally.
@ VerticalBottomAlignment
Align bottom.
@ HorizontalRightAlignment
Align Right.
QList< KoShape * > shapes() const
The undo / redo command for distributing shapes.
Distribute
The different options to distribute with this command.
@ HorizontalRightDistribution
Horizontal Right.
@ VerticalCenterDistribution
Vertical centered.
@ HorizontalGapsDistribution
Horizontal Gaps.
@ VerticalBottomDistribution
Vertical bottom.
@ HorizontalCenterDistribution
Horizontal centered.
@ HorizontalLeftDistribution
Horizontal Left.
QVector< Handle > handles() const
The undo / redo command for grouping shapes.
QList< KoShape * > topLevelShapes() const
KoShape * shapeAt(const QPointF &position, KoFlake::ShapeSelection selection=KoFlake::ShapeOnTop, bool omitHiddenShapes=true)
The KoShapeMergeTextPropertiesCommand class This sets text properties on given text shapes....
QVector< Handle > handles() const
get all nodes in the mesh, don't use this for drawing the path but use path()
The undo / redo command for shape moving.
MoveShapeType
An enum for defining what kind of reordering to use.
@ RaiseShape
raise the selected shape to the level that it is above the shape that is on top of it.
@ SendToBack
Lower the selected shape to be below all other shapes.
@ LowerShape
Lower the selected shape to the level that it is below the shape that is below it.
@ BringToFront
Raise the selected shape to be on top of all shapes.
static KoShapeReorderCommand * createCommand(const QList< KoShape * > &shapes, KoShapeManager *manager, MoveShapeType move, KUndo2Command *parent=0)
virtual SelectionMode currentMode() const
void paint(QPainter &painter, const KoViewConverter &converter) override
The undo / redo command for ungrouping shapes.
virtual QRectF outlineRect() const
Definition KoShape.cpp:637
void addShapeChangeListener(ShapeChangeListener *listener)
Definition KoShape.cpp:1360
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:954
virtual QPainterPath outline() const
Definition KoShape.cpp:630
bool isSelectable() const
Definition KoShape.cpp:1014
QPointF absolutePosition(KoFlake::AnchorPosition anchor=KoFlake::Center) const
Definition KoShape.cpp:653
void removeShapeChangeListener(ShapeChangeListener *listener)
Definition KoShape.cpp:1368
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:1067
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:434
bool isGeometryProtected() const
Definition KoShape.cpp:1024
QRectF absoluteOutlineRect() const
Definition KoShape.cpp:368
KoShapeContainer * parent() const
Definition KoShape.cpp:1039
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:1081
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:918
ChangeType
Used by shapeChanged() to select which change was made.
Definition KoShape.h:95
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:424
qint16 zIndex() const
Definition KoShape.cpp:600
void paint(QPainter &painter, const KoViewConverter &converter)
paints the guide
QRectF boundingRect()
returns the bounding rect of the guide
bool isSnapping() const
returns if snapping is enabled
The SvgConvertTextTypeCommand class This command allows textshapes to be converted between preformatt...
The KoSvgTextPropertiesInterface class.
@ PreformattedText
Text-on-Path falls under this or PrePositionedText depending on collapse of lines.
@ InlineWrap
Uses inline size to wrap and preserves spaces.
TextType textType() const
textType This enum gives an indication of what kind of text this shape is. The different text types a...
KoSvgTextProperties textProperties() const
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
void selectionChanged(bool hasSelection)
QRectF handlePaintRect(const QPointF &position) const
QCursor cursor() const
return the last emitted cursor
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)
virtual void deactivate()
QAction * action(const QString &name) const
bool isActivated() const
int decorationThickness() const
decorationThickness The minimum thickness for tool decoration lines, this is derived from the screen ...
void switchToolRequested(const QString &id)
QString preferredToolForSelection(const QList< KoShape * > &shapes)
static KoToolManager * instance()
Return the toolmanager singleton.
virtual QPointF viewToDocument(const QPointF &viewPoint) const
virtual QPointF documentToView(const QPointF &documentPoint) const
KUndo2Command * createCommand() override
void paint(QPainter &painter, const KoViewConverter &converter) override
void finishInteraction(Qt::KeyboardModifiers) override
NopInteractionStrategy(KoToolBase *parent)
void handleMouseMove(const QPointF &, Qt::KeyboardModifiers) override
QPointer< KoSelection > m_selection
SelectionHandler(DefaultTool *parent)
bool hasSelection() override
return true if the tool currently has something selected that can be copied or deleted.
void cancelInteraction() override
void finishInteraction(Qt::KeyboardModifiers modifiers=QFlags< Qt::KeyboardModifier >()) override
void paint(QPainter &painter, const KoViewConverter &converter) override
SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:194
#define M_PI
Definition kis_global.h:111
bool isSelectionMask(KisNodeSP node)
KUndo2MagicString kundo2_i18n(const char *text)
KUndo2MagicString kundo2_noi18n(const QString &text)
@ PageSize
The size of the (current) page in postscript points.
@ ShapeOnTop
return the shape highest z-ordering, regardless of selection.
Definition KoFlake.h:74
@ NextUnselected
return the first unselected directly under a selected shape, or the top most one if nothing is select...
Definition KoFlake.h:73
AnchorPosition
Definition KoFlake.h:85
@ Left
Definition KoFlake.h:89
@ Right
Definition KoFlake.h:91
@ Bottom
Definition KoFlake.h:93
@ BottomRight
Definition KoFlake.h:94
@ Top
Definition KoFlake.h:87
@ TopRight
Definition KoFlake.h:88
@ TopLeft
Definition KoFlake.h:86
@ BottomLeft
Definition KoFlake.h:92
@ Center
Definition KoFlake.h:90
FillVariant
Definition KoFlake.h:28
@ StrokeFill
Definition KoFlake.h:30
@ Fill
Definition KoFlake.h:29
SelectionHandle
Enum determining which handle is meant, used in KoInteractionTool.
Definition KoFlake.h:55
@ BottomRightHandle
The handle that is at the bottom right of a selection.
Definition KoFlake.h:59
@ BottomLeftHandle
The handle that is at the bottom left of a selection.
Definition KoFlake.h:61
@ RightMiddleHandle
The handle that is at the right - center of a selection.
Definition KoFlake.h:58
@ TopRightHandle
The handle that is at the top - right of a selection.
Definition KoFlake.h:57
@ TopLeftHandle
The handle that is at the top left of a selection.
Definition KoFlake.h:63
@ LeftMiddleHandle
The handle that is at the left center of a selection.
Definition KoFlake.h:62
@ NoHandle
Value to indicate no handle.
Definition KoFlake.h:64
@ TopMiddleHandle
The handle that is at the top - center of a selection.
Definition KoFlake.h:56
@ BottomMiddleHandle
The handle that is at the bottom center of a selection.
Definition KoFlake.h:60
QTransform pathShapeBooleanSpaceWorkaround(KisImageSP image)
Interface to interact with the text property manager.
virtual bool spanSelection() override
Whether the tool is currently selecting a set of characters instead of whole paragraphs.
virtual QList< KoSvgTextProperties > getSelectedProperties() override
getSelectedProperties
const QScopedPointer< Private > d
virtual void notifyShapeChanged(KoShape::ChangeType type, KoShape *shape) override
DefaultToolTextPropertiesInterface(DefaultTool *parent)
virtual KoSvgTextProperties getInheritedProperties() override
getInheritedProperties The properties that should be visible when a given property isn't available in...
virtual void notifyCursorPosChanged(int pos, int anchor) override
virtual void notifyMarkupChanged() override
virtual void setPropertiesOnSelected(KoSvgTextProperties properties, QSet< KoSvgTextProperties::PropertyId > removeProperties=QSet< KoSvgTextProperties::PropertyId >()) override
setPropertiesOnSelected This sets the properties on the selection. The implementation is responsible ...