File indexing completed on 2024-05-12 15:56:53

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2006-2007 Thomas Zander <zander@kde.org>
0003  * SPDX-FileCopyrightText: 2006-2011 Boudewijn Rempt <boud@valdyas.org>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 #include "KoToolProxy.h"
0008 #include "KoToolProxy_p.h"
0009 
0010 #include <QMimeData>
0011 #include <QUrl>
0012 #include <QTimer>
0013 #include <QApplication>
0014 #include <QTouchEvent>
0015 #include <QClipboard>
0016 
0017 #include <kundo2command.h>
0018 #include <KoProperties.h>
0019 
0020 #include <FlakeDebug.h>
0021 #include <klocalizedstring.h>
0022 
0023 #include "KoToolBase.h"
0024 #include "KoPointerEvent.h"
0025 #include "KoInputDevice.h"
0026 #include "KoToolManager_p.h"
0027 #include "KoToolSelection.h"
0028 #include "KoCanvasBase.h"
0029 #include "KoCanvasController.h"
0030 #include "KoShapeManager.h"
0031 #include "KoSelection.h"
0032 #include "KoShapeLayer.h"
0033 #include "KoShapeRegistry.h"
0034 #include "KoShapeController.h"
0035 #include "KoViewConverter.h"
0036 #include "KoShapeFactoryBase.h"
0037 
0038 
0039 KoToolProxyPrivate::KoToolProxyPrivate(KoToolProxy *p)
0040     : parent(p)
0041 {
0042     scrollTimer.setInterval(100);
0043 }
0044 
0045 void KoToolProxyPrivate::timeout() // Auto scroll the canvas
0046 {
0047     Q_ASSERT(controller);
0048 
0049     QPoint offset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY());
0050     QPoint origin = controller->canvas()->documentOrigin();
0051     QPoint viewPoint = widgetScrollPoint - origin - offset;
0052 
0053     QRectF mouseArea(viewPoint, QSizeF(10, 10));
0054     mouseArea.setTopLeft(mouseArea.center());
0055 
0056     controller->ensureVisible(mouseArea, true);
0057 
0058     QPoint newOffset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY());
0059 
0060     QPoint moved = offset - newOffset;
0061     if (moved.isNull())
0062         return;
0063 
0064     widgetScrollPoint += moved;
0065 
0066     QPointF documentPoint = parent->widgetToDocument(widgetScrollPoint);
0067     QMouseEvent event(QEvent::MouseMove, widgetScrollPoint, Qt::LeftButton, Qt::LeftButton, QFlags<Qt::KeyboardModifier>());
0068     KoPointerEvent ev(&event, documentPoint);
0069     activeTool->mouseMoveEvent(&ev);
0070 }
0071 
0072 void KoToolProxyPrivate::checkAutoScroll(const KoPointerEvent &event)
0073 {
0074     if (controller == 0) return;
0075     if (!activeTool) return;
0076     if (!activeTool->wantsAutoScroll()) return;
0077     if (!event.isAccepted()) return;
0078     if (!isToolPressed) return;
0079     if (event.buttons() != Qt::LeftButton) return;
0080 
0081 
0082     widgetScrollPoint = event.pos();
0083 
0084     if (! scrollTimer.isActive())
0085         scrollTimer.start();
0086 }
0087 
0088 void KoToolProxyPrivate::selectionChanged(bool newSelection)
0089 {
0090     if (hasSelection == newSelection)
0091         return;
0092     hasSelection = newSelection;
0093     emit parent->selectionChanged(hasSelection);
0094 }
0095 
0096 bool KoToolProxyPrivate::isActiveLayerEditable()
0097 {
0098     if (!activeTool)
0099         return false;
0100 
0101     KoShapeManager * shapeManager = activeTool->canvas()->shapeManager();
0102     KoShapeLayer * activeLayer = shapeManager->selection()->activeLayer();
0103     if (activeLayer && !activeLayer->isShapeEditable())
0104         return false;
0105     return true;
0106 }
0107 
0108 KoToolProxy::KoToolProxy(KoCanvasBase *canvas, QObject *parent)
0109     : QObject(parent),
0110       d(new KoToolProxyPrivate(this))
0111 {
0112     KoToolManager::instance()->priv()->registerToolProxy(this, canvas);
0113 
0114     connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(timeout()));
0115 }
0116 
0117 KoToolProxy::~KoToolProxy()
0118 {
0119     delete d;
0120 }
0121 
0122 void KoToolProxy::paint(QPainter &painter, const KoViewConverter &converter)
0123 {
0124     if (d->activeTool) d->activeTool->paint(painter, converter);
0125 }
0126 
0127 void KoToolProxy::repaintDecorations()
0128 {
0129     if (d->activeTool) d->activeTool->repaintDecorations();
0130 }
0131 
0132 
0133 QPointF KoToolProxy::widgetToDocument(const QPointF &widgetPoint) const
0134 {
0135     QPoint offset = QPoint(d->controller->canvasOffsetX(), d->controller->canvasOffsetY());
0136     QPoint origin = d->controller->canvas()->documentOrigin();
0137     QPointF viewPoint = widgetPoint.toPoint() - QPointF(origin - offset);
0138 
0139     return d->controller->canvas()->viewConverter()->viewToDocument(viewPoint);
0140 }
0141 
0142 KoCanvasBase* KoToolProxy::canvas() const
0143 {
0144     return d->controller->canvas();
0145 }
0146 
0147 void KoToolProxy::tabletEvent(QTabletEvent *event, const QPointF &point)
0148 {
0149     // We get these events exclusively from KisToolProxy - accept them
0150     event->accept();
0151 
0152     KoInputDevice id(event->device(), event->pointerType(), event->uniqueId());
0153     KoToolManager::instance()->priv()->switchInputDevice(id);
0154 
0155     KoPointerEvent ev(event, point);
0156     switch (event->type()) {
0157     case QEvent::TabletPress:
0158         if (d->activeTool)
0159             d->activeTool->mousePressEvent(&ev);
0160         break;
0161     case QEvent::TabletRelease:
0162         d->scrollTimer.stop();
0163         if (d->activeTool)
0164             d->activeTool->mouseReleaseEvent(&ev);
0165         break;
0166     case QEvent::TabletMove:
0167         if (d->activeTool)
0168             d->activeTool->mouseMoveEvent(&ev);
0169         d->checkAutoScroll(ev);
0170     default:
0171         ; // ignore the rest.
0172     }
0173 
0174     d->mouseLeaveWorkaround = true;
0175     d->lastPointerEvent = ev.deepCopyEvent();
0176 }
0177 
0178 void KoToolProxy::mousePressEvent(KoPointerEvent *ev)
0179 {
0180     d->mouseLeaveWorkaround = false;
0181     KoInputDevice id;
0182     KoToolManager::instance()->priv()->switchInputDevice(id);
0183     d->mouseDownPoint = ev->pos();
0184 
0185 
0186     // this tries to make sure another mouse press event doesn't happen
0187     // before a release event happens
0188     if (d->isToolPressed) {
0189         mouseReleaseEvent(ev);
0190         d->scrollTimer.stop();
0191 
0192         if (d->activeTool) {
0193             d->activeTool->mouseReleaseEvent(ev);
0194         }
0195 
0196         d->isToolPressed = false;
0197 
0198         return;
0199     }
0200 
0201     QPointF globalPoint = ev->globalPos();
0202     if (d->multiClickGlobalPoint != globalPoint) {
0203         if (qAbs(globalPoint.x() - d->multiClickGlobalPoint.x()) > 5||
0204                 qAbs(globalPoint.y() - d->multiClickGlobalPoint.y()) > 5) {
0205             d->multiClickCount = 0;
0206         }
0207         d->multiClickGlobalPoint = globalPoint;
0208     }
0209 
0210     if (d->multiClickCount && d->multiClickTimeStamp.elapsed() < QApplication::doubleClickInterval()) {
0211         // One more multiclick;
0212         d->multiClickCount++;
0213     } else {
0214         d->multiClickTimeStamp.start();
0215         d->multiClickCount = 1;
0216     }
0217 
0218     if (d->activeTool) {
0219         switch (d->multiClickCount) {
0220         case 0:
0221         case 1:
0222             d->activeTool->mousePressEvent(ev);
0223             break;
0224         case 2:
0225             d->activeTool->mouseDoubleClickEvent(ev);
0226             break;
0227         case 3:
0228         default:
0229             d->activeTool->mouseTripleClickEvent(ev);
0230             break;
0231         }
0232     } else {
0233         d->multiClickCount = 0;
0234         ev->ignore();
0235     }
0236 
0237     d->isToolPressed = true;
0238 }
0239 
0240 void KoToolProxy::mousePressEvent(QMouseEvent *event, const QPointF &point)
0241 {
0242     KoPointerEvent ev(event, point);
0243     mousePressEvent(&ev);
0244     d->lastPointerEvent = ev.deepCopyEvent();
0245 }
0246 
0247 void KoToolProxy::mouseDoubleClickEvent(QMouseEvent *event, const QPointF &point)
0248 {
0249     KoPointerEvent ev(event, point);
0250     mouseDoubleClickEvent(&ev);
0251     d->lastPointerEvent = ev.deepCopyEvent();
0252 }
0253 
0254 void KoToolProxy::mouseDoubleClickEvent(KoPointerEvent *event)
0255 {
0256     // let us handle it as any other mousepress (where we then detect multi clicks
0257     mousePressEvent(event);
0258 }
0259 
0260 void KoToolProxy::mouseMoveEvent(QMouseEvent *event, const QPointF &point)
0261 {
0262     KoPointerEvent ev(event, point);
0263     mouseMoveEvent(&ev);
0264     d->lastPointerEvent = ev.deepCopyEvent();
0265 }
0266 
0267 void KoToolProxy::mouseMoveEvent(KoPointerEvent *event)
0268 {
0269     if (d->mouseLeaveWorkaround) {
0270         d->mouseLeaveWorkaround = false;
0271         return;
0272     }
0273     KoInputDevice id;
0274     KoToolManager::instance()->priv()->switchInputDevice(id);
0275     if (d->activeTool == 0) {
0276         event->ignore();
0277         return;
0278     }
0279 
0280     d->activeTool->mouseMoveEvent(event);
0281 
0282     d->checkAutoScroll(*event);
0283 }
0284 
0285 void KoToolProxy::mouseReleaseEvent(QMouseEvent *event, const QPointF &point)
0286 {
0287     KoPointerEvent ev(event, point);
0288     mouseReleaseEvent(&ev);
0289     d->lastPointerEvent = ev.deepCopyEvent();
0290 }
0291 
0292 void KoToolProxy::mouseReleaseEvent(KoPointerEvent* event)
0293 {
0294     d->mouseLeaveWorkaround = false;
0295     KoInputDevice id;
0296     KoToolManager::instance()->priv()->switchInputDevice(id);
0297     d->scrollTimer.stop();
0298 
0299     if (d->activeTool) {
0300         d->activeTool->mouseReleaseEvent(event);
0301     } else {
0302         event->ignore();
0303     }
0304 
0305     d->isToolPressed = false;
0306 }
0307 
0308 void KoToolProxy::keyPressEvent(QKeyEvent *event)
0309 {
0310     if (d->activeTool)
0311         d->activeTool->keyPressEvent(event);
0312     else
0313         event->ignore();
0314 }
0315 
0316 void KoToolProxy::keyReleaseEvent(QKeyEvent *event)
0317 {
0318     if (d->activeTool)
0319         d->activeTool->keyReleaseEvent(event);
0320     else
0321         event->ignore();
0322 
0323     d->isToolPressed = false;
0324 }
0325 
0326 void KoToolProxy::explicitUserStrokeEndRequest()
0327 {
0328     if (d->activeTool) {
0329         d->activeTool->explicitUserStrokeEndRequest();
0330     }
0331 }
0332 
0333 QVariant KoToolProxy::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const
0334 {
0335     if (d->activeTool)
0336         return d->activeTool->inputMethodQuery(query, converter);
0337     return QVariant();
0338 }
0339 
0340 void KoToolProxy::inputMethodEvent(QInputMethodEvent *event)
0341 {
0342     if (d->activeTool) d->activeTool->inputMethodEvent(event);
0343 }
0344 
0345 QMenu *KoToolProxy::popupActionsMenu()
0346 {
0347     return d->activeTool ? d->activeTool->popupActionsMenu() : 0;
0348 }
0349 
0350 KisPopupWidgetInterface* KoToolProxy::popupWidget()
0351 {
0352     return d->activeTool ? d->activeTool->popupWidget() : nullptr;
0353 }
0354 
0355 void KoToolProxy::setActiveTool(KoToolBase *tool)
0356 {
0357     if (d->activeTool)
0358         disconnect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool)));
0359     d->activeTool = tool;
0360     if (tool) {
0361         connect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool)));
0362         d->selectionChanged(hasSelection());
0363         emit toolChanged(tool->toolId());
0364     }
0365 }
0366 
0367 void KoToolProxy::touchEvent(QTouchEvent* event, const QPointF& point)
0368 {
0369     // only one "touchpoint" events should be here
0370     KoPointerEvent ev(event, point);
0371 
0372     if (!d->activeTool) return;
0373 
0374     switch (event->touchPointStates())
0375     {
0376     case Qt::TouchPointPressed:
0377         d->activeTool->mousePressEvent(&ev);
0378         break;
0379     case Qt::TouchPointMoved:
0380         d->activeTool->mouseMoveEvent(&ev);
0381         break;
0382     case Qt::TouchPointReleased:
0383         d->activeTool->mouseReleaseEvent(&ev);
0384         break;
0385     default: // don't care
0386         ;
0387     }
0388 
0389     d->lastPointerEvent = ev.deepCopyEvent();
0390 }
0391 
0392 KoPointerEvent *KoToolProxy::lastDeliveredPointerEvent() const
0393 {
0394     return d->lastPointerEvent ? &(d->lastPointerEvent->event) : 0;
0395 }
0396 
0397 void KoToolProxyPrivate::setCanvasController(KoCanvasController *c)
0398 {
0399     controller = c;
0400 }
0401 
0402 bool KoToolProxy::hasSelection() const
0403 {
0404     return d->activeTool ? d->activeTool->hasSelection() : false;
0405 }
0406 
0407 void KoToolProxy::cut()
0408 {
0409     if (d->activeTool && d->isActiveLayerEditable())
0410         d->activeTool->cut();
0411 }
0412 
0413 void KoToolProxy::copy() const
0414 {
0415     if (d->activeTool)
0416         d->activeTool->copy();
0417 }
0418 
0419 bool KoToolProxy::paste()
0420 {
0421     bool success = false;
0422 
0423     if (d->activeTool && d->isActiveLayerEditable()) {
0424         success = d->activeTool->paste();
0425     }
0426 
0427     return success;
0428 }
0429 
0430 void KoToolProxy::dragMoveEvent(QDragMoveEvent *event, const QPointF &point)
0431 {
0432     if (d->activeTool)
0433         d->activeTool->dragMoveEvent(event, point);
0434 }
0435 
0436 void KoToolProxy::dragLeaveEvent(QDragLeaveEvent *event)
0437 {
0438     if (d->activeTool)
0439         d->activeTool->dragLeaveEvent(event);
0440 }
0441 
0442 void KoToolProxy::dropEvent(QDropEvent *event, const QPointF &point)
0443 {
0444     if (d->activeTool)
0445         d->activeTool->dropEvent(event, point);
0446 }
0447 
0448 void KoToolProxy::deleteSelection()
0449 {
0450     if (d->activeTool)
0451         d->activeTool->deleteSelection();
0452 }
0453 
0454 void KoToolProxy::processEvent(QEvent *e) const
0455 {
0456     if(e->type()==QEvent::ShortcutOverride
0457             && d->activeTool
0458             && d->activeTool->isInTextMode()
0459             && (static_cast<QKeyEvent*>(e)->modifiers()==Qt::NoModifier ||
0460                 static_cast<QKeyEvent*>(e)->modifiers()==Qt::ShiftModifier)) {
0461         e->accept();
0462     }
0463 }
0464 
0465 void KoToolProxy::requestUndoDuringStroke()
0466 {
0467     if (d->activeTool) {
0468         d->activeTool->requestUndoDuringStroke();
0469     }
0470 }
0471 
0472 void KoToolProxy::requestRedoDuringStroke()
0473 {
0474     if (d->activeTool) {
0475         d->activeTool->requestRedoDuringStroke();
0476     }
0477 }
0478 
0479 void KoToolProxy::requestStrokeCancellation()
0480 {
0481     if (d->activeTool) {
0482         d->activeTool->requestStrokeCancellation();
0483     }
0484 }
0485 
0486 void KoToolProxy::requestStrokeEnd()
0487 {
0488     if (d->activeTool) {
0489         d->activeTool->requestStrokeEnd();
0490     }
0491 }
0492 
0493 KoToolProxyPrivate *KoToolProxy::priv()
0494 {
0495     return d;
0496 }
0497 
0498 //have to include this because of Q_PRIVATE_SLOT
0499 #include "moc_KoToolProxy.cpp"