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"