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) override {
132 Q_UNUSED(painter);
133 Q_UNUSED(converter);
134 }
135};
136
138{
139public:
140 explicit SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid)
141 : KoShapeRubberSelectStrategy(parent, clicked, useSnapToGrid)
142 {
143 }
144
145 void paint(QPainter &painter, const KoViewConverter &converter) override {
146 KoShapeRubberSelectStrategy::paint(painter, converter);
147 }
148
149 void cancelInteraction() override
150 {
151 tool()->canvas()->updateCanvas(selectedRectangle() | tool()->decorationsRect());
152 }
153
154 void finishInteraction(Qt::KeyboardModifiers modifiers = QFlags<Qt::KeyboardModifier>()) override
155 {
156 Q_UNUSED(modifiers);
157 DefaultTool *defaultTool = dynamic_cast<DefaultTool*>(tool());
159
160 KoSelection * selection = defaultTool->koSelection();
161
162 const bool useContainedMode = currentMode() == CoveringSelection;
163
164 QList<KoShape *> shapes =
165 defaultTool->shapeManager()->
166 shapesAt(selectedRectangle(), true, useContainedMode);
167
168 Q_FOREACH (KoShape * shape, shapes) {
169 if (!shape->isSelectable()) continue;
170
171 selection->select(shape);
172 }
173
174 tool()->canvas()->updateCanvas(selectedRectangle() | tool()->decorationsRect());
175 }
176};
177#include <KoGradientBackground.h>
180
182{
183public:
185 int priority, const QString &id, DefaultTool *_q)
187 q(_q),
188 m_fillVariant(fillVariant)
189 {
190 }
191
205
206 bool hoverEvent(KoPointerEvent *ev) override
207 {
209 return false;
210 }
211
212 bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
213 {
214 Q_UNUSED(painter);
215 Q_UNUSED(converter);
216 return false;
217 }
218
219 bool tryUseCustomCursor() override {
221 q->useCursor(Qt::OpenHandCursor);
222 return true;
223 }
224
225 return false;
226 }
227
228private:
229
232 QList<KoShape*> shapes = selection->selectedEditableShapes();
233
234 KoShape *shape = 0;
235 if (shapes.size() == 1) {
236 shape = shapes.first();
237 }
238
239 return shape;
240 }
241
244
245 KoShape *shape = onlyEditableShape();
246 if (shape) {
247 KoFlake::SelectionHandle globalHandle = q->handleAt(pos);
248 const qreal distanceThresholdSq =
249 globalHandle == KoFlake::NoHandle ?
251
252 const KoViewConverter *converter = q->canvas()->viewConverter();
253 const QPointF viewPoint = converter->documentToView(pos);
254 qreal minDistanceSq = std::numeric_limits<qreal>::max();
255
257 auto handless = sh.handles();
258 Q_FOREACH (const KoShapeGradientHandles::Handle &handle, sh.handles()) {
259 const QPointF handlePoint = converter->documentToView(handle.pos);
260 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
261
262 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
263 result = handle;
264 minDistanceSq = distanceSq;
265 }
266 }
267 }
268
269 return result;
270 }
271
272private:
276};
277
280
282{
283public:
285 int priority,
286 const QString& id,
287 DefaultTool* _q)
289 , m_fillVariant(fillVariant)
290 , q(_q)
291 {
292 }
293
310
311 bool hoverEvent(KoPointerEvent *ev) override
312 {
313 // for custom cursor
315
316 // refresh
319 }
320
321 m_currentHandle = handle;
323
324 // highlight the decoration which is being hovered
327 }
328 return false;
329 }
330
331 bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
332 {
333 Q_UNUSED(painter);
334 Q_UNUSED(converter);
335 return false;
336 }
337
338 bool tryUseCustomCursor() override
339 {
341 q->useCursor(Qt::OpenHandCursor);
342 return true;
343 }
344
345 return false;
346 }
347
348
349private:
351 // FIXME: copy of KoShapeGradientHandles
353 QList<KoShape*> shapes = selection->selectedEditableShapes();
354
355 KoShape *shape = 0;
356 if (shapes.size() == 1) {
357 shape = shapes.first();
358 }
359
360 return shape;
361 }
362
364 {
365 // FIXME: copy of KoShapeGradientHandles. use a template?
367
368 KoShape *shape = onlyEditableShape();
369 if (shape) {
370 KoFlake::SelectionHandle globalHandle = q->handleAt(pos);
371 const qreal distanceThresholdSq =
372 globalHandle == KoFlake::NoHandle ?
374
375 const KoViewConverter *converter = q->canvas()->viewConverter();
376 const QPointF viewPoint = converter->documentToView(pos);
377 qreal minDistanceSq = std::numeric_limits<qreal>::max();
378
380
381 for (const auto& handle: sh.handles()) {
382 const QPointF handlePoint = converter->documentToView(handle.pos);
383 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
384
385 if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) {
386 result = handle;
387 minDistanceSq = distanceSq;
388 }
389 }
390 }
391
392 return result;
393 }
394
395private:
399};
400
402{
403public:
405 : KoToolSelection(parent)
406 , m_selection(parent->koSelection())
407 {
408 }
409
410 bool hasSelection() override
411 {
412 if (m_selection) {
413 return m_selection->count();
414 }
415 return false;
416 }
417
418private:
420};
421
422DefaultTool::DefaultTool(KoCanvasBase *canvas, bool connectToSelectedShapesProxy)
423 : KoInteractionTool(canvas)
424 , m_lastHandle(KoFlake::NoHandle)
425 , m_hotPosition(KoFlake::TopLeft)
426 , m_mouseWasInsideHandles(false)
427 , m_textOutlineHelper(new KoSvgTextShapeOutlineHelper(canvas))
428 , m_selectionHandler(new SelectionHandler(this))
429 , m_tabbedOptionWidget(0)
430 , m_textPropertyInterface(new DefaultToolTextPropertiesInterface(this))
431{
432 setupActions();
433
434 QPixmap rotatePixmap, shearPixmap;
435 rotatePixmap.load(":/cursor_rotate.png");
436 Q_ASSERT(!rotatePixmap.isNull());
437 shearPixmap.load(":/cursor_shear.png");
438 Q_ASSERT(!shearPixmap.isNull());
439
440 m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45)));
441 m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90)));
442 m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135)));
443 m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180)));
444 m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225)));
445 m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270)));
446 m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315)));
447 m_rotateCursors[7] = QCursor(rotatePixmap);
448 /*
449 m_rotateCursors[0] = QCursor(Qt::RotateNCursor);
450 m_rotateCursors[1] = QCursor(Qt::RotateNECursor);
451 m_rotateCursors[2] = QCursor(Qt::RotateECursor);
452 m_rotateCursors[3] = QCursor(Qt::RotateSECursor);
453 m_rotateCursors[4] = QCursor(Qt::RotateSCursor);
454 m_rotateCursors[5] = QCursor(Qt::RotateSWCursor);
455 m_rotateCursors[6] = QCursor(Qt::RotateWCursor);
456 m_rotateCursors[7] = QCursor(Qt::RotateNWCursor);
457 */
458 m_shearCursors[0] = QCursor(shearPixmap);
459 m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45)));
460 m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90)));
461 m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135)));
462 m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180)));
463 m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225)));
464 m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270)));
465 m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315)));
466 m_sizeCursors[0] = Qt::SizeVerCursor;
467 m_sizeCursors[1] = Qt::SizeBDiagCursor;
468 m_sizeCursors[2] = Qt::SizeHorCursor;
469 m_sizeCursors[3] = Qt::SizeFDiagCursor;
470 m_sizeCursors[4] = Qt::SizeVerCursor;
471 m_sizeCursors[5] = Qt::SizeBDiagCursor;
472 m_sizeCursors[6] = Qt::SizeHorCursor;
473 m_sizeCursors[7] = Qt::SizeFDiagCursor;
474
475 if (connectToSelectedShapesProxy) {
476 connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(updateActions()));
477
478 connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(repaintDecorations()));
479 connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), m_textPropertyInterface, SLOT(slotSelectionChanged()));
480 connect(canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), this, SLOT(repaintDecorations()));
481 }
482
483 m_textOutlineHelper->setDrawBoundingRect(false);
484 m_textOutlineHelper->setDrawShapeOutlines(true);
485}
486
490
492{
493 if (value) {
496 1, EditFillGradientFactoryId, this));
497 } else {
498 removeInteractionFactory(EditFillGradientFactoryId);
499 }
501}
502
504{
505 if (value) {
508 0, EditStrokeGradientFactoryId, this));
509 } else {
510 removeInteractionFactory(EditStrokeGradientFactoryId);
511 }
513}
514
516{
517 if (value) {
519 m_tabbedOptionWidget, SLOT(slotMeshGradientHandleSelected(KoShapeMeshGradientHandles::Handle)));
522 EditFillMeshGradientFactoryId, this));
523 } else {
525 m_tabbedOptionWidget, SLOT(slotMeshGradientHandleSelected(KoShapeMeshGradientHandles::Handle)));
526 removeInteractionFactory(EditFillMeshGradientFactoryId);
527 }
528}
529
534
536{
538
539 if (shapes.isEmpty()) return;
540
542 KUndo2Command *parentCommand = new KUndo2Command();
543 bool convertableShape = false;
545 Q_FOREACH(KoShape *shape, shapes) {
546 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(shape);
547 if (textShape && textShape->textType() != type) {
548 KoSvgConvertTextTypeCommand *cmd = new KoSvgConvertTextTypeCommand(textShape, type, 0, parentCommand);
549 if (!convertableShape) {
550 convertableShape = true;
551 parentCommand->setText(cmd->text());
552 }
554 }
555 }
556
558 if (convertableShape) {
559 canvas()->addCommand(parentCommand);
560 }
561}
562
564{
565 KoSvgTextShape *textShape = nullptr;
566 QList<KoShape*> shapes;
568 if (!selection) return;
569
570 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
571 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
572 if (selectedShapes.isEmpty()) return;
573
574 Q_FOREACH(KoShape *shape, selectedShapes) {
575 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
576 KoPathShape *path = dynamic_cast<KoPathShape*>(shape);
577 if (text && !textShape) {
578 textShape = text;
579 } else if (path && path->isClosedSubpath(0)) {
580 shapes.append(shape);
581 }
582 }
583 if (!textShape) return;
584 if (shapes.isEmpty()) return;
585
586 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Add shapes to text flow."));
587
588 if (textShape->textType() != KoSvgTextShape::InlineWrap) {
589 new KoSvgConvertTextTypeCommand(textShape, KoSvgTextShape::PreformattedText, 0, parentCommand);
590 }
591 KoSvgTextRemoveShapeCommand::removeContourShapesFromFlow(textShape, parentCommand, false, true);
592
593 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
594 Q_FOREACH(KoShape *shape, shapes) {
595 new KoSvgTextAddShapeCommand(textShape, shape, true, parentCommand);
596 }
597 new KoKeepShapesSelectedCommand({}, {textShape}, canvas()->selectedShapesProxy(), true, parentCommand);
598
599 canvas()->addCommand(parentCommand);
600 selection->deselectAll();
601 selection->select(textShape);
602}
603
605{
606 KoSvgTextShape *textShape = nullptr;
607 KoPathShape *textPath = nullptr;
609 if (!selection) return;
610
611 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
612 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
613 if (selectedShapes.isEmpty()) return;
614
615 Q_FOREACH(KoShape *shape, selectedShapes) {
616 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
617 if (text && !textShape) {
618 textShape = text;
619 } else if (KoPathShape *path = dynamic_cast<KoPathShape*>(shape)){
620 textPath = path;
621 }
622 if (textShape && textPath) {
623 break;
624 }
625 }
626 if (!(textShape && textPath)) return;
627
628 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Put Text On Path"));
629
630 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
632 new KoSvgConvertTextTypeCommand(textShape, KoSvgTextShape::PreformattedText, 0, parentCommand);
633 }
634
639 KoSvgTextRemoveShapeCommand::removeContourShapesFromFlow(textShape, parentCommand, true, true);
640 new KoSvgTextSetTextPathOnRangeCommand(textShape, textPath, 0, textShape->posForIndex(textShape->plainText().size()), parentCommand);
641
645 if ((textShape->direction() == KoSvgText::DirectionRightToLeft && anchor == KoSvgText::AnchorStart)
646 || (textShape->direction() == KoSvgText::DirectionLeftToRight && anchor == KoSvgText::AnchorEnd)) {
648 info.startOffset = 100.0;
649 info.startOffsetIsPercentage = true;
650 new KoSvgTextPathInfoChangeCommand(textShape, 0, info, parentCommand);
651 } else if (anchor == KoSvgText::AnchorMiddle) {
653 info.startOffset = 50.0;
654 info.startOffsetIsPercentage = true;
655 new KoSvgTextPathInfoChangeCommand(textShape, 0, info, parentCommand);
656 }
657
658 new KoKeepShapesSelectedCommand({}, {textShape}, canvas()->selectedShapesProxy(), true, parentCommand);
659
660 canvas()->addCommand(parentCommand);
661 selection->deselectAll();
662 selection->select(textShape);
663}
664
666{
667 KoSvgTextShape *textShape = nullptr;
668 QList<KoShape*> shapes;
670 if (!selection) return;
671
672 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
673 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
674 if (selectedShapes.isEmpty()) return;
675
676 Q_FOREACH(KoShape *shape, selectedShapes) {
677 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape*>(shape);
678 KoPathShape *path = dynamic_cast<KoPathShape*>(shape);
679 if (text && !textShape) {
680 textShape = text;
681 } else if (path && path->isClosedSubpath(0)) {
682 shapes.append(shape);
683 }
684 }
685 if (!textShape) return;
686 if (shapes.isEmpty()) return;
687
688 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Subtract shapes from text flow."));
689
690 if (textShape->textType() == KoSvgTextShape::InlineWrap) {
691 new KoSvgConvertTextTypeCommand(textShape, KoSvgTextShape::PreformattedText, 0, parentCommand);
692 }
693 KoSvgTextRemoveShapeCommand::removeContourShapesFromFlow(textShape, parentCommand, false, true);
694
695 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
696 Q_FOREACH(KoShape *shape, shapes) {
697 new KoSvgTextAddShapeCommand(textShape, shape, false, parentCommand);
698 }
699 new KoKeepShapesSelectedCommand({}, {textShape}, canvas()->selectedShapesProxy(), true, parentCommand);
700
701 canvas()->addCommand(parentCommand);
702 selection->deselectAll();
703 selection->select(textShape);
704}
705
710
712{
714 if (!textShape) return;
716 if (!selection) return;
717
718 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
719 if (selectedShapes.isEmpty()) return;
720
721 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Remove shapes from text flow."));
722
723 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, parentCommand);
724 Q_FOREACH(KoShape *shape, selectedShapes) {
725 if (!textShape->shapeInContours(shape)) continue;
726 new KoSvgTextRemoveShapeCommand(textShape, shape, parentCommand);
727 }
728 new KoKeepShapesSelectedCommand({}, {selectedShapes}, canvas()->selectedShapesProxy(), true, parentCommand);
729
730 canvas()->addCommand(parentCommand);
731}
732
734{
736 if (!textShape) return;
738 if (!selection) return;
739
740 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
741 if (selectedShapes.isEmpty()) return;
742 KUndo2Command *parentCommand = new KUndo2Command(kundo2_i18n("Toggle Flow Shape Type"));
743
744 bool addToCanvas = false;
745 Q_FOREACH(KoShape *shape, selectedShapes) {
746 if (!textShape->shapesInside().contains(shape)
747 && !textShape->shapesSubtract().contains(shape)) continue;
748 addToCanvas = true;
749 new KoSvgTextFlipShapeContourTypeCommand(textShape, shape, parentCommand);
750 }
751 if (addToCanvas) {
752 canvas()->addCommand(parentCommand);
753 }
754}
755
757{
759 if (!textShape) return;
761 if (!selection) return;
762
763 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
764 if (selectedShapes.isEmpty()) return;
765
766 KUndo2Command *parentCommand = new KUndo2Command();
768 parentCommand->setText(kundo2_i18n("Set Flow Shape as First"));
770 parentCommand->setText(kundo2_i18n("Decrease Flow Shape Index"));
772 parentCommand->setText(kundo2_i18n("Increase Flow Shape Index"));
773 } else {
774 parentCommand->setText(kundo2_i18n("Set Flow Shape as Last"));
775 }
776
777 QList<KoShape *> shapesInside;
778 Q_FOREACH(KoShape *shape, selectedShapes) {
779 if (!textShape->shapesInside().contains(shape)) continue;
780 shapesInside.append(shape);
781 }
782
783 if (!shapesInside.isEmpty()) {
784 new KoSvgTextReorderShapeInsideCommand(textShape, shapesInside, KoSvgTextReorderShapeInsideCommand::MoveShapeType(type), parentCommand);
785 canvas()->addCommand(parentCommand);
786 }
787}
788
790{
791 return m_textOutlineHelper->updateTextContourMode();
792}
793
795{
796 return true;
797}
798
799void DefaultTool::addMappedAction(KisSignalMapper *mapper, const QString &actionId, int commandType)
800{
801 QAction *a =action(actionId);
802 connect(a, SIGNAL(triggered()), mapper, SLOT(map()));
803 mapper->setMapping(a, commandType);
804}
805
807{
809
816
818
823
828
830
831 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_cw", TransformRotate90CW);
832 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_90_ccw", TransformRotate90CCW);
833 addMappedAction(m_transformSignalsMapper, "object_transform_rotate_180", TransformRotate180);
834 addMappedAction(m_transformSignalsMapper, "object_transform_mirror_horizontally", TransformMirrorX);
835 addMappedAction(m_transformSignalsMapper, "object_transform_mirror_vertically", TransformMirrorY);
836 addMappedAction(m_transformSignalsMapper, "object_transform_reset", TransformReset);
837
839
840 addMappedAction(m_booleanSignalsMapper, "object_unite", BooleanUnion);
841 addMappedAction(m_booleanSignalsMapper, "object_intersect", BooleanIntersection);
842 addMappedAction(m_booleanSignalsMapper, "object_subtract", BooleanSubtraction);
843
848
849 if (!action("text_type_preformatted")->actionGroup()) {
850 QActionGroup *textTypeActions = new QActionGroup(this);
851 textTypeActions->addAction(action("text_type_preformatted"));
852 textTypeActions->addAction(action("text_type_inline_wrap"));
853 textTypeActions->addAction(action("text_type_pre_positioned"));
854 textTypeActions->setExclusive(false);
855 Q_FOREACH (QAction *a, textTypeActions->actions()) {
856 a->setCheckable(false);
857 }
858 }
859
861
866
867 m_contextMenu.reset(new QMenu());
868}
869
871{
872 QPointF selectionCenter = koSelection()->absolutePosition();
873 QPointF direction;
874
875 switch (handle) {
877 if (useEdgeRotation) {
880 } else {
881 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
882 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRight) - handlePosition);
883 direction = handlePosition - selectionCenter;
884 }
885 break;
887 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized()).toPointF();
888 break;
890 if (useEdgeRotation) {
893 } else {
894 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRight);
895 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
896 direction = handlePosition - selectionCenter;
897 }
898 break;
900 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized()).toPointF();
901 break;
903 if (useEdgeRotation) {
906 } else {
907 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeft);
908 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition);
909 direction = handlePosition - selectionCenter;
910 }
911 break;
913 direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - selectionCenter;
914 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized()).toPointF();
915 break;
917 if (useEdgeRotation) {
920 } else {
921 QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft);
922 handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeft) - handlePosition);
923 direction = handlePosition - selectionCenter;
924 }
925 break;
927 direction = koSelection()->absolutePosition(KoFlake::TopLeft) - selectionCenter;
928 direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized()).toPointF();
929 break;
931 return 0.0;
932 break;
933 }
934
935 qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI;
936
937 switch (handle) {
939 if (useEdgeRotation) {
940 rotation -= 0.0;
941 } else {
942 rotation -= 270.0;
943 }
944 break;
946 rotation -= 315.0;
947 break;
949 if (useEdgeRotation) {
950 rotation -= 90.0;
951 } else {
952 rotation -= 0.0;
953 }
954 break;
956 rotation -= 45.0;
957 break;
959 if (useEdgeRotation) {
960 rotation -= 180.0;
961 } else {
962 rotation -= 90.0;
963 }
964 break;
966 rotation -= 135.0;
967 break;
969 if (useEdgeRotation) {
970 rotation -= 270.0;
971 } else {
972 rotation -= 180.0;
973 }
974 break;
976 rotation -= 225.0;
977 break;
978 default:
979 ;
980 }
981
982 if (rotation < 0.0) {
983 rotation += 360.0;
984 }
985
986 return rotation;
987}
988
990{
991 if (tryUseCustomCursor()) return;
992
993 QCursor cursor = Qt::ArrowCursor;
994
995 QString statusText;
996
998 if (selection && selection->count() > 0) { // has a selection
999 bool editable = !selection->selectedEditableShapes().isEmpty();
1000
1003 int rotOctant = 8 + int(8.5 + m_angle / 45);
1004
1005 bool rotateHandle = false;
1006 bool shearHandle = false;
1007 switch (m_lastHandle) {
1009 cursor = m_shearCursors[(0 + rotOctant) % 8];
1010 shearHandle = true;
1011 break;
1013 cursor = m_rotateCursors[(1 + rotOctant) % 8];
1014 rotateHandle = true;
1015 break;
1017 cursor = m_shearCursors[(2 + rotOctant) % 8];
1018 shearHandle = true;
1019 break;
1021 cursor = m_rotateCursors[(3 + rotOctant) % 8];
1022 rotateHandle = true;
1023 break;
1025 cursor = m_shearCursors[(4 + rotOctant) % 8];
1026 shearHandle = true;
1027 break;
1029 cursor = m_rotateCursors[(5 + rotOctant) % 8];
1030 rotateHandle = true;
1031 break;
1033 cursor = m_shearCursors[(6 + rotOctant) % 8];
1034 shearHandle = true;
1035 break;
1037 cursor = m_rotateCursors[(7 + rotOctant) % 8];
1038 rotateHandle = true;
1039 break;
1040 case KoFlake::NoHandle:
1041 cursor = Qt::ArrowCursor;
1042 break;
1043 }
1044 if (rotateHandle) {
1045 statusText = i18n("Left click rotates around center, right click around highlighted position.");
1046 }
1047 if (shearHandle) {
1048 statusText = i18n("Click and drag to shear selection.");
1049 }
1050
1051
1052 } else {
1053 statusText = i18n("Click and drag to resize selection.");
1055 int rotOctant = 8 + int(8.5 + m_angle / 45);
1056 bool cornerHandle = false;
1057 switch (m_lastHandle) {
1059 cursor = m_sizeCursors[(0 + rotOctant) % 8];
1060 break;
1062 cursor = m_sizeCursors[(1 + rotOctant) % 8];
1063 cornerHandle = true;
1064 break;
1066 cursor = m_sizeCursors[(2 + rotOctant) % 8];
1067 break;
1069 cursor = m_sizeCursors[(3 + rotOctant) % 8];
1070 cornerHandle = true;
1071 break;
1073 cursor = m_sizeCursors[(4 + rotOctant) % 8];
1074 break;
1076 cursor = m_sizeCursors[(5 + rotOctant) % 8];
1077 cornerHandle = true;
1078 break;
1080 cursor = m_sizeCursors[(6 + rotOctant) % 8];
1081 break;
1083 cursor = m_sizeCursors[(7 + rotOctant) % 8];
1084 cornerHandle = true;
1085 break;
1086 case KoFlake::NoHandle:
1087 cursor = Qt::SizeAllCursor;
1088 statusText = i18n("Click and drag to move selection.");
1089 break;
1090 }
1091 if (cornerHandle) {
1092 statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position.");
1093 }
1094 }
1095 if (!editable) {
1096 cursor = Qt::ArrowCursor;
1097 }
1098 } else {
1099 // there used to be guides... :'''(
1100 }
1102 if (currentStrategy() == 0) {
1103 Q_EMIT statusTextChanged(statusText);
1104 }
1105}
1106
1107void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter)
1108{
1110 if (selection) {
1111 m_decorator.reset(new SelectionDecorator(canvas()->resourceManager()));
1112
1113 {
1119 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
1120 KisNodeSP node = kisCanvas->viewManager()->nodeManager()->activeNode();
1121 const bool isSelectionMask = node && node->inherits("KisSelectionMask");
1122 m_decorator->setForceShapeOutlines(isSelectionMask);
1123
1124
1125 }
1126
1127 m_decorator->setSelection(selection);
1128 m_decorator->setHandleRadius(handleRadius());
1129 m_decorator->setDecorationThickness(decorationThickness());
1130 m_decorator->setShowFillGradientHandles(hasInteractionFactory(EditFillGradientFactoryId));
1131 m_decorator->setShowStrokeFillGradientHandles(hasInteractionFactory(EditStrokeGradientFactoryId));
1132 m_decorator->setShowFillMeshGradientHandles(hasInteractionFactory(EditFillMeshGradientFactoryId));
1133 m_decorator->setCurrentMeshGradientHandles(m_selectedMeshHandle, m_hoveredMeshHandle);
1134 m_decorator->paint(painter, converter);
1135 }
1136
1137 m_textOutlineHelper->setHandleRadius(handleRadius());
1138 m_textOutlineHelper->setDecorationThickness(decorationThickness());
1139 m_textOutlineHelper->paint(&painter, converter);
1140
1141 KoInteractionTool::paint(painter, converter);
1142
1143 painter.save();
1144 painter.setTransform(converter.documentToView(), true);
1145 canvas()->snapGuide()->paint(painter, converter);
1146 painter.restore();
1147}
1148
1150{
1151 // if the currently active node has a shape manager, then it is
1152 // probably our client :)
1153
1154 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
1155 return bool(kisCanvas->localShapeManager());
1156}
1157
1161
1163{
1164 // this tool only works on a vector layer right now, so give a warning if another layer type is trying to use it
1165 if (!isValidForCurrentLayer()) {
1166 KisCanvas2 *kiscanvas = static_cast<KisCanvas2 *>(canvas());
1167 kiscanvas->viewManager()->showFloatingMessage(
1168 i18n("This tool only works on vector layers. You probably want the move tool."),
1169 QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter);
1170 return;
1171 }
1172
1173 if (KoSvgTextShape *shape = m_textOutlineHelper->contourModeButtonHovered(event->point)) {
1174 m_textOutlineHelper->toggleTextContourMode(shape);
1175 updateActions();
1176 event->accept();
1177 updateCursor();
1178 return;
1179 }
1181 updateCursor();
1182}
1183
1185{
1187 if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) {
1188 QRectF bound = handlesSize();
1189
1190 if (bound.contains(event->point)) {
1191 bool inside;
1192 KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside);
1193
1194 if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) {
1195 m_lastHandle = newDirection;
1196 m_mouseWasInsideHandles = inside;
1197 }
1198 } else {
1201
1202 // there used to be guides... :'''(
1203 }
1204 } else {
1205 // there used to be guides... :'''(
1206 }
1207
1208
1209 updateCursor();
1210}
1211
1213{
1215 if (!selection || !selection->count()) return QRectF();
1216
1218
1219 QRectF bound = m_selectionOutline.boundingRect();
1220
1221 // expansion Border
1222 if (!canvas() || !canvas()->viewConverter()) {
1223 return bound;
1224 }
1225
1226 QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE));
1227 bound.adjust(-border.x(), -border.y(), border.x(), border.y());
1228 return bound;
1229}
1230
1236
1238{
1240
1242 if (shape && selection && !selection->isSelected(shape)) {
1243
1244 if (!(event->modifiers() & Qt::ShiftModifier)) {
1245 selection->deselectAll();
1246 }
1247
1248 selection->select(shape);
1249 }
1250
1252}
1253
1254bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers)
1255{
1256 bool result = false;
1257
1258 qreal x = 0.0, y = 0.0;
1259 if (direction == Qt::Key_Left) {
1260 x = -5;
1261 } else if (direction == Qt::Key_Right) {
1262 x = 5;
1263 } else if (direction == Qt::Key_Up) {
1264 y = -5;
1265 } else if (direction == Qt::Key_Down) {
1266 y = 5;
1267 }
1268
1269 if (x != 0.0 || y != 0.0) { // actually move
1270
1271 if ((modifiers & Qt::ShiftModifier) != 0) {
1272 x *= 10;
1273 y *= 10;
1274 } else if ((modifiers & Qt::AltModifier) != 0) { // more precise
1275 x /= 5;
1276 y /= 5;
1277 }
1278
1280
1281 if (!shapes.isEmpty()) {
1282 canvas()->addCommand(new KoShapeMoveCommand(shapes, QPointF(x, y)));
1283 result = true;
1284 }
1285 }
1286
1287 return result;
1288}
1289
1290void DefaultTool::keyPressEvent(QKeyEvent *event)
1291{
1293 if (currentStrategy() == 0) {
1294 switch (event->key()) {
1295 case Qt::Key_Left:
1296 case Qt::Key_Right:
1297 case Qt::Key_Up:
1298 case Qt::Key_Down:
1299 if (moveSelection(event->key(), event->modifiers())) {
1300 event->accept();
1301 }
1302 break;
1303 default:
1304 return;
1305 }
1306 }
1307}
1308
1310{
1311 QRectF dirtyRect;
1312
1313 if (koSelection() && koSelection()->count() > 0) {
1317 dirtyRect = const_cast<DefaultTool*>(this)->handlesSize();
1318 }
1319
1320 if (canvas()->snapGuide()->isSnapping()) {
1321 dirtyRect |= canvas()->snapGuide()->boundingRect();
1322 }
1323 dirtyRect |= m_textOutlineHelper->decorationRect();
1324
1325 return dirtyRect;
1326}
1327
1329{
1330 // all the selected shapes, not only editable!
1332
1333 if (!shapes.isEmpty()) {
1334 KoDrag drag;
1335 drag.setSvg(shapes);
1336 drag.addToClipboard();
1337 }
1338}
1339
1341{
1342 QList<KoShape *> shapes;
1343 foreach (KoShape *s, koSelection()->selectedShapes()) {
1344 if (s->isGeometryProtected()) {
1345 continue;
1346 }
1347 shapes << s;
1348 }
1349 if (!shapes.empty()) {
1350 canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes));
1351 }
1352}
1353
1355{
1356 // we no longer have to do anything as tool Proxy will do it for us
1357 return false;
1358}
1359
1361{
1362 Q_ASSERT(canvas());
1363 Q_ASSERT(canvas()->selectedShapesProxy());
1364 Q_FOREACH(KoShape *shape, canvas()->shapeManager()->shapes()) {
1365 if (!shape->isSelectable()) continue;
1367 }
1369
1370 return true;
1371}
1372
1374{
1375 Q_ASSERT(canvas());
1376 Q_ASSERT(canvas()->selectedShapesProxy());
1379}
1380
1382{
1383 Q_ASSERT(canvas());
1384 Q_ASSERT(canvas()->selectedShapesProxy());
1385 return canvas()->selectedShapesProxy()->selection();
1386}
1387
1388KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning)
1389{
1390 // check for handles in this order; meaning that when handles overlap the one on top is chosen
1391 static const KoFlake::SelectionHandle handleOrder[] = {
1401 };
1402
1403 const KoViewConverter *converter = canvas()->viewConverter();
1405
1406 if (!selection || !selection->count() || !converter) {
1407 return KoFlake::NoHandle;
1408 }
1409
1411
1412 if (innerHandleMeaning) {
1413 QPainterPath path;
1414 path.addPolygon(m_selectionOutline);
1415 *innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point));
1416 }
1417
1418 const QPointF viewPoint = converter->documentToView(point);
1419
1420 for (int i = 0; i < KoFlake::NoHandle; ++i) {
1421 KoFlake::SelectionHandle handle = handleOrder[i];
1422
1423 const QPointF handlePoint = converter->documentToView(m_selectionBox[handle]);
1424 const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint);
1425
1426 // if just inside the outline
1427 if (distanceSq < HANDLE_DISTANCE_SQ) {
1428
1429 if (innerHandleMeaning) {
1430 if (distanceSq < INNER_HANDLE_DISTANCE_SQ) {
1431 *innerHandleMeaning = true;
1432 }
1433 }
1434
1435 return handle;
1436 }
1437 }
1438 return KoFlake::NoHandle;
1439}
1440
1442{
1444
1445 QTransform matrix = selection->absoluteTransformation();
1446 m_selectionOutline = matrix.map(QPolygonF(selection->outlineRect()));
1447 m_angle = 0.0;
1448
1449 QPolygonF outline = m_selectionOutline; //shorter name in the following :)
1450 m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2;
1451 m_selectionBox[KoFlake::TopRightHandle] = outline.value(1);
1452 m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2;
1453 m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2);
1454 m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2;
1455 m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3);
1456 m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2;
1457 m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0);
1458 if (selection->count() == 1) {
1459#if 0 // TODO detect mirroring
1461
1462 if (s->scaleX() < 0) { // vertically mirrored: swap left / right
1466 }
1467 if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom
1471 }
1472#endif
1473 }
1474}
1475
1476void DefaultTool::activate(const QSet<KoShape *> &shapes)
1477{
1478 KoToolBase::activate(shapes);
1479
1480 QAction *actionBringToFront = action("object_order_front");
1481 connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront()), Qt::UniqueConnection);
1482
1483 QAction *actionRaise = action("object_order_raise");
1484 connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp()), Qt::UniqueConnection);
1485
1486 QAction *actionLower = action("object_order_lower");
1487 connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown()));
1488
1489 QAction *actionSendToBack = action("object_order_back");
1490 connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack()), Qt::UniqueConnection);
1491
1492 QAction *actionGroupBottom = action("object_group");
1493 connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup()), Qt::UniqueConnection);
1494
1495 QAction *actionUngroupBottom = action("object_ungroup");
1496 connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup()), Qt::UniqueConnection);
1497
1498 QAction *actionSplit = action("object_split");
1499 connect(actionSplit, SIGNAL(triggered()), this, SLOT(selectionSplitShapes()), Qt::UniqueConnection);
1500
1501 connect(m_alignSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionAlign(int)));
1502 connect(m_distributeSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionDistribute(int)));
1503 connect(m_transformSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionTransform(int)));
1504 connect(m_booleanSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionBooleanOp(int)));
1505 connect(m_textTypeSignalsMapper, SIGNAL(mapped(int)), SLOT(slotChangeTextType(int)));
1506 connect(m_textFlowSignalsMapper, SIGNAL(mapped(int)), SLOT(slotReorderFlowShapes(int)));
1507
1508 QAction *actionTextInside = action("add_shape_to_flow_area");
1509 connect(actionTextInside, SIGNAL(triggered()), this, SLOT(slotAddShapesToFlow()), Qt::UniqueConnection);
1510
1511 QAction *actionTextSubtract = action("subtract_shape_from_flow_area");
1512 connect(actionTextSubtract, SIGNAL(triggered()), this, SLOT(slotSubtractShapesFromFlow()), Qt::UniqueConnection);
1513
1514 QAction *actionTextOnPath = action("put_text_on_path");
1515 connect(actionTextOnPath, SIGNAL(triggered()), this, SLOT(slotPutTextOnPath()), Qt::UniqueConnection);
1516
1517 QAction *actionTextRemoveFlow = action("remove_shapes_from_text_flow");
1518 connect(actionTextRemoveFlow, SIGNAL(triggered()), this, SLOT(slotRemoveShapesFromFlow()), Qt::UniqueConnection);
1519
1520 QAction *actionTextFlowToggle = action("flow_shape_type_toggle");
1521 connect(actionTextFlowToggle, SIGNAL(triggered()), this, SLOT(slotToggleFlowShapeType()), Qt::UniqueConnection);
1522
1525 useCursor(Qt::ArrowCursor);
1527 updateActions();
1528
1529 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->canvas());
1530 if (canvas2) {
1533 }
1534
1537 }
1538}
1539
1541{
1543
1544 QAction *actionBringToFront = action("object_order_front");
1545 disconnect(actionBringToFront, 0, this, 0);
1546
1547 QAction *actionRaise = action("object_order_raise");
1548 disconnect(actionRaise, 0, this, 0);
1549
1550 QAction *actionLower = action("object_order_lower");
1551 disconnect(actionLower, 0, this, 0);
1552
1553 QAction *actionSendToBack = action("object_order_back");
1554 disconnect(actionSendToBack, 0, this, 0);
1555
1556 QAction *actionGroupBottom = action("object_group");
1557 disconnect(actionGroupBottom, 0, this, 0);
1558
1559 QAction *actionUngroupBottom = action("object_ungroup");
1560 disconnect(actionUngroupBottom, 0, this, 0);
1561
1562 QAction *actionSplit = action("object_split");
1563 disconnect(actionSplit, 0, this, 0);
1564
1565 disconnect(m_alignSignalsMapper, 0, this, 0);
1566 disconnect(m_distributeSignalsMapper, 0, this, 0);
1567 disconnect(m_transformSignalsMapper, 0, this, 0);
1568 disconnect(m_booleanSignalsMapper, 0, this, 0);
1569 disconnect(m_textTypeSignalsMapper, 0, this, 0);
1570 disconnect(m_textFlowSignalsMapper, 0, this, 0);
1571
1572 QAction *actionTextInside = action("add_shape_to_flow_area");
1573 disconnect(actionTextInside, 0, this, 0);
1574 QAction *actionTextSubtract = action("subtract_shape_from_flow_area");
1575 disconnect(actionTextSubtract, 0, this, 0);
1576 QAction *actionTextOnPath = action("put_text_on_path");
1577 disconnect(actionTextOnPath, 0, this, 0);
1578 QAction *actionTextRemoveFlow = action("remove_shapes_from_text_flow");
1579 disconnect(actionTextRemoveFlow, 0, this, 0);
1580 QAction *actionTextFlowToggle = action("flow_shape_type_toggle");
1581 disconnect(actionTextFlowToggle, 0, this, 0);
1582
1583 const KisCanvas2 *canvas2 = qobject_cast<const KisCanvas2 *>(this->canvas());
1584 if (canvas2) {
1587 }
1588
1591 }
1592}
1593
1595{
1597 if (!selection) return;
1598
1599 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1600 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
1601 if (selectedShapes.isEmpty()) return;
1602
1603 const int groupZIndex = selectedShapes.last()->zIndex();
1604
1605 KoShapeGroup *group = new KoShapeGroup();
1606 group->setZIndex(groupZIndex);
1607 // TODO what if only one shape is left?
1608 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes"));
1609 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1610 canvas()->shapeController()->addShapeDirect(group, 0, cmd);
1611 new KoShapeGroupCommand(group, selectedShapes, true, cmd);
1612 new KoKeepShapesSelectedCommand({}, {group}, canvas()->selectedShapesProxy(), true, cmd);
1613 canvas()->addCommand(cmd);
1614
1615 // update selection so we can ungroup immediately again
1616 selection->deselectAll();
1617 selection->select(group);
1618}
1619
1621{
1623 if (!selection) return;
1624
1625 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1626 std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex);
1627
1628 KUndo2Command *cmd = 0;
1629 QList<KoShape*> newShapes;
1630
1631 // add a ungroup command for each found shape container to the macro command
1632 Q_FOREACH (KoShape *shape, selectedShapes) {
1633 KoShapeGroup *group = dynamic_cast<KoShapeGroup *>(shape);
1634 if (group) {
1635 if (!cmd) {
1636 cmd = new KUndo2Command(kundo2_i18n("Ungroup shapes"));
1637 new KoKeepShapesSelectedCommand(selectedShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1638 }
1639 newShapes << group->shapes();
1640 new KoShapeUngroupCommand(group, group->shapes(),
1642 cmd);
1643 canvas()->shapeController()->removeShape(group, cmd);
1644 }
1645 }
1646 if (cmd) {
1647 new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
1648 canvas()->addCommand(cmd);
1649 }
1650}
1651
1652void DefaultTool::selectionTransform(int transformAction)
1653{
1655 if (!selection) return;
1656
1657 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1658 if (editableShapes.isEmpty()) {
1659 return;
1660 }
1661
1662 QTransform applyTransform;
1663 bool shouldReset = false;
1664 KUndo2MagicString actionName = kundo2_noi18n("BUG: No transform action");
1665
1666
1667 switch (TransformActionType(transformAction)) {
1668 case TransformRotate90CW:
1669 applyTransform.rotate(90.0);
1670 actionName = kundo2_i18n("Rotate Object 90° CW");
1671 break;
1672 case TransformRotate90CCW:
1673 applyTransform.rotate(-90.0);
1674 actionName = kundo2_i18n("Rotate Object 90° CCW");
1675 break;
1676 case TransformRotate180:
1677 applyTransform.rotate(180.0);
1678 actionName = kundo2_i18n("Rotate Object 180°");
1679 break;
1680 case TransformMirrorX:
1681 applyTransform.scale(-1.0, 1.0);
1682 actionName = kundo2_i18n("Mirror Object Horizontally");
1683 break;
1684 case TransformMirrorY:
1685 applyTransform.scale(1.0, -1.0);
1686 actionName = kundo2_i18n("Mirror Object Vertically");
1687 break;
1688 case TransformReset:
1689 shouldReset = true;
1690 actionName = kundo2_i18n("Reset Object Transformations");
1691 break;
1692 }
1693
1694 if (!shouldReset && applyTransform.isIdentity()) return;
1695
1696 QList<QTransform> oldTransforms;
1697 QList<QTransform> newTransforms;
1698
1699 const QRectF outlineRect = KoShape::absoluteOutlineRect(editableShapes);
1700 const QPointF centerPoint = outlineRect.center();
1701 const QTransform centerTrans = QTransform::fromTranslate(centerPoint.x(), centerPoint.y());
1702 const QTransform centerTransInv = QTransform::fromTranslate(-centerPoint.x(), -centerPoint.y());
1703
1704 // we also add selection to the list of transformed shapes, so that its outline is updated correctly
1705 QList<KoShape*> transformedShapes = editableShapes;
1706 transformedShapes << selection;
1707
1708 Q_FOREACH (KoShape *shape, transformedShapes) {
1709 oldTransforms.append(shape->transformation());
1710
1711 QTransform t;
1712
1713 if (!shouldReset) {
1714 const QTransform world = shape->absoluteTransformation();
1715 t = world * centerTransInv * applyTransform * centerTrans * world.inverted() * shape->transformation();
1716 } else {
1717 const QPointF center = shape->outlineRect().center();
1718 const QPointF offset = shape->transformation().map(center) - center;
1719 t = QTransform::fromTranslate(offset.x(), offset.y());
1720 }
1721
1722 newTransforms.append(t);
1723 }
1724
1725 KoShapeTransformCommand *cmd = new KoShapeTransformCommand(transformedShapes, oldTransforms, newTransforms);
1726 cmd->setText(actionName);
1727 canvas()->addCommand(cmd);
1728}
1729
1731{
1733 if (!selection) return;
1734
1735 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1736 if (editableShapes.isEmpty()) {
1737 return;
1738 }
1739
1740 QVector<QPainterPath> srcOutlines;
1741 QPainterPath dstOutline;
1742 KUndo2MagicString actionName = kundo2_noi18n("BUG: boolean action name");
1743
1744 // TODO: implement a reference shape selection dialog!
1745 const int referenceShapeIndex = 0;
1746 KoShape *referenceShape = editableShapes[referenceShapeIndex];
1747
1748 KisCanvas2 *kisCanvas = static_cast<KisCanvas2 *>(canvas());
1750 const QTransform booleanWorkaroundTransform =
1752
1753 Q_FOREACH (KoShape *shape, editableShapes) {
1754 srcOutlines <<
1755 booleanWorkaroundTransform.map(
1756 shape->absoluteTransformation().map(
1757 shape->outline()));
1758 }
1759
1760 if (booleanOp == BooleanUnion) {
1761 Q_FOREACH (const QPainterPath &path, srcOutlines) {
1762 dstOutline |= path;
1763 }
1764 actionName = kundo2_i18n("Unite Shapes");
1765 } else if (booleanOp == BooleanIntersection) {
1766 for (int i = 0; i < srcOutlines.size(); i++) {
1767 if (i == 0) {
1768 dstOutline = srcOutlines[i];
1769 } else {
1770 dstOutline &= srcOutlines[i];
1771 }
1772 }
1773
1774 // there is a bug in Qt, sometimes it leaves the resulting
1775 // outline open, so just close it explicitly.
1776 dstOutline.closeSubpath();
1777
1778 actionName = kundo2_i18n("Intersect Shapes");
1779
1780 } else if (booleanOp == BooleanSubtraction) {
1781 for (int i = 0; i < srcOutlines.size(); i++) {
1782 dstOutline = srcOutlines[referenceShapeIndex];
1783 if (i != referenceShapeIndex) {
1784 dstOutline -= srcOutlines[i];
1785 }
1786 }
1787
1788 actionName = kundo2_i18n("Subtract Shapes");
1789 }
1790
1791 dstOutline = booleanWorkaroundTransform.inverted().map(dstOutline);
1792
1793 KoShape *newShape = 0;
1794
1795 if (!dstOutline.isEmpty()) {
1796 newShape = KoPathShape::createShapeFromPainterPath(dstOutline);
1797 }
1798
1799 KUndo2Command *cmd = new KUndo2Command(actionName);
1800
1801 new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1802
1803 QList<KoShape*> newSelectedShapes;
1804
1805 if (newShape) {
1806 newShape->setBackground(referenceShape->background());
1807 newShape->setStroke(referenceShape->stroke());
1808 newShape->setZIndex(referenceShape->zIndex());
1809
1810 KoShapeContainer *parent = referenceShape->parent();
1811 canvas()->shapeController()->addShapeDirect(newShape, parent, cmd);
1812
1813 newSelectedShapes << newShape;
1814 }
1815
1816 canvas()->shapeController()->removeShapes(editableShapes, cmd);
1817
1818 new KoKeepShapesSelectedCommand({}, newSelectedShapes, canvas()->selectedShapesProxy(), true, cmd);
1819
1820 canvas()->addCommand(cmd);
1821}
1822
1824{
1826 if (!selection) return;
1827
1828 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1829 if (editableShapes.isEmpty()) {
1830 return;
1831 }
1832
1833 KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Split Shapes"));
1834
1835 new KoKeepShapesSelectedCommand(editableShapes, {}, canvas()->selectedShapesProxy(), false, cmd);
1836 QList<KoShape*> newShapes;
1837
1838 Q_FOREACH (KoShape *shape, editableShapes) {
1839 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
1840 if (!pathShape) return;
1841
1842 QList<KoPathShape*> splitShapes;
1843 if (pathShape->separate(splitShapes)) {
1844 QList<KoShape*> normalShapes = implicitCastList<KoShape*>(splitShapes);
1845
1846 KoShapeContainer *parent = shape->parent();
1847 canvas()->shapeController()->addShapesDirect(normalShapes, parent, cmd);
1848 canvas()->shapeController()->removeShape(shape, cmd);
1849 newShapes << normalShapes;
1850 }
1851 }
1852
1853 new KoKeepShapesSelectedCommand({}, newShapes, canvas()->selectedShapesProxy(), true, cmd);
1854
1855 canvas()->addCommand(cmd);
1856}
1857
1859{
1861 static_cast<KoShapeAlignCommand::Align>(_align);
1862
1864 if (!selection) return;
1865
1866 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1867 if (editableShapes.isEmpty()) {
1868 return;
1869 }
1870
1871 // TODO add an option to the widget so that one can align to the page
1872 // with multiple selected shapes too
1873
1874 QRectF bb;
1875
1876 // single selected shape is automatically aligned to document rect
1877 if (editableShapes.count() == 1) {
1878 if (!canvas()->resourceManager()->hasResource(KoCanvasResource::PageSize)) {
1879 return;
1880 }
1881 bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResource::PageSize));
1882 } else {
1883 bb = KoShape::absoluteOutlineRect(editableShapes);
1884 }
1885
1886 KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb);
1887 canvas()->addCommand(cmd);
1888}
1889
1891{
1893 static_cast<KoShapeDistributeCommand::Distribute>(_distribute);
1894
1896 if (!selection) return;
1897
1898 QList<KoShape *> editableShapes = selection->selectedEditableShapes();
1899 if (editableShapes.size() < 3) {
1900 return;
1901 }
1902
1903 QRectF bb = KoShape::absoluteOutlineRect(editableShapes);
1904 KoShapeDistributeCommand *cmd = new KoShapeDistributeCommand(editableShapes, distribute, bb);
1905 canvas()->addCommand(cmd);
1906}
1907
1912
1917
1922
1927
1929{
1931 if (!selection) {
1932 return;
1933 }
1934
1935 QList<KoShape *> selectedShapes = selection->selectedEditableShapes();
1936 if (selectedShapes.isEmpty()) {
1937 return;
1938 }
1939
1940 KUndo2Command *cmd = KoShapeReorderCommand::createCommand(selectedShapes, shapeManager(), order);
1941 if (cmd) {
1942 canvas()->addCommand(cmd);
1943 }
1944}
1945
1947{
1948 QList<QPointer<QWidget> > widgets;
1949
1951
1952 if (isActivated()) {
1954 }
1955 widgets.append(m_tabbedOptionWidget);
1956
1957 connect(m_tabbedOptionWidget,
1958 SIGNAL(sigSwitchModeEditFillGradient(bool)),
1959 SLOT(slotActivateEditFillGradient(bool)));
1960
1961 connect(m_tabbedOptionWidget,
1962 SIGNAL(sigSwitchModeEditStrokeGradient(bool)),
1963 SLOT(slotActivateEditStrokeGradient(bool)));
1964
1965 connect(m_tabbedOptionWidget,
1966 SIGNAL(sigSwitchModeEditFillGradient(bool)),
1968 // TODO: strokes!!
1969
1970 connect(m_tabbedOptionWidget,
1971 SIGNAL(sigMeshGradientResetted()),
1973
1974 return widgets;
1975}
1976
1977void DefaultTool::canvasResourceChanged(int key, const QVariant &res)
1978{
1979 if (key == HotPosition) {
1982 }
1983}
1984
1986{
1988 if (!selection) return nullptr;
1989
1990 bool insideSelection = false;
1991 KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection);
1992
1993 bool editableShape = !selection->selectedEditableShapes().isEmpty();
1994
1995 const bool selectMultiple = event->modifiers() & Qt::ShiftModifier;
1996 const bool selectNextInStack = event->modifiers() & Qt::ControlModifier;
1997 const bool avoidSelection = event->modifiers() & Qt::AltModifier;
1998
1999 if (selectNextInStack) {
2000 // change the hot selection position when middle clicking on a handle
2001 KoFlake::AnchorPosition newHotPosition = m_hotPosition;
2002 switch (handle) {
2004 newHotPosition = KoFlake::Top;
2005 break;
2007 newHotPosition = KoFlake::TopRight;
2008 break;
2010 newHotPosition = KoFlake::Right;
2011 break;
2013 newHotPosition = KoFlake::BottomRight;
2014 break;
2016 newHotPosition = KoFlake::Bottom;
2017 break;
2019 newHotPosition = KoFlake::BottomLeft;
2020 break;
2022 newHotPosition = KoFlake::Left;
2023 break;
2025 newHotPosition = KoFlake::TopLeft;
2026 break;
2027 case KoFlake::NoHandle:
2028 default:
2029 // check if we had hit the center point
2030 const KoViewConverter *converter = canvas()->viewConverter();
2031 QPointF pt = converter->documentToView(event->point);
2032
2033 // TODO: use calculated values instead!
2034 QPointF centerPt = converter->documentToView(selection->absolutePosition());
2035
2036 if (kisSquareDistance(pt, centerPt) < HANDLE_DISTANCE_SQ) {
2037 newHotPosition = KoFlake::Center;
2038 }
2039
2040 break;
2041 }
2042
2043 if (m_hotPosition != newHotPosition) {
2044 canvas()->resourceManager()->setResource(HotPosition, newHotPosition);
2045 return new NopInteractionStrategy(this);
2046 }
2047 }
2048
2049 if (!avoidSelection && editableShape) {
2050 // manipulation of selected shapes goes first
2051 if (handle != KoFlake::NoHandle) {
2052 // resizing or shearing only with left mouse button
2053 if (insideSelection) {
2054 bool forceUniformScaling = m_tabbedOptionWidget && m_tabbedOptionWidget->useUniformScaling();
2055 return new ShapeResizeStrategy(this, selection, event->point, handle, forceUniformScaling);
2056 }
2057
2058 if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle ||
2060
2061 return new ShapeShearStrategy(this, selection, event->point, handle);
2062 }
2063
2064 // rotating is allowed for right mouse button too
2065 if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle ||
2067
2068 return new ShapeRotateStrategy(this, selection, event->point, event->buttons());
2069 }
2070 }
2071
2072 if (!selectMultiple && !selectNextInStack) {
2073
2074 if (insideSelection) {
2075 return new ShapeMoveStrategy(this, selection, event->point);
2076 }
2077 }
2078 }
2079
2080 KoShape *shape = shapeManager()->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop);
2081
2082 if (avoidSelection || (!shape && handle == KoFlake::NoHandle)) {
2083 if (!selectMultiple) {
2084 selection->deselectAll();
2085 }
2086 return new SelectionInteractionStrategy(this, event->point, false);
2087 }
2088
2089 if (selection->isSelected(shape)) {
2090 if (selectMultiple) {
2091 selection->deselect(shape);
2092 }
2093 } else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected
2094 if (!selectMultiple) {
2095 selection->deselectAll();
2096 }
2097 selection->select(shape);
2098 // tablet selection isn't precise and may lead to a move, preventing that
2099 if (event->isTabletEvent()) {
2100 return new NopInteractionStrategy(this);
2101 }
2102 return new ShapeMoveStrategy(this, selection, event->point);
2103 }
2104 return 0;
2105}
2106
2108{
2109 QList<KoShape*> editableShapes;
2110
2111 if (koSelection()) {
2112 editableShapes = koSelection()->selectedEditableShapes();
2113 }
2114
2115 const bool hasEditableShapes = !editableShapes.isEmpty();
2116
2117 action("object_order_front")->setEnabled(hasEditableShapes);
2118 action("object_order_raise")->setEnabled(hasEditableShapes);
2119 action("object_order_lower")->setEnabled(hasEditableShapes);
2120 action("object_order_back")->setEnabled(hasEditableShapes);
2121
2122 action("object_transform_rotate_90_cw")->setEnabled(hasEditableShapes);
2123 action("object_transform_rotate_90_ccw")->setEnabled(hasEditableShapes);
2124 action("object_transform_rotate_180")->setEnabled(hasEditableShapes);
2125 action("object_transform_mirror_horizontally")->setEnabled(hasEditableShapes);
2126 action("object_transform_mirror_vertically")->setEnabled(hasEditableShapes);
2127 action("object_transform_reset")->setEnabled(hasEditableShapes);
2128
2129 const bool multipleSelected = editableShapes.size() > 1;
2130
2131 const bool alignmentEnabled =
2132 multipleSelected ||
2133 (!editableShapes.isEmpty() &&
2135
2136 action("object_align_horizontal_left")->setEnabled(alignmentEnabled);
2137 action("object_align_horizontal_center")->setEnabled(alignmentEnabled);
2138 action("object_align_horizontal_right")->setEnabled(alignmentEnabled);
2139 action("object_align_vertical_top")->setEnabled(alignmentEnabled);
2140 action("object_align_vertical_center")->setEnabled(alignmentEnabled);
2141 action("object_align_vertical_bottom")->setEnabled(alignmentEnabled);
2142
2143 const bool distributionEnabled = editableShapes.size() > 2;
2144
2145 action("object_distribute_horizontal_left")->setEnabled(distributionEnabled);
2146 action("object_distribute_horizontal_center")->setEnabled(distributionEnabled);
2147 action("object_distribute_horizontal_right")->setEnabled(distributionEnabled);
2148 action("object_distribute_horizontal_gaps")->setEnabled(distributionEnabled);
2149
2150 action("object_distribute_vertical_top")->setEnabled(distributionEnabled);
2151 action("object_distribute_vertical_center")->setEnabled(distributionEnabled);
2152 action("object_distribute_vertical_bottom")->setEnabled(distributionEnabled);
2153 action("object_distribute_vertical_gaps")->setEnabled(distributionEnabled);
2154
2155 /* Handling the text actions */
2156 bool textShape = false;
2157 bool otherShapes = false;
2158 bool filledShapes = false;
2159 bool shapesInside = false;
2161 const bool editFlowShapes = bool(currentTextShapeGroup);
2162 Q_FOREACH(KoShape *shape, editableShapes) {
2163 KoSvgTextShape *text = dynamic_cast<KoSvgTextShape *>(shape);
2164 if (text && !textShape) {
2165 textShape = true;
2166 } else {
2167 otherShapes = true;
2168 KoPathShape *path = dynamic_cast<KoPathShape*>(shape);
2169 filledShapes = filledShapes? filledShapes: (path && path->isClosedSubpath(0));
2170 if (editFlowShapes) {
2171 if (!shapesInside && currentTextShapeGroup->shapesInside().contains(shape)) {
2172 shapesInside = true;
2173 }
2174 }
2175 }
2176 if (textShape && otherShapes) break;
2177 }
2178 const bool editContours = textShape && otherShapes;
2179 const bool editFilledContours = textShape && filledShapes;
2180
2181 action("add_shape_to_flow_area")->setEnabled(editFilledContours);
2182 action("subtract_shape_from_flow_area")->setEnabled(editFilledContours);
2183 action("put_text_on_path")->setEnabled(editContours);
2184 action("remove_shapes_from_text_flow")->setEnabled(editFlowShapes);
2185 action("flow_shape_type_toggle")->setEnabled(editFlowShapes);
2186 action("flow_shape_order_back")->setEnabled(shapesInside);
2187 action("flow_shape_order_earlier")->setEnabled(shapesInside);
2188 action("flow_shape_order_later")->setEnabled(shapesInside);
2189 action("flow_shape_order_front")->setEnabled(shapesInside);
2190
2191 updateDistinctiveActions(editableShapes);
2192
2193 Q_EMIT selectionChanged(editableShapes.size());
2194}
2195
2197 const bool multipleSelected = editableShapes.size() > 1;
2198
2199 action("object_group")->setEnabled(multipleSelected);
2200
2201 action("object_unite")->setEnabled(multipleSelected);
2202 action("object_intersect")->setEnabled(multipleSelected);
2203 action("object_subtract")->setEnabled(multipleSelected);
2204
2205 bool hasShapesWithMultipleSegments = false;
2206 Q_FOREACH (KoShape *shape, editableShapes) {
2207 KoPathShape *pathShape = dynamic_cast<KoPathShape *>(shape);
2208 if (pathShape && pathShape->subpathCount() > 1) {
2209 hasShapesWithMultipleSegments = true;
2210 break;
2211 }
2212 }
2213 action("object_split")->setEnabled(hasShapesWithMultipleSegments);
2214
2215
2216 bool hasGroupShape = false;
2217 foreach (KoShape *shape, editableShapes) {
2218 if (dynamic_cast<KoShapeGroup *>(shape)) {
2219 hasGroupShape = true;
2220 break;
2221 }
2222 }
2223 action("object_ungroup")->setEnabled(hasGroupShape);
2224
2225 bool enablePreformatted = false;
2226 bool enablePrePositioned = false;
2227 bool enableInlineWrapped = false;
2228 bool text = false;
2229 Q_FOREACH (KoShape *shape, editableShapes) {
2230 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape *>(shape);
2231 if (textShape) {
2232 text = true;
2233 if (textShape->textType() != KoSvgTextShape::PreformattedText && !enablePreformatted) {
2234 enablePreformatted = true;
2235 }
2236 if (textShape && textShape->textType() != KoSvgTextShape::PrePositionedText && !enablePrePositioned) {
2237 enablePrePositioned = true;
2238 }
2239 if (textShape && textShape->textType() != KoSvgTextShape::InlineWrap && !enableInlineWrapped) {
2240 enableInlineWrapped = true;
2241 }
2242 }
2243 }
2244 QActionGroup *group = action("text_type_preformatted")->actionGroup();
2245 if (group) {
2246 group->setEnabled(text);
2247 }
2248
2249 action("text_type_preformatted")->setEnabled(enablePreformatted);
2250 action("text_type_pre_positioned")->setEnabled(enablePrePositioned);
2251 action("text_type_inline_wrap")->setEnabled(enableInlineWrapped);
2252}
2253
2254
2259
2261{
2262 if (m_contextMenu) {
2263 m_contextMenu->clear();
2264
2265 m_contextMenu->addSection(i18n("Vector Shape Actions"));
2266 m_contextMenu->addSeparator();
2267
2268 QMenu *transform = m_contextMenu->addMenu(i18n("Transform"));
2269
2270 transform->addAction(action("object_transform_rotate_90_cw"));
2271 transform->addAction(action("object_transform_rotate_90_ccw"));
2272 transform->addAction(action("object_transform_rotate_180"));
2273 transform->addSeparator();
2274 transform->addAction(action("object_transform_mirror_horizontally"));
2275 transform->addAction(action("object_transform_mirror_vertically"));
2276 transform->addSeparator();
2277 transform->addAction(action("object_transform_reset"));
2278
2279 if (action("object_unite")->isEnabled() ||
2280 action("object_intersect")->isEnabled() ||
2281 action("object_subtract")->isEnabled() ||
2282 action("object_split")->isEnabled()) {
2283
2284 QMenu *transform = m_contextMenu->addMenu(i18n("Logical Operations"));
2285 transform->addAction(action("object_unite"));
2286 transform->addAction(action("object_intersect"));
2287 transform->addAction(action("object_subtract"));
2288 transform->addAction(action("object_split"));
2289 }
2290
2291 m_contextMenu->addSeparator();
2292
2293 m_contextMenu->addAction(action("edit_cut"));
2294 m_contextMenu->addAction(action("edit_copy"));
2295 m_contextMenu->addAction(action("edit_paste"));
2296 m_contextMenu->addAction(action("paste_at"));
2297
2298 m_contextMenu->addSeparator();
2299
2300 m_contextMenu->addAction(action("object_order_front"));
2301 m_contextMenu->addAction(action("object_order_raise"));
2302 m_contextMenu->addAction(action("object_order_lower"));
2303 m_contextMenu->addAction(action("object_order_back"));
2304
2305 if (action("object_group")->isEnabled() || action("object_ungroup")->isEnabled()) {
2306 m_contextMenu->addSeparator();
2307 m_contextMenu->addAction(action("object_group"));
2308 m_contextMenu->addAction(action("object_ungroup"));
2309 }
2310 m_contextMenu->addSeparator();
2311 m_contextMenu->addAction(action("convert_shapes_to_vector_selection"));
2312
2313 m_contextMenu->addSeparator();
2314 QMenu *text = m_contextMenu->addMenu(i18n("Text"));
2315 text->addAction(action("add_shape_to_flow_area"));
2316 text->addAction(action("subtract_shape_from_flow_area"));
2317 text->addAction(action("put_text_on_path"));
2318 text->addSeparator();
2319 text->addAction(action("text_type_preformatted"));
2320 text->addAction(action("text_type_inline_wrap"));
2321 text->addAction(action("text_type_pre_positioned"));
2322 text->addSeparator();
2323 text->addAction(action("remove_shapes_from_text_flow"));
2324 text->addAction(action("flow_shape_type_toggle"));
2325 text->addSeparator();
2326 text->addAction(action("flow_shape_order_back"));
2327 text->addAction(action("flow_shape_order_earlier"));
2328 text->addAction(action("flow_shape_order_later"));
2329 text->addAction(action("flow_shape_order_front"));
2330 }
2331
2332 return m_contextMenu.data();
2333}
2334
2335void DefaultTool::addTransformActions(QMenu *menu) const {
2336 menu->addAction(action("object_transform_rotate_90_cw"));
2337 menu->addAction(action("object_transform_rotate_90_ccw"));
2338 menu->addAction(action("object_transform_rotate_180"));
2339 menu->addSeparator();
2340 menu->addAction(action("object_transform_mirror_horizontally"));
2341 menu->addAction(action("object_transform_mirror_vertically"));
2342 menu->addSeparator();
2343 menu->addAction(action("object_transform_reset"));
2344}
2345
2347{
2349 QString tool = KoToolManager::instance()->preferredToolForSelection(shapes);
2350 QTimer::singleShot(0, [tool = std::move(tool)]() {
2352 });
2353}
2354
2356
2358 : parent(parent)
2359 , compressor(10, KisSignalCompressor::POSTPONE){}
2360
2364};
2365
2368 , d(new Private(parent))
2369{
2370 connect(&d->compressor, SIGNAL(timeout()), this, SIGNAL(textSelectionChanged()));
2371}
2372
2377
2379{
2381 if (!d->parent->selection()->hasSelection()) return props;
2382
2383 QList<KoShape*> shapes = d->shapes;
2384 for (auto it = shapes.begin(); it != shapes.end(); it++) {
2385 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape*>(*it);
2386 if (!textShape) continue;
2387 KoSvgTextProperties p = textShape->textProperties();
2388 props.append(p);
2389 }
2390
2391 return props;
2392}
2393
2398
2403
2404void DefaultToolTextPropertiesInterface::setPropertiesOnSelected(KoSvgTextProperties properties, QSet<KoSvgTextProperties::PropertyId> removeProperties)
2405{
2406 if (d->shapes.isEmpty()) return;
2407 KUndo2Command *cmd = new KoShapeMergeTextPropertiesCommand(d->shapes, properties, removeProperties);
2408 if (cmd) {
2409 d->parent->canvas()->addCommand(cmd);
2410 }
2411}
2412
2413void DefaultToolTextPropertiesInterface::setCharacterPropertiesOnSelected(KoSvgTextProperties properties, QSet<KoSvgTextProperties::PropertyId> removeProperties)
2414{
2415 Q_UNUSED(properties)
2416 Q_UNUSED(removeProperties)
2417 return;
2418}
2419
2421{
2422 return false;
2423}
2424
2429
2431{
2432 Q_UNUSED(pos)
2433 Q_UNUSED(anchor)
2434 d->compressor.start();
2435}
2436
2438{
2439 d->compressor.start();
2440}
2441
2443{
2444 Q_UNUSED(type)
2445 Q_UNUSED(shape)
2446 d->compressor.start();
2447}
2448
2450{
2451 Q_FOREACH(KoShape *shape, d->shapes) {
2452 shape->removeShapeChangeListener(this);
2453 }
2454 d->shapes.clear();
2455}
2456
2458{
2459 if (d->parent->updateTextContourMode()) return;
2460 Q_FOREACH(KoShape *shape, d->shapes) {
2461 if (!shape) continue;
2462 shape->removeShapeChangeListener(this);
2463 }
2464
2465 auto *textShapeGroup = d->parent->tryFetchCurrentShapeManagerOwnerTextShape();
2466
2467 if (textShapeGroup) {
2468 d->shapes = {textShapeGroup};
2469 } else {
2470 d->shapes = d->parent->canvas()->selectedShapesProxy()->selection()->selectedEditableShapes();
2471 }
2472
2473 Q_FOREACH(KoShape *shape, d->shapes) {
2474 if (!shape) continue;
2475 shape->addShapeChangeListener(this);
2476 }
2477 d->compressor.start();
2478}
#define HANDLE_DISTANCE
#define INNER_HANDLE_DISTANCE_SQ
#define HANDLE_DISTANCE_SQ
float value(const T *src, size_t ch)
const Params2D p
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
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) 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)
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 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
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 ...