kis_input_manager.cpp 23.8 KB
Newer Older
1 2 3 4
/* This file is part of the KDE project
 *
 *  Copyright (C) 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
 *  Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 *  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_input_manager.h"

23
#include <kis_debug.h>
24
#include <QQueue>
25
#include <klocalizedstring.h>
26
#include <QApplication>
27
#include <QTouchEvent>
28
#include <QElapsedTimer>
29

30 31
#include <KoToolManager.h>

32
#include "kis_tool_proxy.h"
33

34
#include <kis_config.h>
35
#include <kis_canvas2.h>
36
#include <KisViewManager.h>
37 38
#include <kis_image.h>
#include <kis_canvas_resource_provider.h>
39
#include <kis_favorite_resource_manager.h>
40 41 42 43 44 45 46 47 48 49

#include "kis_abstract_input_action.h"
#include "kis_tool_invocation_action.h"
#include "kis_pan_action.h"
#include "kis_alternate_invocation_action.h"
#include "kis_rotate_canvas_action.h"
#include "kis_zoom_action.h"
#include "kis_show_palette_action.h"
#include "kis_change_primary_setting_action.h"

50 51
#include "kis_shortcut_matcher.h"
#include "kis_stroke_shortcut.h"
52
#include "kis_single_action_shortcut.h"
53
#include "kis_touch_shortcut.h"
54

55 56 57 58
#include "kis_input_profile.h"
#include "kis_input_profile_manager.h"
#include "kis_shortcut_configuration.h"

59
#include <input/kis_tablet_debugger.h>
60
#include <kis_signal_compressor.h>
61

62
#include "kis_extended_modifiers_mapper.h"
63
#include "kis_input_manager_p.h"
64

65 66 67 68
template <typename T>
uint qHash(QPointer<T> value) {
    return reinterpret_cast<quintptr>(value.data());
}
69

70 71
KisInputManager::KisInputManager(QObject *parent)
    : QObject(parent), d(new Private(this))
72
{
73 74
    d->setupActions();

75 76
    connect(KoToolManager::instance(), SIGNAL(aboutToChangeTool(KoCanvasController*)), SLOT(slotAboutToChangeTool()));
    connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), SLOT(slotToolChanged()));
77 78
    connect(&d->moveEventCompressor, SIGNAL(timeout()), SLOT(slotCompressedMoveEvent()));

79 80

    QApplication::instance()->
81
            installEventFilter(new Private::ProximityNotifier(d, this));
82 83 84 85 86 87 88
}

KisInputManager::~KisInputManager()
{
    delete d;
}

89 90
void KisInputManager::addTrackedCanvas(KisCanvas2 *canvas)
{
91
    d->canvasSwitcher.addCanvas(canvas);
92 93 94 95
}

void KisInputManager::removeTrackedCanvas(KisCanvas2 *canvas)
{
96
    d->canvasSwitcher.removeCanvas(canvas);
97 98
}

99 100
void KisInputManager::toggleTabletLogger()
{
101
    KisTabletDebugger::instance()->toggleDebugging();
102 103
}

104
void KisInputManager::attachPriorityEventFilter(QObject *filter, int priority)
105
{
106 107 108 109 110 111 112 113 114 115 116 117 118
    Private::PriorityList::iterator begin = d->priorityEventFilter.begin();
    Private::PriorityList::iterator it = begin;
    Private::PriorityList::iterator end = d->priorityEventFilter.end();

    it = std::find_if(begin, end,
                      [filter] (const Private::PriorityPair &a) { return a.second == filter; });

    if (it != end) return;

    it = std::find_if(begin, end,
                      [priority] (const Private::PriorityPair &a) { return a.first > priority; });

    d->priorityEventFilter.insert(it, qMakePair(priority, filter));
119
    d->priorityEventFilterSeqNo++;
120 121 122 123
}

void KisInputManager::detachPriorityEventFilter(QObject *filter)
{
124 125 126 127 128 129 130 131 132
    Private::PriorityList::iterator it = d->priorityEventFilter.begin();
    Private::PriorityList::iterator end = d->priorityEventFilter.end();

    it = std::find_if(it, end,
                      [filter] (const Private::PriorityPair &a) { return a.second == filter; });

    if (it != end) {
        d->priorityEventFilter.erase(it);
    }
133 134
}

135 136
void KisInputManager::setupAsEventFilter(QObject *receiver)
{
137 138 139
    if (d->eventsReceiver) {
        d->eventsReceiver->removeEventFilter(this);
    }
140 141

    d->eventsReceiver = receiver;
142 143 144 145

    if (d->eventsReceiver) {
        d->eventsReceiver->installEventFilter(this);
    }
146 147
}

148 149
void KisInputManager::stopIgnoringEvents()
{
150
    d->allowMouseEvents();
151 152
}

153 154 155 156
#if defined (__clang__)
#pragma GCC diagnostic ignored "-Wswitch"
#endif

157 158
bool KisInputManager::eventFilter(QObject* object, QEvent* event)
{
159
    if (object != d->eventsReceiver) return false;
160

161 162
    if (d->eventEater.eventFilter(object, event)) return false;

163
    if (!d->matcher.hasRunningShortcut()) {
164

165 166
        int savedPriorityEventFilterSeqNo = d->priorityEventFilterSeqNo;

167
        for (auto it = d->priorityEventFilter.begin(); it != d->priorityEventFilter.end(); /*noop*/) {
168 169
            const QPointer<QObject> &filter = it->second;

170
            if (filter.isNull()) {
171
                it = d->priorityEventFilter.erase(it);
172 173 174

                d->priorityEventFilterSeqNo++;
                savedPriorityEventFilterSeqNo++;
175 176 177 178
                continue;
            }

            if (filter->eventFilter(object, event)) return true;
179 180 181 182 183 184 185 186 187

            /**
             * If the filter removed itself from the filters list or
             * added something there, just exit the loop
             */
            if (d->priorityEventFilterSeqNo != savedPriorityEventFilterSeqNo) {
                return true;
            }

188
            ++it;
189
        }
