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>
49
50#include <KoSnapGuide.h>
52#include "kis_action_registry.h"
53#include "kis_node.h"
54#include "kis_node_manager.h"
55#include "KisViewManager.h"
56#include "kis_canvas2.h"
62
64
65#include <KoIcon.h>
66
67#include <QPainterPath>
68#include <QPointer>
69#include <QAction>
70#include <QKeyEvent>
71#include <QTimer>
72#include <QActionGroup>
73#include <KisSignalMapper.h>
74#include <KoResourcePaths.h>
75
76#include <KoCanvasController.h>
77#include <kactioncollection.h>
78#include <QMenu>
79
80#include <math.h>
81#include "kis_assert.h"
82#include "kis_global.h"
83#include "kis_debug.h"
84#include "krita_utils.h"
85
86#include <QVector2D>
87
88#define HANDLE_DISTANCE 10
89#define HANDLE_DISTANCE_SQ (HANDLE_DISTANCE * HANDLE_DISTANCE)
90
91#define INNER_HANDLE_DISTANCE_SQ 16
92
93namespace {
94static const QString EditFillGradientFactoryId = "edit_fill_gradient";
95static const QString EditStrokeGradientFactoryId = "edit_stroke_gradient";
96static const QString EditFillMeshGradientFactoryId = "edit_fill_meshgradient";
97
98enum TransformActionType {
99 TransformRotate90CW,
100 TransformRotate90CCW,
101 TransformRotate180,
102 TransformMirrorX,
103 TransformMirrorY,
104 TransformReset
105};
106
107enum BooleanOp {
108 BooleanUnion,
109 BooleanIntersection,
110 BooleanSubtraction
111};
112
113}
114
116{
117public:
119 : KoInteractionStrategy(parent)
120 {
121 }
122
124 {
125 return 0;
126 }
127
128 void handleMouseMove(const QPointF & /*mouseLocation*/, Qt::KeyboardModifiers /*modifiers*/) override {}
129 void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {}
130
131 void paint(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) override {
132 Q_UNUSED(painter);
133 Q_UNUSED(converter);
134 Q_UNUSED(displayRendererInterface)
135 }
136};
137
139{
140public:
141 explicit SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid)
142 : KoShapeRubberSelectStrategy(parent, clicked, useSnapToGrid)
143 {
144 }
145
146 void paint(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) override {
147 KoShapeRubberSelectStrategy::paint(painter, converter, displayRendererInterface);
148 }
149
150 void cancelInteraction() override
151 {
152 tool()->canvas()->updateCanvas(selectedRectangle() | tool()->decorationsRect());
153 }
154
155 void finishInteraction(Qt::KeyboardModifiers modifiers = QFlags<Qt::KeyboardModifier>()) override
156 {
157 Q_UNUSED(modifiers);
158 DefaultTool *defaultTool = dynamic_cast<DefaultTool*>(tool());
160
161 KoSelection * selection = defaultTool->koSelection();
162
163 const bool useContainedMode = currentMode() == CoveringSelection;
164
165 QList<KoShape *> shapes =
166 defaultTool->shapeManager()->
167 shapesAt(selectedRectangle(), true, useContainedMode);
168
169 Q_FOREACH (KoShape * shape, shapes) {
170 if (!shape->isSelectable()) continue;
171
172 selection->select(shape);
173 }
174
175 tool()->canvas()->updateCanvas(selectedRectangle() | tool()->decorationsRect());
176 }
177};
178#include <KoGradientBackground.h>
181
183{
184public:
186 int priority, const QString &id, DefaultTool *_q)
188 q(_q),
189 m_fillVariant(fillVariant)
190 {
191 }
192
206
207 bool hoverEvent(KoPointerEvent *ev) override
208 {
210 return false;
211 }
212
213 bool paintOnHover(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) override
214 {
215 Q_UNUSED(painter);
216 Q_UNUSED(converter);
217 Q_UNUSED(displayRendererInterface)
218 return false;
219 }
220
221 bool tryUseCustomCursor() override {
223 q->useCursor(Qt::OpenHandCursor);
224 return true;
225 }
226
227 return false;
228 }
229
230private:
231
234 QList<KoShape*> shapes = selection->selectedEditableShapes();
235
236 KoShape *shape = 0;
237 if (shapes.size() == 1) {
238 shape = shapes.first();
239 }
240
241 return shape;
242 }
243
246
247 KoShape *shape = onlyEditableShape();
248 if (shape) {
249 KoFlake::SelectionHandle globalHandle = q->handleAt(pos);
250 const qreal distanceThresholdSq =
251 globalHandle == KoFlake::NoHandle ?
253
254 const KoViewConverter *converter = q->canvas()->viewConverter();
255 const QPointF viewPoint = converter->documentToView(pos);
256 qreal minDistanceSq = std::numeric_limits<qreal>::max();
257
259 auto handless = sh.handles();
260 Q_FOREACH (const KoShapeGradientHandles::Handle &handle, sh.handles()) {
261 const QPointF handlePoint = converter->documentToView(handle.pos);
262 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
263
264 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
265 result = handle;
266 minDistanceSq = distanceSq;
267 }
268 }
269 }
270
271 return result;
272 }
273
274private:
278};
279
282
284{
285public:
287 int priority,
288 const QString& id,
289 DefaultTool* _q)
291 , m_fillVariant(fillVariant)
292 , q(_q)
293 {
294 }
295
312
313 bool hoverEvent(KoPointerEvent *ev) override
314 {
315 // for custom cursor
317
318 // refresh
321 }
322
323 m_currentHandle = handle;
325
326 // highlight the decoration which is being hovered
329 }
330 return false;
331 }
332
333 bool paintOnHover(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) override
334 {
335 Q_UNUSED(painter);
336 Q_UNUSED(converter);
337 Q_UNUSED(displayRendererInterface);
338 return false;
339 }
340
341 bool tryUseCustomCursor() override
342 {
344 q->useCursor(Qt::OpenHandCursor);
345 return true;
346 }
347
348 return false;
349 }
350
351
352private:
354 // FIXME: copy of KoShapeGradientHandles
356 QList<KoShape*> shapes = selection->selectedEditableShapes();
357
358 KoShape *shape = 0;
359 if (shapes.size() == 1) {
360 shape = shapes.first();
361 }
362
363 return shape;
364 }
365
367 {
368 // FIXME: copy of KoShapeGradientHandles. use a template?
370
371 KoShape *shape = onlyEditableShape();
372 if (shape) {
373 KoFlake::SelectionHandle globalHandle = q->handleAt(pos);
374 const qreal distanceThresholdSq =
375 globalHandle == KoFlake::NoHandle ?
377
378 const KoViewConverter *converter = q->canvas()->viewConverter();
379 const QPointF viewPoint = converter->documentToView(pos);
380 qreal minDistanceSq = std::numeric_limits<qreal>::max();
381
383
384 for (const auto& handle: sh.handles()) {
385 const QPointF handlePoint = converter->documentToView(handle.pos);
386 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
387
388 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
389 result = handle;
390 minDistanceSq = distanceSq;
391 }
392 }
393 }
394
395 return result;
396 }
397
398private:
402};
403
405{
406public:
408 : KoToolSelection(parent)
409 , m_selection(parent->koSelection())
410 {
411 }
412
413 bool hasSelection() override
414 {
415 if (m_selection) {
416 return m_selection->count();
417 }
418 return false;
419 }
420
421private:
423};
424
425DefaultTool::DefaultTool(KoCanvasBase *canvas, bool connectToSelectedShapesProxy)
426 : KoInteractionTool(canvas)
427 , m_lastHandle(KoFlake::NoHandle)
428 , m_hotPosition(KoFlake::TopLeft)
429 , m_mouseWasInsideHandles(false)
430 , m_textOutlineHelper(new KoSvgTextShapeOutlineHelper(canvas))
431 , m_selectionHandler(new SelectionHandler(this))
432 , m_tabbedOptionWidget(0)
433 , m_textPropertyInterface(new DefaultToolTextPropertiesInterface(this))
434{
435 setupActions();
436
437 QPixmap rotatePixmap, shearPixmap;
438 rotatePixmap = QIcon(":/cursor_rotate.svg").pixmap(22, 22);
439 Q_ASSERT(!rotatePixmap.isNull());
440 shearPixmap = QIcon(":/cursor_shear.svg").pixmap(22, 22);
441 Q_ASSERT(!shearPixmap.isNull());
442
443 m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45)));
444 m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90)));
445 m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135)));
446 m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180)));
447 m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225)));
448 m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270)));
449 m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315)));
450 m_rotateCursors[7] = QCursor(rotatePixmap);
451 /*
452 m_rotateCursors[0] = QCursor(Qt::RotateNCursor);
453 m_rotateCursors[1] = QCursor(Qt::RotateNECursor);
454 m_rotateCursors[2] = QCursor(Qt::RotateECursor);
455 m_rotateCursors[3] = QCursor(Qt::RotateSECursor);
456 m_rotateCursors[4] = QCursor(Qt::RotateSCursor);
457 m_rotateCursors[5] = QCursor(Qt::RotateSWCursor);
458 m_rotateCursors[6] = QCursor(Qt::RotateWCursor);
459 m_rotateCursors[7] = QCursor(Qt::RotateNWCursor);
460 */
461 m_shearCursors[0] = QCursor(shearPixmap);
462 m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45)));
463 m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90)));
464 m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135)));
465 m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180)));
466 m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225)));
467 m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270)));
468 m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315)));
469 m_sizeCursors[0] = Qt::SizeVerCursor;
470 m_sizeCursors[1] = Qt::SizeBDiagCursor;
471 m_sizeCursors[2] = Qt::SizeHorCursor;
472 m_sizeCursors[3] = Qt::SizeFDiagCursor;
473 m_sizeCursors[4] = Qt::SizeVerCursor;
474 m_sizeCursors[5] = Qt::SizeBDiagCursor;
475 m_sizeCursors[6] = Qt::SizeHorCursor;
476 m_sizeCursors[7] = Qt::SizeFDiagCursor;
477
478 if (connectToSelectedShapesProxy) {
479 connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(updateActions()));
480
481 connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(repaintDecorations()));
482 connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), m_textPropertyInterface, SLOT(slotSelectionChanged()));
483 connect(canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(repaintDecorations()));
484 }
485
486 m_textOutlineHelper->setDrawBoundingRect(false);
487 m_textOutlineHelper->setDrawShapeOutlines(true);
488}
489
493
495{
496 if (value) {
499 1, EditFillGradientFactoryId, this));
500 } else {
501 removeInteractionFactory(EditFillGradientFactoryId);
502 }
504}
505
507{
508 if (value) {
511 0, EditStrokeGradientFactoryId, this));
512 } else {
513 removeInteractionFactory(EditStrokeGradientFactoryId);
514 }
516}
517
519{
520 if (value) {
522 m_tabbedOptionWidget, SLOT(slotMeshGradientHandleSelected(KoShapeMeshGradientHandles::Handle)));
525 EditFillMeshGradientFactoryId, this));
526 } else {
528 m_tabbedOptionWidget, SLOT(slotMeshGradientHandleSelected(KoShapeMeshGradientHandles::Handle)));
529 removeInteractionFactory(EditFillMeshGradientFactoryId);
530 }
531}
532
537
539{
541
542 if (shapes.isEmpty()) return;
543
545 KUndo2Command *parentCommand = new KUndo2Command();
546 bool convertableShape = false;
548 Q_FOREACH(KoShape *shape, shapes) {
549 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(shape);
550 if (textShape && textShape->textType() != type) {
551 KoSvgConvertTextTypeCommand *cmd = new KoSvgConvertTextTypeCommand(textShape, type, 0, parentCommand);
552 if (!convertableShape) {
553 convertableShape = true;
554 parentCommand->setText(cmd->text());
555 }
557 }
558 }
559
561 if (convertableShape) {
562 canvas()->addCommand(parentCommand);
563 }
564}
565
567{
568 KoSvgTextShape *textShape = nullptr;
569 QList<KoShape*> shapes;
571 if (!selection) return;
572
573 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
574 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
575 if (selectedShapes.isEmpty()) return;
576
577 Q_FOREACH(KoShape *shape, selectedShapes) {
578 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
579 KoPathShape *path = dynamic_cast<KoPathShape*>(shape);
580 if (text && !textShape) {
581 textShape = text;
582 } else if (path && path->isClosedSubpath(0)) {
583 shapes.append(shape);
584 }
585 }
586 if (!textShape) return;
587 if (shapes.isEmpty()) return;
588
589 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Add shapes to text flow."));
590
591 if (textShape->textType() != KoSvgTextShape::InlineWrap) {
592 new KoSvgConvertTextTypeCommand(textShape, KoSvgTextShape::PreformattedText, 0, parentCommand);
593 }
594 KoSvgTextRemoveShapeCommand::removeContourShapesFromFlow(textShape, parentCommand, false, true);
595
596 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
597 Q_FOREACH(KoShape *shape, shapes) {
598 new KoSvgTextAddShapeCommand(textShape, shape, true, parentCommand);
599 }
600 new KoKeepShapesSelectedCommand({}, {textShape}, canvas()->selectedShapesProxy(), true, parentCommand);
601
602 canvas()->addCommand(parentCommand);
603 selection->deselectAll();
604 selection->select(textShape);
605}
606
608{
609 KoSvgTextShape *textShape = nullptr;
610 KoPathShape *textPath = nullptr;
612 if (!selection) return;
613
614 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
615 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
616 if (selectedShapes.isEmpty()) return;
617
618 Q_FOREACH(KoShape *shape, selectedShapes) {
619 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
620 if (text && !textShape) {
621 textShape = text;
622 } else if (KoPathShape *path = dynamic_cast<KoPathShape*>(shape)){
623 textPath = path;
624 }
625 if (textShape && textPath) {
626 break;
627 }
628 }
629 if (!(textShape && textPath)) return;
630
631 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Put Text On Path"));
632
633 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
635 new KoSvgConvertTextTypeCommand(textShape, KoSvgTextShape::PreformattedText, 0, parentCommand);
636 }
637
642 KoSvgTextRemoveShapeCommand::removeContourShapesFromFlow(textShape, parentCommand, true, true);
643 new KoSvgTextSetTextPathOnRangeCommand(textShape, textPath, 0, textShape->posForIndex(textShape->plainText().size()), parentCommand);
644
648 if ((textShape->direction() == KoSvgText::DirectionRightToLeft && anchor == KoSvgText::AnchorStart)
649 || (textShape->direction() == KoSvgText::DirectionLeftToRight && anchor == KoSvgText::AnchorEnd)) {
651 info.startOffset = 100.0;
652 info.startOffsetIsPercentage = true;
653 new KoSvgTextPathInfoChangeCommand(textShape, 0, info, parentCommand);
654 } else if (anchor == KoSvgText::AnchorMiddle) {
656 info.startOffset = 50.0;
657 info.startOffsetIsPercentage = true;
658 new KoSvgTextPathInfoChangeCommand(textShape, 0, info, parentCommand);
659 }
660
661 new KoKeepShapesSelectedCommand({}, {textShape}, canvas()->selectedShapesProxy(), true, parentCommand);
662
663 canvas()->addCommand(parentCommand);
664 selection->deselectAll();
665 selection->select(textShape);
666}
667
669{
670 KoSvgTextShape *textShape = nullptr;
671 QList<KoShape*> shapes;
673 if (!selection) return;
674
675 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
676 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
677 if (selectedShapes.isEmpty()) return;
678
679 Q_FOREACH(KoShape *shape, selectedShapes) {
680 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
681 KoPathShape *path = dynamic_cast<KoPathShape*>(shape);
682 if (text && !textShape) {
683 textShape = text;
684 } else if (path && path->isClosedSubpath(0)) {
685 shapes.append(shape);
686 }
687 }
688 if (!textShape) return;
689 if (shapes.isEmpty()) return;
690
691 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Subtract shapes from text flow."));
692
693 if (textShape->textType() == KoSvgTextShape::InlineWrap) {
694 new KoSvgConvertTextTypeCommand(textShape, KoSvgTextShape::PreformattedText, 0, parentCommand);
695 }
696 KoSvgTextRemoveShapeCommand::removeContourShapesFromFlow(textShape, parentCommand, false, true);
697
698 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
699 Q_FOREACH(KoShape *shape, shapes) {
700 new KoSvgTextAddShapeCommand(textShape, shape, false, parentCommand);
701 }
702 new KoKeepShapesSelectedCommand({}, {textShape}, canvas()->selectedShapesProxy(), true, parentCommand);
703
704 canvas()->addCommand(parentCommand);
705 selection->deselectAll();
706 selection->select(textShape);
707}
708
713
715{
717 if (!textShape) return;
719 if (!selection) return;
720
721 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
722 if (selectedShapes.isEmpty()) return;
723
724 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Remove shapes from text flow."));
725
726 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
727 Q_FOREACH(KoShape *shape, selectedShapes) {
728 if (!textShape->shapeInContours(shape)) continue;
729 new KoSvgTextRemoveShapeCommand(textShape, shape, parentCommand);
730 }
731 new KoKeepShapesSelectedCommand({}, {selectedShapes}, canvas()->selectedShapesProxy(), true, parentCommand);
732
733 canvas()->addCommand(parentCommand);
734}
735
737{
739 if (!textShape) return;
741 if (!selection) return;
742
743 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
744 if (selectedShapes.isEmpty()) return;
745 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Toggle Flow Shape Type"));
746
747 bool addToCanvas = false;
748 Q_FOREACH(KoShape *shape, selectedShapes) {
749 if (!textShape->shapesInside().contains(shape)
750 && !textShape->shapesSubtract().contains(shape)) continue;
751 addToCanvas = true;
752 new KoSvgTextFlipShapeContourTypeCommand(textShape, shape, parentCommand);
753 }
754 if (addToCanvas) {
755 canvas()->addCommand(parentCommand);
756 }
757}
758
760{
762 if (!textShape) return;
764 if (!selection) return;
765
766 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
767 if (selectedShapes.isEmpty()) return;
768
769 KUndo2Command *parentCommand = new KUndo2Command();
771 parentCommand->setText(kundo2_i18n("Set Flow Shape as First"));
773 parentCommand->setText(kundo2_i18n("Decrease Flow Shape Index"));
775 parentCommand->setText(kundo2_i18n("Increase Flow Shape Index"));
776 } else {
777 parentCommand->setText(kundo2_i18n("Set Flow Shape as Last"));
778 }
779
780 QList<KoShape *> shapesInside;
781 Q_FOREACH(KoShape *shape, selectedShapes) {
782 if (!textShape->shapesInside().contains(shape)) continue;
783 shapesInside.append(shape);
784 }
785
786 if (!shapesInside.isEmpty()) {
787 new KoSvgTextReorderShapeInsideCommand(textShape, shapesInside, KoSvgTextReorderShapeInsideCommand::MoveShapeType(type), parentCommand);
788 canvas()->addCommand(parentCommand);
789 }
790}
791
793{
794 return m_textOutlineHelper->updateTextContourMode();
795}
796
798{
799 return true;
800}
801
802void DefaultTool::addMappedAction(KisSignalMapper *mapper, const QString &actionId, int commandType)
803{
804 QAction *a =action(actionId);
805 connect(a, SIGNAL(triggered()), mapper, SLOT(map()));
806 mapper->setMapping(a, commandType);
807}
808
810{
812
819
821
826
831
833
834 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_cw", TransformRotate90CW);
835 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_ccw", TransformRotate90CCW);
836 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_180", TransformRotate180);
837 addMappedAction(m_transformSignalsMapper, "object_transform_mirror_horizontally", TransformMirrorX);
838 addMappedAction(m_transformSignalsMapper, "object_transform_mirror_vertically", TransformMirrorY);
839 addMappedAction(m_transformSignalsMapper, "object_transform_reset", TransformReset);
840
842
843 addMappedAction(m_booleanSignalsMapper, "object_unite", BooleanUnion);
844 addMappedAction(m_booleanSignalsMapper, "object_intersect", BooleanIntersection);
845 addMappedAction(m_booleanSignalsMapper, "object_subtract", BooleanSubtraction);
846
851
852 if (!action("text_type_preformatted")->actionGroup()) {
853 QActionGroup *textTypeActions = new QActionGroup(this);
854 textTypeActions->addAction(action("text_type_preformatted"));
855 textTypeActions->addAction(action("text_type_inline_wrap"));
856 textTypeActions->addAction(action("text_type_pre_positioned"));
857 textTypeActions->setExclusive(false);
858 Q_FOREACH (QAction *a, textTypeActions->actions()) {
859 a->setCheckable(false);
860 }
861 }
862
864
869
870 m_contextMenu.reset(new QMenu());
871}
872
874{
875 QPointF selectionCenter = koSelection()->absolutePosition();
876 QPointF direction;
877
878 switch (handle) {
880 if (useEdgeRotation) {
883 } else {
884 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
885 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRight) - handlePosition);
886 direction = handlePosition - selectionCenter;
887 }
888 break;
890 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized()).toPointF();
891 break;
893 if (useEdgeRotation) {
896 } else {
897 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRight);
898 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
899 direction = handlePosition - selectionCenter;
900 }
901 break;
903 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized()).toPointF();
904 break;
906 if (useEdgeRotation) {
909 } else {
910 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeft);
911 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
912 direction = handlePosition - selectionCenter;
913 }
914 break;
916 direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - selectionCenter;
917 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized()).toPointF();
918 break;
920 if (useEdgeRotation) {
923 } else {
924 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
925 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeft) - handlePosition);
926 direction = handlePosition - selectionCenter;
927 }
928 break;
930 direction = koSelection()->absolutePosition(KoFlake::TopLeft) - selectionCenter;
931 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized()).toPointF();
932 break;
934 return 0.0;
935 break;
936 }
937
938 qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI;
939
940 switch (handle) {
942 if (useEdgeRotation) {
943 rotation -= 0.0;
944 } else {
945 rotation -= 270.0;
946 }
947 break;
949 rotation -= 315.0;
950 break;
952 if (useEdgeRotation) {
953 rotation -= 90.0;
954 } else {
955 rotation -= 0.0;
956 }
957 break;
959 rotation -= 45.0;
960 break;
962 if (useEdgeRotation) {
963 rotation -= 180.0;
964 } else {
965 rotation -= 90.0;
966 }
967 break;
969 rotation -= 135.0;
970 break;
972 if (useEdgeRotation) {
973 rotation -= 270.0;
974 } else {
975 rotation -= 180.0;
976 }
977 break;
979 rotation -= 225.0;
980 break;
981 default:
982 ;
983 }
984
985 if (rotation < 0.0) {
986 rotation += 360.0;
987 }
988
989 return rotation;
990}
991
993{
994 if (tryUseCustomCursor()) return;
995
996 QCursor cursor = Qt::ArrowCursor;
997
998 QString statusText;
999
1001 if (selection && selection->count() > 0) { // has a selection
1002 bool editable = !selection->selectedEditableShapes().isEmpty();
1003
1006 int rotOctant = 8 + int(8.5 + m_angle / 45);
1007
1008 bool rotateHandle = false;
1009 bool shearHandle = false;
1010 switch (m_lastHandle) {
1012 cursor = m_shearCursors[(0 + rotOctant) % 8];
1013 shearHandle = true;
1014 break;
1016 cursor = m_rotateCursors[(1 + rotOctant) % 8];
1017 rotateHandle = true;
1018 break;
1020 cursor = m_shearCursors[(2 + rotOctant) % 8];
1021 shearHandle = true;
1022 break;
1024 cursor = m_rotateCursors[(3 + rotOctant) % 8];
1025 rotateHandle = true;
1026 break;
1028 cursor = m_shearCursors[(4 + rotOctant) % 8];
1029 shearHandle = true;
1030 break;
1032 cursor = m_rotateCursors[(5 + rotOctant) % 8];
1033 rotateHandle = true;
1034 break;
1036 cursor = m_shearCursors[(6 + rotOctant) % 8];
1037 shearHandle = true;
1038 break;
1040 cursor = m_rotateCursors[(7 + rotOctant) % 8];
1041 rotateHandle = true;
1042 break;
1043 case KoFlake::NoHandle:
1044 cursor = Qt::ArrowCursor;
1045 break;
1046 }
1047 if (rotateHandle) {
1048 statusText = i18n("Left click rotates around center, right click around highlighted position.");
1049 }
1050 if (shearHandle) {
1051 statusText = i18n("Click and drag to shear selection.");
1052 }
1053
1054
1055 } else {
1056 statusText = i18n("Click and drag to resize selection.");
1058 int rotOctant = 8 + int(8.5 + m_angle / 45);
1059 bool cornerHandle = false;
1060 switch (m_lastHandle) {
1062 cursor = m_sizeCursors[(0 + rotOctant) % 8];
1063 break;
1065 cursor = m_sizeCursors[(1 + rotOctant) % 8];
1066 cornerHandle = true;
1067 break;
1069 cursor = m_sizeCursors[(2 + rotOctant) % 8];
1070 break;
1072 cursor = m_sizeCursors[(3 + rotOctant) % 8];
1073 cornerHandle = true;
1074 break;
1076 cursor = m_sizeCursors[(4 + rotOctant) % 8];
1077 break;
1079 cursor = m_sizeCursors[(5 + rotOctant) % 8];
1080 cornerHandle = true;
1081 break;
1083 cursor = m_sizeCursors[(6 + rotOctant) % 8];
1084 break;
1086 cursor = m_sizeCursors[(7 + rotOctant) % 8];
1087 cornerHandle = true;
1088 break;
1089 case KoFlake::NoHandle:
1090 cursor = Qt::SizeAllCursor;
1091 statusText = i18n("Click and drag to move selection.");
1092 break;
1093 }
1094 if (cornerHandle) {
1095 statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position.");
1096 }
1097 }
1098 if (!editable) {
1099 cursor = Qt::ArrowCursor;
1100 }
1101 } else {
1102 // there used to be guides... :'''(
1103 }
1105 if (currentStrategy() == 0) {
1106 Q_EMIT statusTextChanged(statusText);
1107 }
1108}
1109
1110void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter)
1111{
1113 if (selection) {
1114 m_decorator.reset(new SelectionDecorator(canvas()->resourceManager(), canvas()->displayRendererInterface()));
1115
1116 {
1122 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
1123 KisNodeSP node = kisCanvas->viewManager()->nodeManager()->activeNode();
1124 const bool isSelectionMask = node && node->inherits("KisSelectionMask");
1125 m_decorator->setForceShapeOutlines(isSelectionMask);
1126
1127
1128 }
1129
1130 m_decorator->setSelection(selection);
1131 m_decorator->setHandleRadius(handleRadius());
1132 m_decorator->setDecorationThickness(decorationThickness());
1133 m_decorator->setShowFillGradientHandles(hasInteractionFactory(EditFillGradientFactoryId));
1134 m_decorator->setShowStrokeFillGradientHandles(hasInteractionFactory(EditStrokeGradientFactoryId));
1135 m_decorator->setShowFillMeshGradientHandles(hasInteractionFactory(EditFillMeshGradientFactoryId));
1136 m_decorator->setCurrentMeshGradientHandles(m_selectedMeshHandle, m_hoveredMeshHandle);
1137 m_decorator->paint(painter, converter);
1138 }
1139
1140 m_textOutlineHelper->setHandleRadius(handleRadius());
1141 m_textOutlineHelper->setDecorationThickness(decorationThickness());
1142 m_textOutlineHelper->paint(&painter, converter);
1143
1144 KoInteractionTool::paint(painter, converter);
1145
1146 painter.save();
1147 painter.setTransform(converter.documentToView(), true);
1148 canvas()->snapGuide()->paint(painter, converter, canvas()->displayRendererInterface());
1149 painter.restore();
1150}
1151
1153{
1154 // if the currently active node has a shape manager, then it is
1155 // probably our client :)
1156
1157 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
1158 return bool(kisCanvas->localShapeManager());
1159}
1160
1164
1166{
1167 // this tool only works on a vector layer right now, so give a warning if another layer type is trying to use it
1168 if (!isValidForCurrentLayer()) {
1169 KisCanvas2 *kiscanvas = static_cast<KisCanvas2 *>(canvas());
1170 kiscanvas->viewManager()->showFloatingMessage(
1171 i18n("This tool only works on vector layers. You probably want the move tool."),
1172 QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter);
1173 return;
1174 }
1175
1176 if (KoSvgTextShape *shape = m_textOutlineHelper->contourModeButtonHovered(event->point)) {
1177 m_textOutlineHelper->toggleTextContourMode(shape);
1178 updateActions();
1179 event->accept();
1180 updateCursor();
1181 return;
1182 }
1184 updateCursor();
1185}
1186
1188{
1190 if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) {
1191 QRectF bound = handlesSize();
1192
1193 if (bound.contains(event->point)) {
1194 bool inside;
1195 KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside);
1196
1197 if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) {
1198 m_lastHandle = newDirection;
1199 m_mouseWasInsideHandles = inside;
1200 }
1201 } else {
1204
1205 // there used to be guides... :'''(
1206 }
1207 } else {
1208 // there used to be guides... :'''(
1209 }
1210
1211
1212 updateCursor();
1213}
1214
1216{
1218 if (!selection || !selection->count()) return QRectF();
1219
1221
1222 QRectF bound = m_selectionOutline.boundingRect();
1223
1224 // expansion Border
1225 if (!canvas() || !canvas()->viewConverter()) {
1226 return bound;
1227 }
1228
1229 QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE));
1230 bound.adjust(-border.x(), -border.y(), border.x(), border.y());
1231 return bound;
1232}
1233
1239
1241{
1243
1245 if (shape && selection && !selection->isSelected(shape)) {
1246
1247 if (!(event->modifiers() & Qt::ShiftModifier)) {
1248 selection->deselectAll();
1249 }
1250
1251 selection->select(shape);
1252 }
1253
1255}
1256
1257bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers)
1258{
1259 bool result = false;
1260
1261 qreal x = 0.0, y = 0.0;
1262 if (direction == Qt::Key_Left) {
1263 x = -5;
1264 } else if (direction == Qt::Key_Right) {
1265 x = 5;
1266 } else if (direction == Qt::Key_Up) {
1267 y = -5;
1268 } else if (direction == Qt::Key_Down) {
1269 y = 5;
1270 }
1271
1272 if (x != 0.0 || y != 0.0) { // actually move
1273
1274 if ((modifiers & Qt::ShiftModifier) != 0) {
1275 x *= 10;
1276 y *= 10;
1277 } else if ((modifiers & Qt::AltModifier) != 0) { // more precise
1278 x /= 5;
1279 y /= 5;
1280 }
1281
1283
1284 if (!shapes.isEmpty()) {
1285 canvas()->addCommand(new KoShapeMoveCommand(shapes, QPointF(x, y)));
1286 result = true;
1287 }
1288 }
1289
1290 return result;
1291}
1292
1293void DefaultTool::keyPressEvent(QKeyEvent *event)
1294{
1296 if (currentStrategy() == 0) {
1297 switch (event->key()) {
1298 case Qt::Key_Left:
1299 case Qt::Key_Right:
1300 case Qt::Key_Up:
1301 case Qt::Key_Down:
1302 if (moveSelection(event->key(), event->modifiers())) {
1303 event->accept();
1304 }
1305 break;
1306 default:
1307 return;
1308 }
1309 }
1310}
1311
1313{
1314 QRectF dirtyRect;
1315
1316 if (koSelection() && koSelection()->count() > 0) {
1320 dirtyRect = const_cast<DefaultTool*>(this)->handlesSize();
1321 }
1322
1323 if (canvas()->snapGuide()->isSnapping()) {
1324 dirtyRect |= canvas()->snapGuide()->boundingRect();
1325 }
1326 dirtyRect |= m_textOutlineHelper->decorationRect();
1327
1328 return dirtyRect;
1329}
1330
1332{
1333 // all the selected shapes, not only editable!
1335
1336 if (!shapes.isEmpty()) {
1337 KoDrag drag;
1338 drag.setSvg(shapes);
1339 drag.addToClipboard();
1340 }
1341}
1342
1344{
1345 QList<KoShape *> shapes;
1346 foreach (KoShape *s, koSelection()->selectedShapes()) {
1347 if (s->isGeometryProtected()) {
1348 continue;
1349 }
1350 shapes << s;
1351 }
1352 if (!shapes.empty()) {
1353 canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes));
1354 }
1355}
1356
1358{
1359 // we no longer have to do anything as tool Proxy will do it for us
1360 return false;
1361}
1362
1364{
1365 Q_ASSERT(canvas());
1366 Q_ASSERT(canvas()->selectedShapesProxy());
1367 Q_FOREACH(KoShape *shape, canvas()->shapeManager()->shapes()) {
1368 if (!shape->isSelectable()) continue;
1370 }
1372
1373 return true;
1374}
1375
1377{
1378 Q_ASSERT(canvas());
1379 Q_ASSERT(canvas()->selectedShapesProxy());
1382}
1383
1385{
1386 Q_ASSERT(canvas());
1387 Q_ASSERT(canvas()->selectedShapesProxy());
1388 return canvas()->selectedShapesProxy()->selection();
1389}
1390
1391KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning)
1392{
1393 // check for handles in this order; meaning that when handles overlap the one on top is chosen
1394 static const KoFlake::SelectionHandle handleOrder[] = {
1404 };
1405
1406 const KoViewConverter *converter = canvas()->viewConverter();
1408
1409 if (!selection || !selection->count() || !converter) {
1410 return KoFlake::NoHandle;
1411 }
1412
1414
1415 if (innerHandleMeaning) {
1416 QPainterPath path;
1417 path.addPolygon(m_selectionOutline);
1418 *innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point));
1419 }
1420
1421 const QPointF viewPoint = converter->documentToView(point);
1422
1423 for (int i = 0; i < KoFlake::NoHandle; ++i) {
1424 KoFlake::SelectionHandle handle = handleOrder[i];
1425
1426 const QPointF handlePoint = converter->documentToView(m_selectionBox[handle]);
1427 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
1428
1429 // if just inside the outline
1430 if (distanceSq < HANDLE_DISTANCE_SQ) {
1431
1432 if (innerHandleMeaning) {
1433 if (distanceSq < INNER_HANDLE_DISTANCE_SQ) {
1434 *innerHandleMeaning = true;
1435 }
1436 }
1437
1438 return handle;
1439 }
1440 }
1441 return KoFlake::NoHandle;
1442}
1443
1445{
1447
1448 QTransform matrix = selection->absoluteTransformation();
1449 m_selectionOutline = matrix.map(QPolygonF(selection->outlineRect()));
1450 m_angle = 0.0;
1451
1452 QPolygonF outline = m_selectionOutline; //shorter name in the following :)
1453 m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2;
1454 m_selectionBox[KoFlake::TopRightHandle] = outline.value(1);
1455 m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2;
1456 m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2);
1457 m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2;
1458 m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3);
1459 m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2;
1460 m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0);
1461 if (selection->count() == 1) {
1462#if 0 // TODO detect mirroring
1464
1465 if (s->scaleX() < 0) { // vertically mirrored: swap left / right
1469 }
1470 if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom
1474 }
1475#endif
1476 }
1477}
1478
1479void DefaultTool::activate(const QSet<KoShape *> &shapes)
1480{
1481 KoToolBase::activate(shapes);
1482
1483 QAction *actionBringToFront = action("object_order_front");
1484 connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront()), Qt::UniqueConnection);
1485
1486 QAction *actionRaise = action("object_order_raise");
1487 connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp()), Qt::UniqueConnection);
1488
1489 QAction *actionLower = action("object_order_lower");
1490 connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown()));
1491
1492 QAction *actionSendToBack = action("object_order_back");
1493 connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack()), Qt::UniqueConnection);
1494
1495 QAction *actionGroupBottom = action("object_group");
1496 connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup()), Qt::UniqueConnection);
1497
1498 QAction *actionUngroupBottom = action("object_ungroup");
1499 connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup()), Qt::UniqueConnection);
1500
1501 QAction *actionSplit = action("object_split");
1502 connect(actionSplit, SIGNAL(triggered()), this, SLOT(selectionSplitShapes()), Qt::UniqueConnection);
1503
1504 connect(m_alignSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionAlign(int)));
1505 connect(m_distributeSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionDistribute(int)));
1506 connect(m_transformSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionTransform(int)));
1507 connect(m_booleanSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionBooleanOp(int)));
1508 connect(m_textTypeSignalsMapper, SIGNAL(mapped(int)), SLOT(slotChangeTextType(int)));
1509 connect(m_textFlowSignalsMapper, SIGNAL(mapped(int)), SLOT(slotReorderFlowShapes(int)));
1510
1511 QAction *actionTextInside = action("add_shape_to_flow_area");
1512 connect(actionTextInside, SIGNAL(triggered()), this, SLOT(slotAddShapesToFlow()), Qt::UniqueConnection);
1513
1514 QAction *actionTextSubtract = action("subtract_shape_from_flow_area");
1515 connect(actionTextSubtract, SIGNAL(triggered()), this, SLOT(slotSubtractShapesFromFlow()), Qt::UniqueConnection);
1516
1517 QAction *actionTextOnPath = action("put_text_on_path");
1518 connect(actionTextOnPath, SIGNAL(triggered()), this, SLOT(slotPutTextOnPath()), Qt::UniqueConnection);
1519
1520 QAction *actionTextRemoveFlow = action("remove_shapes_from_text_flow");
1521 connect(actionTextRemoveFlow, SIGNAL(triggered()), this, SLOT(slotRemoveShapesFromFlow()), Qt::UniqueConnection);
1522
1523 QAction *actionTextFlowToggle = action("flow_shape_type_toggle");
1524 connect(actionTextFlowToggle, SIGNAL(triggered()), this, SLOT(slotToggleFlowShapeType()), Qt::UniqueConnection);
1525
1528 useCursor(Qt::ArrowCursor);
1530 updateActions();
1531
1532 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->canvas());
1533 if (canvas2) {
1536 }
1537
1540 }
1541}
1542
1544{
1546
1547 QAction *actionBringToFront = action("object_order_front");
1548 disconnect(actionBringToFront, 0, this, 0);
1549
1550 QAction *actionRaise = action("object_order_raise");
1551 disconnect(actionRaise, 0, this, 0);
1552
1553 QAction *actionLower = action("object_order_lower");
1554 disconnect(actionLower, 0, this, 0);
1555
1556 QAction *actionSendToBack = action("object_order_back");
1557 disconnect(actionSendToBack, 0, this, 0);
1558
1559 QAction *actionGroupBottom = action("object_group");
1560 disconnect(actionGroupBottom, 0, this, 0);
1561
1562 QAction *actionUngroupBottom = action("object_ungroup");
1563 disconnect(actionUngroupBottom, 0, this, 0);
1564
1565 QAction *actionSplit = action("object_split");
1566 disconnect(actionSplit, 0, this, 0);
1567
1568 disconnect(m_alignSignalsMapper, 0, this, 0);
1569 disconnect(m_distributeSignalsMapper, 0, this, 0);
1570 disconnect(m_transformSignalsMapper, 0, this, 0);
1571 disconnect(m_booleanSignalsMapper, 0, this, 0);
1572 disconnect(m_textTypeSignalsMapper, 0, this, 0);
1573 disconnect(m_textFlowSignalsMapper, 0, this, 0);
1574
1575 QAction *actionTextInside = action("add_shape_to_flow_area");
1576 disconnect(actionTextInside, 0, this, 0);
1577 QAction *actionTextSubtract = action("subtract_shape_from_flow_area");
1578 disconnect(actionTextSubtract, 0, this, 0);
1579 QAction *actionTextOnPath = action("put_text_on_path");
1580 disconnect(actionTextOnPath, 0, this, 0);
1581 QAction *actionTextRemoveFlow = action("remove_shapes_from_text_flow");
1582 disconnect(actionTextRemoveFlow, 0, this, 0);
1583 QAction *actionTextFlowToggle = action("flow_shape_type_toggle");
1584 disconnect(actionTextFlowToggle, 0, this, 0);
1585
1586 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->canvas());
1587 if (canvas2) {
1590 }
1591
1594 }
1595}
1596
1598{
1600 if (!selection) return;
1601
1602 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1603 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
1604 if (selectedShapes.isEmpty()) return;
1605
1606 const int groupZIndex = selectedShapes.last()->zIndex();
1607
1608 KoShapeGroup *group = new KoShapeGroup();
1609 group->setZIndex(groupZIndex);
1610 // TODO what if only one shape is left?
1611 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes"));
1612 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1613 canvas()->shapeController()->addShapeDirect(group, 0, cmd);
1614 new KoShapeGroupCommand(group, selectedShapes, true, cmd);
1615 new KoKeepShapesSelectedCommand({}, {group}, canvas()->selectedShapesProxy(), true, cmd);
1616 canvas()->addCommand(cmd);
1617
1618 // update selection so we can ungroup immediately again
1619 selection->deselectAll();
1620 selection->select(group);
1621}
1622
1624{
1626 if (!selection) return;
1627
1628 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1629 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
1630
1631 KUndo2Command *cmd = 0;
1632 QList<KoShape*> newShapes;
1633
1634 // add a ungroup command for each found shape container to the macro command
1635 Q_FOREACH (KoShape *shape, selectedShapes) {
1636 KoShapeGroup *group = dynamic_cast<KoShapeGroup *>(shape);
1637 if (group) {
1638 if (!cmd) {
1639 cmd = new KUndo2Command(kundo2_i18n("Ungroup shapes"));
1640 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1641 }
1642 newShapes << group->shapes();
1643 new KoShapeUngroupCommand(group, group->shapes(),
1645 cmd);
1646 canvas()->shapeController()->removeShape(group, cmd);
1647 }
1648 }
1649 if (cmd) {
1650 new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
1651 canvas()->addCommand(cmd);
1652 }
1653}
1654
1655void DefaultTool::selectionTransform(int transformAction)
1656{
1658 if (!selection) return;
1659
1660 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1661 if (editableShapes.isEmpty()) {
1662 return;
1663 }
1664
1665 QTransform applyTransform;
1666 bool shouldReset = false;
1667 KUndo2MagicString actionName = kundo2_noi18n("BUG: No transform action");
1668
1669
1670 switch (TransformActionType(transformAction)) {
1671 case TransformRotate90CW:
1672 applyTransform.rotate(90.0);
1673 actionName = kundo2_i18n("Rotate Object 90° CW");
1674 break;
1675 case TransformRotate90CCW:
1676 applyTransform.rotate(-90.0);
1677 actionName = kundo2_i18n("Rotate Object 90° CCW");
1678 break;
1679 case TransformRotate180:
1680 applyTransform.rotate(180.0);
1681 actionName = kundo2_i18n("Rotate Object 180°");
1682 break;
1683 case TransformMirrorX:
1684 applyTransform.scale(-1.0, 1.0);
1685 actionName = kundo2_i18n("Mirror Object Horizontally");
1686 break;
1687 case TransformMirrorY:
1688 applyTransform.scale(1.0, -1.0);
1689 actionName = kundo2_i18n("Mirror Object Vertically");
1690 break;
1691 case TransformReset:
1692 shouldReset = true;
1693 actionName = kundo2_i18n("Reset Object Transformations");
1694 break;
1695 }
1696
1697 if (!shouldReset && applyTransform.isIdentity()) return;
1698
1699 QList<QTransform> oldTransforms;
1700 QList<QTransform> newTransforms;
1701
1702 const QRectF outlineRect = KoShape::absoluteOutlineRect(editableShapes);
1703 const QPointF centerPoint = outlineRect.center();
1704 const QTransform centerTrans = QTransform::fromTranslate(centerPoint.x(), centerPoint.y());
1705 const QTransform centerTransInv = QTransform::fromTranslate(-centerPoint.x(), -centerPoint.y());
1706
1707 // we also add selection to the list of transformed shapes, so that its outline is updated correctly
1708 QList<KoShape*> transformedShapes = editableShapes;
1709 transformedShapes << selection;
1710
1711 Q_FOREACH (KoShape *shape, transformedShapes) {
1712 oldTransforms.append(shape->transformation());
1713
1714 QTransform t;
1715
1716 if (!shouldReset) {
1717 const QTransform world = shape->absoluteTransformation();
1718 t = world * centerTransInv * applyTransform * centerTrans * world.inverted() * shape->transformation();
1719 } else {
1720 const QPointF center = shape->outlineRect().center();
1721 const QPointF offset = shape->transformation().map(center) - center;
1722 t = QTransform::fromTranslate(offset.x(), offset.y());
1723 }
1724
1725 newTransforms.append(t);
1726 }
1727
1728 KoShapeTransformCommand *cmd = new KoShapeTransformCommand(transformedShapes, oldTransforms, newTransforms);
1729 cmd->setText(actionName);
1730 canvas()->addCommand(cmd);
1731}
1732
1734{
1736 if (!selection) return;
1737
1738 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1739 if (editableShapes.isEmpty()) {
1740 return;
1741 }
1742
1743 QVector<QPainterPath> srcOutlines;
1744 QPainterPath dstOutline;
1745 KUndo2MagicString actionName = kundo2_noi18n("BUG: boolean action name");
1746
1747 // TODO: implement a reference shape selection dialog!
1748 const int referenceShapeIndex = 0;
1749 KoShape *referenceShape = editableShapes[referenceShapeIndex];
1750
1751 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
1753 const QTransform booleanWorkaroundTransform =
1755
1756 Q_FOREACH (KoShape *shape, editableShapes) {
1757 srcOutlines <<
1758 booleanWorkaroundTransform.map(
1759 shape->absoluteTransformation().map(
1760 shape->outline()));
1761 }
1762
1763 if (booleanOp == BooleanUnion) {
1764 Q_FOREACH (const QPainterPath &path, srcOutlines) {
1765 dstOutline |= path;
1766 }
1767 actionName = kundo2_i18n("Unite Shapes");
1768 } else if (booleanOp == BooleanIntersection) {
1769 for (int i = 0; i < srcOutlines.size(); i++) {
1770 if (i == 0) {
1771 dstOutline = srcOutlines[i];
1772 } else {
1773 dstOutline &= srcOutlines[i];
1774 }
1775 }
1776
1777 // there is a bug in Qt, sometimes it leaves the resulting
1778 // outline open, so just close it explicitly.
1779 dstOutline.closeSubpath();
1780
1781 actionName = kundo2_i18n("Intersect Shapes");
1782
1783 } else if (booleanOp == BooleanSubtraction) {
1784 for (int i = 0; i < srcOutlines.size(); i++) {
1785 dstOutline = srcOutlines[referenceShapeIndex];
1786 if (i != referenceShapeIndex) {
1787 dstOutline -= srcOutlines[i];
1788 }
1789 }
1790
1791 actionName = kundo2_i18n("Subtract Shapes");
1792 }
1793
1794 dstOutline = booleanWorkaroundTransform.inverted().map(dstOutline);
1795
1796 KoShape *newShape = 0;
1797
1798 if (!dstOutline.isEmpty()) {
1799 newShape = KoPathShape::createShapeFromPainterPath(dstOutline);
1800 }
1801
1802 KUndo2Command *cmd = new KUndo2Command(actionName);
1803
1804 new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1805
1806 QList<KoShape*> newSelectedShapes;
1807
1808 if (newShape) {
1809 newShape->setBackground(referenceShape->background());
1810 newShape->setStroke(referenceShape->stroke());
1811 newShape->setZIndex(referenceShape->zIndex());
1812
1813 KoShapeContainer *parent = referenceShape->parent();
1814 canvas()->shapeController()->addShapeDirect(newShape, parent, cmd);
1815
1816 newSelectedShapes << newShape;
1817 }
1818
1819 canvas()->shapeController()->removeShapes(editableShapes, cmd);
1820
1821 new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(), true, cmd);
1822
1823 canvas()->addCommand(cmd);
1824}
1825
1827{
1829 if (!selection) return;
1830
1831 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1832 if (editableShapes.isEmpty()) {
1833 return;
1834 }
1835
1836 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Split Shapes"));
1837
1838 new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1839 QList<KoShape*> newShapes;
1840
1841 Q_FOREACH (KoShape *shape, editableShapes) {
1842 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
1843 if (!pathShape) return;
1844
1845 QList<KoPathShape*> splitShapes;
1846 if (pathShape->separate(splitShapes)) {
1847 QList<KoShape*> normalShapes = implicitCastList<KoShape*>(splitShapes);
1848
1849 KoShapeContainer *parent = shape->parent();
1850 canvas()->shapeController()->addShapesDirect(normalShapes, parent, cmd);
1851 canvas()->shapeController()->removeShape(shape, cmd);
1852 newShapes << normalShapes;
1853 }
1854 }
1855
1856 new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
1857
1858 canvas()->addCommand(cmd);
1859}
1860
1862{
1864 static_cast<KoShapeAlignCommand::Align>(_align);
1865
1867 if (!selection) return;
1868
1869 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1870 if (editableShapes.isEmpty()) {
1871 return;
1872 }
1873
1874 // TODO add an option to the widget so that one can align to the page
1875 // with multiple selected shapes too
1876
1877 QRectF bb;
1878
1879 // single selected shape is automatically aligned to document rect
1880 if (editableShapes.count() == 1) {
1881 if (!canvas()->resourceManager()->hasResource(KoCanvasResource::PageSize)) {
1882 return;
1883 }
1884 bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResource::PageSize));
1885 } else {
1886 bb = KoShape::absoluteOutlineRect(editableShapes);
1887 }
1888
1889 KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb);
1890 canvas()->addCommand(cmd);
1891}
1892
1894{
1896 static_cast<KoShapeDistributeCommand::Distribute>(_distribute);
1897
1899 if (!selection) return;
1900
1901 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1902 if (editableShapes.size() < 3) {
1903 return;
1904 }
1905
1906 QRectF bb = KoShape::absoluteOutlineRect(editableShapes);
1907 KoShapeDistributeCommand *cmd = new KoShapeDistributeCommand(editableShapes, distribute, bb);
1908 canvas()->addCommand(cmd);
1909}
1910
1915
1920
1925
1930
1932{
1934 if (!selection) {
1935 return;
1936 }
1937
1938 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1939 if (selectedShapes.isEmpty()) {
1940 return;
1941 }
1942
1943 KUndo2Command *cmd = KoShapeReorderCommand::createCommand(selectedShapes, shapeManager(), order);
1944 if (cmd) {
1945 canvas()->addCommand(cmd);
1946 }
1947}
1948
1950{
1951 QList<QPointer<QWidget> > widgets;
1952
1954
1955 if (isActivated()) {
1957 }
1958 widgets.append(m_tabbedOptionWidget);
1959
1960 connect(m_tabbedOptionWidget,
1961 SIGNAL(sigSwitchModeEditFillGradient(bool)),
1962 SLOT(slotActivateEditFillGradient(bool)));
1963
1964 connect(m_tabbedOptionWidget,
1965 SIGNAL(sigSwitchModeEditStrokeGradient(bool)),
1966 SLOT(slotActivateEditStrokeGradient(bool)));
1967
1968 connect(m_tabbedOptionWidget,
1969 SIGNAL(sigSwitchModeEditFillGradient(bool)),
1971 // TODO: strokes!!
1972
1973 connect(m_tabbedOptionWidget,
1974 SIGNAL(sigMeshGradientResetted()),
1976
1977 return widgets;
1978}
1979
1980void DefaultTool::canvasResourceChanged(int key, const QVariant &res)
1981{
1982 if (key == HotPosition) {
1985 }
1986}
1987
1989{
1991 if (!selection) return nullptr;
1992
1993 bool insideSelection = false;
1994 KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection);
1995
1996 bool editableShape = !selection->selectedEditableShapes().isEmpty();
1997
1998 const bool selectMultiple = event->modifiers() & Qt::ShiftModifier;
1999 const bool selectNextInStack = event->modifiers() & Qt::ControlModifier;
2000 const bool avoidSelection = event->modifiers() & Qt::AltModifier;
2001
2002 if (selectNextInStack) {
2003 // change the hot selection position when middle clicking on a handle
2004 KoFlake::AnchorPosition newHotPosition = m_hotPosition;
2005 switch (handle) {
2007 newHotPosition = KoFlake::Top;
2008 break;
2010 newHotPosition = KoFlake::TopRight;
2011 break;
2013 newHotPosition = KoFlake::Right;
2014 break;
2016 newHotPosition = KoFlake::BottomRight;
2017 break;
2019 newHotPosition = KoFlake::Bottom;
2020 break;
2022 newHotPosition = KoFlake::BottomLeft;
2023 break;
2025 newHotPosition = KoFlake::Left;
2026 break;
2028 newHotPosition = KoFlake::TopLeft;
2029 break;
2030 case KoFlake::NoHandle:
2031 default:
2032 // check if we had hit the center point
2033 const KoViewConverter *converter = canvas()->viewConverter();
2034 QPointF pt = converter->documentToView(event->point);
2035
2036 // TODO: use calculated values instead!
2037 QPointF centerPt = converter->documentToView(selection->absolutePosition());
2038
2039 if (kisSquareDistance(pt, centerPt) < HANDLE_DISTANCE_SQ) {
2040 newHotPosition = KoFlake::Center;
2041 }
2042
2043 break;
2044 }
2045
2046 if (m_hotPosition != newHotPosition) {
2047 canvas()->resourceManager()->setResource(HotPosition, newHotPosition);
2048 return new NopInteractionStrategy(this);
2049 }
2050 }
2051
2052 if (!avoidSelection && editableShape) {
2053 // manipulation of selected shapes goes first
2054 if (handle != KoFlake::NoHandle) {
2055 // resizing or shearing only with left mouse button
2056 if (insideSelection) {
2057 bool forceUniformScaling = m_tabbedOptionWidget && m_tabbedOptionWidget->useUniformScaling();
2058 return new ShapeResizeStrategy(this, selection, event->point, handle, forceUniformScaling);
2059 }
2060
2061 if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle ||
2063
2064 return new ShapeShearStrategy(this, selection, event->point, handle);
2065 }
2066
2067 // rotating is allowed for right mouse button too
2068 if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle ||
2070
2071 return new ShapeRotateStrategy(this, selection, event->point, event->buttons());
2072 }
2073 }
2074
2075 if (!selectMultiple && !selectNextInStack) {
2076
2077 if (insideSelection) {
2078 return new ShapeMoveStrategy(this, selection, event->point);
2079 }
2080 }
2081 }
2082
2083 KoShape *shape = shapeManager()->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop);
2084
2085 if (avoidSelection || (!shape && handle == KoFlake::NoHandle)) {
2086 if (!selectMultiple) {
2087 selection->deselectAll();
2088 }
2089 return new SelectionInteractionStrategy(this, event->point, false);
2090 }
2091
2092 if (selection->isSelected(shape)) {
2093 if (selectMultiple) {
2094 selection->deselect(shape);
2095 }
2096 } else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected
2097 if (!selectMultiple) {
2098 selection->deselectAll();
2099 }
2100 selection->select(shape);
2101 // tablet selection isn't precise and may lead to a move, preventing that
2102 if (event->isTabletEvent()) {
2103 return new NopInteractionStrategy(this);
2104 }
2105 return new ShapeMoveStrategy(this, selection, event->point);
2106 }
2107 return 0;
2108}
2109
2111{
2112 QList<KoShape*> editableShapes;
2113
2114 if (koSelection()) {
2115 editableShapes = koSelection()->selectedEditableShapes();
2116 }
2117
2118 const bool hasEditableShapes = !editableShapes.isEmpty();
2119
2120 action("object_order_front")->setEnabled(hasEditableShapes);
2121 action("object_order_raise")->setEnabled(hasEditableShapes);
2122 action("object_order_lower")->setEnabled(hasEditableShapes);
2123 action("object_order_back")->setEnabled(hasEditableShapes);
2124
2125 action("object_transform_rotate_90_cw")->setEnabled(hasEditableShapes);
2126 action("object_transform_rotate_90_ccw")->setEnabled(hasEditableShapes);
2127 action("object_transform_rotate_180")->setEnabled(hasEditableShapes);
2128 action("object_transform_mirror_horizontally")->setEnabled(hasEditableShapes);
2129 action("object_transform_mirror_vertically")->setEnabled(hasEditableShapes);
2130 action("object_transform_reset")->setEnabled(hasEditableShapes);
2131
2132 const bool multipleSelected = editableShapes.size() > 1;
2133
2134 const bool alignmentEnabled =
2135 multipleSelected ||
2136 (!editableShapes.isEmpty() &&
2138
2139 action("object_align_horizontal_left")->setEnabled(alignmentEnabled);
2140 action("object_align_horizontal_center")->setEnabled(alignmentEnabled);
2141 action("object_align_horizontal_right")->setEnabled(alignmentEnabled);
2142 action("object_align_vertical_top")->setEnabled(alignmentEnabled);
2143 action("object_align_vertical_center")->setEnabled(alignmentEnabled);
2144 action("object_align_vertical_bottom")->setEnabled(alignmentEnabled);
2145
2146 const bool distributionEnabled = editableShapes.size() > 2;
2147
2148 action("object_distribute_horizontal_left")->setEnabled(distributionEnabled);
2149 action("object_distribute_horizontal_center")->setEnabled(distributionEnabled);
2150 action("object_distribute_horizontal_right")->setEnabled(distributionEnabled);
2151 action("object_distribute_horizontal_gaps")->setEnabled(distributionEnabled);
2152
2153 action("object_distribute_vertical_top")->setEnabled(distributionEnabled);
2154 action("object_distribute_vertical_center")->setEnabled(distributionEnabled);
2155 action("object_distribute_vertical_bottom")->setEnabled(distributionEnabled);
2156 action("object_distribute_vertical_gaps")->setEnabled(distributionEnabled);
2157
2158 /* Handling the text actions */
2159 bool textShape = false;
2160 bool otherShapes = false;
2161 bool filledShapes = false;
2162 bool shapesInside = false;
2164 const bool editFlowShapes = bool(currentTextShapeGroup);
2165 Q_FOREACH(KoShape *shape, editableShapes) {
2166 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape *>(shape);
2167 if (text && !textShape) {
2168 textShape = true;
2169 } else {
2170 otherShapes = true;
2171 KoPathShape *path = dynamic_cast<KoPathShape*>(shape);
2172 filledShapes = filledShapes? filledShapes: (path && path->isClosedSubpath(0));
2173 if (editFlowShapes) {
2174 if (!shapesInside && currentTextShapeGroup->shapesInside().contains(shape)) {
2175 shapesInside = true;
2176 }
2177 }
2178 }
2179 if (textShape && otherShapes) break;
2180 }
2181 const bool editContours = textShape && otherShapes;
2182 const bool editFilledContours = textShape && filledShapes;
2183
2184 action("add_shape_to_flow_area")->setEnabled(editFilledContours);
2185 action("subtract_shape_from_flow_area")->setEnabled(editFilledContours);
2186 action("put_text_on_path")->setEnabled(editContours);
2187 action("remove_shapes_from_text_flow")->setEnabled(editFlowShapes);
2188 action("flow_shape_type_toggle")->setEnabled(editFlowShapes);
2189 action("flow_shape_order_back")->setEnabled(shapesInside);
2190 action("flow_shape_order_earlier")->setEnabled(shapesInside);
2191 action("flow_shape_order_later")->setEnabled(shapesInside);
2192 action("flow_shape_order_front")->setEnabled(shapesInside);
2193
2194 updateDistinctiveActions(editableShapes);
2195
2196 Q_EMIT selectionChanged(editableShapes.size());
2197}
2198
2200 const bool multipleSelected = editableShapes.size() > 1;
2201
2202 action("object_group")->setEnabled(multipleSelected);
2203
2204 action("object_unite")->setEnabled(multipleSelected);
2205 action("object_intersect")->setEnabled(multipleSelected);
2206 action("object_subtract")->setEnabled(multipleSelected);
2207
2208 bool hasShapesWithMultipleSegments = false;
2209 Q_FOREACH (KoShape *shape, editableShapes) {
2210 KoPathShape *pathShape = dynamic_cast<KoPathShape *>(shape);
2211 if (pathShape && pathShape->subpathCount() > 1) {
2212 hasShapesWithMultipleSegments = true;
2213 break;
2214 }
2215 }
2216 action("object_split")->setEnabled(hasShapesWithMultipleSegments);
2217
2218
2219 bool hasGroupShape = false;
2220 foreach (KoShape *shape, editableShapes) {
2221 if (dynamic_cast<KoShapeGroup *>(shape)) {
2222 hasGroupShape = true;
2223 break;
2224 }
2225 }
2226 action("object_ungroup")->setEnabled(hasGroupShape);
2227
2228 bool enablePreformatted = false;
2229 bool enablePrePositioned = false;
2230 bool enableInlineWrapped = false;
2231 bool text = false;
2232 Q_FOREACH (KoShape *shape, editableShapes) {
2233 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape *>(shape);
2234 if (textShape) {
2235 text = true;
2236 if (textShape->textType() != KoSvgTextShape::PreformattedText && !enablePreformatted) {
2237 enablePreformatted = true;
2238 }
2239 if (textShape && textShape->textType() != KoSvgTextShape::PrePositionedText && !enablePrePositioned) {
2240 enablePrePositioned = true;
2241 }
2242 if (textShape && textShape->textType() != KoSvgTextShape::InlineWrap && !enableInlineWrapped) {
2243 enableInlineWrapped = true;
2244 }
2245 }
2246 }
2247 QActionGroup *group = action("text_type_preformatted")->actionGroup();
2248 if (group) {
2249 group->setEnabled(text);
2250 }
2251
2252 action("text_type_preformatted")->setEnabled(enablePreformatted);
2253 action("text_type_pre_positioned")->setEnabled(enablePrePositioned);
2254 action("text_type_inline_wrap")->setEnabled(enableInlineWrapped);
2255}
2256
2257
2262
2264{
2265 if (m_contextMenu) {
2266 m_contextMenu->clear();
2267
2268 m_contextMenu->addSection(i18n("Vector Shape Actions"));
2269 m_contextMenu->addSeparator();
2270
2271 QMenu *transform = m_contextMenu->addMenu(i18n("Transform"));
2272
2273 transform->addAction(action("object_transform_rotate_90_cw"));
2274 transform->addAction(action("object_transform_rotate_90_ccw"));
2275 transform->addAction(action("object_transform_rotate_180"));
2276 transform->addSeparator();
2277 transform->addAction(action("object_transform_mirror_horizontally"));
2278 transform->addAction(action("object_transform_mirror_vertically"));
2279 transform->addSeparator();
2280 transform->addAction(action("object_transform_reset"));
2281
2282 if (action("object_unite")->isEnabled() ||
2283 action("object_intersect")->isEnabled() ||
2284 action("object_subtract")->isEnabled() ||
2285 action("object_split")->isEnabled()) {
2286
2287 QMenu *transform = m_contextMenu->addMenu(i18n("Logical Operations"));
2288 transform->addAction(action("object_unite"));
2289 transform->addAction(action("object_intersect"));
2290 transform->addAction(action("object_subtract"));
2291 transform->addAction(action("object_split"));
2292 }
2293
2294 m_contextMenu->addSeparator();
2295
2296 m_contextMenu->addAction(action("edit_cut"));
2297 m_contextMenu->addAction(action("edit_copy"));
2298 m_contextMenu->addAction(action("edit_paste"));
2299 m_contextMenu->addAction(action("paste_at"));
2300
2301 m_contextMenu->addSeparator();
2302
2303 m_contextMenu->addAction(action("object_order_front"));
2304 m_contextMenu->addAction(action("object_order_raise"));
2305 m_contextMenu->addAction(action("object_order_lower"));
2306 m_contextMenu->addAction(action("object_order_back"));
2307
2308 if (action("object_group")->isEnabled() || action("object_ungroup")->isEnabled()) {
2309 m_contextMenu->addSeparator();
2310 m_contextMenu->addAction(action("object_group"));
2311 m_contextMenu->addAction(action("object_ungroup"));
2312 }
2313 m_contextMenu->addSeparator();
2314 m_contextMenu->addAction(action("convert_shapes_to_vector_selection"));
2315
2316 m_contextMenu->addSeparator();
2317 QMenu *text = m_contextMenu->addMenu(i18n("Text"));
2318 text->addAction(action("add_shape_to_flow_area"));
2319 text->addAction(action("subtract_shape_from_flow_area"));
2320 text->addAction(action("put_text_on_path"));
2321 text->addSeparator();
2322 text->addAction(action("text_type_preformatted"));
2323 text->addAction(action("text_type_inline_wrap"));
2324 text->addAction(action("text_type_pre_positioned"));
2325 text->addSeparator();
2326 text->addAction(action("remove_shapes_from_text_flow"));
2327 text->addAction(action("flow_shape_type_toggle"));
2328 text->addSeparator();
2329 text->addAction(action("flow_shape_order_back"));
2330 text->addAction(action("flow_shape_order_earlier"));
2331 text->addAction(action("flow_shape_order_later"));
2332 text->addAction(action("flow_shape_order_front"));
2333 }
2334
2335 return m_contextMenu.data();
2336}
2337
2338void DefaultTool::addTransformActions(QMenu *menu) const {
2339 menu->addAction(action("object_transform_rotate_90_cw"));
2340 menu->addAction(action("object_transform_rotate_90_ccw"));
2341 menu->addAction(action("object_transform_rotate_180"));
2342 menu->addSeparator();
2343 menu->addAction(action("object_transform_mirror_horizontally"));
2344 menu->addAction(action("object_transform_mirror_vertically"));
2345 menu->addSeparator();
2346 menu->addAction(action("object_transform_reset"));
2347}
2348
2350{
2352 QString tool = KoToolManager::instance()->preferredToolForSelection(shapes);
2353 QTimer::singleShot(0, [tool = std::move(tool)]() {
2355 });
2356}
2357
2359
2361 : parent(parent)
2362 , compressor(10, KisSignalCompressor::POSTPONE){}
2363
2367};
2368
2371 , d(new Private(parent))
2372{
2373 connect(&d->compressor, SIGNAL(timeout()), this, SIGNAL(textSelectionChanged()));
2374}
2375
2380
2382{
2384 if (!d->parent->selection()->hasSelection()) return props;
2385
2386 QList<KoShape*> shapes = d->shapes;
2387 for (auto it = shapes.begin(); it != shapes.end(); it++) {
2388 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(*it);
2389 if (!textShape) continue;
2390 KoSvgTextProperties p = textShape->textProperties();
2391 props.append(p);
2392 }
2393
2394 return props;
2395}
2396
2401
2406
2407void DefaultToolTextPropertiesInterface::setPropertiesOnSelected(KoSvgTextProperties properties, QSet<KoSvgTextProperties::PropertyId> removeProperties)
2408{
2409 if (d->shapes.isEmpty()) return;
2410 KUndo2Command *cmd = new KoShapeMergeTextPropertiesCommand(d->shapes, properties, removeProperties);
2411 if (cmd) {
2412 d->parent->canvas()->addCommand(cmd);
2413 }
2414}
2415
2416void DefaultToolTextPropertiesInterface::setCharacterPropertiesOnSelected(KoSvgTextProperties properties, QSet<KoSvgTextProperties::PropertyId> removeProperties)
2417{
2418 Q_UNUSED(properties)
2419 Q_UNUSED(removeProperties)
2420 return;
2421}
2422
2424{
2425 return false;
2426}
2427
2432
2434{
2435 Q_UNUSED(pos)
2436 Q_UNUSED(anchor)
2437 d->compressor.start();
2438}
2439
2441{
2442 d->compressor.start();
2443}
2444
2446{
2447 Q_UNUSED(type)
2448 Q_UNUSED(shape)
2449 d->compressor.start();
2450}
2451
2453{
2454 Q_FOREACH(KoShape *shape, d->shapes) {
2455 shape->removeShapeChangeListener(this);
2456 }
2457 d->shapes.clear();
2458}
2459
2461{
2462 if (d->parent->updateTextContourMode()) return;
2463 Q_FOREACH(KoShape *shape, d->shapes) {
2464 if (!shape) continue;
2465 shape->removeShapeChangeListener(this);
2466 }
2467
2468 auto *textShapeGroup = d->parent->tryFetchCurrentShapeManagerOwnerTextShape();
2469
2470 if (textShapeGroup) {
2471 d->shapes = {textShapeGroup};
2472 } else {
2473 d->shapes = d->parent->canvas()->selectedShapesProxy()->selection()->selectedEditableShapes();
2474 }
2475
2476 Q_FOREACH(KoShape *shape, d->shapes) {
2477 if (!shape) continue;
2478 shape->addShapeChangeListener(this);
2479 }
2480 d->compressor.start();
2481}
#define HANDLE_DISTANCE
#define INNER_HANDLE_DISTANCE_SQ
#define HANDLE_DISTANCE_SQ
float value(const T *src, size_t ch)
const Params2D p
bool paintOnHover(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) override
KoShapeGradientHandles::Handle m_currentHandle
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
bool paintOnHover(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) override
KoShapeMeshGradientHandles::Handle handleAt(const QPointF &pos) const
MoveMeshGradientHandleInteractionFactory(KoFlake::FillVariant fillVariant, int priority, const QString &id, DefaultTool *_q)
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 the Enter key or an...
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
void slotRemoveShapesFromFlow()
virtual KoSelection * koSelection() const
DefaultTool(KoCanvasBase *canvas, bool connectToSelectedShapesProxy=false)
void slotAddShapesToFlow()
qreal rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation)
Returns rotation angle of given handle of the current selection.
void slotReorderFlowShapes(int type)
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 slotSubtractShapesFromFlow()
void slotPutTextOnPath()
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 slotToggleFlowShapeType()
bool updateTextContourMode()
void recalcSelectionBox(KoSelection *selection)
void selectionBooleanOp(int booleanOp)
QScopedPointer< KoSvgTextShapeOutlineHelper > m_textOutlineHelper
KisSignalMapper * m_alignSignalsMapper
KisSignalMapper * m_textFlowSignalsMapper
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
KoSvgTextShape * tryFetchCurrentShapeManagerOwnerTextShape() const
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
virtual KoShape * currentShapeManagerOwnerShape() const
the shape that owns the currently active shape manager
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, const KoColorDisplayRendererInterface *displayRendererInterface) override
The undo / redo command for ungrouping shapes.
virtual QRectF outlineRect() const
Definition KoShape.cpp:561
void addShapeChangeListener(ShapeChangeListener *listener)
Definition KoShape.cpp:1152
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:782
virtual QPainterPath outline() const
Definition KoShape.cpp:554
bool isSelectable() const
Definition KoShape.cpp:832
QPointF absolutePosition(KoFlake::AnchorPosition anchor=KoFlake::Center) const
Definition KoShape.cpp:568
void removeShapeChangeListener(ShapeChangeListener *listener)
Definition KoShape.cpp:1160
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:885
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:388
bool isGeometryProtected() const
Definition KoShape.cpp:842
QRectF absoluteOutlineRect() const
Definition KoShape.cpp:319
KoShapeContainer * parent() const
Definition KoShape.cpp:857
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:899
QTransform absoluteTransformation() const
Definition KoShape.cpp:330
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:746
ChangeType
Used by shapeChanged() to select which change was made.
Definition KoShape.h:92
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:754
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:378
qint16 zIndex() const
Definition KoShape.cpp:524
void paint(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRenderer)
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.
void textSelectionChanged()
Emit to signal to KisTextPropertiesManager to call getSelectedProperties.
@ TextAnchorId
KoSvgText::TextAnchor.
QVariant propertyOrDefault(PropertyId id) const
static void removeContourShapesFromFlow(KoSvgTextShape *textShape, KUndo2Command *parent, bool textInShape, bool textPaths)
removeContourShapesFromFlow Create a command to remove all contour shapes of a certain type from the ...
The KoSvgTextReorderShapeInsideCommand class Within a text shape, the order of the shapes inside dete...
The KoSvgTextShapeOutlineHelper class helper class that draws the text outlines and contour mode butt...
KoSvgText::Direction direction() const
direction Whether the text is left to right or right to left.
@ PreformattedText
Text-on-Path falls under this or PrePositionedText depending on collapse of lines.
@ TextInShape
Uses shape-inside to wrap and preserves spaces.
@ InlineWrap
Uses inline size to wrap and preserves spaces.
QList< KoShape * > shapesInside
bool shapeInContours(KoShape *shape)
shapeInContours
TextType textType() const
textType This enum gives an indication of what kind of text this shape is. The different text types a...
int posForIndex(int index, bool firstIndex=false, bool skipSynthetic=false) const
posForIndex Get the cursor position for a given index in a string.
QList< KoShape * > shapesSubtract
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 finishInteraction(Qt::KeyboardModifiers) override
void paint(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) 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
SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid)
void paint(QPainter &painter, const KoViewConverter &converter, const KoColorDisplayRendererInterface *displayRendererInterface) override
#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
TextAnchor
Where the text is anchored for SVG 1.1 text and 'inline-size'.
Definition KoSvgText.h:79
@ AnchorEnd
Anchor right for LTR, left for RTL.
Definition KoSvgText.h:82
@ AnchorStart
Anchor left for LTR, right for RTL.
Definition KoSvgText.h:80
@ AnchorMiddle
Anchor to the middle.
Definition KoSvgText.h:81
@ DirectionLeftToRight
Definition KoSvgText.h:49
@ DirectionRightToLeft
Definition KoSvgText.h:50
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 bool characterPropertiesEnabled() override
Whether character selections are possible at all.
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 setCharacterPropertiesOnSelected(KoSvgTextProperties properties, QSet< KoSvgTextProperties::PropertyId > removeProperties=QSet< KoSvgTextProperties::PropertyId >()) override
setCharacterPropertiesOnSelected This sets the properties for a character selection instead of the fu...
virtual QList< KoSvgTextProperties > getCharacterProperties() override
getSelectedProperties
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 ...