File indexing completed on 2024-04-21 04:58:24

0001 /* This file is part of the KDE libraries
0002     SPDX-FileCopyrightText: 2003 Stephan Binner <binner@kde.org>
0003     SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
0004     SPDX-FileCopyrightText: 2009 Urs Wolfer <uwolfer @ kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "ktabbar.h"
0010 
0011 #include <QTimer>
0012 #include <QApplication>
0013 #include <QCursor>
0014 #include <QMouseEvent>
0015 
0016 class Q_DECL_HIDDEN KTabBar::Private
0017 {
0018 public:
0019     Private()
0020         :
0021           mDragSwitchTab(-1),
0022           mActivateDragSwitchTabTimer(nullptr),
0023           mMiddleMouseTabMoveInProgress(false)
0024     {
0025     }
0026 
0027     QPoint mDragStart;
0028     int mDragSwitchTab;
0029     QTimer *mActivateDragSwitchTabTimer;
0030 
0031     bool mMiddleMouseTabMoveInProgress : 1;
0032 
0033 };
0034 
0035 KTabBar::KTabBar(QWidget *parent)
0036     : QTabBar(parent),
0037       d(new Private)
0038 {
0039     setAcceptDrops(true);
0040     setMouseTracking(true);
0041 
0042     d->mActivateDragSwitchTabTimer = new QTimer(this);
0043     d->mActivateDragSwitchTabTimer->setSingleShot(true);
0044     connect(d->mActivateDragSwitchTabTimer, SIGNAL(timeout()), SLOT(activateDragSwitchTab()));
0045 }
0046 
0047 KTabBar::~KTabBar()
0048 {
0049     delete d;
0050 }
0051 
0052 void KTabBar::mouseDoubleClickEvent(QMouseEvent *event)
0053 {
0054     if (event->button() != Qt::LeftButton) {
0055         return;
0056     }
0057 
0058     int tab = selectTab(event->pos());
0059 
0060     if (tab == -1) {
0061         emit newTabRequest();
0062     } else {
0063         emit tabDoubleClicked(tab);
0064     }
0065 
0066     QTabBar::mouseDoubleClickEvent(event);
0067 }
0068 
0069 void KTabBar::mousePressEvent(QMouseEvent *event)
0070 {
0071     if (event->button() == Qt::LeftButton) {
0072         d->mDragStart = event->pos();
0073     } else if (event->button() == Qt::RightButton) {
0074         int tab = selectTab(event->pos());
0075         if (tab != -1) {
0076             emit contextMenu(tab, mapToGlobal(event->pos()));
0077         } else {
0078             emit emptyAreaContextMenu(mapToGlobal(event->pos()));
0079         }
0080         return;
0081     }
0082 
0083     QTabBar::mousePressEvent(event);
0084 }
0085 
0086 void KTabBar::mouseMoveEvent(QMouseEvent *event)
0087 {
0088     if (event->buttons() == Qt::LeftButton && !isMovable()) {
0089         int tab = selectTab(event->pos());
0090         if (d->mDragSwitchTab && tab != d->mDragSwitchTab) {
0091             d->mActivateDragSwitchTabTimer->stop();
0092             d->mDragSwitchTab = 0;
0093         }
0094 
0095         int delay = QApplication::startDragDistance();
0096         QPoint newPos = event->pos();
0097         if (newPos.x() > d->mDragStart.x() + delay || newPos.x() < d->mDragStart.x() - delay ||
0098                 newPos.y() > d->mDragStart.y() + delay || newPos.y() < d->mDragStart.y() - delay) {
0099             if (tab != -1) {
0100                 emit initiateDrag(tab);
0101                 return;
0102             }
0103         }
0104     }
0105 
0106     QTabBar::mouseMoveEvent(event);
0107 }
0108 
0109 void KTabBar::activateDragSwitchTab()
0110 {
0111     int tab = selectTab(mapFromGlobal(QCursor::pos()));
0112     if (tab != -1 && d->mDragSwitchTab == tab) {
0113         setCurrentIndex(d->mDragSwitchTab);
0114     }
0115 
0116     d->mDragSwitchTab = 0;
0117 }
0118 
0119 void KTabBar::dragEnterEvent(QDragEnterEvent *event)
0120 {
0121     int tab = selectTab(event->pos());
0122     if (tab != -1) {
0123         bool accept = false;
0124         // The receivers of the testCanDecode() signal has to adjust
0125         // 'accept' accordingly.
0126         emit testCanDecode(event, accept);
0127         if (accept && tab != currentIndex()) {
0128             d->mDragSwitchTab = tab;
0129             d->mActivateDragSwitchTabTimer->start(QApplication::doubleClickInterval() * 2);
0130         }
0131 
0132         event->setAccepted(accept);
0133         return;
0134     }
0135 
0136     QTabBar::dragEnterEvent(event);
0137 }
0138 
0139 void KTabBar::dragMoveEvent(QDragMoveEvent *event)
0140 {
0141     int tab = selectTab(event->pos());
0142     if (tab != -1) {
0143         bool accept = false;
0144         // The receivers of the testCanDecode() signal has to adjust
0145         // 'accept' accordingly.
0146         emit testCanDecode(event, accept);
0147         if (accept && tab != currentIndex()) {
0148             d->mDragSwitchTab = tab;
0149             d->mActivateDragSwitchTabTimer->start(QApplication::doubleClickInterval() * 2);
0150         }
0151 
0152         event->setAccepted(accept);
0153         return;
0154     }
0155 
0156     QTabBar::dragMoveEvent(event);
0157 }
0158 
0159 void KTabBar::dropEvent(QDropEvent *event)
0160 {
0161     int tab = selectTab(event->pos());
0162     if (tab != -1) {
0163         d->mActivateDragSwitchTabTimer->stop();
0164         d->mDragSwitchTab = 0;
0165         emit receivedDropEvent(tab, event);
0166         return;
0167     }
0168 
0169     QTabBar::dropEvent(event);
0170 }
0171 
0172 #ifndef QT_NO_WHEELEVENT
0173 void KTabBar::wheelEvent(QWheelEvent *event)
0174 {
0175     if (event->angleDelta().y() != 0) {
0176         if (receivers(SIGNAL(wheelDelta(int)))) {
0177             emit(wheelDelta(event->angleDelta().y()));
0178             return;
0179         }
0180         int lastIndex = count() - 1;
0181         //Set an invalid index as base case
0182         int targetIndex = -1;
0183         bool forward = event->angleDelta().y() < 0;
0184         if (forward && lastIndex == currentIndex()) {
0185             targetIndex = 0;
0186         } else if (!forward && 0 == currentIndex()) {
0187             targetIndex = lastIndex;
0188         }
0189         //Will not move when targetIndex is invalid
0190         setCurrentIndex(targetIndex);
0191         //If it has not moved yet (targetIndex == -1), or if it moved but current tab is disabled
0192         if (targetIndex != currentIndex() || !isTabEnabled(targetIndex)) {
0193             QTabBar::wheelEvent(event);
0194         }
0195         event->accept();
0196     } else {
0197         event->ignore();
0198     }
0199 }
0200 #endif
0201 
0202 void KTabBar::tabLayoutChange()
0203 {
0204     d->mActivateDragSwitchTabTimer->stop();
0205     d->mDragSwitchTab = 0;
0206 }
0207 
0208 int KTabBar::selectTab(const QPoint &pos) const
0209 {
0210     const int tabCount = count();
0211     for (int i = 0; i < tabCount; ++i)
0212         if (tabRect(i).contains(pos)) {
0213             return i;
0214         }
0215 
0216     return -1;
0217 }
0218