190

191 192 193 194
        // KoToolProxy needs to pre-process some events to ensure the
        // global shortcuts (not the input manager's ones) are not
        // executed, in particular, this line will accept events when the
        // tool is in text editing, preventing shortcut triggering
195 196 197
        if (d->toolProxy) {
            d->toolProxy->processEvent(event);
        }
198 199
    }

200 201 202 203
    // Continue with the actual switch statement...
    return eventFilterImpl(event);
}

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
// Qt's events do not have copy-ctors yes, so we should emulate them
// See https://bugreports.qt.io/browse/QTBUG-72488

template <class Event> void copyEventHack(Event *src, QScopedPointer<QEvent> &dst);

template<> void copyEventHack(QMouseEvent *src, QScopedPointer<QEvent> &dst) {
    QMouseEvent *tmp = new QMouseEvent(src->type(),
                                       src->localPos(), src->windowPos(), src->screenPos(),
                                       src->button(), src->buttons(), src->modifiers(),
                                       src->source());
    tmp->setTimestamp(src->timestamp());
    dst.reset(tmp);
}

template<> void copyEventHack(QTabletEvent *src, QScopedPointer<QEvent> &dst) {
    QTabletEvent *tmp = new QTabletEvent(src->type(),
                                         src->posF(), src->globalPosF(),
                                         src->device(), src->pointerType(),
                                         src->pressure(),
                                         src->xTilt(), src->yTilt(),
                                         src->tangentialPressure(),
                                         src->rotation(),
                                         src->z(),
                                         src->modifiers(),
                                         src->uniqueId(),
                                         src->button(), src->buttons());
    tmp->setTimestamp(src->timestamp());
    dst.reset(tmp);
}



