diff --git a/libs/ui/tool/kis_tool_select_base.h b/libs/ui/tool/kis_tool_select_base.h index 697df68dc6..4449ba7154 100644 --- a/libs/ui/tool/kis_tool_select_base.h +++ b/libs/ui/tool/kis_tool_select_base.h @@ -1,402 +1,402 @@ /* This file is part of the KDE project * Copyright (C) 2009 Boudewijn Rempt * Copyright (C) 2015 Michael Abrahams * * 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 KISTOOLSELECTBASE_H #define KISTOOLSELECTBASE_H #include "KoPointerEvent.h" #include "kis_tool.h" #include "kis_canvas2.h" #include "kis_selection.h" #include "kis_selection_options.h" #include "kis_selection_tool_config_widget_helper.h" #include "KisViewManager.h" #include "kis_selection_manager.h" #include "kis_selection_modifier_mapper.h" #include "strokes/move_stroke_strategy.h" #include "kis_image.h" #include "kis_cursor.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_signal_auto_connection.h" #include "kis_selection_tool_helper.h" /** * This is a basic template to create selection tools from basic path based drawing tools. * The template overrides the ability to execute alternate actions correctly. * The default behavior for the modifier keys is as follows: * * Shift: add to selection * Alt: subtract from selection * Shift+Alt: intersect current selection * Ctrl: replace selection * * The mapping itself is done in KisSelectionModifierMapper. * * Certain tools also use modifier keys to alter their behavior, e.g. forcing square proportions with the rectangle tool. * The template enables the following rules for forwarding keys: * 1) Any modifier keys held *when the tool is first activated* will determine * the new selection method. This is recorded in m_selectionActionAlternate. A * value of m_selectionActionAlternate = SELECTION_DEFAULT means no modifier was * being pressed when the tool was activated. * * 2) If the underlying tool *does not take modifier keys*, pressing modifier * keys in the middle of a stroke will change the selection method. This is * recorded in m_selectionAction. A value of SELECTION_DEFAULT means no modifier * is being pressed. Applies to the lasso tool and polygon tool. * * 3) If the underlying tool *takes modifier keys,* they will always be * forwarded to the underlying tool, and it is not possible to change the * selection method in the middle of a stroke. */ template class KisToolSelectBase : public BaseClass { public: KisToolSelectBase(KoCanvasBase* canvas, const QString toolName) : BaseClass(canvas) , m_widgetHelper(toolName) , m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); } KisToolSelectBase(KoCanvasBase* canvas, const QCursor cursor, const QString toolName) : BaseClass(canvas, cursor) , m_widgetHelper(toolName) , m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); } - KisToolSelectBase(KoCanvasBase* canvas, QCursor cursor, QString toolName, KisTool *delegateTool) + KisToolSelectBase(KoCanvasBase* canvas, QCursor cursor, QString toolName, KoToolBase *delegateTool) : BaseClass(canvas, cursor, delegateTool) , m_widgetHelper(toolName) , m_selectionActionAlternate(SELECTION_DEFAULT) { KisSelectionModifierMapper::instance(); } void updateActionShortcutToolTips() { KisSelectionOptions *widget = m_widgetHelper.optionWidget(); if (widget) { widget->updateActionButtonToolTip( SELECTION_REPLACE, this->action("selection_tool_mode_replace")->shortcut()); widget->updateActionButtonToolTip( SELECTION_ADD, this->action("selection_tool_mode_add")->shortcut()); widget->updateActionButtonToolTip( SELECTION_SUBTRACT, this->action("selection_tool_mode_subtract")->shortcut()); widget->updateActionButtonToolTip( SELECTION_INTERSECT, this->action("selection_tool_mode_intersect")->shortcut()); } } void activate(KoToolBase::ToolActivation activation, const QSet &shapes) { BaseClass::activate(activation, shapes); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_replace"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotReplaceModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_add"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotAddModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_subtract"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotSubtractModeRequested())); m_modeConnections.addUniqueConnection( this->action("selection_tool_mode_intersect"), SIGNAL(triggered()), &m_widgetHelper, SLOT(slotIntersectModeRequested())); updateActionShortcutToolTips(); } void deactivate() { BaseClass::deactivate(); m_modeConnections.clear(); } QWidget* createOptionWidget() { KisCanvas2* canvas = dynamic_cast(this->canvas()); Q_ASSERT(canvas); m_widgetHelper.createOptionWidget(canvas, this->toolId()); this->connect(this, SIGNAL(isActiveChanged(bool)), &m_widgetHelper, SLOT(slotToolActivatedChanged(bool))); this->connect(&m_widgetHelper, SIGNAL(selectionActionChanged(int)), this, SLOT(resetCursorStyle())); updateActionShortcutToolTips(); return m_widgetHelper.optionWidget(); } SelectionMode selectionMode() const { return m_widgetHelper.selectionMode(); } SelectionAction selectionAction() const { if (alternateSelectionAction() == SELECTION_DEFAULT) { return m_widgetHelper.selectionAction(); } return alternateSelectionAction(); } bool antiAliasSelection() const { return m_widgetHelper.antiAliasSelection(); } SelectionAction alternateSelectionAction() const { return m_selectionActionAlternate; } KisSelectionOptions* selectionOptionWidget() { return m_widgetHelper.optionWidget(); } virtual void setAlternateSelectionAction(SelectionAction action) { m_selectionActionAlternate = action; dbgKrita << "Changing to selection action" << m_selectionActionAlternate; } void activateAlternateAction(KisTool::AlternateAction action) { Q_UNUSED(action); BaseClass::activatePrimaryAction(); } void deactivateAlternateAction(KisTool::AlternateAction action) { Q_UNUSED(action); BaseClass::deactivatePrimaryAction(); } void beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); beginPrimaryAction(event); } void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); continuePrimaryAction(event); } void endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) { Q_UNUSED(action); endPrimaryAction(event); } KisNodeSP locateSelectionMaskUnderCursor(const QPointF &pos, Qt::KeyboardModifiers modifiers) { if (modifiers != Qt::NoModifier) return 0; KisCanvas2* canvas = dynamic_cast(this->canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(canvas, 0); KisSelectionSP selection = canvas->viewManager()->selection(); if (selection && selection->outlineCacheValid()) { const qreal handleRadius = qreal(this->handleRadius()) / canvas->coordinatesConverter()->effectiveZoom(); QPainterPath samplePath; samplePath.addEllipse(pos, handleRadius, handleRadius); const QPainterPath selectionPath = selection->outlineCache(); if (selectionPath.intersects(samplePath) && !selectionPath.contains(samplePath)) { KisNodeSP parent = selection->parentNode(); if (parent && parent->isEditable()) { return parent; } } } return 0; } void keyPressEvent(QKeyEvent *event) { if (this->mode() != KisTool::PAINT_MODE) { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); this->resetCursorStyle(); } BaseClass::keyPressEvent(event); } void keyReleaseEvent(QKeyEvent *event) { if (this->mode() != KisTool::PAINT_MODE) { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); this->resetCursorStyle(); } BaseClass::keyPressEvent(event); } void mouseMoveEvent(KoPointerEvent *event) { if (!this->hasUserInteractionRunning() && (m_moveStrokeId || this->mode() != KisTool::PAINT_MODE)) { const QPointF pos = this->convertToPixelCoord(event->point); KisNodeSP selectionMask = locateSelectionMaskUnderCursor(pos, event->modifiers()); if (selectionMask) { this->useCursor(KisCursor::moveSelectionCursor()); } else { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); this->resetCursorStyle(); } } BaseClass::mouseMoveEvent(event); } virtual void beginPrimaryAction(KoPointerEvent *event) { if (!this->hasUserInteractionRunning()) { const QPointF pos = this->convertToPixelCoord(event->point); KisCanvas2* canvas = dynamic_cast(this->canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN(canvas); KisNodeSP selectionMask = locateSelectionMaskUnderCursor(pos, event->modifiers()); if (selectionMask) { KisStrokeStrategy *strategy = new MoveStrokeStrategy({selectionMask}, this->image().data(), this->image().data()); m_moveStrokeId = this->image()->startStroke(strategy); m_dragStartPos = pos; return; } } keysAtStart = event->modifiers(); setAlternateSelectionAction(KisSelectionModifierMapper::map(keysAtStart)); if (alternateSelectionAction() != SELECTION_DEFAULT) { BaseClass::listenToModifiers(false); } BaseClass::beginPrimaryAction(event); } virtual void continuePrimaryAction(KoPointerEvent *event) { if (m_moveStrokeId) { const QPointF pos = this->convertToPixelCoord(event->point); const QPoint offset((pos - m_dragStartPos).toPoint()); this->image()->addJob(m_moveStrokeId, new MoveStrokeStrategy::Data(offset)); return; } //If modifier keys have changed, tell the base tool it can start capturing modifiers if ((keysAtStart != event->modifiers()) && !BaseClass::listeningToModifiers()) { BaseClass::listenToModifiers(true); } //Always defer to the base class if it signals it is capturing modifier keys if (!BaseClass::listeningToModifiers()) { setAlternateSelectionAction(KisSelectionModifierMapper::map(event->modifiers())); } BaseClass::continuePrimaryAction(event); } void endPrimaryAction(KoPointerEvent *event) { if (m_moveStrokeId) { this->image()->endStroke(m_moveStrokeId); m_moveStrokeId.clear(); return; } keysAtStart = Qt::NoModifier; //reset this with each action BaseClass::endPrimaryAction(event); } bool selectionDragInProgress() const { return m_moveStrokeId; } QMenu* popupActionsMenu() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(kisCanvas, 0); return KisSelectionToolHelper::getSelectionContextMenu(kisCanvas); } protected: using BaseClass::canvas; KisSelectionToolConfigWidgetHelper m_widgetHelper; SelectionAction m_selectionActionAlternate; private: Qt::KeyboardModifiers keysAtStart; QPointF m_dragStartPos; KisStrokeId m_moveStrokeId; KisSignalAutoConnectionsStore m_modeConnections; }; struct FakeBaseTool : KisTool { FakeBaseTool(KoCanvasBase* canvas) : KisTool(canvas, QCursor()) { } FakeBaseTool(KoCanvasBase* canvas, const QString &toolName) : KisTool(canvas, QCursor()) { Q_UNUSED(toolName); } FakeBaseTool(KoCanvasBase* canvas, const QCursor &cursor) : KisTool(canvas, cursor) { } bool hasUserInteractionRunning() const { return false; } }; typedef KisToolSelectBase KisToolSelect; #endif // KISTOOLSELECTBASE_H diff --git a/plugins/tools/selectiontools/kis_tool_select_path.cc b/plugins/tools/selectiontools/kis_tool_select_path.cc index c34eddf067..d00bb19605 100644 --- a/plugins/tools/selectiontools/kis_tool_select_path.cc +++ b/plugins/tools/selectiontools/kis_tool_select_path.cc @@ -1,192 +1,192 @@ /* * Copyright (c) 2007 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 "kis_tool_select_path.h" #include #include "kis_cursor.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_selection_options.h" #include "kis_canvas_resource_provider.h" #include "kis_canvas2.h" #include "kis_pixel_selection.h" #include "kis_selection_tool_helper.h" #include KisToolSelectPath::KisToolSelectPath(KoCanvasBase * canvas) : KisToolSelectBase(canvas, KisCursor::load("tool_polygonal_selection_cursor.png", 6, 6), i18n("Select path"), - (KisTool*) (new __KisToolSelectPathLocalTool(canvas, this))) + new __KisToolSelectPathLocalTool(canvas, this)) { } void KisToolSelectPath::requestStrokeEnd() { localTool()->endPathWithoutLastPoint(); } void KisToolSelectPath::requestStrokeCancellation() { localTool()->cancelPath(); } void KisToolSelectPath::mousePressEvent(KoPointerEvent* event) { if (!selectionEditable()) return; DelegatedSelectPathTool::mousePressEvent(event); } // Install an event filter to catch right-click events. // This code is duplicated in kis_tool_path.cc bool KisToolSelectPath::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) { QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::RightButton) { localTool()->removeLastPoint(); return true; } } else if (event->type() == QEvent::TabletPress) { QTabletEvent *tabletEvent = static_cast(event); if (tabletEvent->button() == Qt::RightButton) { localTool()->removeLastPoint(); return true; } } return false; } QList > KisToolSelectPath::createOptionWidgets() { QList > widgetsList = DelegatedSelectPathTool::createOptionWidgets(); QList > filteredWidgets; Q_FOREACH (QWidget* widget, widgetsList) { if (widget->objectName() != "Stroke widget") { filteredWidgets.push_back(widget); } } return filteredWidgets; } void KisDelegatedSelectPathWrapper::beginPrimaryAction(KoPointerEvent *event) { mousePressEvent(event); } void KisDelegatedSelectPathWrapper::continuePrimaryAction(KoPointerEvent *event){ mouseMoveEvent(event); } void KisDelegatedSelectPathWrapper::endPrimaryAction(KoPointerEvent *event) { mouseReleaseEvent(event); } bool KisDelegatedSelectPathWrapper::hasUserInteractionRunning() const { /** * KoCreatePathTool doesn't support moving interventions from KisToolselectBase, * because it doesn't use begin/continue/endPrimaryAction and uses direct event * handling instead. * * TODO: refactor KoCreatePathTool and port it to action infrastructure */ return true; } __KisToolSelectPathLocalTool::__KisToolSelectPathLocalTool(KoCanvasBase * canvas, KisToolSelectPath* parentTool) : KoCreatePathTool(canvas), m_selectionTool(parentTool) { setEnableClosePathShortcut(false); } void __KisToolSelectPathLocalTool::paintPath(KoPathShape &pathShape, QPainter &painter, const KoViewConverter &converter) { Q_UNUSED(converter); KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (!kisCanvas) return; QTransform matrix; matrix.scale(kisCanvas->image()->xRes(), kisCanvas->image()->yRes()); matrix.translate(pathShape.position().x(), pathShape.position().y()); m_selectionTool->paintToolOutline(&painter, m_selectionTool->pixelToView(matrix.map(pathShape.outline()))); } void __KisToolSelectPathLocalTool::addPathShape(KoPathShape* pathShape) { pathShape->normalize(); pathShape->close(); KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (!kisCanvas) return; KisImageWSP image = kisCanvas->image(); KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select by Bezier Curve")); const SelectionMode mode = helper.tryOverrideSelectionMode(kisCanvas->viewManager()->selection(), m_selectionTool->selectionMode(), m_selectionTool->selectionAction()); if (mode == PIXEL_SELECTION) { KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); KisPainter painter(tmpSel); painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace())); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setAntiAliasPolygonFill(m_selectionTool->antiAliasSelection()); painter.setStrokeStyle(KisPainter::StrokeStyleNone); QTransform matrix; matrix.scale(image->xRes(), image->yRes()); matrix.translate(pathShape->position().x(), pathShape->position().y()); QPainterPath path = matrix.map(pathShape->outline()); painter.fillPainterPath(path); tmpSel->setOutlineCache(path); helper.selectPixelSelection(tmpSel, m_selectionTool->selectionAction()); delete pathShape; } else { helper.addSelectionShape(pathShape, m_selectionTool->selectionAction()); } } void KisToolSelectPath::resetCursorStyle() { if (selectionAction() == SELECTION_ADD) { useCursor(KisCursor::load("tool_polygonal_selection_cursor_add.png", 6, 6)); } else if (selectionAction() == SELECTION_SUBTRACT) { useCursor(KisCursor::load("tool_polygonal_selection_cursor_sub.png", 6, 6)); } else { KisToolSelectBase::resetCursorStyle(); } } diff --git a/plugins/tools/selectiontools/kis_tool_select_path.h b/plugins/tools/selectiontools/kis_tool_select_path.h index 0c9a4424c7..bc21e6d9de 100644 --- a/plugins/tools/selectiontools/kis_tool_select_path.h +++ b/plugins/tools/selectiontools/kis_tool_select_path.h @@ -1,110 +1,111 @@ /* * Copyright (c) 2007 Sven Langkamp * Copyright (c) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU 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 KIS_TOOL_SELECT_PATH_H_ #define KIS_TOOL_SELECT_PATH_H_ #include #include #include "kis_tool_select_base.h" #include "kis_delegated_tool.h" #include class KoCanvasBase; class KisToolSelectPath; class __KisToolSelectPathLocalTool : public KoCreatePathTool { public: - __KisToolSelectPathLocalTool(KoCanvasBase * canvas, KisToolSelectPath* parentTool); + __KisToolSelectPathLocalTool(KoCanvasBase *canvas, KisToolSelectPath *parentTool); void paintPath(KoPathShape &path, QPainter &painter, const KoViewConverter &converter) override; void addPathShape(KoPathShape* pathShape) override; using KoCreatePathTool::createOptionWidgets; using KoCreatePathTool::endPathWithoutLastPoint; using KoCreatePathTool::endPath; using KoCreatePathTool::cancelPath; using KoCreatePathTool::removeLastPoint; private: KisToolSelectPath* const m_selectionTool; }; typedef KisDelegatedTool DelegatedSelectPathTool; struct KisDelegatedSelectPathWrapper : public DelegatedSelectPathTool { KisDelegatedSelectPathWrapper(KoCanvasBase *canvas, const QCursor &cursor, - KisTool* delegateTool) - : DelegatedSelectPathTool(canvas, cursor, (__KisToolSelectPathLocalTool*) delegateTool) + KoToolBase *delegateTool) + : DelegatedSelectPathTool(canvas, cursor, dynamic_cast<__KisToolSelectPathLocalTool*>(delegateTool)) { + Q_ASSERT(dynamic_cast<__KisToolSelectPathLocalTool*>(delegateTool)); } // If an event is explicitly forwarded only as an action (e.g. shift-click is captured by "change size") // we will receive a primary action but no mousePressEvent. Thus these events must be explicitly forwarded. void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; bool hasUserInteractionRunning() const; }; class KisToolSelectPath : public KisToolSelectBase { Q_OBJECT public: KisToolSelectPath(KoCanvasBase * canvas); void mousePressEvent(KoPointerEvent* event) override; bool eventFilter(QObject *obj, QEvent *event) override; void resetCursorStyle(); protected: void requestStrokeCancellation() override; void requestStrokeEnd() override; friend class __KisToolSelectPathLocalTool; QList > createOptionWidgets() override; }; class KisToolSelectPathFactory : public KisSelectionToolFactoryBase { public: KisToolSelectPathFactory() : KisSelectionToolFactoryBase("KisToolSelectPath") { setToolTip(i18n("Bezier Curve Selection Tool")); setSection(TOOL_TYPE_SELECTION); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("tool_path_selection")); setPriority(6); } ~KisToolSelectPathFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolSelectPath(canvas); } }; #endif // KIS_TOOL_SELECT_PATH_H_