diff --git a/libs/flake/tools/KoShapeRubberSelectStrategy.cpp b/libs/flake/tools/KoShapeRubberSelectStrategy.cpp index 458c3b90d9..83862e25d0 100644 --- a/libs/flake/tools/KoShapeRubberSelectStrategy.cpp +++ b/libs/flake/tools/KoShapeRubberSelectStrategy.cpp @@ -1,140 +1,122 @@ /* This file is part of the KDE project Copyright (C) 2006 Thorsten Zachmann Copyright (C) 2006 Thomas Zander This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoShapeRubberSelectStrategy.h" #include "KoShapeRubberSelectStrategy_p.h" #include "KoViewConverter.h" #include #include "KoShapeManager.h" #include "KoSelection.h" #include "KoCanvasBase.h" - KoShapeRubberSelectStrategy::KoShapeRubberSelectStrategy(KoToolBase *tool, const QPointF &clicked, bool useSnapToGrid) : KoInteractionStrategy(*(new KoShapeRubberSelectStrategyPrivate(tool))) { Q_D(KoShapeRubberSelectStrategy); d->snapGuide->enableSnapStrategies(KoSnapGuide::GridSnapping); d->snapGuide->enableSnapping(useSnapToGrid); d->selectRect = QRectF(d->snapGuide->snap(clicked, 0), QSizeF(0, 0)); } void KoShapeRubberSelectStrategy::paint(QPainter &painter, const KoViewConverter &converter) { Q_D(KoShapeRubberSelectStrategy); painter.setRenderHint(QPainter::Antialiasing, false); const QColor crossingColor(80,130,8); const QColor coveringColor(8,60,167); QColor selectColor( currentMode() == CrossingSelection ? crossingColor : coveringColor); selectColor.setAlphaF(0.8); painter.setPen(QPen(selectColor, 0)); selectColor.setAlphaF(0.4); const QBrush fillBrush(selectColor); painter.setBrush(fillBrush); QRectF paintRect = converter.documentToView(d->selectedRect()); paintRect = paintRect.normalized(); painter.drawRect(paintRect); } void KoShapeRubberSelectStrategy::handleMouseMove(const QPointF &p, Qt::KeyboardModifiers modifiers) { Q_D(KoShapeRubberSelectStrategy); QPointF point = d->snapGuide->snap(p, modifiers); if (modifiers & Qt::ControlModifier) { d->tool->canvas()->updateCanvas(d->selectedRect()); d->selectRect.moveTopLeft(d->selectRect.topLeft() - (d->lastPos - point)); d->lastPos = point; d->tool->canvas()->updateCanvas(d->selectedRect()); return; } d->lastPos = point; QPointF old = d->selectRect.bottomRight(); d->selectRect.setBottomRight(point); /* +---------------|--+ | | | We need to figure out rects A and B based on the two points. BUT | old | A| we need to do that even if the points are switched places | \ | | (i.e. the rect got smaller) and even if the rect is mirrored +---------------+ | in either the horizontal or vertical axis. | B | +------------------+ `- point */ QPointF x1 = old; x1.setY(d->selectRect.topLeft().y()); qreal h1 = point.y() - x1.y(); qreal h2 = old.y() - x1.y(); QRectF A(x1, QSizeF(point.x() - x1.x(), point.y() < d->selectRect.top() ? qMin(h1, h2) : qMax(h1, h2))); A = A.normalized(); d->tool->canvas()->updateCanvas(A); QPointF x2 = old; x2.setX(d->selectRect.topLeft().x()); qreal w1 = point.x() - x2.x(); qreal w2 = old.x() - x2.x(); QRectF B(x2, QSizeF(point.x() < d->selectRect.left() ? qMin(w1, w2) : qMax(w1, w2), point.y() - x2.y())); B = B.normalized(); d->tool->canvas()->updateCanvas(B); } -void KoShapeRubberSelectStrategy::finishInteraction(Qt::KeyboardModifiers modifiers) -{ - Q_D(KoShapeRubberSelectStrategy); - Q_UNUSED(modifiers); - KoSelection * selection = d->tool->canvas()->shapeManager()->selection(); - - const bool useContainedMode = currentMode() == CoveringSelection; - - QList shapes = - d->tool->canvas()->shapeManager()-> - shapesAt(d->selectedRect(), true, useContainedMode); - - Q_FOREACH (KoShape * shape, shapes) { - if (!shape->isSelectable()) continue; - - selection->select(shape); - } - - d->tool->repaintDecorations(); - d->tool->canvas()->updateCanvas(d->selectedRect()); -} - KoShapeRubberSelectStrategy::SelectionMode KoShapeRubberSelectStrategy::currentMode() const { Q_D(const KoShapeRubberSelectStrategy); return d->selectRect.left() < d->selectRect.right() ? CoveringSelection : CrossingSelection; } KUndo2Command *KoShapeRubberSelectStrategy::createCommand() { return 0; } + +QRectF KoShapeRubberSelectStrategy::selectedRectangle() const { + Q_D(const KoShapeRubberSelectStrategy); + return d->selectedRect(); +} \ No newline at end of file diff --git a/libs/flake/tools/KoShapeRubberSelectStrategy.h b/libs/flake/tools/KoShapeRubberSelectStrategy.h index 13beb562ab..1b5464903c 100644 --- a/libs/flake/tools/KoShapeRubberSelectStrategy.h +++ b/libs/flake/tools/KoShapeRubberSelectStrategy.h @@ -1,74 +1,76 @@ /* This file is part of the KDE project Copyright (C) 2006 Thorsten Zachmann Copyright (C) 2006 Thomas Zander This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOSHAPERUBBERSELECTSTRATEGY_H #define KOSHAPERUBBERSELECTSTRATEGY_H #include "KoInteractionStrategy.h" #include #include "kritaflake_export.h" class KoToolBase; class KoShapeRubberSelectStrategyPrivate; /** - * Implement the rubber band selection of flake objects. + * This is a base class for interactions based on dragging a rectangular area on the canvas, + * such as selection, zooming or shape creation. * * When the user selects stuff in left-to-right way, selection is in "covering" * (or "containing") mode, when in "left-to-right" in "crossing" mode */ class KRITAFLAKE_EXPORT KoShapeRubberSelectStrategy : public KoInteractionStrategy { public: /** * Constructor that initiates the rubber select. * A rubber select is basically rectangle area that the user drags out * from @p clicked to a point later provided in the handleMouseMove() continuously * showing a semi-transarant 'rubber-mat' over the objects it is about to select. * @param tool the parent tool which controls this strategy * @param clicked the initial point that the user depressed (in pt). * @param useSnapToGrid use the snap-to-grid settings while doing the rubberstamp. */ KoShapeRubberSelectStrategy(KoToolBase *tool, const QPointF &clicked, bool useSnapToGrid = false); void paint(QPainter &painter, const KoViewConverter &converter) override; void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override; KUndo2Command *createCommand() override; - void finishInteraction(Qt::KeyboardModifiers modifiers) override; protected: /// constructor KoShapeRubberSelectStrategy(KoShapeRubberSelectStrategyPrivate &); + QRectF selectedRectangle() const; + enum SelectionMode { CrossingSelection, CoveringSelection }; virtual SelectionMode currentMode() const; private: Q_DECLARE_PRIVATE(KoShapeRubberSelectStrategy) }; #endif /* KOSHAPERUBBERSELECTSTRATEGY_H */ diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp index 794a41595e..1a5caa9e66 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp @@ -1,1368 +1,1401 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2008-2009 Jan Hambrecht Copyright (C) 2008 C. Boemann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "DefaultTool.h" #include "DefaultToolGeometryWidget.h" #include "DefaultToolTabbedWidget.h" #include "SelectionDecorator.h" #include "ShapeMoveStrategy.h" #include "ShapeRotateStrategy.h" #include "ShapeShearStrategy.h" #include "ShapeResizeStrategy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_action_registry.h" #include "kis_node.h" #include "kis_node_manager.h" #include "KisViewManager.h" #include "kis_canvas2.h" #include "kis_canvas_resource_provider.h" #include #include "kis_document_aware_spin_box_unit_manager.h" #include #include #include #include #include #include #include #include #include #include #include "kis_assert.h" #include "kis_global.h" #include "kis_debug.h" #include #define HANDLE_DISTANCE 10 #define HANDLE_DISTANCE_SQ (HANDLE_DISTANCE * HANDLE_DISTANCE) #define INNER_HANDLE_DISTANCE_SQ 16 namespace { static const QString EditFillGradientFactoryId = "edit_fill_gradient"; static const QString EditStrokeGradientFactoryId = "edit_stroke_gradient"; } QPolygonF selectionPolygon(KoSelection *selection) { QPolygonF result; QList selectedShapes = selection->selectedShapes(); if (!selectedShapes.size()) { return result; } if (selectedShapes.size() > 1) { QTransform matrix = selection->absoluteTransformation(0); result = matrix.map(QPolygonF(QRectF(QPointF(0, 0), selection->size()))); } else { KoShape *selectedShape = selectedShapes.first(); QTransform matrix = selectedShape->absoluteTransformation(0); result = matrix.map(QPolygonF(QRectF(QPointF(0, 0), selectedShape->size()))); } return result; } class NopInteractionStrategy : public KoInteractionStrategy { public: explicit NopInteractionStrategy(KoToolBase *parent) : KoInteractionStrategy(parent) { } KUndo2Command *createCommand() override { return 0; } void handleMouseMove(const QPointF & /*mouseLocation*/, Qt::KeyboardModifiers /*modifiers*/) override {} void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {} void paint(QPainter &painter, const KoViewConverter &converter) override { Q_UNUSED(painter); Q_UNUSED(converter); } }; class SelectionInteractionStrategy : public KoShapeRubberSelectStrategy { public: explicit SelectionInteractionStrategy(KoToolBase *parent, const QPointF &clicked, bool useSnapToGrid) : KoShapeRubberSelectStrategy(parent, clicked, useSnapToGrid) { } void paint(QPainter &painter, const KoViewConverter &converter) override { KoShapeRubberSelectStrategy::paint(painter, converter); } + + void finishInteraction(Qt::KeyboardModifiers modifiers) override + { + Q_UNUSED(modifiers); + DefaultTool *defaultTool = dynamic_cast(tool()); + KIS_SAFE_ASSERT_RECOVER_RETURN(defaultTool); + + KoSelection * selection = defaultTool->koSelection(); + + const bool useContainedMode = currentMode() == CoveringSelection; + + QList shapes = + defaultTool->shapeManager()-> + shapesAt(selectedRectangle(), true, useContainedMode); + + Q_FOREACH (KoShape * shape, shapes) { + if (!shape->isSelectable()) continue; + + selection->select(shape); + } + + defaultTool->repaintDecorations(); + defaultTool->canvas()->updateCanvas(selectedRectangle()); + } }; #include #include "KoShapeGradientHandles.h" #include "ShapeGradientEditStrategy.h" class DefaultTool::MoveGradientHandleInteractionFactory : public KoInteractionStrategyFactory { public: MoveGradientHandleInteractionFactory(KoFlake::FillVariant fillVariant, int priority, const QString &id, DefaultTool *_q) : KoInteractionStrategyFactory(priority, id), q(_q), m_fillVariant(fillVariant) { } KoInteractionStrategy* createStrategy(KoPointerEvent *ev) override { m_currentHandle = handleAt(ev->point); if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) { KoShape *shape = onlyEditableShape(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, 0); return new ShapeGradientEditStrategy(q, m_fillVariant, shape, m_currentHandle.type, ev->point); } return 0; } bool hoverEvent(KoPointerEvent *ev) override { m_currentHandle = handleAt(ev->point); return false; } bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override { Q_UNUSED(painter); Q_UNUSED(converter); return false; } bool tryUseCustomCursor() override { if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) { q->useCursor(Qt::OpenHandCursor); } return m_currentHandle.type != KoShapeGradientHandles::Handle::None; } private: KoShape* onlyEditableShape() const { KoSelection *selection = q->koSelection(); QList shapes = selection->selectedEditableShapes(); KoShape *shape = 0; if (shapes.size() == 1) { shape = shapes.first(); } return shape; } KoShapeGradientHandles::Handle handleAt(const QPointF &pos) { KoShapeGradientHandles::Handle result; KoShape *shape = onlyEditableShape(); if (shape) { KoFlake::SelectionHandle globalHandle = q->handleAt(pos); const qreal distanceThresholdSq = globalHandle == KoFlake::NoHandle ? HANDLE_DISTANCE_SQ : 0.25 * HANDLE_DISTANCE_SQ; const KoViewConverter *converter = q->canvas()->viewConverter(); const QPointF viewPoint = converter->documentToView(pos); qreal minDistanceSq = std::numeric_limits::max(); KoShapeGradientHandles sh(m_fillVariant, shape); Q_FOREACH (const KoShapeGradientHandles::Handle &handle, sh.handles()) { const QPointF handlePoint = converter->documentToView(handle.pos); const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint); if (distanceSq < distanceThresholdSq && distanceSq < minDistanceSq) { result = handle; minDistanceSq = distanceSq; } } } return result; } private: DefaultTool *q; KoFlake::FillVariant m_fillVariant; KoShapeGradientHandles::Handle m_currentHandle; }; class SelectionHandler : public KoToolSelection { public: SelectionHandler(DefaultTool *parent) : KoToolSelection(parent) , m_selection(parent->koSelection()) { } bool hasSelection() override { if (m_selection) { return m_selection->count(); } return false; } private: QPointer m_selection; }; DefaultTool::DefaultTool(KoCanvasBase *canvas) : KoInteractionTool(canvas) , m_lastHandle(KoFlake::NoHandle) , m_hotPosition(KoFlake::TopLeft) , m_mouseWasInsideHandles(false) , m_selectionHandler(new SelectionHandler(this)) - , m_customEventStrategy(0) , m_tabbedOptionWidget(0) { setupActions(); QPixmap rotatePixmap, shearPixmap; rotatePixmap.load(":/cursor_rotate.png"); Q_ASSERT(!rotatePixmap.isNull()); shearPixmap.load(":/cursor_shear.png"); Q_ASSERT(!shearPixmap.isNull()); m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45))); m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90))); m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135))); m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180))); m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225))); m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270))); m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315))); m_rotateCursors[7] = QCursor(rotatePixmap); /* m_rotateCursors[0] = QCursor(Qt::RotateNCursor); m_rotateCursors[1] = QCursor(Qt::RotateNECursor); m_rotateCursors[2] = QCursor(Qt::RotateECursor); m_rotateCursors[3] = QCursor(Qt::RotateSECursor); m_rotateCursors[4] = QCursor(Qt::RotateSCursor); m_rotateCursors[5] = QCursor(Qt::RotateSWCursor); m_rotateCursors[6] = QCursor(Qt::RotateWCursor); m_rotateCursors[7] = QCursor(Qt::RotateNWCursor); */ m_shearCursors[0] = QCursor(shearPixmap); m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45))); m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90))); m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135))); m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180))); m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225))); m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270))); m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315))); m_sizeCursors[0] = Qt::SizeVerCursor; m_sizeCursors[1] = Qt::SizeBDiagCursor; m_sizeCursors[2] = Qt::SizeHorCursor; m_sizeCursors[3] = Qt::SizeFDiagCursor; m_sizeCursors[4] = Qt::SizeVerCursor; m_sizeCursors[5] = Qt::SizeBDiagCursor; m_sizeCursors[6] = Qt::SizeHorCursor; m_sizeCursors[7] = Qt::SizeFDiagCursor; connect(canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), this, SLOT(updateActions())); } DefaultTool::~DefaultTool() { } void DefaultTool::slotActivateEditFillGradient(bool value) { if (value) { addInteractionFactory( new MoveGradientHandleInteractionFactory(KoFlake::Fill, 1, EditFillGradientFactoryId, this)); } else { removeInteractionFactory(EditFillGradientFactoryId); } repaintDecorations(); } void DefaultTool::slotActivateEditStrokeGradient(bool value) { if (value) { addInteractionFactory( new MoveGradientHandleInteractionFactory(KoFlake::StrokeFill, 0, EditStrokeGradientFactoryId, this)); } else { removeInteractionFactory(EditStrokeGradientFactoryId); } repaintDecorations(); } bool DefaultTool::wantsAutoScroll() const { return true; } void DefaultTool::addMappedAction(QSignalMapper *mapper, const QString &actionId, int commandType) { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); QAction *action = actionRegistry->makeQAction(actionId, this); addAction(actionId, action); connect(action, SIGNAL(triggered()), mapper, SLOT(map())); mapper->setMapping(action, commandType); } void DefaultTool::setupActions() { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); QAction *actionBringToFront = actionRegistry->makeQAction("object_order_front", this); addAction("object_order_front", actionBringToFront); connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront())); QAction *actionRaise = actionRegistry->makeQAction("object_order_raise", this); addAction("object_order_raise", actionRaise); connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp())); QAction *actionLower = actionRegistry->makeQAction("object_order_lower", this); addAction("object_order_lower", actionLower); connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown())); QAction *actionSendToBack = actionRegistry->makeQAction("object_order_back", this); addAction("object_order_back", actionSendToBack); connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack())); QSignalMapper *alignSignalsMapper = new QSignalMapper(this); connect(alignSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionAlign(int))); addMappedAction(alignSignalsMapper, "object_align_horizontal_left", KoShapeAlignCommand::HorizontalLeftAlignment); addMappedAction(alignSignalsMapper, "object_align_horizontal_center", KoShapeAlignCommand::HorizontalCenterAlignment); addMappedAction(alignSignalsMapper, "object_align_horizontal_right", KoShapeAlignCommand::HorizontalRightAlignment); addMappedAction(alignSignalsMapper, "object_align_vertical_top", KoShapeAlignCommand::VerticalTopAlignment); addMappedAction(alignSignalsMapper, "object_align_vertical_center", KoShapeAlignCommand::VerticalCenterAlignment); addMappedAction(alignSignalsMapper, "object_align_vertical_bottom", KoShapeAlignCommand::VerticalBottomAlignment); QSignalMapper *distributeSignalsMapper = new QSignalMapper(this); connect(distributeSignalsMapper, SIGNAL(mapped(int)), SLOT(selectionDistribute(int))); addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_left", KoShapeDistributeCommand::HorizontalLeftDistribution); addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_center", KoShapeDistributeCommand::HorizontalCenterDistribution); addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_right", KoShapeDistributeCommand::HorizontalRightDistribution); addMappedAction(distributeSignalsMapper, "object_distribute_horizontal_gaps", KoShapeDistributeCommand::HorizontalGapsDistribution); addMappedAction(distributeSignalsMapper, "object_distribute_vertical_top", KoShapeDistributeCommand::VerticalTopDistribution); addMappedAction(distributeSignalsMapper, "object_distribute_vertical_center", KoShapeDistributeCommand::VerticalCenterDistribution); addMappedAction(distributeSignalsMapper, "object_distribute_vertical_bottom", KoShapeDistributeCommand::VerticalBottomDistribution); addMappedAction(distributeSignalsMapper, "object_distribute_vertical_gaps", KoShapeDistributeCommand::VerticalGapsDistribution); QAction *actionGroupBottom = actionRegistry->makeQAction("object_group", this); addAction("object_group", actionGroupBottom); connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup())); QAction *actionUngroupBottom = actionRegistry->makeQAction("object_ungroup", this); addAction("object_ungroup", actionUngroupBottom); connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup())); m_contextMenu.reset(new QMenu()); } qreal DefaultTool::rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation) { QPointF selectionCenter = koSelection()->absolutePosition(); QPointF direction; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRight) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRight) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized()).toPointF(); break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRight); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRight) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized()).toPointF(); break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeft); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRight) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomLeftHandle: direction = koSelection()->absolutePosition(KoFlake::BottomLeft) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::BottomRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeft) - koSelection()->absolutePosition(KoFlake::TopLeft)).normalized()).toPointF(); break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeft); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeft) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopLeftHandle: direction = koSelection()->absolutePosition(KoFlake::TopLeft) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::TopRight)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeft) - koSelection()->absolutePosition(KoFlake::BottomLeft)).normalized()).toPointF(); break; case KoFlake::NoHandle: return 0.0; break; } qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { rotation -= 0.0; } else { rotation -= 270.0; } break; case KoFlake::TopRightHandle: rotation -= 315.0; break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { rotation -= 90.0; } else { rotation -= 0.0; } break; case KoFlake::BottomRightHandle: rotation -= 45.0; break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { rotation -= 180.0; } else { rotation -= 90.0; } break; case KoFlake::BottomLeftHandle: rotation -= 135.0; break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { rotation -= 270.0; } else { rotation -= 180.0; } break; case KoFlake::TopLeftHandle: rotation -= 225.0; break; case KoFlake::NoHandle: break; } if (rotation < 0.0) { rotation += 360.0; } return rotation; } void DefaultTool::updateCursor() { if (tryUseCustomCursor()) return; QCursor cursor = Qt::ArrowCursor; QString statusText; - if (koSelection()->count() > 0) { // has a selection - bool editable = !koSelection()->selectedEditableShapes().isEmpty(); + KoSelection *selection = koSelection(); + if (selection && selection->count() > 0) { // has a selection + bool editable = !selection->selectedEditableShapes().isEmpty(); if (!m_mouseWasInsideHandles) { m_angle = rotationOfHandle(m_lastHandle, true); int rotOctant = 8 + int(8.5 + m_angle / 45); bool rotateHandle = false; bool shearHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_shearCursors[(0 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopRightHandle: cursor = m_rotateCursors[(1 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_shearCursors[(2 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomRightHandle: cursor = m_rotateCursors[(3 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_shearCursors[(4 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomLeftHandle: cursor = m_rotateCursors[(5 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_shearCursors[(6 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopLeftHandle: cursor = m_rotateCursors[(7 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::NoHandle: cursor = Qt::ArrowCursor; break; } if (rotateHandle) { statusText = i18n("Left click rotates around center, right click around highlighted position."); } if (shearHandle) { statusText = i18n("Click and drag to shear selection."); } } else { statusText = i18n("Click and drag to resize selection."); m_angle = rotationOfHandle(m_lastHandle, false); int rotOctant = 8 + int(8.5 + m_angle / 45); bool cornerHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_sizeCursors[(0 + rotOctant) % 8]; break; case KoFlake::TopRightHandle: cursor = m_sizeCursors[(1 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_sizeCursors[(2 + rotOctant) % 8]; break; case KoFlake::BottomRightHandle: cursor = m_sizeCursors[(3 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_sizeCursors[(4 + rotOctant) % 8]; break; case KoFlake::BottomLeftHandle: cursor = m_sizeCursors[(5 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_sizeCursors[(6 + rotOctant) % 8]; break; case KoFlake::TopLeftHandle: cursor = m_sizeCursors[(7 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::NoHandle: cursor = Qt::SizeAllCursor; statusText = i18n("Click and drag to move selection."); break; } if (cornerHandle) { statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position."); } } if (!editable) { cursor = Qt::ArrowCursor; } } else { // there used to be guides... :'''( } useCursor(cursor); if (currentStrategy() == 0) { emit statusTextChanged(statusText); } } void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter) { // this tool only works on a vector layer right now, so give a warning if another layer type is trying to use it - KisNodeSP currentNode = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); - - if (currentNode.isNull() || !currentNode->inherits("KisShapeLayer")) { - - KisCanvas2 * kiscanvas = static_cast(canvas()); + if (!isValidForCurrentLayer()) { + KisCanvas2 *kiscanvas = static_cast(canvas()); kiscanvas->viewManager()->showFloatingMessage( - i18n("This tool only works on vector layers. You probably want the move tool."), - QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter); + i18n("This tool only works on vector layers. You probably want the move tool."), + QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter); return; } - - SelectionDecorator decorator(canvas()->resourceManager()); - decorator.setSelection(koSelection()); - decorator.setHandleRadius(handleRadius()); - decorator.setShowFillGradientHandles(hasInteractioFactory(EditFillGradientFactoryId)); - decorator.setShowStrokeFillGradientHandles(hasInteractioFactory(EditStrokeGradientFactoryId)); - decorator.paint(painter, converter); + KoSelection *selection = koSelection(); + if (selection) { + SelectionDecorator decorator(canvas()->resourceManager()); + decorator.setSelection(selection); + decorator.setHandleRadius(handleRadius()); + decorator.setShowFillGradientHandles(hasInteractioFactory(EditFillGradientFactoryId)); + decorator.setShowStrokeFillGradientHandles(hasInteractioFactory(EditStrokeGradientFactoryId)); + decorator.paint(painter, converter); + } KoInteractionTool::paint(painter, converter); painter.save(); KoShape::applyConversion(painter, converter); canvas()->snapGuide()->paint(painter, converter); painter.restore(); } +bool DefaultTool::isValidForCurrentLayer() const +{ + KisNodeSP currentNode = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); + return !currentNode.isNull() && currentNode->inherits("KisShapeLayer"); +} + +KoShapeManager *DefaultTool::shapeManager() const { + return canvas()->shapeManager(); +} + void DefaultTool::mousePressEvent(KoPointerEvent *event) { KoInteractionTool::mousePressEvent(event); updateCursor(); } void DefaultTool::mouseMoveEvent(KoPointerEvent *event) { KoInteractionTool::mouseMoveEvent(event); if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) { QRectF bound = handlesSize(); if (bound.contains(event->point)) { bool inside; KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside); if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) { m_lastHandle = newDirection; m_mouseWasInsideHandles = inside; //repaintDecorations(); } } else { /*if (m_lastHandle != KoFlake::NoHandle) repaintDecorations(); */ m_lastHandle = KoFlake::NoHandle; m_mouseWasInsideHandles = false; // there used to be guides... :'''( } } else { // there used to be guides... :'''( } updateCursor(); } QRectF DefaultTool::handlesSize() { KoSelection *selection = koSelection(); - if (!selection->count()) return QRectF(); + if (!selection || !selection->count()) return QRectF(); recalcSelectionBox(selection); QRectF bound = m_selectionOutline.boundingRect(); // expansion Border if (!canvas() || !canvas()->viewConverter()) { return bound; } QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE)); bound.adjust(-border.x(), -border.y(), border.x(), border.y()); return bound; } void DefaultTool::mouseReleaseEvent(KoPointerEvent *event) { KoInteractionTool::mouseReleaseEvent(event); updateCursor(); } void DefaultTool::mouseDoubleClickEvent(KoPointerEvent *event) { - KoSelection *selection = canvas()->selectedShapesProxy()->selection(); + KoSelection *selection = koSelection(); - KoShape *shape = canvas()->shapeManager()->shapeAt(event->point, KoFlake::ShapeOnTop); - if (shape && !selection->isSelected(shape)) { + KoShape *shape = shapeManager()->shapeAt(event->point, KoFlake::ShapeOnTop); + if (shape && selection && !selection->isSelected(shape)) { if (!(event->modifiers() & Qt::ShiftModifier)) { selection->deselectAll(); } selection->select(shape); } explicitUserStrokeEndRequest(); } bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers) { bool result = false; qreal x = 0.0, y = 0.0; if (direction == Qt::Key_Left) { x = -5; } else if (direction == Qt::Key_Right) { x = 5; } else if (direction == Qt::Key_Up) { y = -5; } else if (direction == Qt::Key_Down) { y = 5; } if (x != 0.0 || y != 0.0) { // actually move if ((modifiers & Qt::ShiftModifier) != 0) { x *= 10; y *= 10; } else if ((modifiers & Qt::AltModifier) != 0) { // more precise x /= 5; y /= 5; } QList shapes = koSelection()->selectedEditableShapes(); if (!shapes.isEmpty()) { canvas()->addCommand(new KoShapeMoveCommand(shapes, QPointF(x, y))); result = true; } } return result; } void DefaultTool::keyPressEvent(QKeyEvent *event) { KoInteractionTool::keyPressEvent(event); if (currentStrategy() == 0) { switch (event->key()) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: if (moveSelection(event->key(), event->modifiers())) { event->accept(); } break; case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: canvas()->resourceManager()->setResource(HotPosition, event->key() - Qt::Key_1); event->accept(); break; default: return; } } } void DefaultTool::repaintDecorations() { if (koSelection() && koSelection()->count() > 0) { canvas()->updateCanvas(handlesSize()); } } void DefaultTool::copy() const { // all the selected shapes, not only editable! - QList shapes = canvas()->selectedShapesProxy()->selection()->selectedShapes(); + QList shapes = koSelection()->selectedShapes(); if (!shapes.isEmpty()) { KoDrag drag; drag.setSvg(shapes); drag.addToClipboard(); } } void DefaultTool::deleteSelection() { QList shapes; - foreach (KoShape *s, canvas()->selectedShapesProxy()->selection()->selectedShapes()) { + foreach (KoShape *s, koSelection()->selectedShapes()) { if (s->isGeometryProtected()) { continue; } shapes << s; } if (!shapes.empty()) { canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes)); } } bool DefaultTool::paste() { // we no longer have to do anything as tool Proxy will do it for us return false; } -KoSelection *DefaultTool::koSelection() +KoSelection *DefaultTool::koSelection() const { Q_ASSERT(canvas()); Q_ASSERT(canvas()->selectedShapesProxy()); return canvas()->selectedShapesProxy()->selection(); } KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning) { // check for handles in this order; meaning that when handles overlap the one on top is chosen static const KoFlake::SelectionHandle handleOrder[] = { KoFlake::BottomRightHandle, KoFlake::TopLeftHandle, KoFlake::BottomLeftHandle, KoFlake::TopRightHandle, KoFlake::BottomMiddleHandle, KoFlake::RightMiddleHandle, KoFlake::LeftMiddleHandle, KoFlake::TopMiddleHandle, KoFlake::NoHandle }; const KoViewConverter *converter = canvas()->viewConverter(); KoSelection *selection = koSelection(); - if (!selection->count() || !converter) { + if (!selection || !selection->count() || !converter) { return KoFlake::NoHandle; } recalcSelectionBox(selection); if (innerHandleMeaning) { QPainterPath path; path.addPolygon(m_selectionOutline); *innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point)); } const QPointF viewPoint = converter->documentToView(point); for (int i = 0; i < KoFlake::NoHandle; ++i) { KoFlake::SelectionHandle handle = handleOrder[i]; const QPointF handlePoint = converter->documentToView(m_selectionBox[handle]); const qreal distanceSq = kisSquareDistance(viewPoint, handlePoint); // if just inside the outline if (distanceSq < HANDLE_DISTANCE_SQ) { if (innerHandleMeaning) { if (distanceSq < INNER_HANDLE_DISTANCE_SQ) { *innerHandleMeaning = true; } } return handle; } } return KoFlake::NoHandle; } void DefaultTool::recalcSelectionBox(KoSelection *selection) { KIS_ASSERT_RECOVER_RETURN(selection->count()); QTransform matrix = selection->absoluteTransformation(0); m_selectionOutline = matrix.map(QPolygonF(selection->outlineRect())); m_angle = 0.0; QPolygonF outline = m_selectionOutline; //shorter name in the following :) m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2; m_selectionBox[KoFlake::TopRightHandle] = outline.value(1); m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2; m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2); m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2; m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3); m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2; m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0); if (selection->count() == 1) { #if 0 // TODO detect mirroring KoShape *s = koSelection()->firstSelectedShape(); if (s->scaleX() < 0) { // vertically mirrored: swap left / right std::swap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::TopRightHandle]); std::swap(m_selectionBox[KoFlake::LeftMiddleHandle], m_selectionBox[KoFlake::RightMiddleHandle]); std::swap(m_selectionBox[KoFlake::BottomLeftHandle], m_selectionBox[KoFlake::BottomRightHandle]); } if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom std::swap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::BottomLeftHandle]); std::swap(m_selectionBox[KoFlake::TopMiddleHandle], m_selectionBox[KoFlake::BottomMiddleHandle]); std::swap(m_selectionBox[KoFlake::TopRightHandle], m_selectionBox[KoFlake::BottomRightHandle]); } #endif } } void DefaultTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); m_mouseWasInsideHandles = false; m_lastHandle = KoFlake::NoHandle; useCursor(Qt::ArrowCursor); repaintDecorations(); updateActions(); if (m_tabbedOptionWidget) { m_tabbedOptionWidget->activate(); } } void DefaultTool::deactivate() { KoToolBase::deactivate(); if (m_tabbedOptionWidget) { m_tabbedOptionWidget->deactivate(); } } void DefaultTool::selectionGroup() { KoSelection *selection = koSelection(); if (!selection) return; QList selectedShapes = selection->selectedEditableShapes(); std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex); KoShapeGroup *group = new KoShapeGroup(); // TODO what if only one shape is left? KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes")); canvas()->shapeController()->addShapeDirect(group, cmd); new KoShapeGroupCommand(group, selectedShapes, false, true, true, cmd); canvas()->addCommand(cmd); // update selection so we can ungroup immediately again selection->deselectAll(); selection->select(group); } void DefaultTool::selectionUngroup() { KoSelection *selection = koSelection(); if (!selection) return; QList selectedShapes = selection->selectedEditableShapes(); std::sort(selectedShapes.begin(), selectedShapes.end(), KoShape::compareShapeZIndex); KUndo2Command *cmd = 0; // add a ungroup command for each found shape container to the macro command Q_FOREACH (KoShape *shape, selectedShapes) { KoShapeGroup *group = dynamic_cast(shape); if (group) { cmd = cmd ? cmd : new KUndo2Command(kundo2_i18n("Ungroup shapes")); new KoShapeUngroupCommand(group, group->shapes(), - group->parent() ? QList() : canvas()->shapeManager()->topLevelShapes(), + group->parent() ? QList() : shapeManager()->topLevelShapes(), cmd); canvas()->shapeController()->removeShape(group, cmd); } } if (cmd) { canvas()->addCommand(cmd); } } void DefaultTool::selectionAlign(int _align) { KoShapeAlignCommand::Align align = static_cast(_align); KoSelection *selection = koSelection(); if (!selection) return; QList editableShapes = selection->selectedEditableShapes(); if (editableShapes.isEmpty()) { return; } // TODO add an option to the widget so that one can align to the page // with multiple selected shapes too QRectF bb; // single selected shape is automatically aligned to document rect if (editableShapes.count() == 1) { if (!canvas()->resourceManager()->hasResource(KoCanvasResourceManager::PageSize)) { return; } bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResourceManager::PageSize)); } else { bb = KoShape::absoluteOutlineRect(editableShapes); } KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb); canvas()->addCommand(cmd); } void DefaultTool::selectionDistribute(int _distribute) { KoShapeDistributeCommand::Distribute distribute = static_cast(_distribute); KoSelection *selection = koSelection(); if (!selection) return; QList editableShapes = selection->selectedEditableShapes(); if (editableShapes.size() < 3) { return; } QRectF bb = KoShape::absoluteOutlineRect(editableShapes); KoShapeDistributeCommand *cmd = new KoShapeDistributeCommand(editableShapes, distribute, bb); canvas()->addCommand(cmd); } void DefaultTool::selectionBringToFront() { selectionReorder(KoShapeReorderCommand::BringToFront); } void DefaultTool::selectionMoveUp() { selectionReorder(KoShapeReorderCommand::RaiseShape); } void DefaultTool::selectionMoveDown() { selectionReorder(KoShapeReorderCommand::LowerShape); } void DefaultTool::selectionSendToBack() { selectionReorder(KoShapeReorderCommand::SendToBack); } void DefaultTool::selectionReorder(KoShapeReorderCommand::MoveShapeType order) { - KoSelection *selection = canvas()->selectedShapesProxy()->selection(); + KoSelection *selection = koSelection(); if (!selection) { return; } QList selectedShapes = selection->selectedEditableShapes(); if (selectedShapes.isEmpty()) { return; } - KUndo2Command *cmd = KoShapeReorderCommand::createCommand(selectedShapes, canvas()->shapeManager(), order); + KUndo2Command *cmd = KoShapeReorderCommand::createCommand(selectedShapes, shapeManager(), order); if (cmd) { canvas()->addCommand(cmd); } } QList > DefaultTool::createOptionWidgets() { QList > widgets; m_tabbedOptionWidget = new DefaultToolTabbedWidget(this); if (isActivated()) { m_tabbedOptionWidget->activate(); } widgets.append(m_tabbedOptionWidget); connect(m_tabbedOptionWidget, SIGNAL(sigSwitchModeEditFillGradient(bool)), SLOT(slotActivateEditFillGradient(bool))); connect(m_tabbedOptionWidget, SIGNAL(sigSwitchModeEditStrokeGradient(bool)), SLOT(slotActivateEditStrokeGradient(bool))); return widgets; } void DefaultTool::canvasResourceChanged(int key, const QVariant &res) { if (key == HotPosition) { m_hotPosition = KoFlake::AnchorPosition(res.toInt()); repaintDecorations(); } } KoInteractionStrategy *DefaultTool::createStrategy(KoPointerEvent *event) { - KoShapeManager *shapeManager = canvas()->shapeManager(); KoSelection *selection = koSelection(); + if (!selection) return nullptr; bool insideSelection = false; KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection); bool editableShape = !selection->selectedEditableShapes().isEmpty(); const bool selectMultiple = event->modifiers() & Qt::ShiftModifier; const bool selectNextInStack = event->modifiers() & Qt::ControlModifier; const bool avoidSelection = event->modifiers() & Qt::AltModifier; if (selectNextInStack) { // change the hot selection position when middle clicking on a handle KoFlake::AnchorPosition newHotPosition = m_hotPosition; switch (handle) { case KoFlake::TopMiddleHandle: newHotPosition = KoFlake::Top; break; case KoFlake::TopRightHandle: newHotPosition = KoFlake::TopRight; break; case KoFlake::RightMiddleHandle: newHotPosition = KoFlake::Right; break; case KoFlake::BottomRightHandle: newHotPosition = KoFlake::BottomRight; break; case KoFlake::BottomMiddleHandle: newHotPosition = KoFlake::Bottom; break; case KoFlake::BottomLeftHandle: newHotPosition = KoFlake::BottomLeft; break; case KoFlake::LeftMiddleHandle: newHotPosition = KoFlake::Left; break; case KoFlake::TopLeftHandle: newHotPosition = KoFlake::TopLeft; break; case KoFlake::NoHandle: default: // check if we had hit the center point const KoViewConverter *converter = canvas()->viewConverter(); QPointF pt = converter->documentToView(event->point); // TODO: use calculated values instead! QPointF centerPt = converter->documentToView(selection->absolutePosition()); if (kisSquareDistance(pt, centerPt) < HANDLE_DISTANCE_SQ) { newHotPosition = KoFlake::Center; } break; } if (m_hotPosition != newHotPosition) { canvas()->resourceManager()->setResource(HotPosition, newHotPosition); return new NopInteractionStrategy(this); } } if (!avoidSelection && editableShape) { // manipulation of selected shapes goes first if (handle != KoFlake::NoHandle) { // resizing or shearing only with left mouse button if (insideSelection) { - return new ShapeResizeStrategy(this, event->point, handle); + return new ShapeResizeStrategy(this, selection, event->point, handle); } if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle || handle == KoFlake::BottomMiddleHandle || handle == KoFlake::LeftMiddleHandle) { - return new ShapeShearStrategy(this, event->point, handle); + return new ShapeShearStrategy(this, selection, event->point, handle); } - // rotating is allowed for rigth mouse button too + // rotating is allowed for right mouse button too if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle || handle == KoFlake::BottomLeftHandle || handle == KoFlake::BottomRightHandle) { - return new ShapeRotateStrategy(this, event->point, event->buttons()); + return new ShapeRotateStrategy(this, selection, event->point, event->buttons()); } } if (!selectMultiple && !selectNextInStack) { if (insideSelection) { - return new ShapeMoveStrategy(this, event->point); + return new ShapeMoveStrategy(this, selection, event->point); } } } - KoShape *shape = shapeManager->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop); + KoShape *shape = shapeManager()->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop); if (avoidSelection || (!shape && handle == KoFlake::NoHandle)) { if (!selectMultiple) { repaintDecorations(); selection->deselectAll(); } return new SelectionInteractionStrategy(this, event->point, false); } if (selection->isSelected(shape)) { if (selectMultiple) { repaintDecorations(); selection->deselect(shape); } } else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected repaintDecorations(); if (!selectMultiple) { - shapeManager->selection()->deselectAll(); + selection->deselectAll(); } selection->select(shape); repaintDecorations(); // tablet selection isn't precise and may lead to a move, preventing that if (event->isTabletEvent()) { return new NopInteractionStrategy(this); } - return new ShapeMoveStrategy(this, event->point); + return new ShapeMoveStrategy(this, selection, event->point); } return 0; } void DefaultTool::updateActions() { QList editableShapes; if (koSelection()) { editableShapes = koSelection()->selectedEditableShapes(); } const bool orderingEnabled = !editableShapes.isEmpty(); action("object_order_front")->setEnabled(orderingEnabled); action("object_order_raise")->setEnabled(orderingEnabled); action("object_order_lower")->setEnabled(orderingEnabled); action("object_order_back")->setEnabled(orderingEnabled); const bool alignmentEnabled = editableShapes.size() > 1 || (!editableShapes.isEmpty() && canvas()->resourceManager()->hasResource(KoCanvasResourceManager::PageSize)); action("object_align_horizontal_left")->setEnabled(alignmentEnabled); action("object_align_horizontal_center")->setEnabled(alignmentEnabled); action("object_align_horizontal_right")->setEnabled(alignmentEnabled); action("object_align_vertical_top")->setEnabled(alignmentEnabled); action("object_align_vertical_center")->setEnabled(alignmentEnabled); action("object_align_vertical_bottom")->setEnabled(alignmentEnabled); action("object_group")->setEnabled(editableShapes.size() > 1); const bool distributionEnabled = editableShapes.size() > 2; action("object_distribute_horizontal_left")->setEnabled(distributionEnabled); action("object_distribute_horizontal_center")->setEnabled(distributionEnabled); action("object_distribute_horizontal_right")->setEnabled(distributionEnabled); action("object_distribute_horizontal_gaps")->setEnabled(distributionEnabled); action("object_distribute_vertical_top")->setEnabled(distributionEnabled); action("object_distribute_vertical_center")->setEnabled(distributionEnabled); action("object_distribute_vertical_bottom")->setEnabled(distributionEnabled); action("object_distribute_vertical_gaps")->setEnabled(distributionEnabled); bool hasGroupShape = false; foreach (KoShape *shape, editableShapes) { if (dynamic_cast(shape)) { hasGroupShape = true; break; } } action("object_ungroup")->setEnabled(hasGroupShape); emit selectionChanged(editableShapes.size()); } KoToolSelection *DefaultTool::selection() { return m_selectionHandler; } QMenu* DefaultTool::popupActionsMenu() { if (m_contextMenu) { m_contextMenu->clear(); KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); m_contextMenu->addAction(collection->action("edit_cut")); m_contextMenu->addAction(collection->action("edit_copy")); m_contextMenu->addAction(collection->action("edit_paste")); m_contextMenu->addSeparator(); m_contextMenu->addAction(action("object_order_front")); m_contextMenu->addAction(action("object_order_raise")); m_contextMenu->addAction(action("object_order_lower")); m_contextMenu->addAction(action("object_order_back")); if (action("object_group")->isEnabled() || action("object_ungroup")->isEnabled()) { m_contextMenu->addSeparator(); m_contextMenu->addAction(action("object_group")); m_contextMenu->addAction(action("object_ungroup")); } } return m_contextMenu.data(); } void DefaultTool::explicitUserStrokeEndRequest() { QList shapes = koSelection()->selectedEditableShapesAndDelegates(); emit activateTemporary(KoToolManager::instance()->preferredToolForSelection(shapes)); } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.h b/plugins/tools/defaulttool/defaulttool/DefaultTool.h index 2953600eed..1a8deac977 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultTool.h +++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.h @@ -1,174 +1,177 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2008 Thomas Zander This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef DEFAULTTOOL_H #define DEFAULTTOOL_H #include #include #include #include #include #include class QSignalMapper; class KoInteractionStrategy; class KoShapeMoveCommand; class KoSelection; class DefaultToolTabbedWidget; class KisViewManager; /** * The default tool (associated with the arrow icon) implements the default * interactions you have with flake objects.
* The tool provides scaling, moving, selecting, rotation and soon skewing of * any number of shapes. *

Note that the implementation of those different strategies are delegated * to the InteractionStrategy class and its subclasses. */ class DefaultTool : public KoInteractionTool { Q_OBJECT public: /** * Constructor for basic interaction tool where user actions are translated * and handled by interaction strategies of type KoInteractionStrategy. * @param canvas the canvas this tool will be working for. */ explicit DefaultTool(KoCanvasBase *canvas); ~DefaultTool() override; enum CanvasResource { HotPosition = 1410100299 }; public: bool wantsAutoScroll() const override; void paint(QPainter &painter, const KoViewConverter &converter) override; void repaintDecorations() override; ///reimplemented void copy() const override; ///reimplemented void deleteSelection() override; ///reimplemented bool paste() override; ///reimplemented KoToolSelection *selection() override; QMenu* popupActionsMenu() override; /** * Returns which selection handle is at params point (or NoHandle if none). * @return which selection handle is at params point (or NoHandle if none). * @param point the location (in pt) where we should look for a handle * @param innerHandleMeaning this boolean is altered to true if the point * is inside the selection rectangle and false if it is just outside. * The value of innerHandleMeaning is undefined if the handle location is NoHandle */ KoFlake::SelectionHandle handleAt(const QPointF &point, bool *innerHandleMeaning = 0); public Q_SLOTS: void activate(ToolActivation activation, const QSet &shapes) override; void deactivate() override; private Q_SLOTS: void selectionAlign(int _align); void selectionDistribute(int _distribute); void selectionBringToFront(); void selectionSendToBack(); void selectionMoveUp(); void selectionMoveDown(); void selectionGroup(); void selectionUngroup(); void slotActivateEditFillGradient(bool value); void slotActivateEditStrokeGradient(bool value); /// Update actions on selection change void updateActions(); public: // Events void mousePressEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void mouseDoubleClickEvent(KoPointerEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void explicitUserStrokeEndRequest() override; protected: QList > createOptionWidgets() override; KoInteractionStrategy *createStrategy(KoPointerEvent *event) override; +protected: + friend class SelectionInteractionStrategy; + virtual bool isValidForCurrentLayer() const; + virtual KoShapeManager *shapeManager() const; + virtual KoSelection *koSelection() const; + private: class MoveGradientHandleInteractionFactory; private: void setupActions(); void recalcSelectionBox(KoSelection *selection); void updateCursor(); /// Returns rotation angle of given handle of the current selection qreal rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation); void addMappedAction(QSignalMapper *mapper, const QString &actionId, int type); void selectionReorder(KoShapeReorderCommand::MoveShapeType order); bool moveSelection(int direction, Qt::KeyboardModifiers modifiers); /// Returns selection rectangle adjusted by handle proximity threshold QRectF handlesSize(); - // convenience method; - KoSelection *koSelection(); void canvasResourceChanged(int key, const QVariant &res) override; KoFlake::SelectionHandle m_lastHandle; KoFlake::AnchorPosition m_hotPosition; bool m_mouseWasInsideHandles; QPointF m_selectionBox[8]; QPolygonF m_selectionOutline; QPointF m_lastPoint; // TODO alter these 3 arrays to be static const instead QCursor m_sizeCursors[8]; QCursor m_rotateCursors[8]; QCursor m_shearCursors[8]; qreal m_angle; KoToolSelection *m_selectionHandler; friend class SelectionHandler; - KoInteractionStrategy *m_customEventStrategy; QScopedPointer m_contextMenu; DefaultToolTabbedWidget *m_tabbedOptionWidget; }; #endif diff --git a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp index 1bc4a766c5..34eead6ddf 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp +++ b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.cpp @@ -1,128 +1,127 @@ /* This file is part of the KDE project Copyright (C) 2006 Thorsten Zachmann Copyright (C) 2006-2007 Thomas Zander This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ShapeMoveStrategy.h" #include "SelectionDecorator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" -ShapeMoveStrategy::ShapeMoveStrategy(KoToolBase *tool, const QPointF &clicked) +ShapeMoveStrategy::ShapeMoveStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked) : KoInteractionStrategy(tool) , m_start(clicked) , m_canvas(tool->canvas()) { - QList selectedShapes = m_canvas->shapeManager()->selection()->selectedEditableShapes(); + QList selectedShapes = selection->selectedEditableShapes(); QRectF boundingRect; Q_FOREACH (KoShape *shape, selectedShapes) { m_selectedShapes << shape; m_previousPositions << shape->absolutePosition(KoFlake::Center); m_newPositions << shape->absolutePosition(KoFlake::Center); boundingRect = boundingRect.united(shape->boundingRect()); } KoFlake::AnchorPosition anchor = KoFlake::AnchorPosition( m_canvas->resourceManager()->resource(KoFlake::HotPosition).toInt()); - KoSelection *selection = m_canvas->shapeManager()->selection(); m_initialOffset = selection->absolutePosition(anchor) - m_start; m_canvas->snapGuide()->setIgnoredShapes(KoShape::linearizeSubtree(m_selectedShapes)); tool->setStatusText(i18n("Press Shift to hold x- or y-position.")); } void ShapeMoveStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers) { if (m_selectedShapes.isEmpty()) { return; } QPointF diff = point - m_start; if (modifiers & Qt::ShiftModifier) { // Limit change to one direction only diff = snapToClosestAxis(diff); } else { QPointF positionToSnap = point + m_initialOffset; tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); QPointF snappedPosition = tool()->canvas()->snapGuide()->snap(positionToSnap, modifiers); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); diff = snappedPosition - m_initialOffset - m_start; } moveSelection(diff); m_finalMove = diff; } void ShapeMoveStrategy::moveSelection(const QPointF &diff) { Q_ASSERT(m_newPositions.count()); int i = 0; Q_FOREACH (KoShape *shape, m_selectedShapes) { QPointF delta = m_previousPositions.at(i) + diff - shape->absolutePosition(KoFlake::Center); if (shape->parent()) { shape->parent()->model()->proposeMove(shape, delta); } tool()->canvas()->clipToDocument(shape, delta); QPointF newPos(shape->absolutePosition(KoFlake::Center) + delta); m_newPositions[i] = newPos; const QRectF oldDirtyRect = shape->boundingRect(); shape->setAbsolutePosition(newPos, KoFlake::Center); shape->updateAbsolute(oldDirtyRect | oldDirtyRect.translated(delta)); i++; } } KUndo2Command *ShapeMoveStrategy::createCommand() { tool()->canvas()->snapGuide()->reset(); if (m_finalMove.isNull()) { return 0; } return new KoShapeMoveCommand(m_selectedShapes, m_previousPositions, m_newPositions); } void ShapeMoveStrategy::finishInteraction(Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); } void ShapeMoveStrategy::paint(QPainter &painter, const KoViewConverter &converter) { Q_UNUSED(painter); Q_UNUSED(converter); } diff --git a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h index a43dcf70ff..78e1c4835c 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h +++ b/plugins/tools/defaulttool/defaulttool/ShapeMoveStrategy.h @@ -1,64 +1,65 @@ /* This file is part of the KDE project Copyright (C) 2006 Thorsten Zachmann Copyright (C) 2006-2007 Thomas Zander This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef SHAPEMOVESTRATEGY_H #define SHAPEMOVESTRATEGY_H #include #include #include #include #include class KoToolBase; class KoShape; +class KoSelection; /** * Implements the Move action on an object or selected objects. */ class ShapeMoveStrategy : public KoInteractionStrategy { public: /** * Constructor that starts to move the objects. * @param tool the parent tool which controls this strategy * @param canvas the canvas interface which will supply things like a selection object * @param clicked the initial point that the user depressed (in pt). */ - ShapeMoveStrategy(KoToolBase *tool, const QPointF &clicked); + ShapeMoveStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked); ~ShapeMoveStrategy() override {} void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override; KUndo2Command *createCommand() override; void finishInteraction(Qt::KeyboardModifiers modifiers) override; void paint(QPainter &painter, const KoViewConverter &converter) override; private: void moveSelection(const QPointF &diff); QList m_previousPositions; QList m_newPositions; QPointF m_start, m_finalMove, m_initialOffset; QList m_selectedShapes; QPointer m_canvas; }; #endif diff --git a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp index 385ca872fe..02c3240232 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp +++ b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp @@ -1,245 +1,245 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2007,2011 Jan Hambrecht * Copyright (C) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ShapeResizeStrategy.h" #include "SelectionDecorator.h" #include #include #include #include #include #include #include #include #include #include #include #include -ShapeResizeStrategy::ShapeResizeStrategy(KoToolBase *tool, const QPointF &clicked, KoFlake::SelectionHandle direction) +ShapeResizeStrategy::ShapeResizeStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, KoFlake::SelectionHandle direction) : KoInteractionStrategy(tool) { - Q_ASSERT(tool->canvas()->shapeManager()->selection()); - Q_ASSERT(tool->canvas()->shapeManager()->selection()->count() > 0); - m_selectedShapes = tool->canvas()->shapeManager()->selection()->selectedEditableShapes(); + KIS_SAFE_ASSERT_RECOVER_RETURN(selection && selection->count() > 0); + + m_selectedShapes = selection->selectedEditableShapes(); m_start = clicked; KoShape *shape = 0; if (m_selectedShapes.size() > 1) { - shape = tool->canvas()->shapeManager()->selection(); + shape = selection; } else if (m_selectedShapes.size() == 1) { shape = m_selectedShapes.first(); } if (shape) { const qreal w = shape->size().width(); const qreal h = shape->size().height(); switch (direction) { case KoFlake::TopMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::TopLeft) + shape->absolutePosition(KoFlake::TopRight)); m_top = true; m_bottom = false; m_left = false; m_right = false; m_globalStillPoint = QPointF(0.5 * w, h); break; case KoFlake::TopRightHandle: m_start = shape->absolutePosition(KoFlake::TopRight); m_top = true; m_bottom = false; m_left = false; m_right = true; m_globalStillPoint = QPointF(0, h); break; case KoFlake::RightMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::TopRight) + shape->absolutePosition(KoFlake::BottomRight)); m_top = false; m_bottom = false; m_left = false; m_right = true; m_globalStillPoint = QPointF(0, 0.5 * h); break; case KoFlake::BottomRightHandle: m_start = shape->absolutePosition(KoFlake::BottomRight); m_top = false; m_bottom = true; m_left = false; m_right = true; m_globalStillPoint = QPointF(0, 0); break; case KoFlake::BottomMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::BottomRight) + shape->absolutePosition(KoFlake::BottomLeft)); m_top = false; m_bottom = true; m_left = false; m_right = false; m_globalStillPoint = QPointF(0.5 * w, 0); break; case KoFlake::BottomLeftHandle: m_start = shape->absolutePosition(KoFlake::BottomLeft); m_top = false; m_bottom = true; m_left = true; m_right = false; m_globalStillPoint = QPointF(w, 0); break; case KoFlake::LeftMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::BottomLeft) + shape->absolutePosition(KoFlake::TopLeft)); m_top = false; m_bottom = false; m_left = true; m_right = false; m_globalStillPoint = QPointF(w, 0.5 * h); break; case KoFlake::TopLeftHandle: m_start = shape->absolutePosition(KoFlake::TopLeft); m_top = true; m_bottom = false; m_left = true; m_right = false; m_globalStillPoint = QPointF(w, h); break; default: Q_ASSERT(0); // illegal 'corner' } const QPointF p0 = shape->outlineRect().topLeft(); m_globalStillPoint = shape->absoluteTransformation(0).map(p0 + m_globalStillPoint); m_globalCenterPoint = shape->absolutePosition(KoFlake::Center); m_unwindMatrix = shape->absoluteTransformation(0).inverted(); m_initialSelectionSize = shape->size(); m_postScalingCoveringTransform = shape->transformation(); } tool->setStatusText(i18n("Press CTRL to resize from center.")); tool->canvas()->snapGuide()->setIgnoredShapes(KoShape::linearizeSubtree(m_selectedShapes)); } ShapeResizeStrategy::~ShapeResizeStrategy() { } void ShapeResizeStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers) { tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); QPointF newPos = tool()->canvas()->snapGuide()->snap(point, modifiers); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); bool keepAspect = modifiers & Qt::ShiftModifier; Q_FOREACH (KoShape *shape, m_selectedShapes) { keepAspect = keepAspect || shape->keepAspectRatio(); } qreal startWidth = m_initialSelectionSize.width(); if (startWidth < std::numeric_limits::epsilon()) { startWidth = std::numeric_limits::epsilon(); } qreal startHeight = m_initialSelectionSize.height(); if (startHeight < std::numeric_limits::epsilon()) { startHeight = std::numeric_limits::epsilon(); } QPointF distance = m_unwindMatrix.map(newPos) - m_unwindMatrix.map(m_start); // guard against resizing zero width shapes, which would result in huge zoom factors if (m_initialSelectionSize.width() < std::numeric_limits::epsilon()) { distance.rx() = 0.0; } // guard against resizing zero height shapes, which would result in huge zoom factors if (m_initialSelectionSize.height() < std::numeric_limits::epsilon()) { distance.ry() = 0.0; } const bool scaleFromCenter = modifiers & Qt::ControlModifier; if (scaleFromCenter) { distance *= 2.0; } qreal newWidth = startWidth; qreal newHeight = startHeight; if (m_left) { newWidth = startWidth - distance.x(); } else if (m_right) { newWidth = startWidth + distance.x(); } if (m_top) { newHeight = startHeight - distance.y(); } else if (m_bottom) { newHeight = startHeight + distance.y(); } /** * Do not let a shape be less than 1px in size in current view * coordinates. If the user wants it to be smaller, he can just * zoom-in a bit. */ QSizeF minViewSize(1.0, 1.0); QSizeF minDocSize = tool()->canvas()->viewConverter()->viewToDocument(minViewSize); if (qAbs(newWidth) < minDocSize.width()) { int sign = newWidth >= 0.0 ? 1 : -1; // zero -> '1' newWidth = sign * minDocSize.width(); } if (qAbs(newHeight) < minDocSize.height()) { int sign = newHeight >= 0.0 ? 1 : -1; // zero -> '1' newHeight = sign * minDocSize.height(); } qreal zoomX = newWidth / startWidth; qreal zoomY = newHeight / startHeight; if (keepAspect) { const bool cornerUsed = ((m_bottom ? 1 : 0) + (m_top ? 1 : 0) + (m_left ? 1 : 0) + (m_right ? 1 : 0)) == 2; if ((cornerUsed && startWidth < startHeight) || m_left || m_right) { zoomY = zoomX; } else { zoomX = zoomY; } } resizeBy(scaleFromCenter ? m_globalCenterPoint : m_globalStillPoint, zoomX, zoomY); } void ShapeResizeStrategy::resizeBy(const QPointF &stillPoint, qreal zoomX, qreal zoomY) { if (m_executedCommand) { m_executedCommand->undo(); m_executedCommand.reset(); } const bool usePostScaling = m_selectedShapes.size() > 1; m_executedCommand.reset( new KoShapeResizeCommand( m_selectedShapes, zoomX, zoomY, stillPoint, false, usePostScaling, m_postScalingCoveringTransform)); m_executedCommand->redo(); } KUndo2Command *ShapeResizeStrategy::createCommand() { tool()->canvas()->snapGuide()->reset(); if (m_executedCommand) { m_executedCommand->setSkipOneRedo(true); } return m_executedCommand.take(); } void ShapeResizeStrategy::finishInteraction(Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); } void ShapeResizeStrategy::paint(QPainter &painter, const KoViewConverter &converter) { Q_UNUSED(painter); Q_UNUSED(converter); } diff --git a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.h b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.h index 8e0a2b7e07..c2f5a12521 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.h +++ b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.h @@ -1,70 +1,71 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef SHAPERESIZESTRATEGY_H #define SHAPERESIZESTRATEGY_H #include #include #include #include #include #include class KoToolBase; class KoShape; class KoShapeResizeCommand; +class KoSelection; /** * A strategy for the KoInteractionTool. * This strategy is invoked when the user starts a resize of a selection of objects, * the stategy will then resize the objects interactively and provide a command afterwards. */ class ShapeResizeStrategy : public KoInteractionStrategy { public: /** * Constructor */ - ShapeResizeStrategy(KoToolBase *tool, const QPointF &clicked, KoFlake::SelectionHandle direction); + ShapeResizeStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, KoFlake::SelectionHandle direction); ~ShapeResizeStrategy() override; void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override; KUndo2Command *createCommand() override; void finishInteraction(Qt::KeyboardModifiers modifiers) override; void paint(QPainter &painter, const KoViewConverter &converter) override; private: void resizeBy(const QPointF &stillPoint, qreal zoomX, qreal zoomY); QPointF m_start; QList m_selectedShapes; QTransform m_postScalingCoveringTransform; QSizeF m_initialSelectionSize; QTransform m_unwindMatrix; bool m_top, m_left, m_bottom, m_right; QPointF m_globalStillPoint; QPointF m_globalCenterPoint; QScopedPointer m_executedCommand; }; #endif diff --git a/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp b/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp index a9df04da7b..17aa6cd312 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp +++ b/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.cpp @@ -1,111 +1,111 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2007-2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ShapeRotateStrategy.h" #include "SelectionDecorator.h" #include #include #include #include #include #include #include #include #include #include -ShapeRotateStrategy::ShapeRotateStrategy(KoToolBase *tool, const QPointF &clicked, Qt::MouseButtons buttons) +ShapeRotateStrategy::ShapeRotateStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, Qt::MouseButtons buttons) : KoInteractionStrategy(tool) , m_start(clicked) { - m_selectedShapes = tool->canvas()->shapeManager()->selection()->selectedEditableShapes(); + m_selectedShapes = selection->selectedEditableShapes(); Q_FOREACH (KoShape *shape, m_selectedShapes) { m_oldTransforms << shape->transformation(); } KoFlake::AnchorPosition anchor = !(buttons & Qt::RightButton) ? KoFlake::Center : KoFlake::AnchorPosition(tool->canvas()->resourceManager()->resource(KoFlake::HotPosition).toInt()); - m_rotationCenter = tool->canvas()->shapeManager()->selection()->absolutePosition(anchor); + m_rotationCenter = selection->absolutePosition(anchor); tool->setStatusText(i18n("Press ALT to rotate in 45 degree steps.")); } void ShapeRotateStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers) { qreal angle = atan2(point.y() - m_rotationCenter.y(), point.x() - m_rotationCenter.x()) - atan2(m_start.y() - m_rotationCenter.y(), m_start.x() - m_rotationCenter.x()); angle = angle / M_PI * 180; // convert to degrees. if (modifiers & (Qt::AltModifier | Qt::ControlModifier)) { // limit to 45 degree angles qreal modula = qAbs(angle); while (modula > 45.0) { modula -= 45.0; } if (modula > 22.5) { modula -= 45.0; } angle += (angle > 0 ? -1 : 1) * modula; } rotateBy(angle); } void ShapeRotateStrategy::rotateBy(qreal angle) { QTransform matrix; matrix.translate(m_rotationCenter.x(), m_rotationCenter.y()); matrix.rotate(angle); matrix.translate(-m_rotationCenter.x(), -m_rotationCenter.y()); QTransform applyMatrix = matrix * m_rotationMatrix.inverted(); m_rotationMatrix = matrix; Q_FOREACH (KoShape *shape, m_selectedShapes) { const QRectF oldDirtyRect = shape->boundingRect(); shape->applyAbsoluteTransformation(applyMatrix); shape->updateAbsolute(oldDirtyRect | shape->boundingRect()); } } void ShapeRotateStrategy::paint(QPainter &painter, const KoViewConverter &converter) { // paint the rotation center painter.setPen(QPen(Qt::red)); painter.setBrush(QBrush(Qt::red)); painter.setRenderHint(QPainter::Antialiasing, true); QRectF circle(0, 0, 5, 5); circle.moveCenter(converter.documentToView(m_rotationCenter)); painter.drawEllipse(circle); } KUndo2Command *ShapeRotateStrategy::createCommand() { QList newTransforms; Q_FOREACH (KoShape *shape, m_selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(m_selectedShapes, m_oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Rotate")); return cmd; } diff --git a/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.h b/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.h index 6f20d3eef4..877bfac803 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.h +++ b/plugins/tools/defaulttool/defaulttool/ShapeRotateStrategy.h @@ -1,68 +1,69 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef SHAPEROTATESTRATEGY_H #define SHAPEROTATESTRATEGY_H #include #include #include #include #include class KoToolBase; class KoShape; +class KoSelection; /** * A strategy for the KoInteractionTool. * This strategy is invoked when the user starts a rotate of a selection of objects, * the stategy will then rotate the objects interactively and provide a command afterwards. */ class ShapeRotateStrategy : public KoInteractionStrategy { public: /** * Constructor that starts to rotate the objects. * @param tool the parent tool which controls this strategy * @param clicked the initial point that the user depressed (in pt). */ - ShapeRotateStrategy(KoToolBase *tool, const QPointF &clicked, Qt::MouseButtons buttons); + ShapeRotateStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, Qt::MouseButtons buttons); ~ShapeRotateStrategy() override {} void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override; KUndo2Command *createCommand() override; void finishInteraction(Qt::KeyboardModifiers modifiers) override { Q_UNUSED(modifiers); } void paint(QPainter &painter, const KoViewConverter &converter) override; private: void rotateBy(qreal angle); QPointF m_start; QTransform m_rotationMatrix; QList m_oldTransforms; QPointF m_rotationCenter; QList m_selectedShapes; }; #endif diff --git a/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp b/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp index 0739e84b1f..90563927ef 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp +++ b/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.cpp @@ -1,171 +1,170 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2006 C. Boemann * Copyright (C) 2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "ShapeShearStrategy.h" #include "SelectionDecorator.h" #include #include #include #include #include #include #include #include #include #include #include #include -ShapeShearStrategy::ShapeShearStrategy(KoToolBase *tool, const QPointF &clicked, KoFlake::SelectionHandle direction) +ShapeShearStrategy::ShapeShearStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, KoFlake::SelectionHandle direction) : KoInteractionStrategy(tool) , m_start(clicked) { - KoSelection *sel = tool->canvas()->shapeManager()->selection(); - m_selectedShapes = sel->selectedEditableShapes(); + m_selectedShapes = selection->selectedEditableShapes(); Q_FOREACH (KoShape *shape, m_selectedShapes) { m_oldTransforms << shape->transformation(); } - // Eventhoug we aren't currently activated by the corner handles we might as well code like it + // Even though we aren't currently activated by the corner handles we might as well code like it switch (direction) { case KoFlake::TopMiddleHandle: m_top = true; m_bottom = false; m_left = false; m_right = false; break; case KoFlake::TopRightHandle: m_top = true; m_bottom = false; m_left = false; m_right = true; break; case KoFlake::RightMiddleHandle: m_top = false; m_bottom = false; m_left = false; m_right = true; break; case KoFlake::BottomRightHandle: m_top = false; m_bottom = true; m_left = false; m_right = true; break; case KoFlake::BottomMiddleHandle: m_top = false; m_bottom = true; m_left = false; m_right = false; break; case KoFlake::BottomLeftHandle: m_top = false; m_bottom = true; m_left = true; m_right = false; break; case KoFlake::LeftMiddleHandle: m_top = false; m_bottom = false; m_left = true; m_right = false; break; case KoFlake::TopLeftHandle: m_top = true; m_bottom = false; m_left = true; m_right = false; break; default: ;// throw exception ? TODO } - m_initialSize = sel->size(); + m_initialSize = selection->size(); m_solidPoint = QPointF(m_initialSize.width() / 2, m_initialSize.height() / 2); if (m_top) { m_solidPoint += QPointF(0, m_initialSize.height() / 2); } else if (m_bottom) { m_solidPoint -= QPointF(0, m_initialSize.height() / 2); } if (m_left) { m_solidPoint += QPointF(m_initialSize.width() / 2, 0); } else if (m_right) { m_solidPoint -= QPointF(m_initialSize.width() / 2, 0); } - m_solidPoint = sel->absoluteTransformation(0).map(sel->outlineRect().topLeft() + m_solidPoint); + m_solidPoint = selection->absoluteTransformation(0).map(selection->outlineRect().topLeft() + m_solidPoint); QPointF edge; qreal angle = 0.0; if (m_top) { - edge = sel->absolutePosition(KoFlake::BottomLeft) - sel->absolutePosition(KoFlake::BottomRight); + edge = selection->absolutePosition(KoFlake::BottomLeft) - selection->absolutePosition(KoFlake::BottomRight); angle = 180.0; } else if (m_bottom) { - edge = sel->absolutePosition(KoFlake::TopRight) - sel->absolutePosition(KoFlake::TopLeft); + edge = selection->absolutePosition(KoFlake::TopRight) - selection->absolutePosition(KoFlake::TopLeft); angle = 0.0; } else if (m_left) { - edge = sel->absolutePosition(KoFlake::BottomLeft) - sel->absolutePosition(KoFlake::TopLeft); + edge = selection->absolutePosition(KoFlake::BottomLeft) - selection->absolutePosition(KoFlake::TopLeft); angle = 90.0; } else if (m_right) { - edge = sel->absolutePosition(KoFlake::TopRight) - sel->absolutePosition(KoFlake::BottomRight); + edge = selection->absolutePosition(KoFlake::TopRight) - selection->absolutePosition(KoFlake::BottomRight); angle = 270.0; } qreal currentAngle = atan2(edge.y(), edge.x()) / M_PI * 180; m_initialSelectionAngle = currentAngle - angle; - // use crossproduct of top edge and left edge of selection bounding rect + // use cross product of top edge and left edge of selection bounding rect // to determine if the selection is mirrored - QPointF top = sel->absolutePosition(KoFlake::TopRight) - sel->absolutePosition(KoFlake::TopLeft); - QPointF left = sel->absolutePosition(KoFlake::BottomLeft) - sel->absolutePosition(KoFlake::TopLeft); + QPointF top = selection->absolutePosition(KoFlake::TopRight) - selection->absolutePosition(KoFlake::TopLeft); + QPointF left = selection->absolutePosition(KoFlake::BottomLeft) - selection->absolutePosition(KoFlake::TopLeft); m_isMirrored = (top.x() * left.y() - top.y() * left.x()) < 0.0; } void ShapeShearStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); QPointF shearVector = point - m_start; QTransform m; m.rotate(-m_initialSelectionAngle); shearVector = m.map(shearVector); qreal shearX = 0, shearY = 0; if (m_top || m_left) { shearVector = - shearVector; } if (m_top || m_bottom) { shearX = shearVector.x() / m_initialSize.height(); } if (m_left || m_right) { shearY = shearVector.y() / m_initialSize.width(); } // if selection is mirrored invert the shear values if (m_isMirrored) { shearX *= -1.0; shearY *= -1.0; } QTransform matrix; matrix.translate(m_solidPoint.x(), m_solidPoint.y()); matrix.rotate(m_initialSelectionAngle); matrix.shear(shearX, shearY); matrix.rotate(-m_initialSelectionAngle); matrix.translate(-m_solidPoint.x(), -m_solidPoint.y()); QTransform applyMatrix = matrix * m_shearMatrix.inverted(); Q_FOREACH (KoShape *shape, m_selectedShapes) { const QRectF oldDirtyRect = shape->boundingRect(); shape->applyAbsoluteTransformation(applyMatrix); shape->updateAbsolute(oldDirtyRect | shape->boundingRect()); } m_shearMatrix = matrix; } void ShapeShearStrategy::paint(QPainter &painter, const KoViewConverter &converter) { Q_UNUSED(painter); Q_UNUSED(converter); } KUndo2Command *ShapeShearStrategy::createCommand() { QList newTransforms; Q_FOREACH (KoShape *shape, m_selectedShapes) { newTransforms << shape->transformation(); } KoShapeTransformCommand *cmd = new KoShapeTransformCommand(m_selectedShapes, m_oldTransforms, newTransforms); cmd->setText(kundo2_i18n("Shear")); return cmd; } diff --git a/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.h b/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.h index 53667a1304..a1e0377c62 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.h +++ b/plugins/tools/defaulttool/defaulttool/ShapeShearStrategy.h @@ -1,71 +1,72 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef SHAPESHEARSTRATEGY_H #define SHAPESHEARSTRATEGY_H #include #include #include #include #include class KoToolBase; class KoShape; +class KoSelection; /** * A strategy for the KoInteractionTool. * This strategy is invoked when the user starts a shear of a selection of objects, * the stategy will then shear the objects interactively and provide a command afterwards. */ class ShapeShearStrategy : public KoInteractionStrategy { public: /** * Constructor that starts to rotate the objects. * @param tool the parent tool which controls this strategy * @param clicked the initial point that the user depressed (in pt). * @param direction the handle that was grabbed */ - ShapeShearStrategy(KoToolBase *tool, const QPointF &clicked, KoFlake::SelectionHandle direction); + ShapeShearStrategy(KoToolBase *tool, KoSelection *selection, const QPointF &clicked, KoFlake::SelectionHandle direction); ~ShapeShearStrategy() override {} void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override; KUndo2Command *createCommand() override; void finishInteraction(Qt::KeyboardModifiers modifiers) override { Q_UNUSED(modifiers); } void paint(QPainter &painter, const KoViewConverter &converter) override; private: QPointF m_start; QPointF m_solidPoint; QSizeF m_initialSize; bool m_top, m_left, m_bottom, m_right; qreal m_initialSelectionAngle; QTransform m_shearMatrix; bool m_isMirrored; QList m_oldTransforms; QList m_selectedShapes; }; #endif diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp index 694342ca02..f8ff5f8e9d 100644 --- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp +++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp @@ -1,125 +1,142 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ToolReferenceImages.h" #include #include #include #include #include #include #include #include #include "kis_global.h" #include #include #include #include #include "ToolReferenceImagesWidget.h" ToolReferenceImages::ToolReferenceImages(KoCanvasBase * canvas) : DefaultTool(canvas) { setObjectName("ToolReferenceImages"); } ToolReferenceImages::~ToolReferenceImages() { } void ToolReferenceImages::activate(ToolActivation toolActivation, const QSet &shapes) { // Add code here to initialize your tool when it got activated DefaultTool::activate(toolActivation, shapes); } void ToolReferenceImages::deactivate() { DefaultTool::deactivate(); } void ToolReferenceImages::removeAllReferenceImages() { } void ToolReferenceImages::loadReferenceImages() { /* KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImage"); dialog.setCaption(i18n("Select a Reference Image")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); // dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-"); QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; QFile file(filename); file.open(QIODevice::ReadOnly); m_canvas->updateCanvas(); */ } void ToolReferenceImages::saveReferenceImages() { } QList> ToolReferenceImages::createOptionWidgets() { // Instead of inheriting DefaultTool's multi-tab implementation, inherit straight from KoToolBase return KoToolBase::createOptionWidgets(); } QWidget *ToolReferenceImages::createOptionWidget() { if (!m_optionsWidget) { m_optionsWidget = new ToolReferenceImagesWidget(this); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); } return m_optionsWidget; } void ToolReferenceImages::addReferenceImage() { } +bool ToolReferenceImages::isValidForCurrentLayer() const +{ + return true; +} + +KoShapeManager *ToolReferenceImages::shapeManager() const +{ + auto layer = referenceImagesLayer(); + return layer ? referenceImagesLayer()->shapeManager() : nullptr; +} + KisReferenceImagesLayer *ToolReferenceImages::referenceImagesLayer() const { auto kisCanvas = dynamic_cast(canvas()); KisDocument *document = kisCanvas->imageView()->document(); return document->referenceImagesLayer(); } KisReferenceImagesLayer *ToolReferenceImages::getOrCreteReferenceImagesLayer() { auto kisCanvas = dynamic_cast(canvas()); KisDocument *document = kisCanvas->imageView()->document(); return document->createReferenceImagesLayer(); } + +KoSelection *ToolReferenceImages::koSelection() const +{ + auto manager = shapeManager(); + return manager ? manager->selection() : nullptr; +} diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h index 7ef206a771..8d31760eef 100644 --- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h +++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h @@ -1,98 +1,102 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TOOL_REFERENCE_IMAGES_H #define TOOL_REFERENCE_IMAGES_H #include #include #include #include #include "kis_painting_assistant.h" #include #include #include class ToolReferenceImagesWidget; class KisReferenceImagesLayer; class ToolReferenceImages : public DefaultTool { Q_OBJECT public: ToolReferenceImages(KoCanvasBase * canvas); ~ToolReferenceImages() override; virtual quint32 priority() { return 3; } protected: QList> createOptionWidgets() override; QWidget *createOptionWidget() override; + bool isValidForCurrentLayer() const override; + KoShapeManager *shapeManager() const override; + KoSelection *koSelection() const override; + public: void addReferenceImage(); public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; private Q_SLOTS: void removeAllReferenceImages(); void saveReferenceImages(); void loadReferenceImages(); private: ToolReferenceImagesWidget *m_optionsWidget = nullptr; KisReferenceImagesLayer *referenceImagesLayer() const; KisReferenceImagesLayer *getOrCreteReferenceImagesLayer(); }; class ToolReferenceImagesFactory : public KoToolFactoryBase { public: ToolReferenceImagesFactory() : KoToolFactoryBase("ToolReferenceImages") { setToolTip(i18n("Reference Images Tool")); setSection(TOOL_TYPE_VIEW); setIconName(koIconNameCStr("krita_tool_reference_images")); setPriority(0); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); }; ~ToolReferenceImagesFactory() override {} KoToolBase * createTool(KoCanvasBase * canvas) override { return new ToolReferenceImages(canvas); } }; #endif