236 237 238 239 240 241 242 243 244
template <class Event>
bool KisInputManager::compressMoveEventCommon(Event *event)
{
    /**
     * We construct a copy of this event object, so we must ensure it
     * has a correct type.
     */
    static_assert(std::is_same<Event, QMouseEvent>::value ||
                  std::is_same<Event, QTabletEvent>::value,
Frederik Gladhorn's avatar
Frederik Gladhorn committed
245
                  "event should be a mouse or a tablet event");
246 247 248 249 250 251 252 253

    bool retval = false;

    /**
     * Compress the events if the tool doesn't need high resolution input
     */
    if ((event->type() == QEvent::MouseMove ||
         event->type() == QEvent::TabletMove) &&
254 255
            (!d->matcher.supportsHiResInputEvents() ||
             d->testingCompressBrushEvents)) {
256

257
        copyEventHack(event, d->compressedMoveEvent);
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
        d->moveEventCompressor.start();

        /**
         * On Linux Qt eats the rest of unneeded events if we
         * ignore the first of the chunk of tablet events. So
         * generally we should never activate this feature. Only
         * for testing purposes!
         */
        if (d->testingAcceptCompressedTabletEvents) {
            event->setAccepted(true);
        }

        retval = true;
    } else {
        slotCompressedMoveEvent();
        retval = d->handleCompressedTabletEvent(event);
    }

    return retval;
}

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
bool shouldResetWheelDelta(QEvent * event)
{
    return
        event->type() == QEvent::FocusIn ||
        event->type() == QEvent::FocusOut ||
        event->type() == QEvent::MouseButtonPress ||
        event->type() == QEvent::MouseButtonRelease ||
        event->type() == QEvent::MouseButtonDblClick ||
        event->type() == QEvent::TabletPress ||
        event->type() == QEvent::TabletRelease ||
        event->type() == QEvent::Enter ||
        event->type() == QEvent::Leave ||
        event->type() == QEvent::TouchBegin ||
        event->type() == QEvent::TouchEnd ||
        event->type() == QEvent::TouchCancel ||
        event->type() == QEvent::NativeGesture;

}

298 299 300 301
bool KisInputManager::eventFilterImpl(QEvent * event)
{
    bool retval = false;

302
    if (shouldResetWheelDelta(event)) {
303 304 305
        d->accumulatedScrollDelta = 0;
    }

306
    switch (event->type()) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
307 308
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonDblClick: {
309
        d->debugEvent<QMouseEvent, true>(event);
310
        if (d->touchHasBlockedPressEvents) break;
311

312
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
313

314
        if (d->tryHidePopupPalette()) {
315 316
            retval = true;
        } else {
317 318
            //Make sure the input actions know we are active.
            KisAbstractInputAction::setInputManager(this);
319
            retval = d->matcher.buttonPressed(mouseEvent->button(), mouseEvent);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
320
        }
321 322
        //Reset signal compressor to prevent processing events before press late
        d->resetCompressor();
323
        event->setAccepted(retval);
324 325 326
        break;
    }
    case QEvent::MouseButtonRelease: {
327
        d->debugEvent<QMouseEvent, true>(event);
328
        if (d->touchHasBlockedPressEvents) break;
329

330 331
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
        retval = d->matcher.buttonReleased(mouseEvent->button(), mouseEvent);
332
        event->setAccepted(retval);
333 334
        break;
    }
335
    case QEvent::ShortcutOverride: {
336
        d->debugEvent<QKeyEvent, false>(event);
337
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
338

339
        Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
340

341
        if (!keyEvent->isAutoRepeat()) {
342
            retval = d->matcher.keyPressed(key);
343 344
        } else {
            retval = d->matcher.autoRepeatedKeyPressed(key);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
345
        }
346 347 348 349 350 351 352

        /**
         * Workaround for temporary switching of tools by
         * KoCanvasControllerWidget. We don't need this switch because
         * we handle it ourselves.
         */
        retval |= !d->forwardAllEventsToTool &&
353 354
                (keyEvent->key() == Qt::Key_Space ||
                 keyEvent->key() == Qt::Key_Escape);
355 356 357 358

        break;
    }
    case QEvent::KeyRelease: {
359
        d->debugEvent<QKeyEvent, false>(event);
360
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
361

362
        if (!keyEvent->isAutoRepeat()) {
363
            Qt::Key key = KisExtendedModifiersMapper::workaroundShiftAltMetaHell(keyEvent);
364
            retval = d->matcher.keyReleased(key);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
365
        }
366 367 368
        break;
    }
    case QEvent::MouseMove: {
369
        d->debugEvent<QMouseEvent, true>(event);
370

371 372 373
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
        retval = compressMoveEventCommon(mouseEvent);

374 375 376
        break;
    }
    case QEvent::Wheel: {
377
        d->debugEvent<QWheelEvent, false>(event);
378
        QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
379

380
#ifdef Q_OS_MACOS
381 382 383 384 385 386 387 388 389 390 391 392 393
        // Some QT wheel events are actually touch pad pan events. From the QT docs:
        // "Wheel events are generated for both mouse wheels and trackpad scroll gestures."

        // We differentiate between touchpad events and real mouse wheels by inspecting the
        // event source.

        if (wheelEvent->source() == Qt::MouseEventSource::MouseEventSynthesizedBySystem) {
            KisAbstractInputAction::setInputManager(this);
            retval = d->matcher.wheelEvent(KisSingleActionShortcut::WheelTrackpad, wheelEvent);
            break;
        }
#endif

394
        d->accumulatedScrollDelta += wheelEvent->delta();
395 396
        KisSingleActionShortcut::WheelAction action;

397 398 399 400
        /**
         * Ignore delta 0 events on OSX, since they are triggered by tablet
         * proximity when using Wacom devices.
         */
401
#ifdef Q_OS_MACOS
402 403 404 405 406 407
        if(wheelEvent->delta() == 0) {
            retval = true;
            break;
        }
#endif

408
        if (wheelEvent->orientation() == Qt::Horizontal) {
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
            if(wheelEvent->delta() < 0) {
                action = KisSingleActionShortcut::WheelRight;
            }
            else {
                action = KisSingleActionShortcut::WheelLeft;
            }
        }
        else {
            if(wheelEvent->delta() > 0) {
                action = KisSingleActionShortcut::WheelUp;
            }
            else {
                action = KisSingleActionShortcut::WheelDown;
            }
        }
424

425 426 427 428 429 430 431 432 433
        if (qAbs(d->accumulatedScrollDelta) >= QWheelEvent::DefaultDeltasPerStep) {
            //Make sure the input actions know we are active.
            KisAbstractInputAction::setInputManager(this);
            retval = d->matcher.wheelEvent(action, wheelEvent);
            d->accumulatedScrollDelta = 0;
        }
        else {
            retval = true;
        }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
434
        break;
435
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
436
    case QEvent::Enter:
437
        d->debugEvent<QEvent, false>(event);
438 439
        //Make sure the input actions know we are active.
        KisAbstractInputAction::setInputManager(this);
440 441
        if (!d->containsPointer) {
            d->containsPointer = true;
442

443 444 445
            d->allowMouseEvents();
            d->touchHasBlockedPressEvents = false;
        }
446 447

        d->matcher.enterEvent();
448
        break;
449
    case QEvent::Leave:
450
        d->debugEvent<QEvent, false>(event);
451
        d->containsPointer = false;
452 453 454 455 456
        /**
         * We won't get a TabletProximityLeave event when the tablet
         * is hovering above some other widget, so restore cursor
         * events processing right now.
         */
457 458
        d->allowMouseEvents();
        d->touchHasBlockedPressEvents = false;
459 460

        d->matcher.leaveEvent();
461
        break;
462
    case QEvent::FocusIn:
463
        d->debugEvent<QEvent, false>(event);
464 465
        KisAbstractInputAction::setInputManager(this);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
466
        //Clear all state so we don't have half-matched shortcuts dangling around.
467
        d->matcher.reinitialize();
468

469 470
    { // Emulate pressing of the key that are already pressed
        KisExtendedModifiersMapper mapper;
471

472 473 474 475
        Qt::KeyboardModifiers modifiers = mapper.queryStandardModifiers();
        Q_FOREACH (Qt::Key key, mapper.queryExtendedModifiers()) {
            QKeyEvent kevent(QEvent::ShortcutOverride, key, modifiers);
            eventFilterImpl(&kevent);
476
        }
477
    }
478

479
        d->allowMouseEvents();
480
        break;
481 482 483 484 485 486

    case QEvent::FocusOut: {
        d->debugEvent<QEvent, false>(event);
        KisAbstractInputAction::setInputManager(this);

        QPointF currentLocalPos =
487
                canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
488 489 490 491 492

        d->matcher.lostFocusEvent(currentLocalPos);

        break;
    }
493
    case QEvent::TabletPress: {
494 495
        d->debugEvent<QTabletEvent, false>(event);
        QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
496 497 498 499 500 501
        if (d->tryHidePopupPalette()) {
            retval = true;
        } else {
            //Make sure the input actions know we are active.
            KisAbstractInputAction::setInputManager(this);
            retval = d->matcher.buttonPressed(tabletEvent->button(), tabletEvent);
502 503 504 505
            if (!d->containsPointer) {
                d->containsPointer = true;
                d->touchHasBlockedPressEvents = false;
            }
506
        }
507
        event->setAccepted(true);
508 509 510 511 512
        retval = true;
        d->blockMouseEvents();
        //Reset signal compressor to prevent processing events before press late
        d->resetCompressor();
        d->eatOneMousePress();
513 514 515 516 517 518 519

#if defined Q_OS_LINUX && !defined QT_HAS_ENTER_LEAVE_PATCH
        // remove this hack when this patch is integrated:
        // https://codereview.qt-project.org/#/c/255384/
        event->setAccepted(false);
#endif

520 521 522 523
        break;
    }
    case QEvent::TabletMove: {
        d->debugEvent<QTabletEvent, false>(event);
524

525 526
        QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
        retval = compressMoveEventCommon(tabletEvent);
527

528 529 530 531
        if (d->tabletLatencyTracker) {
            d->tabletLatencyTracker->push(tabletEvent->timestamp());
        }

532 533 534 535 536 537 538
        /**
         * The flow of tablet events means the tablet is in the
         * proximity area, so activate it even when the
         * TabletEnterProximity event was missed (may happen when
         * changing focus of the window with tablet in the proximity
         * area)
         */
539
        d->blockMouseEvents();
540

541 542 543 544 545 546
#if defined Q_OS_LINUX && !defined QT_HAS_ENTER_LEAVE_PATCH
        // remove this hack when this patch is integrated:
        // https://codereview.qt-project.org/#/c/255384/
        event->setAccepted(false);
#endif

547 548
        break;
    }
549 550 551 552
    case QEvent::TabletRelease: {
#ifdef Q_OS_MAC
        d->allowMouseEvents();
#endif
Dmitry Kazakov's avatar
Dmitry Kazakov committed
553
        d->debugEvent<QTabletEvent, false>(event);
554

555
        QTabletEvent *tabletEvent = static_cast<QTabletEvent*>(event);
556
        retval = d->matcher.buttonReleased(tabletEvent->button(), tabletEvent);
557
        retval = true;
558
        event->setAccepted(true);
559 560 561 562 563 564 565

#if defined Q_OS_LINUX && !defined QT_HAS_ENTER_LEAVE_PATCH
        // remove this hack when this patch is integrated:
        // https://codereview.qt-project.org/#/c/255384/
        event->setAccepted(false);
#endif

566 567
        break;
    }
568

569
    case QEvent::TouchBegin:
570
    {
571 572
        if (startTouch(retval)) {
            QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
573
            KisAbstractInputAction::setInputManager(this);
574
            retval = d->matcher.touchBeginEvent(tevent);
575 576
            event->accept();
        }
577
        break;
578 579 580
    }
    case QEvent::TouchUpdate:
    {
581
        QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
582
#ifdef Q_OS_MAC
583 584 585 586 587 588
        int count = 0;
        Q_FOREACH (const QTouchEvent::TouchPoint &point, tevent->touchPoints()) {
            if (point.state() != Qt::TouchPointReleased) {
                count++;
            }
        }
589

590
        if (count < 2 && tevent->touchPoints().length() > count) {
591
            d->touchHasBlockedPressEvents = false;
592 593 594
            retval = d->matcher.touchEndEvent(tevent);
        } else {
#endif
595
            d->touchHasBlockedPressEvents = KisConfig(true).disableTouchOnCanvas();
596 597
            KisAbstractInputAction::setInputManager(this);
            retval = d->matcher.touchUpdateEvent(tevent);
598
#ifdef Q_OS_MACOS
599 600
        }
#endif
601 602
        event->accept();
        break;
603
    }
604
    case QEvent::TouchEnd:
605
    {
606
        endTouch();
607 608
        QTouchEvent *tevent = static_cast<QTouchEvent*>(event);
        retval = d->matcher.touchEndEvent(tevent);
609 610
        event->accept();
        break;
611
    }
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

    case QEvent::NativeGesture:
    {
        QNativeGestureEvent *gevent = static_cast<QNativeGestureEvent*>(event);
        switch (gevent->gestureType()) {
            case Qt::BeginNativeGesture:
            {
                if (startTouch(retval)) {
                    KisAbstractInputAction::setInputManager(this);
                    retval = d->matcher.nativeGestureBeginEvent(gevent);
                    event->accept();
                }
                break;
            }
            case Qt::EndNativeGesture:
            {
                endTouch();
                retval = d->matcher.nativeGestureEndEvent(gevent);
                event->accept();
                break;
            }
            default:
            {
                KisAbstractInputAction::setInputManager(this);
                retval = d->matcher.nativeGestureEvent(gevent);
                event->accept();
                break;
            }
        }
        break;
    }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
644 645
    default:
        break;
646 647
    }

648
    return !retval ? d->processUnhandledEvent(event) : true;
649 650
}

651 652
bool KisInputManager::startTouch(bool &retval)
{
653
    d->touchHasBlockedPressEvents = KisConfig(true).disableTouchOnCanvas();
654
    // Touch rejection: if touch is disabled on canvas, no need to block mouse press events
655
    if (KisConfig(true).disableTouchOnCanvas()) {
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
        d->eatOneMousePress();
    }
    if (d->tryHidePopupPalette()) {
        retval = true;
        return false;
    } else {
        return true;
    }
}

void KisInputManager::endTouch()
{
    d->touchHasBlockedPressEvents = false;
}

671 672 673
void KisInputManager::slotCompressedMoveEvent()
{
    if (d->compressedMoveEvent) {
674
        // d->touchHasBlockedPressEvents = false;
675

676
        (void) d->handleCompressedTabletEvent(d->compressedMoveEvent.data());
677
        d->compressedMoveEvent.reset();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
678
        //dbgInput << "Compressed move event received.";
679
    } else {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
680
        //dbgInput << "Unexpected empty move event";
681 682 683
    }
}

684 685 686 687 688
KisCanvas2* KisInputManager::canvas() const
{
    return d->canvas;
}

689
QPointer<KisToolProxy> KisInputManager::toolProxy() const
690 691 692 693
{
    return d->toolProxy;
}

694 695
void KisInputManager::slotAboutToChangeTool()
{
696 697 698 699
    QPointF currentLocalPos;
    if (canvas() && canvas()->canvasWidget()) {
        currentLocalPos = canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
    }
700 701 702
    d->matcher.lostFocusEvent(currentLocalPos);
}

Sven Langkamp's avatar
Sven Langkamp committed
703 704
void KisInputManager::slotToolChanged()
{
705
    if (!d->canvas) return;
706 707
    KoToolManager *toolManager = KoToolManager::instance();
    KoToolBase *tool = toolManager->toolById(canvas(), toolManager->activeToolId());
708 709 710 711 712
    if (tool) {
        d->setMaskSyntheticEvents(tool->maskSyntheticEvents());
        if (tool->isInTextMode()) {
            d->forwardAllEventsToTool = true;
            d->matcher.suppressAllActions(true);
713 714 715
        } else {
            d->forwardAllEventsToTool = false;
            d->matcher.suppressAllActions(false);
716
        }
Sven Langkamp's avatar
Sven Langkamp committed
717 718 719
    }
}

720

721 722 723 724
void KisInputManager::profileChanged()
{
    d->matcher.clearShortcuts();

725 726
    KisInputProfile *profile = KisInputProfileManager::instance()->currentProfile();
    if (profile) {
727 728 729 730
        const QList<KisShortcutConfiguration*> shortcuts = profile->allShortcuts();

        for (KisShortcutConfiguration * const shortcut : shortcuts) {
            dbgUI << "Adding shortcut" << shortcut->keys() << "for action" << shortcut->action()->name();
731
            switch(shortcut->type()) {
732 733 734 735 736
            case KisShortcutConfiguration::KeyCombinationType:
                d->addKeyShortcut(shortcut->action(), shortcut->mode(), shortcut->keys());
                break;
            case KisShortcutConfiguration::MouseButtonType:
                d->addStrokeShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->buttons());
737
                break;
738 739 740
            case KisShortcutConfiguration::MouseWheelType:
                d->addWheelShortcut(shortcut->action(), shortcut->mode(), shortcut->keys(), shortcut->wheel());
                break;
741
            case KisShortcutConfiguration::GestureType:
742 743 744
                if (!d->addNativeGestureShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture())) {
                    d->addTouchShortcut(shortcut->action(), shortcut->mode(), shortcut->gesture());
                }
745
                break;
746 747
            default:
                break;
748
            }
749 750
        }
    }
751
    else {
752
        dbgInput << "No Input Profile Found: canvas interaction will be impossible";
753
    }
754
}